Moveit2使用说明(C++)
Moveit安装:https://moveit.ai/如果是Ubuntu22.04 ros2-humble 系统可以参考:Ubuntu22.04 ros2-humble 源码安装 Moveit2
关于Moveit入门的官方文档:https://moveit.picknik.ai/main/index.html
PS:本文基于官方文档内容,记录学习的过程,同时有一些错误做了修改。
https://github.com/moveit/moveit2_tutorials/issues/1006
由于官方声明humble版本中没有Python API,所以本文是基于C++。
目录
- 0.MoveIt2的核心架构
- 1.Tutorials——Getting Started
- 2.Tutorials——MoveIt Quickstart in RViz
- 3.Tutorials——Your First C++ MoveIt Project
- 4.Tutorials——Visualizing In RViz
- 5.Tutorials——Planning Around Objects
- 6.Tutorials——Pick and Place with MoveIt Task Constructor
0.MoveIt2的核心架构
Planning Scene(规划场景):相当于城市地图,记录所有道路和障碍物信息MoveGroup Interface(运动组接口):类似交通指挥中心,接收指令并协调各个组件Trajectory Execution(轨迹执行):如同交通信号灯和车辆调度,确保运动按计划进行
1.Tutorials——Getting Started
https://moveit.picknik.ai/main/doc/tutorials/getting_started/getting_started.html
- 关于moveit的安装我没参考官网的方式,而是用的源码编译安装的,具体的可以参考:
https://blog.ZEEKLOG.net/qq_45445740/article/details/154125527?spm=1001.2014.3001.5501
官网最后这里说了需要设置你的 Colcon 工作区
因为我是通过源码编译安装的,所有的包都在install里面,所以如果需要随时使用moveit,可以考虑将下面的工作空间添加到.bashrc中,这里我怕污染环境就先没设置。

2.Tutorials——MoveIt Quickstart in RViz
https://moveit.picknik.ai/main/doc/tutorials/quickstart_in_rviz/quickstart_in_rviz_tutorial.html
MoveIt 在 RViz 中的快速入门指南:本教程将教如何使用 RViz 和 MoveIt 显示插件在 MoveIt 中创建运动规划。RViz 是 ROS 中的主要可视化工具,也是机器人调试的实用工具。MoveIt 显示插件支持你搭建虚拟环境(规划场景)、交互式设置机器人的起始状态和目标状态、测试各类运动规划器并可视化输出结果。
步骤 1:启动演示并配置插件
启动演示程序:
source install/setup.bash ros2 launch moveit2_tutorials demo.launch.py 若为首次操作,你会看到 RViz 中显示一个空世界,需手动添加运动规划插件:
- 1.在 RViz 的 Displays 选项卡中,点击 Add。
- 2.在 moveit_ros_visualization 文件夹中,选择 “Motion Planning” 作为显示类型,点击 OK。
- 3.此时你应能在 RViz 中看到 Kinova 机器人。
加载运动规划插件后,进行如下配置:
- 在 Displays 子窗口的 Global Options 标签页中,将 Fixed Frame 字段设为
/base_link。 - 在 Displays 列表中点击 Motion Planning,进入机器人专属配置:
- 确保 Robot Description 字段设为 robot_description。
- 确保 Planning Scene Topic 字段设为 /monitored_planning_scene(点击话题名称可展开下拉菜单选择)。
- 确保 Planned Path 下的 Trajectory Topic 设为 /display_planned_path。
- 在 Planning Request 中,将 Planning Group 改为 manipulator(也可在左下角的 Motion Planning 面板中设置)。
实际因为本地加载的机器人(当前是 panda 熊猫机器人)和官方文档中的不是同一个机器人Kinova Gen 3 机器人,所以有些参数不一样。
1.将 Fixed Frame 字段设为/panda_link0
2.在 Planning Request 中,将 Planning Group 改为panda_arm




步骤 2:体验机器人可视化效果
RViz 中包含四种重叠的可视化效果:
- 规划场景(/monitored_planning_scene)中机器人的配置(默认启用)。
- 机器人的规划路径(默认启用)。
- 绿色:运动规划的起始状态(默认禁用)。
橙色:运动规划的目标状态(默认启用)。

可通过复选框切换各可视化效果的显示 / 隐藏:
- 目标状态:勾选 Planning Request 树形菜单中的 Query Goal State。
尝试操作这些复选框,体验不同可视化效果的切换。
起始状态:勾选 Planning Request 树形菜单中的 Query Start State。

规划路径:勾选 Planned Path 树形菜单中的 Show Robot Visual。

场景机器人:勾选 Scene Robot 树形菜单中的 Show Robot Visual。

步骤 3:与 Kinova Gen 3 机器人交互
实际本地加载的是 panda 熊猫机器人
后续步骤需仅显示场景机器人、起始状态和目标状态:
- 1.勾选 Planned Path 树形菜单中的 Show Robot Visual。
- 2.取消勾选 Scene Robot 树形菜单中的 Show Robot Visual。
- 3.勾选 Planning Request 树形菜单中的 Query Goal State。
- 4.勾选 Planning Request 树形菜单中的 Query Start State。
此时会出现两个交互式标记(Interactive Markers):橙色机械臂对应的标记用于设置运动规划的 “目标状态”,绿色机械臂对应的标记用于设置 “起始状态”。若未看到交互式标记,点击 RViz 顶部菜单中的 Interact(注:部分工具可能隐藏,可点击顶部菜单中的 “+” 添加 Interact 工具)。
现在你可以通过这些标记拖动机械臂,调整其姿态,动手尝试吧!
移动至碰撞状态
本小节需隐藏规划路径和目标状态:
- 1.取消勾选 Planned Path 树形菜单中的 Show Robot Visual。
- 2.取消勾选 Planning Request 树形菜单中的 Query Goal State。
此时仅显示起始状态(绿色机械臂)。尝试将机械臂调整到两连杆(links)相互碰撞的姿态(若操作困难,确保 Motion Planning 插件 Planning 标签页下的 Use Collision-Aware IK 复选框已取消勾选)。碰撞发生后,相撞的连杆会变为红色。

勾选 Use Collision-Aware IK 复选框后再次尝试:勾选状态下,IK 求解器会持续为期望的末端执行器(end-effector)姿态寻找无碰撞解;未勾选时,求解器允许解中存在碰撞。无论复选框状态如何,相撞的连杆始终会以红色可视化显示。
移动至工作空间可达范围外
注意观察当末端执行器(end-effector)被移至可达工作空间(reachable workspace)外时的现象。
进入下一小节前,重新启用规划路径和目标状态:
- 1.勾选 Planned Path 树形菜单中的 Show Robot Visual。
- 2.勾选 Planning Request 树形菜单中的 Query Goal State。
关节运动或零空间运动
你可以通过 Joints 选项卡移动单个关节,或 7 自由度(7-DOF)机器人的冗余关节(redundant joints)。尝试拖动如下动画中的 “Null Space Exploration” 滑块,观察末端执行器保持静止时关节的运动情况。
步骤 4:使用 Kinova Gen 3 进行运动规划
现在可以通过 MoveIt RViz 插件为 Kinova Gen 3 机器人进行运动规划:
- 1.将起始状态移动到期望位置。
- 2.将目标状态移动到另一个期望位置。
- 3.确保两个状态均未与机器人自身发生碰撞。
- 5.在 Motion Planning 窗口的 Planning 标签页下,点击 Plan 按钮。
6.勾选 Planned Path 树形菜单中的 Show Trail,你会看到机械臂的路径以一系列机械臂姿态(manipulator poses)呈现。

4.取消勾选 Planned Path 树形菜单中的 Show Trail。

查看轨迹路径点
你可以在 RViz 中逐点可视化查看轨迹(trajectories):
- 2.设置目标姿态(goal pose),然后运行
Plan。
3.操作滑块面板,例如拖动滑块、点击 “Play” 按钮。
注:将末端执行器移动到新目标后,务必先运行 Plan 再点击 Play—— 否则会显示上一个目标的路径点(若存在)。

1.从 Panels 菜单中,选择 Trajectory - Trajectory Slider,RViz 会新增一个滑块面板。

规划笛卡尔运动
若勾选 Use Cartesian Path 复选框,机器人会尝试让末端执行器在笛卡尔空间(Cartesian space)中线性移动。

勾选 Use Cartesian Path 复选框(直线运动)

不勾选 Use Cartesian Path 复选框(关节运动)

执行轨迹与调整速度
规划成功后,点击 “Plan & Execute” 或 “Execute” 会将轨迹发送给机器人 —— 本教程中,由于使用的是 kinova_demo.launch,机器人仅为仿真模式。
初始状态下,速度(velocity)和加速度(acceleration)的默认缩放比例为机器人最大值的 10%(0.1)。你可以在下方的 Planning 标签页中修改这些缩放系数,或在机器人的 moveit_config 配置文件(joint_limits.yaml 中)修改默认值。

后续步骤
RViz 可视化工具
许多教程会使用 moveit_visual_tools 逐步演示。继续学习下一个教程前,建议启用 RViz Visual Tools GUI:
从 Panels 菜单中,选择 Add New Panels。
在菜单中选择 RViz Visual Tools GUI,点击 OK,新面板会添加到 RViz 中。

保存配置
RViz 支持通过 File -> Save Config 保存配置,建议在继续下一个教程前执行此操作。若需自定义配置文件名,可通过 File -> Save Config As 保存,之后通过以下命令调用该配置文件:
ros2 launch moveit2_tutorials demo.launch.py rviz_config:=your_rviz_config.rviz 将 your_rviz_config.rviz 替换为你保存的文件名,并存放到 moveit2_tutorials/doc/tutorials/quickstart_in_rviz/launch/ 目录下,然后构建工作空间(workspace)使其可被识别。

3.Tutorials——Your First C++ MoveIt Project
https://moveit.picknik.ai/main/doc/tutorials/your_first_project/your_first_project.html
3.1 创建功能包(Package)
打开终端,配置 ROS2 环境,确保 ros2 命令可正常使用。导航至 “入门指南” 中创建的 ws_moveit 目录。进入 src 目录(源代码存放位置)。通过 ROS 2 命令行工具创建新功能包:
ros2 pkg create \ --build-type ament_cmake \ --dependencies moveit_ros_planning_interface rclcpp \ --node-name hello_moveit_node hello_moveit 命令执行后,会在新目录中生成相关文件。
注:我们添加了 moveit_ros_planning_interface 和 rclcpp 作为依赖项。这会自动在 package.xml 和 CMakeLists.txt 文件中添加必要配置,使功能包可依赖这两个包。
用你常用的编辑器打开新生成的源代码文件:ws_moveit/src/hello_moveit/src/hello_moveit.cpp。
3.2 创建 ROS 节点(Node)和执行器(Executor)
这里的代码和官方给的源代码中有几处不同:
1.move_group_interface.h,而不是move_group_interface.hpp
2.机器人已经变成了panda,所以规划组要改:panda_arm
CMakeLists.txt
# 1. 声明 C++ 标准(MoveIt 要求 C++17 及以上) cmake_minimum_required(VERSION 3.8) project(hello_moveit) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() # 2. 查找依赖包 find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(moveit_ros_planning_interface REQUIRED) find_package(geometry_msgs REQUIRED) # 用到 Pose 消息时需要 # 3. 编译节点(替换为你的节点名) add_executable(hello_moveit_node src/hello_moveit_node.cpp) ament_target_dependencies( hello_moveit_node rclcpp moveit_ros_planning_interface geometry_msgs ) # 4. 安装节点 install(TARGETS hello_moveit_node DESTINATION lib/${PROJECT_NAME} ) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() endif() ament_package() hello_moveit_node.cpp
#include<memory>#include<rclcpp/rclcpp.hpp>#include<moveit/move_group_interface/move_group_interface.h>intmain(int argc,char* argv[]){// 初始化 ROS 并创建节点 rclcpp::init(argc, argv);autoconst node = std::make_shared<rclcpp::Node>("hello_moveit", rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true));// 创建 ROS 日志器autoconst logger = rclcpp::get_logger("hello_moveit !");// 创建 MoveIt MoveGroup 接口using moveit::planning_interface::MoveGroupInterface;auto move_group_interface =MoveGroupInterface(node,"panda_arm");// 设置目标姿态(Target Pose)autoconst target_pose =[]{ geometry_msgs::msg::Pose msg; msg.orientation.w =1.0; msg.position.x =0.28; msg.position.y =-0.2; msg.position.z =0.5;return msg;}(); move_group_interface.setPoseTarget(target_pose);// 规划到目标姿态的路径autoconst[success, plan]=[&move_group_interface]{ moveit::planning_interface::MoveGroupInterface::Plan msg;autoconst ok =static_cast<bool>(move_group_interface.plan(msg));return std::make_pair(ok, msg);}();// 执行规划路径if(success){ move_group_interface.execute(plan);}else{RCLCPP_ERROR(logger,"规划失败!");}// 关闭 ROS rclcpp::shutdown();return0;}代码解析


3.4 构建与运行
与之前相同,先构建代码(在 ws_moveit 目录下):
colcon build 构建成功后,在新终端配置环境并启动上一教程中的演示启动文件(用于启动 RViz 和 MoveGroup 节点):
# 加载你之前保存的rviz文件,如果没保存会打开后显示默认的 ros2 launch moveit2_tutorials demo.launch.py rviz_config:=your_rviz_config.rviz 在 RViz 的 Displays 窗口中,展开 MotionPlanning/Planning Request,取消勾选 Query Goal State。
再打开一个终端,配置环境并运行程序:
ros2 run hello_moveit hello_moveit_node 此时 RViz 中的机器人会移动,最终到达目标姿态(效果如下)。


PS:若未先启动演示文件就运行 hello_moveit 节点,程序会等待 10 秒后输出以下错误并退出:
[ERROR] [1644181704.350825487] [hello_moveit]: Could not find
parameter robot_description and did not receive robot_description via
std_msgs::msg::String subscription within 10.000000 seconds. 原因是
demo.launch.py 会启动提供机器人描述(robot description)的 MoveGroup
节点。MoveGroupInterface 构建时会查找发布机器人描述话题的节点,若 10 秒内未找到,就会输出错误并终止程序。
代码解析

4.Tutorials——Visualizing In RViz
https://moveit.picknik.ai/main/doc/tutorials/visualizing_in_rviz/visualizing_in_rviz.html
4.1 添加 moveit_visual_tools 依赖
- 在 hello_moveit 项目的
package.xml文件中,在其他<depend>语句之后添加以下内容:
<depend>moveit_visual_tools</depend>- 然后在CMakeLists.txt文件的
find_package语句部分添加以下内容:
find_package(moveit_visual_tools REQUIRED)- 在该文件的后续部分,扩展ament_target_dependencies宏调用,将新的依赖项包含其中,如下所示:
ament_target_dependencies( hello_moveit "moveit_ros_planning_interface""moveit_visual_tools""rclcpp")- 为验证依赖项添加是否正确,请在源文件hello_moveit.cpp中添加所需的头文件引用:
#include <moveit_visual_tools/moveit_visual_tools.h> - 要测试上述配置是否生效,可以回到初始的moveit2_ws空间,单独编译
hello_moveit试下有没有问题
cd /moveit_ws colcon build --packages-select hello_moveit 4.2 创建 ROS 执行器并在线程中运行节点
- 在初始化 MoveItVisualTools 之前,我们需要让 ROS 节点上的执行器保持运行状态。这是因为 MoveItVisualTools 与 ROS 服务和话题的交互方式决定了这一步是必需的。首先,在文件顶部的头文件引用部分添加线程库:
#include<thread>// <---- 添加到顶部的头文件引用集合中- 通过创建并命名日志记录器,我们可以让程序日志更有条理:
// 创建ROS日志记录器autoconst logger = rclcpp::get_logger("hello_moveit");- 接下来,在创建 MoveIt 的 MoveGroup 接口之前添加执行器相关代码:
// 启动单线程执行器,使MoveItVisualTools能够与ROS进行交互 rclcpp::executors::SingleThreadedExecutor executor; executor.add_node(node);auto spinner = std::thread([&executor](){ executor.spin();});// 创建MoveIt的MoveGroup接口- 最后,确保在程序退出前合并线程:
// 关闭ROS rclcpp::shutdown();// <--- 这将使线程中的spin函数返回 spinner.join();// <--- 退出前合并线程return0;完成上述修改后,重新构建工作空间,确保没有语法错误。
4.3 创建并初始化 MoveItVisualTools
- 在构建完
MoveGroupInterface之后,我们将构建并初始化MoveItVisualTools:
注意这里因为默认是panda机器人,所以将manipulator改为panda_arm
// 创建MoveIt的MoveGroup接口using moveit::planning_interface::MoveGroupInterface;auto move_group_interface =MoveGroupInterface(node,"panda_arm");// 构建并初始化MoveItVisualToolsauto moveit_visual_tools = moveit_visual_tools::MoveItVisualTools{ node,"base_link", rviz_visual_tools::RVIZ_MARKER_TOPIC, move_group_interface.getRobotModel()}; moveit_visual_tools.deleteAllMarkers(); moveit_visual_tools.loadRemoteControl();我们向构造函数传入以下参数:ROS 节点、机器人的基础连杆(base link)、要使用的标记话题(后续会详细说明)以及机器人模型(通过 move_group_interface 获取)。接下来,调用删除所有标记的方法,这会清除 RViz 中之前运行留下的任何渲染状态。最后,加载远程控制功能。远程控制是一个非常简单的插件,它能让我们在 RViz 中通过一个按钮与程序进行交互。
4.4 编写可视化相关的闭包
- 构建并初始化完成后,我们创建一些闭包(能够访问当前作用域中变量的函数对象),后续可在程序中使用这些闭包在 RViz 中渲染可视化效果:
注意这里因为默认是panda机器人,所以将manipulator改为panda_arm
// 创建可视化相关的闭包autoconst draw_title =[&moveit_visual_tools](auto text){autoconst text_pose =[]{auto msg = Eigen::Isometry3d::Identity(); msg.translation().z()=1.0;// 将文本放置在基础连杆上方1米处return msg;}(); moveit_visual_tools.publishText(text_pose, text, rviz_visual_tools::WHITE, rviz_visual_tools::XLARGE);};autoconst prompt =[&moveit_visual_tools](auto text){ moveit_visual_tools.prompt(text);};autoconst draw_trajectory_tool_path =[&moveit_visual_tools, jmg = move_group_interface.getRobotModel()->getJointModelGroup("panda_arm")](autoconst trajectory){ moveit_visual_tools.publishTrajectoryLine(trajectory, jmg);};- 这三个闭包均通过引用捕获moveit_visual_tools,最后一个闭包还捕获了我们用于规划的关节模型组对象的指针。每个闭包都会调用moveit_visual_tools上的一个函数,以改变 RViz 中的显示内容。
第一个闭包draw_title会在机器人基础上方 1 米处添加文本,这是一种从宏观层面展示程序状态的有效方式。
第二个闭包调用prompt函数,该函数会阻塞程序,直到用户在 RViz 中按下 “下一步” 按钮,这在调试时逐步执行程序非常有帮助。
最后一个闭包会绘制规划轨迹的工具路径,通常有助于从工具的角度理解规划的轨迹。
你可能会疑惑为什么要创建这样的 lambda 表达式,原因很简单,就是为了让后续的代码更易于阅读和理解。在编写软件时,将功能拆分为命名函数通常很有帮助,这些函数可以方便地重复使用并单独进行测试。在下一部分中,你将看到如何使用这些创建好的函数。
4.5 可视化程序的执行步骤
- 现在,我们来完善程序中间部分的代码。更新规划和执行相关的代码,加入这些新功能:
// 设置目标姿态autoconst target_pose =[]{ geometry_msgs::msg::Pose msg; msg.orientation.w =1.0; msg.position.x =0.28; msg.position.y =-0.2; msg.position.z =0.5;return msg;}(); move_group_interface.setPoseTarget(target_pose);// 创建到达该目标姿态的规划prompt("在RvizVisualToolsGui窗口中按下'下一步'开始规划");draw_title("规划中"); moveit_visual_tools.trigger();autoconst[success, plan]=[&move_group_interface]{ moveit::planning_interface::MoveGroupInterface::Plan msg;autoconst ok =static_cast<bool>(move_group_interface.plan(msg));return std::make_pair(ok, msg);}();// 执行规划if(success){draw_trajectory_tool_path(plan.trajectory_); moveit_visual_tools.trigger();prompt("在RvizVisualToolsGui窗口中按下'下一步'开始执行");draw_title("执行中"); moveit_visual_tools.trigger(); move_group_interface.execute(plan);}else{draw_title("规划失败!"); moveit_visual_tools.trigger();RCLCPP_ERROR(logger,"规划失败!");}你会很快发现,每次调用函数改变 RViz 中的渲染内容后,都需要调用moveit_visual_tools的trigger方法。这是因为发送到 RViz 的消息会被批量处理,只有调用trigger时才会发送,以此减少标记话题的带宽占用。
- 最后,再次构建项目,确保所有新增代码都正确无误:
cd /moveit_ws colcon build --packages-select hello_moveit 4.6 在 RViz 中启用可视化功能
- 打开一个新终端,获取工作区的源代码,然后启动打开RViz的演示启动文件
cd /moveit_ws source install/setup.bash ros2 launch moveit2_tutorials demo.launch.py 
在 “显示”(Displays)面板中向下滚动到该新增项,将其使用的话题修改为/rviz_visual_tools。但我这里没有这个话题

现在,你已准备好运行带有可视化功能的新程序。
最后,我们需要添加 “标记数组”(Marker Array)来渲染已添加的可视化内容。在 “显示”(Displays)面板中点击 “添加”(Add)按钮:

选择 “标记数组”(MarkerArray)并点击 “确定”(OK)。
然后选择RvizVisualToolsGui并点击 “确定”(OK),这将在左下角创建一个新面板,其中包含我们后续会使用的 “下一步”(Next)按钮。

要添加用于与程序中提示交互的按钮,请通过 左上角“面板”(Panels)/“添加新面板”(Add New Panel)菜单打开对话框:

在 “显示”(Displays)选项卡中取消勾选 “运动规划”(MotionPlanning)以隐藏该功能,接下来的部分我们不会使用 “运动规划” 插件。——后面的规划是通过代码实现的,不是界面上的功能

4.7 程序运行效果
- 在新的终端中,进入工作空间,设置环境变量,然后运行
hello_moveit:
source install/setup.bash ros2 run hello_moveit hello_moveit_node 你会发现程序已暂停,并显示如下日志提醒你点击 RvizVisualToolsGui 中的 Next
点击 RViz 中的 “下一步”(Next)按钮,观察应用程序的运行进度。
点击 “下一步” 按钮后,你会看到应用程序开始规划,在机器人上方添加了标题,并绘制了一条代表工具路径的线条。要继续执行,请再次按下 “下一步”(Next)按钮,观察机器人执行规划的过程。

源码
- package.xml
<?xml version="1.0"?><?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?><packageformat="3"><name>hello_moveit</name><version>0.0.0</version><description>TODO: Package description</description><maintaineremail="[email protected]">zzw</maintainer><license>TODO: License declaration</license><buildtool_depend>ament_cmake</buildtool_depend><depend>moveit_ros_planning_interface</depend><depend>rclcpp</depend><depend>moveit_visual_tools</depend><test_depend>ament_lint_auto</test_depend><test_depend>ament_lint_common</test_depend><export><build_type>ament_cmake</build_type></export></package>- CMakeLists.txt
# 1. 声明 C++ 标准(MoveIt 要求 C++17 及以上) cmake_minimum_required(VERSION 3.8) project(hello_moveit) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() # 2. 查找依赖包 find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(moveit_ros_planning_interface REQUIRED) find_package(geometry_msgs REQUIRED) # 用到 Pose 消息时需要 find_package(moveit_visual_tools REQUIRED) # 3. 编译节点(替换为你的节点名) add_executable(hello_moveit_node src/hello_moveit_node.cpp) ament_target_dependencies( hello_moveit_node "rclcpp" "moveit_ros_planning_interface" "moveit_visual_tools" "geometry_msgs" ) # 4. 安装节点 install(TARGETS hello_moveit_node DESTINATION lib/${PROJECT_NAME} ) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() endif() ament_package() - hello_moveit_node.cpp
#include<memory>#include<thread>#include<rclcpp/rclcpp.hpp>#include<moveit/move_group_interface/move_group_interface.h>#include<moveit_visual_tools/moveit_visual_tools.h>intmain(int argc,char*argv[]){// 初始化 ROS 并创建节点 rclcpp::init(argc, argv);autoconst node = std::make_shared<rclcpp::Node>("hello_moveit", rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true));// 创建 ROS 日志器autoconst logger = rclcpp::get_logger("hello_moveit !");// 为当前状态监视器启动一个SingleThreadedExecutor,以获取有关机器人状态的信息 rclcpp::executors::SingleThreadedExecutor executor; executor.add_node(node);auto spinner = std::thread([&executor](){ executor.spin();});// 创建 MoveIt MoveGroup 接口using moveit::planning_interface::MoveGroupInterface;auto move_group_interface =MoveGroupInterface(node,"panda_arm");// 构造并初始化MoveItVisualToolsauto moveit_visual_tools = moveit_visual_tools::MoveItVisualTools{ node,"base_link", rviz_visual_tools::RVIZ_MARKER_TOPIC, move_group_interface.getRobotModel()}; moveit_visual_tools.deleteAllMarkers(); moveit_visual_tools.loadRemoteControl();// Create a closure for updating the text in rvizautoconst draw_title =[&moveit_visual_tools](auto text){autoconst text_pose =[]{auto msg = Eigen::Isometry3d::Identity(); msg.translation().z()=1.0;// Place text 1m above the base linkreturn msg;}(); moveit_visual_tools.publishText(text_pose, text, rviz_visual_tools::WHITE, rviz_visual_tools::XLARGE);};autoconst prompt =[&moveit_visual_tools](auto text){ moveit_visual_tools.prompt(text);};autoconst draw_trajectory_tool_path =[&moveit_visual_tools, jmg = move_group_interface.getRobotModel()->getJointModelGroup("panda_arm")](autoconst trajectory){ moveit_visual_tools.publishTrajectoryLine(trajectory, jmg);};// 设置目标姿态(Target Pose)autoconst target_pose =[]{ geometry_msgs::msg::Pose msg; msg.orientation.w =1.0; msg.position.x =0.28; msg.position.y =-0.2; msg.position.z =0.5;return msg;}(); move_group_interface.setPoseTarget(target_pose);// 规划到目标姿态的路径prompt("Press 'next' in the RvizVisualToolsGui window to plan");draw_title("Planning"); moveit_visual_tools.trigger();autoconst[success, plan]=[&move_group_interface]{ moveit::planning_interface::MoveGroupInterface::Plan msg;autoconst ok =static_cast<bool>(move_group_interface.plan(msg));return std::make_pair(ok, msg);}();// 执行规划if(success){draw_trajectory_tool_path(plan.trajectory_); moveit_visual_tools.trigger();prompt("Press 'next' in the RvizVisualToolsGui window to execute");draw_title("Executing"); moveit_visual_tools.trigger(); move_group_interface.execute(plan);}else{draw_title("Planning Failed!"); moveit_visual_tools.trigger();RCLCPP_ERROR(logger,"Planning failed!");}// 关闭 ros rclcpp::shutdown(); spinner.join();return0;}5.Tutorials——Planning Around Objects
https://moveit.picknik.ai/main/doc/tutorials/planning_around_objects/planning_around_objects.html
本教程将向你介绍如何在规划场景中添加障碍物,并实现绕障碍物规划。
5.1 添加规划场景接口的头文件
在源文件顶部的头文件列表中添加以下内容:
#include<moveit/planning_scene_interface/planning_scene_interface.hpp>5.2 修改目标位姿
首先,通过以下修改更新目标位姿,使机器人规划到新的位置:
// 设置更新后的目标位姿autoconst target_pose =[]{ geometry_msgs::msg::Pose msg; msg.orientation.y =0.8; msg.orientation.w =0.6; msg.position.x =0.1; msg.position.y =0.4; msg.position.z =0.4;return msg;}(); move_group_interface.setPoseTarget(target_pose);5.3 创建碰撞对象
在接下来的代码块中,我们将创建一个碰撞对象。需要注意的是,该对象将被放置在机器人的坐标系中。
**如果我们有感知系统可报告场景中障碍物的位置,其生成的消息格式与此类似。**本示例中我们将手动创建该对象。代码块末尾设置消息的操作类型为 ADD,这将使该对象被添加到碰撞场景中。将此代码块插入到 “设置目标位姿”(步骤 2)和 “创建规划” 的代码之间。
// 创建机器人需要避开的碰撞对象autoconst collision_object =[frame_id = move_group_interface.getPlanningFrame()]{ moveit_msgs::msg::CollisionObject collision_object; collision_object.header.frame_id = frame_id; collision_object.id ="box1"; shape_msgs::msg::SolidPrimitive primitive;// 定义立方体的尺寸(单位:米) primitive.type = primitive.BOX; primitive.dimensions.resize(3); primitive.dimensions[primitive.BOX_X]=0.5;// X轴方向长度 primitive.dimensions[primitive.BOX_Y]=0.1;// Y轴方向长度 primitive.dimensions[primitive.BOX_Z]=0.5;// Z轴方向长度// 定义立方体的位姿(相对于frame_id坐标系) geometry_msgs::msg::Pose box_pose; box_pose.orientation.w =1.0;// 四元数的x、y、z分量默认初始化为0,此处可省略 box_pose.position.x =0.2; box_pose.position.y =0.2; box_pose.position.z =0.25; collision_object.primitives.push_back(primitive); collision_object.primitive_poses.push_back(box_pose); collision_object.operation = collision_object.ADD;// 操作类型:添加对象return collision_object;}();5.4 将对象添加到规划场景
最后,我们需要将该碰撞对象添加到规划场景中。通过 PlanningSceneInterface 类实现这一操作,该类利用 ROS 接口将规划场景的变更传递给 MoveGroup。此代码块应紧跟在 “创建碰撞对象” 的代码之后。
// 将碰撞对象添加到场景中 moveit::planning_interface::PlanningSceneInterface planning_scene_interface; planning_scene_interface.applyCollisionObject(collision_object);5.5 运行程序并观察效果
按照上一教程的操作方式,使用 demo.launch.py 脚本启动 RViz,然后运行我们的程序。
- 终端一:
source install/setup.bash ros2 launch moveit2_tutorials demo.launch.py 同样的关闭 MotionPlanning,打开RivzVisualToolsGui

- 终端二:
colcon build --packages-select hello_moveit source install/setup.bash ros2 run hello_moveit hello_moveit_node 终端二运行之后然后根据终端的提示点击RivzVisualToolsGui的Next按钮,可以看到确实避开了障碍物进行规划。



源码
- package.xml
<?xml version="1.0"?><?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?><packageformat="3"><name>hello_moveit</name><version>0.0.0</version><description>TODO: Package description</description><maintaineremail="[email protected]">zzw</maintainer><license>TODO: License declaration</license><buildtool_depend>ament_cmake</buildtool_depend><depend>moveit_ros_planning_interface</depend><depend>rclcpp</depend><depend>moveit_visual_tools</depend><test_depend>ament_lint_auto</test_depend><test_depend>ament_lint_common</test_depend><export><build_type>ament_cmake</build_type></export></package>- CMakeLists.txt
# 1. 声明 C++ 标准(MoveIt 要求 C++17 及以上) cmake_minimum_required(VERSION 3.8) project(hello_moveit) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() # 2. 查找依赖包 find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(moveit_ros_planning_interface REQUIRED) find_package(geometry_msgs REQUIRED) # 用到 Pose 消息时需要 find_package(moveit_visual_tools REQUIRED) # 3. 编译节点(替换为你的节点名) add_executable(hello_moveit_node src/hello_moveit_node.cpp) ament_target_dependencies( hello_moveit_node "rclcpp" "moveit_ros_planning_interface" "moveit_visual_tools" "geometry_msgs" ) # 4. 安装节点 install(TARGETS hello_moveit_node DESTINATION lib/${PROJECT_NAME} ) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() endif() ament_package() - hello_moveit_node.cpp
#include<memory>#include<thread>#include<rclcpp/rclcpp.hpp>#include<moveit/move_group_interface/move_group_interface.h>#include<moveit_visual_tools/moveit_visual_tools.h>#include<moveit/planning_scene_interface/planning_scene_interface.h>intmain(int argc,char*argv[]){// 初始化 ROS 并创建节点 rclcpp::init(argc, argv);autoconst node = std::make_shared<rclcpp::Node>("hello_moveit", rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true));// 创建 ROS 日志器autoconst logger = rclcpp::get_logger("hello_moveit !");// 为当前状态监视器启动一个SingleThreadedExecutor,以获取有关机器人状态的信息 rclcpp::executors::SingleThreadedExecutor executor; executor.add_node(node);auto spinner = std::thread([&executor](){ executor.spin();});// 创建 MoveIt MoveGroup 接口using moveit::planning_interface::MoveGroupInterface;auto move_group_interface =MoveGroupInterface(node,"panda_arm");// 构造并初始化MoveItVisualToolsauto moveit_visual_tools = moveit_visual_tools::MoveItVisualTools{ node,"base_link", rviz_visual_tools::RVIZ_MARKER_TOPIC, move_group_interface.getRobotModel()}; moveit_visual_tools.deleteAllMarkers(); moveit_visual_tools.loadRemoteControl();// Create a closure for updating the text in rvizautoconst draw_title =[&moveit_visual_tools](auto text){autoconst text_pose =[]{auto msg = Eigen::Isometry3d::Identity(); msg.translation().z()=1.0;return msg;}(); moveit_visual_tools.publishText(text_pose, text, rviz_visual_tools::WHITE, rviz_visual_tools::XLARGE);};autoconst prompt =[&moveit_visual_tools](auto text){ moveit_visual_tools.prompt(text);};autoconst draw_trajectory_tool_path =[&moveit_visual_tools, jmg = move_group_interface.getRobotModel()->getJointModelGroup("panda_arm")](autoconst trajectory){ moveit_visual_tools.publishTrajectoryLine(trajectory, jmg);};// 设置更新后的目标位姿autoconst target_pose =[]{ geometry_msgs::msg::Pose msg; msg.orientation.y =0.8; msg.orientation.w =0.6; msg.position.x =0.1; msg.position.y =0.4; msg.position.z =0.4;return msg;}(); move_group_interface.setPoseTarget(target_pose);// 创建机器人需要避开的碰撞对象autoconst collision_object =[frame_id = move_group_interface.getPlanningFrame()]{ moveit_msgs::msg::CollisionObject collision_object; collision_object.header.frame_id = frame_id; collision_object.id ="box1"; shape_msgs::msg::SolidPrimitive primitive;// 定义立方体的尺寸(单位:米) primitive.type = primitive.BOX; primitive.dimensions.resize(3); primitive.dimensions[primitive.BOX_X]=0.5;// X轴方向长度 primitive.dimensions[primitive.BOX_Y]=0.1;// Y轴方向长度 primitive.dimensions[primitive.BOX_Z]=0.5;// Z轴方向长度// 定义立方体的位姿(相对于frame_id坐标系) geometry_msgs::msg::Pose box_pose; box_pose.orientation.w =1.0;// 四元数的x、y、z分量默认初始化为0,此处可省略 box_pose.position.x =0.2; box_pose.position.y =0.2; box_pose.position.z =0.25; collision_object.primitives.push_back(primitive); collision_object.primitive_poses.push_back(box_pose); collision_object.operation = collision_object.ADD;// 操作类型:添加对象return collision_object;}();// 将碰撞对象添加到场景中 moveit::planning_interface::PlanningSceneInterface planning_scene_interface; planning_scene_interface.applyCollisionObject(collision_object);// 创建规划器去目标位姿态prompt("Press 'next' in the RvizVisualToolsGui window to plan");draw_title("Planning"); moveit_visual_tools.trigger();autoconst[success, plan]=[&move_group_interface]{ moveit::planning_interface::MoveGroupInterface::Plan msg;autoconst ok =static_cast<bool>(move_group_interface.plan(msg));return std::make_pair(ok, msg);}();// 执行规划if(success){draw_trajectory_tool_path(plan.trajectory_); moveit_visual_tools.trigger();prompt("Press 'next' in the RvizVisualToolsGui window to execute");draw_title("Executing"); moveit_visual_tools.trigger(); move_group_interface.execute(plan);}else{draw_title("Planning Failed!"); moveit_visual_tools.trigger();RCLCPP_ERROR(logger,"Planning failed!");} rclcpp::shutdown(); spinner.join();return0;}使用规划场景进行碰撞和约束检查的示例。使用规划场景ROS API的示例。可视化碰撞对象的示例。
用于规划对象的子帧示例。

6.Tutorials——Pick and Place with MoveIt Task Constructor
https://moveit.picknik.ai/main/doc/tutorials/pick_and_place_with_moveit_task_constructor/pick_and_place_with_moveit_task_constructor.html