【ROS2】Movei2!之 C++ MoveIt 项目

一、案例 

1. 创建功能包 

使用 ROS 2 命令行工具创建一个新包:

ros2 pkg create \
 --build-type ament_cmake \
 --dependencies moveit_ros_planning_interface rclcpp \
 --node-name hello_moveit hello_moveit

2. 创建 ROS 节点和 Executor

#include <memory>

#include <rclcpp/rclcpp.hpp>
#include <moveit/move_group_interface/move_group_interface.h>

int main(int argc, char * argv[])
{
  // Initialize ROS and create the Node
  rclcpp::init(argc, argv);
  auto const node = std::make_shared<rclcpp::Node>(
    "hello_moveit",
    rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true)
  );

  // Create a ROS logger
  auto const logger = rclcpp::get_logger("hello_moveit");

  // Next step goes here

  // Shutdown ROS
  rclcpp::shutdown();
  return 0;
}
2.1 构建和运行
colcon build --mixin debug

打开一个新终端,然后在该新终端中获取工作区环境脚本,以便我们可以运行我们的程序。

cd ~/ws_moveit2
source install/setup.bash

运行程序并查看输出:

ros2 run hello_moveit hello_moveit

3. 使用 MoveGroupInterface 进行规划和执行

在显示“Next step goes here”的注释中,添加以下代码:

// Create the MoveIt MoveGroup Interface
using moveit::planning_interface::MoveGroupInterface;
auto move_group_interface = MoveGroupInterface(node, "panda_arm");

// Set a 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);

// Create a plan to that 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);
}();

// Execute the plan
if(success) {
  move_group_interface.execute(plan);
} else {
  RCLCPP_ERROR(logger, "Planing failed!");
}
3.1 构建和运行
colcon build --mixin debug

打开一个新终端,然后在该新终端中获取工作区环境脚本,以便我们可以运行我们的程序。

cd ~/ws_moveit2
source install/setup.bash

运行程序并查看输出:

ros2 launch moveit2_tutorials demo.launch.py

然后在窗口中,取消选中复选框 :DisplaysMotionPlanning/Planning RequestQuery Goal State

在第三个终端中,source 工作区并运行您的程序。

ros2 run hello_moveit hello_moveit

 

3.2 代码解释

我们做的第一件事是创建 MoveGroupInterface。这个对象将用于与 move_group 交互,这使我们能够规划和执行轨迹。 请注意,这是我们在此程序中创建的唯一可变对象。 另一件需要注意的事情是我们在此处创建的对象的第二个接口:. 那是机器人描述中定义的一组关节,我们将使用 this 进行操作。MoveGroupInterface"panda_arm"MoveGroupInterface

using moveit::planning_interface::MoveGroupInterface;
auto move_group_interface = MoveGroupInterface(node, "panda_arm");

 然后,我们设置目标姿势和计划。请注意,仅设置目标姿势(通过 .起始姿势隐式是 joint state publisher 发布的位置,可以使用函数系列进行更改(但不在本教程中)。setPoseTargetMoveGroupInterface::setStartState*

关于下一节,还需要注意的另一件事是使用 lambda 来构建消息类型和规划。 这是现代 C++ 代码库中的一种模式,支持以更具声明性的样式进行编写。 

// Set a 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);

// Create a plan to that 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);
}();

最后,如果规划成功,我们将执行我们的计划,否则我们将记录一个错误:

// Execute the plan
if(success) {
  move_group_interface.execute(plan);
} else {
  RCLCPP_ERROR(logger, "Planning failed!");
}

 

二、在 RViz 中可视化

1. 添加依赖项 moveit_visual_tools

将此行添加到 package.xml:

<depend>moveit_visual_tools</depend>

再将此行添加到 CMakeLists.txt:

find_package(moveit_visual_tools REQUIRED)

在 ament_target_dependencies 中添加下面信息:

ament_target_dependencies(
  hello_moveit
  "moveit_ros_planning_interface"
  "moveit_visual_tools"
  "rclcpp"
)

要验证是否正确添加了依赖项,请将所需的 include 添加到源文件中:hello_moveit.cpp

#include <moveit_visual_tools/moveit_visual_tools.h>

要测试这一切是否有效,请在 workspace 目录中打开一个终端(记住在 opt 中获取您的 ROS 安装),然后使用 colcon 构建:

cd ~/ws_moveit2
colcon build --mixin debug

2. 创建一个 ROS 执行器并在线程上旋转节点

在初始化 MoveItVisualTools 之前,我们需要在 ROS 节点上启动一个执行程序。 这是MoveItVisualTools 与 ROS 服务和主题交互的方式。

#include <thread>  // <---- add this to the set of includes at the top

  ...

  // Create a ROS logger
  auto const logger = rclcpp::get_logger("hello_moveit");

  // We spin up a SingleThreadedExecutor so MoveItVisualTools interact with ROS
  rclcpp::executors::SingleThreadedExecutor executor; // 单线程执行器,按顺序处理回调
  executor.add_node(node); // 将节点加入执行器管理
  auto spinner = std::thread([&executor]() { executor.spin(); });

  // Create the MoveIt MoveGroup Interface
  ...

  // Shutdown ROS
  rclcpp::shutdown();  // <--- This will cause the spin function in the thread to return
  spinner.join();  // <--- Join the thread before exiting
  return 0;
}

在进行这些更改后,您应该重新构建工作区,以确保您没有任何语法错误。

3. 创建并初始化 MoveItVisualTools

接下来,我们将在构造 MoveGroupInterface 之后构造并初始化 MoveItVisualTools。

// Create the MoveIt MoveGroup Interface
using moveit::planning_interface::MoveGroupInterface;
auto move_group_interface = MoveGroupInterface(node, "panda_arm");

// Construct and initialize MoveItVisualTools
auto moveit_visual_tools = moveit_visual_tools::MoveItVisualTools{
    node, "panda_link0", rviz_visual_tools::RVIZ_MARKER_TOPIC, move_group_interface.getRobotModel()};
moveit_visual_tools.deleteAllMarkers();    // 清除 RViz 中我们从之前运行中留下的任何渲染状态
moveit_visual_tools.loadRemoteControl();  // 启用RViz按钮交互

我们将以下内容传递到构造函数中:ROS 节点、机器人的基本链接、要使用的标记主题(稍后会详细介绍)和机器人模型(我们从 move_group_interface 中获得)。 接下来,我们调用 delete all the markers,这将清除 RViz 中我们从之前运行中留下的任何渲染状态。 最后,我们加载远程控制。 Remote control 是一个非常简单的插件,它允许我们在 RViz 中有一个按钮来与我们的程序进行交互。

4. 为可视化编写闭包

构建和初始化后,我们现在创建一些闭包(可以访问当前范围内的变量的函数对象),我们稍后可以在程序中使用它们来帮助在 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);
    };

三个闭包中的每一个都是通过引用捕获的,最后一个捕获一个指向我们正在规划的联合模型组对象的指针。 它们中的每一个都调用一个函数 on 来更改 RViz 中的某些内容。 第一个选项是在机器人底座上方一米处添加文本。 这是从高级别显示程序状态的有用方法。 第二个调用名为 . 此功能会阻止您的程序,直到用户按下 RViz 中的按钮。 这对于在调试时单步调试程序很有帮助。 最后一个绘制我们计划的轨迹的刀具路径。 这通常有助于从工具的角度理解规划的轨迹。

您可能会问自己,为什么我们要创建这样的 lambda,原因只是为了让后面的代码更易于阅读和理解。 作为您的编写软件,将您的功能分解为命名函数通常很有帮助,这些函数可以很容易地重用和自行测试。 您将在下一节中看到我们如何使用我们创建的这些函数。

5. 可视化程序的步骤

现在,我们将在程序中间扩充代码。 更新用于规划和执行的代码以包含以下新功能:

// Set a 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);

// Create a plan to that 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);
}();

// Execute the plan
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, "Planing failed!");
}

您很快就会注意到的一件事是,我们必须在每次调用后调用一个方法,以更改 RViz 中呈现的内容。 这样做的原因是,发送到 RViz 的消息会在您调用时进行批处理和发送,以减少标记主题的带宽。

最后,再次构建您的项目,以确保所有添加的代码都是正确的。

cd ~/ws_moveit2
source /opt/ros/humble/setup.bash
colcon build --mixin debug

6. 在 RViz 中启用可视化

打开新终端,获取工作区,然后启动打开 RViz 的演示启动文件。

cd ~/ws_moveit2
source install/setup.bash
ros2 launch moveit2_tutorials demo.launch.py

取消选中“Displays”选项卡中的“MotionPlanning”以隐藏它。 我们不打算在下一部分使用“MotionPlanning”插件。

../../../_images/uncheck_motion_planning.png

../../../_images/unchecked_motion_planning.png

要添加按钮以与我们添加到程序中的提示进行交互,请使用 “Panels/Add New Panel” 菜单打开对话框:

../../../_images/panel_menu.png

然后选择并单击 OK。 这将在左下角创建一个新面板,其中包含一个我们稍后将使用的按钮。RvizVisualToolsGuiNext

../../../_images/add_rviz_tools_gui.png

../../../_images/next_button.png

最后,我们需要添加Marker Array来呈现我们添加的可视化效果。 单击 “Displays” 面板中的 “Add” 按钮。

../../../_images/add_button.png

选择并单击 。

../../../_images/marker_array.png

滚动到 Displays 面板中项目的底部,然后编辑新 Marker Array 正在使用的主题。/rviz_visual_tools

../../../_images/marker_array_topic.png

现在,您可以运行具有可视化效果的新程序。

7. 运行程序

在新终端中,转到工作区,获取工作区,然后运行 :hello_moveit

cd ~/ws_moveit2
source install/setup.bash
ros2 run hello_moveit hello_moveit

您会注意到您的程序已停止,并显示如下所示的日志:

[INFO] [1652822889.492940200] [hello_moveit.remote_control]: Waiting to continue: Press 'Next' in the RvizVisualToolsGui window to plan

单击 RViz 中的按钮,查看您的应用程序进度。Next

../../../_images/planning.png

单击下一步按钮后,您将看到您的应用程序已规划,在机器人上方添加了标题,并绘制了一条表示工具路径的线。 要继续,请再次按下以查看您的机器人执行计划。Next

../../../_images/executing.png

总结

您扩展了使用 MoveIt 编写的程序,以便与 RViz 中的 GUI 进行交互,从而允许您使用按钮逐步完成程序,在机器人上方呈现一些文本,并显示您规划的工具路径。

### ROS 2 MoveIt 安装教程 对于希望在ROS 2环境中部署MoveIt的应用开发者而言,了解如何安装和配置MoveIt至关重要。官方提供了详细的指导来帮助完成这一过程。 #### 准备工作环境 为了确保顺利安装MoveIt,在开始之前需确认已正确设置ROS 2的工作空间,并且已经安装了必要的依赖项[^1]。 #### 获取MoveIt源码 通过克隆仓库到本地开发环境中获取最新版本的MoveIt源代码: ```bash cd ~/ros2_ws/src/ git clone https://github.com/ros-planning/moveit2.git -b main ``` 此命令会下载MoveIt项目的主分支至指定路径下。 #### 编译MoveIt 编译整个工作区以构建MoveIt及其所有依赖包: ```bash colcon build --symlink-install --packages-select moveit2_tutorials source install/setup.bash ``` 上述指令不仅能够编译MoveIt本身,还会一同处理其配套的教学资源。 ### 使用指南 一旦成功安装好MoveIt之后,就可以着手探索各种功能特性以及学习具体应用场景下的实践技巧了。官方文档中包含了丰富的实例说明,有助于快速上手并掌握核心概念和技术要点[^2]。 例如,可以尝试加载一个简单的机器人模型进入规划场景内,并利用可视化工具RViz观察交互效果;或是编写自定义的动作序列实现特定任务目标等操作均被详尽记录于在线手册之中。 ### 示例代码 下面给出一段Python脚本作为入门级案例展示,它展示了怎样调用MoveGroup接口控制机械臂末端执行器到达给定位置姿态: ```python import sys from moveit_commander import ( RobotCommander, PlanningSceneInterface, roscpp_initialize, roscpp_shutdown, ) from geometry_msgs.msg import PoseStamped, Quaternion def go_to_pose_goal(pose_x=0.7, pose_y=0.0, pose_z=0.498): group_name = "panda_arm" robot = RobotCommander() scene = PlanningSceneInterface() group = robot.commander[group_name] # 设置目标位姿 pose_goal = PoseStamped() pose_goal.header.frame_id = "panda_link0" pose_goal.pose.position.x = pose_x pose_goal.pose.position.y = pose_y pose_goal.pose.position.z = pose_z q = Quaternion(*tf.transformations.quaternion_from_euler(-pi / 2., pi / 2., 0)) pose_goal.pose.orientation = q plan_success, traj, planning_time, error_code = group.plan(pose_goal) if not plan_success: print("Planning failed!") return False execute_success = group.execute(traj) if not execute_success: print("Execution failed.") return False return True if __name__ == "__main__": roscpp_initialize(sys.argv) try: result = go_to_pose_goal(float(sys.argv[1]), float(sys.argv[2]), float(sys.argv[3])) print(f"Result of moving to target position is {'success' if result else 'failure'}") finally: roscpp_shutdown() ``` 这段程序接收三个参数分别代表期望达到的空间坐标(x,y,z),并通过MoveGroup API指挥Panda仿真手臂移动到位。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gui林

你的热爱是我更新的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值