Soup's Blog

Back

ROS2_Moveit2_Ur5e_Grasp项目详解(八):start_grasp详解Blur image

前面已经介绍了仿真软件的启动,接下来开始介绍抓取的代码start_grasp.launch.py

第一步,先看generate_launch_description代码#

def generate_launch_description():
    ## 生成启动描述的主函数
    ## 该函数定义了所有需要声明的参数和启动设置
    
    ## 声明参数列表(当前为空,可根据需要添加参数)
    declared_arguments = []

    ## 返回LaunchDescription对象,包含所有声明的参数和启动设置函数
    return LaunchDescription(declared_arguments + [OpaqueFunction(function=launch_setup)])
python

这里没什么内容,跳过

第二步,看launch_setup函数代码#

这里调用了ur5e_gripper_control下面的launch目录下的demo.launch.py代码。该部分的代码如下:

  1. 首先加载了机器人逆运动学求解器的配置参数,这个参数文件是在通过使用Setup assistant生成的文件,可以参考博客

  2. 这里定义了机器人需要到达的一系列目标位姿,也就是将6个正方体移动的目标位置

  3. 上述两个文件是通过创建节点调用demo.cpp,从而传入到demo.cpp文件中,可以理解demo.launch.py是一个中介。

  4. 返回需要启动的节点列表和参数

接下来重点来了!

Node(
     package='ur5e_gripper_control',           ## 节点所属的功能包
      executable='demo',                        ## 可执行文件名
      name='demo_node',                         ## 节点名称
      parameters=[{                             ## 节点参数列表
              "use_sim_time": True,             ## 使用仿真时间
          },
          robot_description_kinematics,         ## 机器人运动学参数
          target_pose_list                      ## 目标位姿列表
      ], 
      output='screen'                           ## 输出方式:打印到屏幕
        )
python

这个配置指定了:

  • 功能包: ur5e_gripper_control
  • 可执行文件: demo
  • 节点名称: demo_node
  1. 构建配置:CMakeLists.txt 在ur5e_gripper_control包的CMakeLists.txt中,定义了demo可执行文件的构建规则:

    add_executable(demo src/demo.cpp src/ur5e_gripper.cpp)
    ament_target_dependencies(demo
      rclcpp moveit_ros_planning_interface tf2 
      moveit_core moveit_ros_planning control_msgs)
    python

    这表明demo可执行文件由demo.cpp和ur5e_gripper.cpp两个源文件编译而成

  2. 可执行文件安装 同样在CMakeLists.txt中,定义了demo可执行文件的安装路径

    install(TARGETS demo
      DESTINATION lib/${PROJECT_NAME}
    )
    python

    这表示demo可执行文件将被安装到lib/ur5e_gripper_control/目录下。

  3. 实际执行流程 a. ROS 2 launch系统加载并执行start_grasp.launch.py b. start_grasp.launch.py包含并执行demo.launch.py c. demo.launch.py创建并启动demo_node节点 d. ROS 2系统在功能包ur5e_gripper_control的lib目录中查找名为demo的可执行文件 e. 系统找到并执行demo可执行文件(由demo.cpp编译生成) f. demo.cpp中的main函数开始执行,控制机械臂完成抓取和放置任务

详细介绍demo.cpp文件,代码如下:

接下来进行解释代码:

  1. 初始化ROS2
rclcpp::init(argc, argv);
cpp
  • 作用:初始化 ROS 2 客户端库。
  • 所有 ROS 2 C++ 程序都必须先调用此函数。
  • 它会解析命令行参数(如 —ros-args),启动通信中间件(如 DDS)
  1. 设置节点选项
rclcpp::NodeOptions node_options;
node_options.automatically_declare_parameters_from_overrides(true);
cpp
  • 创建一个 NodeOptions 对象,用于配置节点行为。
  • automatically_declare_parameters_from_overrides(true):
    • 表示允许通过命令行直接传入参数(如 -p some_param:=value),而无需在代码中预先声明。
    • 提高灵活性,便于调试和配置。
  1. 创建机器人控制节点
auto node = std::make_shared<UR5eGripper>(node_options);
cpp
  • 使用智能指针创建一个 UR5eGripper 类的实例。
  • UR5eGripper 是一个继承自 rclcpp::Node 的类,封装了 UR5e 机械臂和夹爪的所有控制逻辑。
  • 传入 node_options 配置参数自动声明功能。
  1. 初始化机器人控制接口
node->init();
cpp
  • 调用自定义类的初始化函数
  1. 创建单线程执行器并运行
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
std::thread([&executor]() { executor.spin(); }).detach();
cpp
  • 让 ROS 2 节点开始持续处理回调函数(如订阅消息、服务请求、定时器等)
  1. 获取预设的放置位置列表
std::vector<std::vector<double>> target_pose_list;
node->get_target_pose_list(target_pose_list);
cpp
  • target_pose_list 是一个二维数组,存储多个“放置点”的位姿。
  • 每个位姿是一个长度为 6 的 double 数组:[x, y, z, roll, pitch, yaw]
  1. 设置坐标参考帧
std::string from_frame = "base_link";
cpp
  • base_link 是 UR5e 机器人的基座坐标系,是整个机器人系统的参考原点。
  • 所有其他坐标系(如 tool0, camera_link, cube1)都是相对于 base_link 定义的
  1. 定义要抓取的立方体名称列表
std::vector<std::string> to_frame_list = {"cube1", "cube2", "cube3", "cube4", "cube5", "cube6"};
cpp
  • 视觉系统已经检测出最多 6 个立方体,并通过 TF 发布了它们的坐标系:cube1, cube2, …, cube6
  • 这些坐标系是通过前面 Python 节点广播的(如 camera_link → cube1)
  1. 存储立方体抓取位置的列表
std::vector<std::vector<double>> cube_pose_list;
cpp
  • 用于保存所有立方体调整后的抓取位姿(包含位置和姿态)。
  • 后续会遍历这个列表,依次抓取每个立方体。
  1. 核心循环 1:获取并调整立方体位置
  • node->get_cube_pose(…)
    • 该函数内部使用 tf2_ros::Buffer 和 TransformListener 查询 from_frame 到 to_frame 的变换。
    • 例如:查询 base_link → cube1 的平移和旋转。
    • 返回一个 6 维向量:[x, y, z, roll, pitch, yaw]
  1. 核心循环 2:抓取与放置
for (size_t i = 0; i < std::min<size_t>(6, cube_pose_list.size()); i++) {
cpp
  • 最多处理 6 个立方体。
  • 使用 std::min 防止越界(比如只检测到 3 个立方体)。

步骤一:移动到抓取位置

bool grasp_success = node->plan_and_execute(cube_pose_list[i]);
cpp
  • plan_and_execute():
    • 使用 MoveIt 2 规划一条从当前位姿到目标位姿的无碰撞路径。
    • 执行该路径,控制机械臂运动。
    • 返回 true 表示成功,false 表示规划失败(如路径被阻挡)。

步骤二:闭合夹爪抓取

node->grasp(0.36);
rclcpp::sleep_for(std::chrono::seconds(1));
cpp
  • grasp(0.36):控制夹爪闭合到 0.36,刚好夹住立方体。
  • sleep_for(1s):等待夹爪完全闭合,确保夹紧。

步骤三:移动到放置位置

if (i < target_pose_list.size()) {
    bool place_success = node->plan_and_execute(target_pose_list[i]);
    if (place_success) {
        node->grasp(0);  // 打开夹爪
        rclcpp::sleep_for(std::chrono::seconds(1));
    }
}
cpp
  • 移动到第 i 个预设放置点。
  • 成功后打开夹爪(grasp(0)),释放立方体。
  • 等待 1 秒确保物体稳定落下。
  1. 任务完成:返回准备位置
node->go_to_ready_position();
cpp
  • 调用预设的“准备位姿”(Home Position),通常是安全抬高的位置。
  • 避免下次启动时发生碰撞。
  1. 结束程序
rclcpp::shutdown();
return 0;
cpp

整体流程

补充 target_pose_list.yaml内容如下时:

/**:
  ros__parameters:
    target_pose_list:
    ##   x, y, z, roll, pitch, yaw, grasp
      - "0.5, -0.52, 0.2, 0, 3.14, 0.0" ## 2
      - "0.5, -0.45, 0.2, 0, 3.14, 0.0" ## 1
      - "0.5, -0.38, 0.28, 0, 3.14, 0.0" ## 4
      - "0.5, 0.38, 0.2, 0, 3.14, 0.0" ## 3
      - "0.5, 0.45, 0.28, 0, 3.14, 0.0" ## 5
      - "0.5, 0.52, 0.35, 0, 3.14, 0.0" ## 6
yaml

执行的效果如下: 在这里插入图片描述 浅浅将x的0.5都改为0.3,执行的效果如下: 在这里插入图片描述 效果不太好,掉了一个~

抓取期间生成的日志内容如下,ROS2日志的解析参考博客在这里插入图片描述 举个例子: 在这里插入图片描述

ROS2_Moveit2_Ur5e_Grasp项目详解(八):start_grasp详解
http://www.soupcola.top/blog/ros2_blogs/ros2_blogs-8
Author Soup Cola
Published at 2026年2月4日