Moveit2使用说明(C++)

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的核心架构

  • Planning Scene(规划场景):相当于城市地图,记录所有道路和障碍物信息
  • MoveGroup Interface(运动组接口):类似交通指挥中心,接收指令并协调各个组件
  • Trajectory Execution(轨迹执行):如同交通信号灯和车辆调度,确保运动按计划进行

1.Tutorials——Getting Started

https://moveit.picknik.ai/main/doc/tutorials/getting_started/getting_started.html

官网最后这里说了需要设置你的 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_interfacerclcpp 作为依赖项。这会自动在 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 

终端二运行之后然后根据终端的提示点击RivzVisualToolsGuiNext按钮,可以看到确实避开了障碍物进行规划。

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

源码

  • 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

Read more

鸿蒙金融理财全栈项目——生态合作、用户运营、数据变现

鸿蒙金融理财全栈项目——生态合作、用户运营、数据变现

《鸿蒙APP开发从入门到精通》第19篇:鸿蒙金融理财全栈项目——生态合作、用户运营、数据变现 📊🌍💰 内容承接与核心价值 这是《鸿蒙APP开发从入门到精通》的第19篇——生态合作、用户运营、数据变现篇,100%承接第18篇的风险控制、合规审计、产品创新架构,并基于金融场景的生态合作、用户运营、数据变现要求,设计并实现鸿蒙金融理财全栈项目的生态合作、用户运营、数据变现功能。 学习目标: * 掌握鸿蒙金融理财项目的生态合作设计与实现; * 实现金融机构合作、支付渠道合作、数据分析合作; * 理解用户运营在金融场景的核心设计与实现; * 实现用户增长、用户留存、用户转化; * 掌握数据变现在金融场景的设计与实现; * 实现数据服务、数据产品、数据变现; * 优化金融理财项目的用户体验(生态合作、用户运营、数据变现)。 学习重点: * 鸿蒙金融理财项目的生态合作设计原则; * 用户运营在金融场景的应用; * 数据变现在金融场景的设计要点。 一、 生态合作基础 🎯 1.1 生态合作定义 生态合作是指金融理财项目与其他金融机构、

By Ne0inhk
Flutter for OpenHarmony:cli_util 告别手写 print,用专业级日志系统构建你的 Dart 命令行工具 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:cli_util 告别手写 print,用专业级日志系统构建你的 Dart 命令行工具 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 随着 Flutter 和 Dart 生态的爆发,越来越多的开发者开始使用 Dart 编写命令行工具(CLI)。从官方的 flutter 工具链,到社区的 melos、very_good_cli,Dart 因其 AOT 编译出的独立二进制文件(无需安装运行时)和极快的启动速度,已成为编写跨平台 CLI 的首选语言。 但在开发 CLI 时,我们经常面临一些底层痛点: * SDK 哪里找? 如何准确找到当前运行环境的 Dart SDK 路径?(用于调用 dart format 或 dart pub)。 * 日志怎么打? 简单的

By Ne0inhk
鸿蒙金融理财全栈项目——运维监控、性能优化、安全加固

鸿蒙金融理财全栈项目——运维监控、性能优化、安全加固

《鸿蒙APP开发从入门到精通》第20篇:鸿蒙金融理财全栈项目——运维监控、性能优化、安全加固 📊🔧🛡️ 内容承接与核心价值 这是《鸿蒙APP开发从入门到精通》的第20篇——运维监控、性能优化、安全加固篇,100%承接第19篇的生态合作、用户运营、数据变现架构,并基于金融场景的运维监控、性能优化、安全加固要求,设计并实现鸿蒙金融理财全栈项目的运维监控、性能优化、安全加固功能。 学习目标: * 掌握鸿蒙金融理财项目的运维监控设计与实现; * 实现应用监控、服务器监控、数据库监控; * 理解性能优化在金融场景的核心设计与实现; * 实现前端优化、后端优化、数据库优化; * 掌握安全加固在金融场景的设计与实现; * 实现代码加固、数据加密、安全审计; * 优化金融理财项目的用户体验(运维监控、性能优化、安全加固)。 学习重点: * 鸿蒙金融理财项目的运维监控设计原则; * 性能优化在金融场景的应用; * 安全加固在金融场景的设计要点。 一、 运维监控基础 🎯 1.1 运维监控定义 运维监控是指对金融理财项目的应用、

By Ne0inhk
国产化消息中间件双雄:东方通TongLINK/Q与华为RabbitMQ的运维核心技术全解析

国产化消息中间件双雄:东方通TongLINK/Q与华为RabbitMQ的运维核心技术全解析

在信创产业全面推进“2+8+N”替代工程的背景下,消息中间件作为分布式系统的“神经中枢”,承担着跨系统数据传输、应用解耦、流量削峰的核心使命,其稳定性、安全性与适配性直接决定政企数字化系统的运行效能。作为国产消息中间件领域的标杆产品,东方通TongLINK/Q凭借三十余年行业积淀的全场景适配能力,与华为RabbitMQ国产化适配版依托鲲鹏架构的高性能优化,成为政企信创改造的首选组合。 对于运维人员而言,熟练掌握两款产品的队列配置、消息路由管理与死信队列处理技术,是保障信创系统高效运转的关键。本文将深度拆解两大产品的核心运维技术,结合实操场景与行业案例,揭秘国产化消息中间件如何通过精细化配置实现业务价值最大化,为信创运维工作提供实战指南。 一、国产化适配基石:两款产品的核心定位与信创价值 消息中间件作为基础软件“三驾马车”之一,是信创生态建设的核心环节。东方通与华为凭借各自技术优势,构建了适配全场景信创需求的消息中间件解决方案,为政企系统替代升级筑牢根基。 (一)东方通TongLINK/Q:深耕行业的国产化消息中间件标杆 东方通TongLINK/Q作为国内最早自主研发的消息

By Ne0inhk