- 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](#1.Tutorials——Getting Started)
- [2.Tutorials------MoveIt Quickstart in RViz](#2.Tutorials——MoveIt Quickstart in RViz)
- [3.Tutorials------Your First C++ MoveIt Project](#3.Tutorials——Your First C++ MoveIt Project)
-
- [3.1 创建功能包(Package)](#3.1 创建功能包(Package))
- [3.2 创建 ROS 节点(Node)和执行器(Executor)](#3.2 创建 ROS 节点(Node)和执行器(Executor))
- [3.4 构建与运行](#3.4 构建与运行)
- [4.Tutorials------Visualizing In RViz](#4.Tutorials——Visualizing In RViz)
-
- [4.1 添加 moveit_visual_tools 依赖](#4.1 添加 moveit_visual_tools 依赖)
- [4.2 创建 ROS 执行器并在线程中运行节点](#4.2 创建 ROS 执行器并在线程中运行节点)
- [4.3 创建并初始化 MoveItVisualTools](#4.3 创建并初始化 MoveItVisualTools)
- [4.4 编写可视化相关的闭包](#4.4 编写可视化相关的闭包)
- [4.5 可视化程序的执行步骤](#4.5 可视化程序的执行步骤)
- [4.6 在 RViz 中启用可视化功能](#4.6 在 RViz 中启用可视化功能)
- [4.7 程序运行效果](#4.7 程序运行效果)
- 源码
- [5.Tutorials------Planning Around Objects](#5.Tutorials——Planning Around Objects)
-
- [5.1 添加规划场景接口的头文件](#5.1 添加规划场景接口的头文件)
- [5.2 修改目标位姿](#5.2 修改目标位姿)
- [5.3 创建碰撞对象](#5.3 创建碰撞对象)
- [5.4 将对象添加到规划场景](#5.4 将对象添加到规划场景)
- [5.5 运行程序并观察效果](#5.5 运行程序并观察效果)
- 源码
- [6.Tutorials------Pick and Place with MoveIt Task Constructor](#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.csdn.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:启动演示并配置插件
启动演示程序:
bash
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_link02.在 Planning Request 中,将 Planning Group 改为
panda_arm




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

可通过复选框切换各可视化效果的显示 / 隐藏:
- 场景机器人:勾选 Scene Robot 树形菜单中的 Show Robot Visual。

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

- 起始状态:勾选 Planning Request 树形菜单中的 Query Start State。

- 目标状态:勾选 Planning Request 树形菜单中的 Query Goal State。
尝试操作这些复选框,体验不同可视化效果的切换。
步骤 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.确保两个状态均未与机器人自身发生碰撞。
- 4.取消勾选 Planned Path 树形菜单中的 Show Trail。

- 5.在 Motion Planning 窗口的 Planning 标签页下,点击 Plan 按钮。
- 6.勾选 Planned Path 树形菜单中的 Show Trail,你会看到机械臂的路径以一系列机械臂姿态(manipulator poses)呈现。

查看轨迹路径点
你可以在 RViz 中逐点可视化查看轨迹(trajectories):
- 1.从 Panels 菜单中,选择 Trajectory - Trajectory Slider,RViz 会新增一个滑块面板。

- 2.设置目标姿态(goal pose),然后运行
Plan。 - 3.操作滑块面板,例如拖动滑块、点击 "Play" 按钮。
注:将末端执行器移动到新目标后,务必先运行 Plan 再点击 Play------ 否则会显示上一个目标的路径点(若存在)。

规划笛卡尔运动
若勾选 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 保存,之后通过以下命令调用该配置文件:
bash
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 命令行工具创建新功能包:
bash
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
xml
# 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
cpp
#include <memory>
#include <rclcpp/rclcpp.hpp>
#include <moveit/move_group_interface/move_group_interface.h>
int main(int argc, char* argv[])
{
// 初始化 ROS 并创建节点
rclcpp::init(argc, argv);
auto const node = std::make_shared<rclcpp::Node>(
"hello_moveit",
rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true)
);
// 创建 ROS 日志器
auto const logger = rclcpp::get_logger("hello_moveit !");
// 创建 MoveIt MoveGroup 接口
using moveit::planning_interface::MoveGroupInterface;
auto move_group_interface = MoveGroupInterface(node, "panda_arm");
// 设置目标姿态(Target Pose)
auto const 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);
// 规划到目标姿态的路径
auto const [success, plan] = [&move_group_interface]{
moveit::planning_interface::MoveGroupInterface::Plan msg;
auto const 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();
return 0;
}
- 代码解析


3.4 构建与运行
与之前相同,先构建代码(在 ws_moveit 目录下):
bash
colcon build
构建成功后,在新终端配置环境并启动上一教程中的演示启动文件(用于启动 RViz 和 MoveGroup 节点):
bash
# 加载你之前保存的rviz文件,如果没保存会打开后显示默认的
ros2 launch moveit2_tutorials demo.launch.py rviz_config:=your_rviz_config.rviz
在 RViz 的 Displays 窗口中,展开 MotionPlanning/Planning Request,取消勾选 Query Goal State。
再打开一个终端,配置环境并运行程序:
bash
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>语句之后添加以下内容:
xml
<depend>moveit_visual_tools</depend>
- 然后在CMakeLists.txt文件的
find_package语句部分添加以下内容:
bash
find_package(moveit_visual_tools REQUIRED)
- 在该文件的后续部分,扩展ament_target_dependencies宏调用,将新的依赖项包含其中,如下所示:
bash
ament_target_dependencies(
hello_moveit
"moveit_ros_planning_interface"
"moveit_visual_tools"
"rclcpp"
)
- 为验证依赖项添加是否正确,请在源文件hello_moveit.cpp中添加所需的头文件引用:
cpp#include
#include <moveit_visual_tools/moveit_visual_tools.h>
- 要测试上述配置是否生效,可以回到初始的moveit2_ws空间,单独编译
hello_moveit试下有没有问题
bash
cd /moveit_ws
colcon build --packages-select hello_moveit
4.2 创建 ROS 执行器并在线程中运行节点
- 在初始化 MoveItVisualTools 之前,我们需要让 ROS 节点上的执行器保持运行状态。这是因为 MoveItVisualTools 与 ROS 服务和话题的交互方式决定了这一步是必需的。首先,在文件顶部的头文件引用部分添加线程库:
cpp
#include <thread> // <---- 添加到顶部的头文件引用集合中
- 通过创建并命名日志记录器,我们可以让程序日志更有条理:
cpp
// 创建ROS日志记录器
auto const logger = rclcpp::get_logger("hello_moveit");
- 接下来,在创建 MoveIt 的 MoveGroup 接口之前添加执行器相关代码:
cpp
// 启动单线程执行器,使MoveItVisualTools能够与ROS进行交互
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
auto spinner = std::thread([&executor]() { executor.spin(); });
// 创建MoveIt的MoveGroup接口
- 最后,确保在程序退出前合并线程:
cpp
// 关闭ROS
rclcpp::shutdown(); // <--- 这将使线程中的spin函数返回
spinner.join(); // <--- 退出前合并线程
return 0;
完成上述修改后,重新构建工作空间,确保没有语法错误。
4.3 创建并初始化 MoveItVisualTools
- 在构建完
MoveGroupInterface之后,我们将构建并初始化MoveItVisualTools:
注意这里因为默认是panda机器人,所以将
manipulator改为panda_arm
cpp
// 创建MoveIt的MoveGroup接口
using moveit::planning_interface::MoveGroupInterface;
auto move_group_interface = MoveGroupInterface(node, "panda_arm");
// 构建并初始化MoveItVisualTools
auto 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
cpp
// 创建可视化相关的闭包
auto const draw_title = [&moveit_visual_tools](auto text) {
auto const 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);
};
auto const prompt = [&moveit_visual_tools](auto text) {
moveit_visual_tools.prompt(text);
};
auto const draw_trajectory_tool_path =
[&moveit_visual_tools,
jmg = move_group_interface.getRobotModel()->getJointModelGroup(
"panda_arm")](auto const trajectory) {
moveit_visual_tools.publishTrajectoryLine(trajectory, jmg);
};
- 这三个闭包均通过引用捕获moveit_visual_tools,最后一个闭包还捕获了我们用于规划的关节模型组对象的指针。每个闭包都会调用moveit_visual_tools上的一个函数,以改变 RViz 中的显示内容。
第一个闭包 draw_title会在机器人基础上方 1 米处添加文本,这是一种从宏观层面展示程序状态的有效方式。
第二个闭包 调用prompt函数,该函数会阻塞程序,直到用户在 RViz 中按下 "下一步" 按钮,这在调试时逐步执行程序非常有帮助。
最后一个闭包会绘制规划轨迹的工具路径,通常有助于从工具的角度理解规划的轨迹。
你可能会疑惑为什么要创建这样的 lambda 表达式,原因很简单,就是为了让后续的代码更易于阅读和理解。在编写软件时,将功能拆分为命名函数通常很有帮助,这些函数可以方便地重复使用并单独进行测试。在下一部分中,你将看到如何使用这些创建好的函数。
4.5 可视化程序的执行步骤
- 现在,我们来完善程序中间部分的代码。更新规划和执行相关的代码,加入这些新功能:
cpp
// 设置目标姿态
auto const 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();
auto const [success, plan] = [&move_group_interface] {
moveit::planning_interface::MoveGroupInterface::Plan msg;
auto const 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时才会发送,以此减少标记话题的带宽占用。
- 最后,再次构建项目,确保所有新增代码都正确无误:
bash
cd /moveit_ws
colcon build --packages-select hello_moveit
4.6 在 RViz 中启用可视化功能
- 打开一个新终端,获取工作区的源代码,然后启动打开RViz的演示启动文件
bash
cd /moveit_ws
source install/setup.bash
ros2 launch moveit2_tutorials demo.launch.py

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

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

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

- 最后,我们需要添加 "标记数组"(Marker Array)来渲染已添加的可视化内容。在 "显示"(Displays)面板中点击 "添加"(Add)按钮:

选择 "标记数组"(MarkerArray)并点击 "确定"(OK)。 - 在 "显示"(Displays)面板中向下滚动到该新增项,将其使用的话题修改为
/rviz_visual_tools。但我这里没有这个话题

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

源码
- package.xml
xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>hello_moveit</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="zw.zhou@duoduo.ai">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
xml
# 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
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>
int main(int argc, char *argv[])
{
// 初始化 ROS 并创建节点
rclcpp::init(argc, argv);
auto const node =
std::make_shared<rclcpp::Node>("hello_moveit",
rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true));
// 创建 ROS 日志器
auto const 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");
// 构造并初始化MoveItVisualTools
auto 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 rviz
auto const draw_title = [&moveit_visual_tools](auto text) {
auto const text_pose = [] {
auto msg = Eigen::Isometry3d::Identity();
msg.translation().z() = 1.0; // Place text 1m above the base link
return msg;
}();
moveit_visual_tools.publishText(text_pose, text, rviz_visual_tools::WHITE, rviz_visual_tools::XLARGE);
};
auto const prompt = [&moveit_visual_tools](auto text) { moveit_visual_tools.prompt(text); };
auto const draw_trajectory_tool_path =
[&moveit_visual_tools, jmg = move_group_interface.getRobotModel()->getJointModelGroup("panda_arm")](
auto const trajectory) { moveit_visual_tools.publishTrajectoryLine(trajectory, jmg); };
// 设置目标姿态(Target Pose)
auto const 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();
auto const [success, plan] = [&move_group_interface] {
moveit::planning_interface::MoveGroupInterface::Plan msg;
auto const 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();
return 0;
}
5.Tutorials------Planning Around Objects
https://moveit.picknik.ai/main/doc/tutorials/planning_around_objects/planning_around_objects.html
本教程将向你介绍如何在规划场景中添加障碍物,并实现绕障碍物规划。
5.1 添加规划场景接口的头文件
在源文件顶部的头文件列表中添加以下内容:
cpp
#include <moveit/planning_scene_interface/planning_scene_interface.hpp>
5.2 修改目标位姿
首先,通过以下修改更新目标位姿,使机器人规划到新的位置:
cpp
// 设置更新后的目标位姿
auto const 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)和 "创建规划" 的代码之间。
cpp
// 创建机器人需要避开的碰撞对象
auto const 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。此代码块应紧跟在 "创建碰撞对象" 的代码之后。
cpp
// 将碰撞对象添加到场景中
moveit::planning_interface::PlanningSceneInterface planning_scene_interface;
planning_scene_interface.applyCollisionObject(collision_object);
5.5 运行程序并观察效果
按照上一教程的操作方式,使用 demo.launch.py 脚本启动 RViz,然后运行我们的程序。
- 终端一:
bash
source install/setup.bash
ros2 launch moveit2_tutorials demo.launch.py
同样的关闭 MotionPlanning ,打开RivzVisualToolsGui

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



源码
- package.xml
xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>hello_moveit</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="zw.zhou@duoduo.ai">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
xml
# 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
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>
int main(int argc, char *argv[])
{
// 初始化 ROS 并创建节点
rclcpp::init(argc, argv);
auto const node =
std::make_shared<rclcpp::Node>("hello_moveit",
rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true));
// 创建 ROS 日志器
auto const 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");
// 构造并初始化MoveItVisualTools
auto 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 rviz
auto const draw_title = [&moveit_visual_tools](auto text) {
auto const 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);
};
auto const prompt = [&moveit_visual_tools](auto text) { moveit_visual_tools.prompt(text); };
auto const draw_trajectory_tool_path =
[&moveit_visual_tools, jmg = move_group_interface.getRobotModel()->getJointModelGroup("panda_arm")](
auto const trajectory) { moveit_visual_tools.publishTrajectoryLine(trajectory, jmg); };
// 设置更新后的目标位姿
auto const 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);
// 创建机器人需要避开的碰撞对象
auto const 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();
auto const [success, plan] = [&move_group_interface] {
moveit::planning_interface::MoveGroupInterface::Plan msg;
auto const 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();
return 0;
}
- 使用规划场景进行碰撞和约束检查的示例。
- 使用规划场景ROS API的示例。
- 可视化碰撞对象的示例。
- 用于规划对象的子帧示例。

