在 MoveIt 中,运动规划器被设置为规划路径。然而,有时我们可能希望对运动规划请求进行预处理或对规划的路径进行后处理(例如,进行时间参数化)。在这种情况下,我们使用规划管道,它将运动规划器与预处理和后处理阶段链接起来。预处理和后处理阶段称为规划请求适配器,可以通过 ROS 参数服务器按名称进行配置。在本教程中,我们将通过 C++ 代码向您介绍如何实例化和调用这样的规划管道。
入门
如果您还没有这样做,请确保您已完成入门中的步骤。
运行代码
打开两个 shell。在第一个 shell 中启动 RViz 并等待所有内容加载完成:
ros2 launch moveit2_tutorials move_group.launch.py
在第二个 shell 中,运行启动文件:
ros2 launch moveit2_tutorials motion_planning_pipeline_tutorial.launch.py注意:本教程使用 RvizVisualToolsGui 面板逐步演示。要将此面板添加到 RViz,请按照可视化教程中的说明进行操作。
片刻之后,RViz 窗口应该会出现,并且看起来与此页面顶部的窗口类似。要通过每个演示步骤,请按屏幕底部的 RvizVisualToolsGui 面板中的 Next 按钮,或在屏幕顶部的 Tools 面板中选择 Key Tool,然后在 RViz 聚焦时按键盘上的 N 键。
预期输出
在 RViz 中,我们最终应该能够看到三个轨迹被重放:
机器人将其右臂移动到前方的目标姿势,
机器人将其右臂移动到侧面的关节目标,
机器人将其右臂移回到它前面的原始姿势目标,
整个代码
整个代码可以在 MoveIt GitHub 项目中看到。https://github.com/moveit/moveit2_tutorials/blob/main/doc/examples/motion_planning_pipeline
#include <pluginlib/class_loader.hpp> // 插件库类加载器
// MoveIt
#include <moveit/robot_model_loader/robot_model_loader.h> // 机器人模型加载器
#include <moveit/robot_state/conversions.h> // 机器人状态转换
#include <moveit/planning_pipeline/planning_pipeline.h> // 规划管道
#include <moveit/planning_interface/planning_interface.h> // 规划接口
#include <moveit/planning_scene_monitor/planning_scene_monitor.h> // 规划场景监视器
#include <moveit/kinematic_constraints/utils.h> // 运动学约束工具
#include <moveit_msgs/msg/display_trajectory.hpp> // 显示轨迹消息
#include <moveit_msgs/msg/planning_scene.hpp> // 规划场景消息
#include <moveit_visual_tools/moveit_visual_tools.h> // MoveIt可视化工具
static const rclcpp::Logger LOGGER = rclcpp::get_logger("motion_planning_pipeline"); // 日志记录器
int main(int argc, char** argv)
{
rclcpp::init(argc, argv); // 初始化ROS
rclcpp::NodeOptions node_options; // 节点选项
node_options.automatically_declare_parameters_from_overrides(true); // 自动声明参数
auto node = rclcpp::Node::make_shared("motion_planning_pipeline_tutorial", node_options); // 创建共享节点
rclcpp::executors::SingleThreadedExecutor executor; // 单线程执行器
executor.add_node(node); // 添加节点到执行器
std::thread([&executor]() { executor.spin(); }).detach(); // 分离线程执行
// BEGIN_TUTORIAL
// 开始
// ^^^^^
// 设置使用规划管道非常简单。在加载规划器之前,我们需要两个对象,
// 一个机器人模型和一个规划场景。
//
// 我们将通过实例化一个
// :moveit_codedir:`RobotModelLoader<moveit_ros/planning/robot_model_loader/include/moveit/robot_model_loader/robot_model_loader.h>`
// 对象,该对象将在ROS参数服务器上查找机器人描述并构建一个
// :moveit_codedir:`RobotModel<moveit_core/robot_model/include/moveit/robot_model/robot_model.h>`
// 供我们使用。
robot_model_loader::RobotModelLoaderPtr robot_model_loader(
new robot_model_loader::RobotModelLoader(node, "robot_description")); // 机器人模型加载器
// 使用RobotModelLoader,我们可以构建一个规划场景监视器,
// 它将创建一个规划场景,监视规划场景差异,并将差异应用到其内部规划场景。
// 然后我们调用startSceneMonitor、startWorldGeometryMonitor和
// startStateMonitor来完全初始化规划场景监视器
planning_scene_monitor::PlanningSceneMonitorPtr psm(
new planning_scene_monitor::PlanningSceneMonitor(node, robot_model_loader)); // 规划场景监视器
/* 监听主题/XXX上的规划场景消息并将其应用到内部规划场景
相应地更新内部规划场景 */
psm->startSceneMonitor(); // 启动场景监视器
/* 监听世界几何、碰撞对象和(可选)八叉树地图的变化
世界几何、碰撞对象和可选的八叉树地图 */
psm->startWorldGeometryMonitor(); // 启动世界几何监视器
/* 监听关节状态更新以及附加碰撞对象的变化
并相应地更新内部规划场景 */
psm->startStateMonitor(); // 启动状态监视器
/* 我们还可以使用RobotModelLoader获取包含机器人运动学信息的机器人模型 */
moveit::core::RobotModelPtr robot_model = robot_model_loader->getModel(); // 获取机器人模型
/* 我们可以通过锁定内部规划场景来从PlanningSceneMonitor获取最新的机器人状态
以进行读取。此锁定确保在我们读取其状态时,底层场景不会更新。
RobotState在计算机器人的正向和逆向运动学等许多方面非常有用 */
moveit::core::RobotStatePtr robot_state(
new moveit::core::RobotState(planning_scene_monitor::LockedPlanningSceneRO(psm)->getCurrentState())); // 获取机器人状态
/* 创建一个JointModelGroup来跟踪当前的机器人姿态和规划组。关节模型
组在处理一组关节时非常有用,例如左臂或末端执行器 */
const moveit::core::JointModelGroup* joint_model_group = robot_state->getJointModelGroup("panda_arm"); // 获取关节模型组
// 我们现在可以设置PlanningPipeline对象,它将使用ROS参数服务器
// 来确定请求适配器和规划插件的集合
planning_pipeline::PlanningPipelinePtr planning_pipeline(
new planning_pipeline::PlanningPipeline(robot_model, node, "ompl")); // 设置规划管道
// 可视化
// ^^^^^^^^^^^^^
// MoveItVisualTools包提供了许多可视化对象、机器人和轨迹的功能,
// 以及调试工具,例如逐步检查脚本。
namespace rvt = rviz_visual_tools;
moveit_visual_tools::MoveItVisualTools visual_tools(node, "panda_link0", "move_group_tutorial", psm); // MoveIt可视化工具
visual_tools.deleteAllMarkers(); // 删除所有标记
/* 远程控制是一个检查工具,允许用户通过按钮和键盘快捷键在RViz中逐步执行高级脚本 */
visual_tools.loadRemoteControl(); // 加载远程控制
/* RViz提供了多种类型的标记,在本演示中我们将使用文本、圆柱体和球体 */
Eigen::Isometry3d text_pose = Eigen::Isometry3d::Identity(); // 文本位置
text_pose.translation().z() = 1.75; // 设置文本位置
visual_tools.publishText(text_pose, "Motion Planning Pipeline Demo", rvt::WHITE, rvt::XLARGE); // 发布文本
/* 批量发布用于减少发送到RViz的大型可视化消息的数量 */
visual_tools.trigger(); // 触发批量发布
/* 我们还可以使用visual_tools等待用户输入 */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to start the demo"); // 提示用户输入
// 姿态目标
// ^^^^^^^^^
// 我们现在将为Panda的右臂创建一个运动计划请求
// 指定末端执行器的期望姿态作为输入。
planning_interface::MotionPlanRequest req; // 运动计划请求
req.pipeline_id = "ompl"; // 管道ID
req.planner_id = "RRTConnectkConfigDefault"; // 规划器ID
req.allowed_planning_time = 1.0; // 允许的规划时间
req.max_velocity_scaling_factor = 1.0; // 最大速度缩放因子
req.max_acceleration_scaling_factor = 1.0; // 最大加速度缩放因子
planning_interface::MotionPlanResponse res; // 运动计划响应
geometry_msgs::msg::PoseStamped pose; // 姿态消息
pose.header.frame_id = "panda_link0"; // 帧ID
pose.pose.position.x = 0.3; // 位置X
pose.pose.position.y = 0.0; // 位置Y
pose.pose.position.z = 0.75; // 位置Z
pose.pose.orientation.w = 1.0; // 方向W
// 在位置上指定0.01米的公差
// 在方向上指定0.01弧度的公差
std::vector<double> tolerance_pose(3, 0.1); // 位置公差
std::vector<double> tolerance_angle(3, 0.1); // 方向公差
// 我们将使用一个助手函数将请求创建为约束
// 该函数可从
// :moveit_codedir:`kinematic_constraints<moveit_core/kinematic_constraints/include/moveit/kinematic_constraints/kinematic_constraint.h>`
// 包中获得。
req.group_name = "panda_arm"; // 组名
moveit_msgs::msg::Constraints pose_goal =
kinematic_constraints::constructGoalConstraints("panda_link8", pose, tolerance_pose, tolerance_angle); // 构建目标约束
req.goal_constraints.push_back(pose_goal); // 添加目标约束
// 在规划之前,我们需要对规划场景进行只读锁定,以便在规划期间不会修改世界表示
{
planning_scene_monitor::LockedPlanningSceneRO lscene(psm); // 锁定规划场景
/* 现在,调用管道并检查规划是否成功。*/
/* 检查规划是否成功 */
if (!planning_pipeline->generatePlan(lscene, req, res) || res.error_code.val != res.error_code.SUCCESS)
{
RCLCPP_ERROR(LOGGER, "Could not compute plan successfully"); // 规划失败
rclcpp::shutdown(); // 关闭ROS
return -1; // 返回错误代码
}
}
// 可视化结果
// ^^^^^^^^^^^^^^^^^^^^
rclcpp::Publisher<moveit_msgs::msg::DisplayTrajectory>::SharedPtr display_publisher =
node->create_publisher<moveit_msgs::msg::DisplayTrajectory>("/display_planned_path", 1); // 创建发布者
moveit_msgs::msg::DisplayTrajectory display_trajectory; // 显示轨迹消息
/* 可视化轨迹 */
RCLCPP_INFO(LOGGER, "Visualizing the trajectory"); // 可视化轨迹
moveit_msgs::msg::MotionPlanResponse response; // 运动计划响应
res.getMessage(response); // 获取响应消息
display_trajectory.trajectory_start = response.trajectory_start; // 设置轨迹起点
display_trajectory.trajectory.push_back(response.trajectory); // 添加轨迹
display_publisher->publish(display_trajectory); // 发布轨迹
visual_tools.publishTrajectoryLine(display_trajectory.trajectory.back(), joint_model_group); // 发布轨迹线
visual_tools.trigger(); // 触发可视化
/* 等待用户输入 */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo"); // 提示用户输入
// 关节空间目标
// ^^^^^^^^^^^^^^^^^
/* 首先,将规划场景中的状态设置为上一个计划的最终状态 */
robot_state = planning_scene_monitor::LockedPlanningSceneRO(psm)->getCurrentStateUpdated(response.trajectory_start); // 更新机器人状态
robot_state->setJointGroupPositions(joint_model_group, response.trajectory.joint_trajectory.points.back().positions); // 设置关节组位置
moveit::core::robotStateToRobotStateMsg(*robot_state, req.start_state); // 转换机器人状态消息
// 现在,设置一个关节空间目标
moveit::core::RobotState goal_state(*robot_state); // 目标状态
std::vector<double> joint_values = { -1.0, 0.7, 0.7, -1.5, -0.7, 2.0, 0.0 }; // 关节值
goal_state.setJointGroupPositions(joint_model_group, joint_values); // 设置关节组位置
moveit_msgs::msg::Constraints joint_goal =
kinematic_constraints::constructGoalConstraints(goal_state, joint_model_group); // 构建关节目标约束
req.goal_constraints.clear(); // 清除目标约束
req.goal_constraints.push_back(joint_goal); // 添加关节目标约束
// 在规划之前,我们需要对规划场景进行只读锁定,以便在规划期间不会修改世界表示
{
planning_scene_monitor::LockedPlanningSceneRO lscene(psm); // 锁定规划场景
/* 现在,调用管道并检查规划是否成功。*/
if (!planning_pipeline->generatePlan(lscene, req, res) || res.error_code.val != res.error_code.SUCCESS)
{
RCLCPP_ERROR(LOGGER, "Could not compute plan successfully"); // 规划失败
rclcpp::shutdown(); // 关闭ROS
return -1; // 返回错误代码
}
}
/* 可视化轨迹 */
RCLCPP_INFO(LOGGER, "Visualizing the trajectory"); // 可视化轨迹
res.getMessage(response); // 获取响应消息
display_trajectory.trajectory_start = response.trajectory_start; // 设置轨迹起点
display_trajectory.trajectory.push_back(response.trajectory); // 添加轨迹
// 现在你应该看到两个连续的规划轨迹
display_publisher->publish(display_trajectory); // 发布轨迹
visual_tools.publishTrajectoryLine(display_trajectory.trajectory.back(), joint_model_group); // 发布轨迹线
visual_tools.trigger(); // 触发可视化
/* 等待用户输入 */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo"); // 提示用户输入
// 使用规划请求适配器
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// 规划请求适配器允许我们指定一系列操作,
// 这些操作应在规划发生之前或规划完成后对结果路径进行处理
/* 首先,将规划场景中的状态设置为上一个计划的最终状态 */
robot_state = planning_scene_monitor::LockedPlanningSceneRO(psm)->getCurrentStateUpdated(response.trajectory_start); // 更新机器人状态
robot_state->setJointGroupPositions(joint_model_group, response.trajectory.joint_trajectory.points.back().positions); // 设置关节组位置
moveit::core::robotStateToRobotStateMsg(*robot_state, req.start_state); // 转换机器人状态消息
// 现在,将其中一个关节稍微设置在其上限之外
const moveit::core::JointModel* joint_model = joint_model_group->getJointModel("panda_joint3"); // 获取关节模型
const moveit::core::JointModel::Bounds& joint_bounds = joint_model->getVariableBounds(); // 获取关节边界
std::vector<double> tmp_values(1, 0.0); // 临时值
tmp_values[0] = joint_bounds[0].min_position_ - 0.01; // 设置关节位置
robot_state->setJointPositions(joint_model, tmp_values); // 设置关节位置
req.goal_constraints.clear(); // 清除目标约束
req.goal_constraints.push_back(pose_goal); // 添加姿态目标约束
// 在规划之前,我们需要对规划场景进行只读锁定,以便在规划期间不会修改世界表示
{
planning_scene_monitor::LockedPlanningSceneRO lscene(psm); // 锁定规划场景
/* 现在,调用管道并检查规划是否成功。*/
if (!planning_pipeline->generatePlan(lscene, req, res) || res.error_code.val != res.error_code.SUCCESS)
{
RCLCPP_ERROR(LOGGER, "Could not compute plan successfully"); // 规划失败
rclcpp::shutdown(); // 关闭ROS
return -1; // 返回错误代码
}
}
/* 可视化轨迹 */
RCLCPP_INFO(LOGGER, "Visualizing the trajectory"); // 可视化轨迹
res.getMessage(response); // 获取响应消息
display_trajectory.trajectory_start = response.trajectory_start; // 设置轨迹起点
display_trajectory.trajectory.push_back(response.trajectory); // 添加轨迹
/* 现在你应该看到三个连续的规划轨迹 */
display_publisher->publish(display_trajectory); // 发布轨迹
visual_tools.publishTrajectoryLine(display_trajectory.trajectory.back(), joint_model_group); // 发布轨迹线
visual_tools.trigger(); // 触发可视化
/* 等待用户输入 */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to finish the demo"); // 提示用户输入
RCLCPP_INFO(LOGGER, "Done"); // 完成
rclcpp::shutdown(); // 关闭ROS
return 0; // 返回成功代码
}开始
设置开始使用规划管道非常容易。在我们加载规划器之前,我们需要两个对象,一个 RobotModel 和一个 PlanningScene。
我们将首先实例化一个 RobotModelLoader 对象,它将在 ROS 参数服务器上查找机器人描述并为我们构建一个 RobotModel 以供使用。
robot_model_loader::RobotModelLoaderPtr robot_model_loader(
new robot_model_loader::RobotModelLoader(node, "robot_description")); // 机器人模型加载器使用 RobotModelLoader,我们可以构建一个规划场景监视器,该监视器将创建一个规划场景,监视规划场景差异,并将差异应用于其内部规划场景。然后我们调用 startSceneMonitor、startWorldGeometryMonitor 和 startStateMonitor 来完全初始化规划场景监视器。
planning_scene_monitor::PlanningSceneMonitorPtr psm(
new planning_scene_monitor::PlanningSceneMonitor(node, robot_model_loader)); // 规划场景监视器
/* 监听主题/XXX上的规划场景消息并将其应用到内部规划场景
相应地更新内部规划场景 */
psm->startSceneMonitor(); // 启动场景监视器
/* 监听世界几何、碰撞对象和(可选)八叉树地图的变化
世界几何、碰撞对象和可选的八叉树地图 */
psm->startWorldGeometryMonitor(); // 启动世界几何监视器
/* 监听关节状态更新以及附加碰撞对象的变化
并相应地更新内部规划场景 */
psm->startStateMonitor(); // 启动状态监视器
/* 我们还可以使用RobotModelLoader获取包含机器人运动学信息的机器人模型 */
moveit::core::RobotModelPtr robot_model = robot_model_loader->getModel(); // 获取机器人模型
/* 我们可以通过锁定内部规划场景来从PlanningSceneMonitor获取最新的机器人状态
以进行读取。此锁定确保在我们读取其状态时,底层场景不会更新。
RobotState在计算机器人的正向和逆向运动学等许多方面非常有用 */
moveit::core::RobotStatePtr robot_state(
new moveit::core::RobotState(planning_scene_monitor::LockedPlanningSceneRO(psm)->getCurrentState())); // 获取机器人状态
/* 创建一个JointModelGroup来跟踪当前的机器人姿态和规划组。关节模型
组在处理一组关节时非常有用,例如左臂或末端执行器 */
const moveit::core::JointModelGroup* joint_model_group = robot_state->getJointModelGroup("panda_arm"); // 获取关节模型组我们现在可以设置 PlanningPipeline 对象,它将使用 ROS 参数服务器来确定请求适配器和规划插件的集合
planning_pipeline::PlanningPipelinePtr planning_pipeline(
new planning_pipeline::PlanningPipeline(robot_model, node, "ompl")); // 设置规划管道可视化
该软件包 MoveItVisualTools 提供了许多在 RViz 中可视化对象、机器人和轨迹的功能,以及诸如脚本逐步内省等调试工具。
namespace rvt = rviz_visual_tools;
moveit_visual_tools::MoveItVisualTools visual_tools(node, "panda_link0", "move_group_tutorial", psm); // MoveIt可视化工具
visual_tools.deleteAllMarkers(); // 删除所有标记
/* 远程控制是一个检查工具,允许用户通过按钮和键盘快捷键在RViz中逐步执行高级脚本 */
visual_tools.loadRemoteControl(); // 加载远程控制
/* RViz提供了多种类型的标记,在本演示中我们将使用文本、圆柱体和球体 */
Eigen::Isometry3d text_pose = Eigen::Isometry3d::Identity(); // 文本位置
text_pose.translation().z() = 1.75; // 设置文本位置
visual_tools.publishText(text_pose, "Motion Planning Pipeline Demo", rvt::WHITE, rvt::XLARGE); // 发布文本
/* 批量发布用于减少发送到RViz的大型可视化消息的数量 */
visual_tools.trigger(); // 触发批量发布
/* 我们还可以使用visual_tools等待用户输入 */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to start the demo"); // 提示用户输入姿势目标
我们现在将为熊猫的右臂创建一个运动计划请求,指定末端执行器的期望姿态作为输入。
planning_interface::MotionPlanRequest req; // 运动计划请求
req.pipeline_id = "ompl"; // 管道ID
req.planner_id = "RRTConnectkConfigDefault"; // 规划器ID
req.allowed_planning_time = 1.0; // 允许的规划时间
req.max_velocity_scaling_factor = 1.0; // 最大速度缩放因子
req.max_acceleration_scaling_factor = 1.0; // 最大加速度缩放因子
planning_interface::MotionPlanResponse res; // 运动计划响应
geometry_msgs::msg::PoseStamped pose; // 姿态消息
pose.header.frame_id = "panda_link0"; // 帧ID
pose.pose.position.x = 0.3; // 位置X
pose.pose.position.y = 0.0; // 位置Y
pose.pose.position.z = 0.75; // 位置Z
pose.pose.orientation.w = 1.0; // 方向W位置公差为 0.01 米,方向公差为 0.01 弧度。
std::vector<double> tolerance_pose(3, 0.1); // 位置公差
std::vector<double> tolerance_angle(3, 0.1); // 方向公差我们将使用 kinematic_constraints 包中可用的辅助函数将请求创建为约束。
req.group_name = "panda_arm"; // 组名
moveit_msgs::msg::Constraints pose_goal =
kinematic_constraints::constructGoalConstraints("panda_link8", pose, tolerance_pose, tolerance_angle); // 构建目标约束
req.goal_constraints.push_back(pose_goal); // 添加目标约束在规划之前,我们需要在规划场景上设置一个只读锁,以便在规划时不会修改世界表示
{
planning_scene_monitor::LockedPlanningSceneRO lscene(psm); // 锁定规划场景
/* 现在,调用管道并检查规划是否成功。 */
/* 检查规划是否成功 */
if (!planning_pipeline->generatePlan(lscene, req, res) || res.error_code.val != res.error_code.SUCCESS)
{
RCLCPP_ERROR(LOGGER, "Could not compute plan successfully"); // 规划失败
rclcpp::shutdown(); // 关闭ROS
return -1; // 返回错误代码
}
}可视化结果

rclcpp::Publisher<moveit_msgs::msg::DisplayTrajectory>::SharedPtr display_publisher =
node->create_publisher<moveit_msgs::msg::DisplayTrajectory>("/display_planned_path", 1); // 创建发布者
moveit_msgs::msg::DisplayTrajectory display_trajectory; // 显示轨迹消息
/* 可视化轨迹 */
RCLCPP_INFO(LOGGER, "Visualizing the trajectory"); // 可视化轨迹
moveit_msgs::msg::MotionPlanResponse response; // 运动计划响应
res.getMessage(response); // 获取响应消息
display_trajectory.trajectory_start = response.trajectory_start; // 设置轨迹起点
display_trajectory.trajectory.push_back(response.trajectory); // 添加轨迹
display_publisher->publish(display_trajectory); // 发布轨迹
visual_tools.publishTrajectoryLine(display_trajectory.trajectory.back(), joint_model_group); // 发布轨迹线
visual_tools.trigger(); // 触发可视化
/* 等待用户输入 */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo"); // 提示用户输入关节空间目标
robot_state = planning_scene_monitor::LockedPlanningSceneRO(psm)->getCurrentStateUpdated(response.trajectory_start); // 更新机器人状态
robot_state->setJointGroupPositions(joint_model_group, response.trajectory.joint_trajectory.points.back().positions); // 设置关节组位置
moveit::core::robotStateToRobotStateMsg(*robot_state, req.start_state); // 转换机器人状态消息现在,设定一个关节空间目标
moveit::core::RobotState goal_state(*robot_state); // 目标状态
std::vector<double> joint_values = { -1.0, 0.7, 0.7, -1.5, -0.7, 2.0, 0.0 }; // 关节值
goal_state.setJointGroupPositions(joint_model_group, joint_values); // 设置关节组位置
moveit_msgs::msg::Constraints joint_goal =
kinematic_constraints::constructGoalConstraints(goal_state, joint_model_group); // 构建关节目标约束
req.goal_constraints.clear(); // 清除目标约束
req.goal_constraints.push_back(joint_goal); // 添加关节目标约束在规划之前,我们需要在规划场景上设置一个只读锁,以便在规划时不会修改世界表示
{
planning_scene_monitor::LockedPlanningSceneRO lscene(psm); // 锁定规划场景
/* 现在,调用管道并检查规划是否成功。 */
if (!planning_pipeline->generatePlan(lscene, req, res) || res.error_code.val != res.error_code.SUCCESS)
{
RCLCPP_ERROR(LOGGER, "Could not compute plan successfully"); // 规划失败
rclcpp::shutdown(); // 关闭ROS
return -1; // 返回错误代码
}
}
/* 可视化轨迹 */
RCLCPP_INFO(LOGGER, "Visualizing the trajectory"); // 可视化轨迹
res.getMessage(response); // 获取响应消息
display_trajectory.trajectory_start = response.trajectory_start; // 设置轨迹起点
display_trajectory.trajectory.push_back(response.trajectory); // 添加轨迹现在你应该看到两个连续的规划轨迹

display_publisher->publish(display_trajectory); // 发布轨迹
visual_tools.publishTrajectoryLine(display_trajectory.trajectory.back(), joint_model_group); // 发布轨迹线
visual_tools.trigger(); // 触发可视化
/* 等待用户输入 */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo"); // 提示用户输入使用规划请求适配器
规划请求适配器允许我们指定一系列操作,这些操作应在规划发生之前或规划完成后对结果路径进行处理
/* 首先,将规划场景中的状态设置为上一个规划的最终状态 */
robot_state = planning_scene_monitor::LockedPlanningSceneRO(psm)->getCurrentStateUpdated(response.trajectory_start); // 更新机器人状态
robot_state->setJointGroupPositions(joint_model_group, response.trajectory.joint_trajectory.points.back().positions); // 设置关节组位置
moveit::core::robotStateToRobotStateMsg(*robot_state, req.start_state); // 转换机器人状态消息现在,将其中一个关节设置在其上限之外一点点
const moveit::core::JointModel* joint_model = joint_model_group->getJointModel("panda_joint3"); // 获取关节模型
const moveit::core::JointModel::Bounds& joint_bounds = joint_model->getVariableBounds(); // 获取关节边界
std::vector<double> tmp_values(1, 0.0); // 临时值
tmp_values[0] = joint_bounds[0].min_position_ - 0.01; // 设置关节位置
robot_state->setJointPositions(joint_model, tmp_values); // 设置关节位置
req.goal_constraints.clear(); // 清除目标约束
req.goal_constraints.push_back(pose_goal); // 添加姿态目标约束在规划之前,我们需要在规划场景上设置一个只读锁,以便在规划时不会修改世界表示
// 在规划之前,我们需要对规划场景进行只读锁定,以便在规划期间不会修改世界表示
{
planning_scene_monitor::LockedPlanningSceneRO lscene(psm); // 锁定规划场景
/* 现在,调用管道并检查规划是否成功。 */
if (!planning_pipeline->generatePlan(lscene, req, res) || res.error_code.val != res.error_code.SUCCESS)
{
RCLCPP_ERROR(LOGGER, "Could not compute plan successfully"); // 规划失败
rclcpp::shutdown(); // 关闭ROS
return -1; // 返回错误代码
}
}
/* 可视化轨迹 */
RCLCPP_INFO(LOGGER, "Visualizing the trajectory"); // 可视化轨迹
res.getMessage(response); // 获取响应消息
display_trajectory.trajectory_start = response.trajectory_start; // 设置轨迹起点
display_trajectory.trajectory.push_back(response.trajectory); // 添加轨迹
/* 现在你应该看到三个连续的规划轨迹 */
display_publisher->publish(display_trajectory); // 发布轨迹
visual_tools.publishTrajectoryLine(display_trajectory.trajectory.back(), joint_model_group); // 发布轨迹线
visual_tools.trigger(); // 触发可视化
/* 等待用户输入 */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to finish the demo"); // 提示用户输入
RCLCPP_INFO(LOGGER, "Done"); // 完成
rclcpp::shutdown(); // 关闭ROS
return 0; // 返回成功代码
}
启动文件
整个启动文件在 GitHub 上。 本教程中的所有代码都可以从您作为 MoveIt 设置一部分的 moveit_tutorials 包中编译和运行。https://github.com/moveit/moveit2_tutorials/blob/main/doc/examples/motion_planning_pipeline/launch/motion_planning_pipeline_tutorial.launch.py
import os # 导入os模块
import yaml # 导入yaml模块
from launch import LaunchDescription # 从launch模块导入LaunchDescription类
from launch_ros.actions import Node # 从launch_ros.actions模块导入Node类
from ament_index_python.packages import get_package_share_directory # 从ament_index_python.packages模块导入get_package_share_directory函数
from moveit_configs_utils import MoveItConfigsBuilder # 从moveit_configs_utils模块导入MoveItConfigsBuilder类
def load_file(package_name, file_path): # 定义load_file函数,参数为package_name和file_path
package_path = get_package_share_directory(package_name) # 获取包的共享目录路径
absolute_file_path = os.path.join(package_path, file_path) # 拼接得到文件的绝对路径
try:
with open(absolute_file_path, "r") as file: # 尝试以只读模式打开文件
return file.read() # 读取文件内容并返回
except EnvironmentError: # 捕获环境错误(包括IOError、OSError和WindowsError)
return None # 返回None
def load_yaml(package_name, file_path): # 定义load_yaml函数,参数为package_name和file_path
package_path = get_package_share_directory(package_name) # 获取包的共享目录路径
absolute_file_path = os.path.join(package_path, file_path) # 拼接得到文件的绝对路径
try:
with open(absolute_file_path, "r") as file: # 尝试以只读模式打开文件
return yaml.safe_load(file) # 安全地加载yaml文件内容并返回
except EnvironmentError: # 捕获环境错误(包括IOError、OSError和WindowsError)
return None # 返回None
def generate_launch_description(): # 定义generate_launch_description函数
moveit_config = (
MoveItConfigsBuilder("moveit_resources_panda") # 创建MoveIt配置构建器对象
.robot_description(file_path="config/panda.urdf.xacro") # 设置机器人描述文件路径
.planning_scene_monitor( # 设置规划场景监视器
publish_robot_description=True, publish_robot_description_semantic=True
)
.planning_pipelines("ompl", ["ompl"]) # 设置规划管道
.to_moveit_configs() # 转换为MoveIt配置
)
# 运动规划管道演示可执行文件
motion_planning_pipeline_demo = Node(
name="motion_planning_pipeline_tutorial", # 节点名称
package="moveit2_tutorials", # 包名称
executable="motion_planning_pipeline_tutorial", # 可执行文件名称
output="screen", # 输出到屏幕
parameters=[
moveit_config.robot_description, # 机器人描述参数
moveit_config.robot_description_semantic, # 机器人语义描述参数
moveit_config.robot_description_kinematics, # 机器人运动学描述参数
moveit_config.planning_pipelines, # 规划管道参数
moveit_config.joint_limits, # 关节限制参数
],
)
return LaunchDescription([motion_planning_pipeline_demo]) # 返回LaunchDescription对象
通过 C++ API 使用 MoveIt 运动规划管道
3993

被折叠的 条评论
为什么被折叠?



