在机器人运动规划领域,OMPL(Open Motion Planning Library)可以说是最流行的开源算法库之一。它提供了RRT、PRM、EST等经典规划算法,被广泛集成在MoveIt等框架中。但实际项目中,我们经常会遇到标准算法无法满足需求的情况:
这时候就需要魔改OMPL算法了。我去年给一个汽车生产线项目做喷涂机械臂规划时,就遇到过标准RRTConnect在复杂曲面上的规划成功率不足60%的情况。后来通过继承RRTConnect类,加入曲面法向量引导采样策略,最终将成功率提升到92%。
不过修改算法只是第一步,真正的挑战在于如何让MoveIt2正确加载你的定制算法。下面我就用最直白的语言,带你走通从源码修改到RViz验证的全流程。
推荐使用以下组合搭建开发环境:
bash复制# 系统与ROS版本
OS: Ubuntu 22.04
ROS: Humble Hawksbill
MoveIt2: 从源码编译
OMPL: 1.5.2源码安装
# 工作空间结构
~/workspaces
├── ompl_src # OMPL源码
├── ws_moveit2 # MoveIt2源码
└── robot_ws # 机械臂配置包
建议将OMPL安装到自定义目录(如/opt/ompl_humble),避免与系统自带版本冲突。编译时关键参数:
bash复制cmake ../.. \
-DCMAKE_INSTALL_PREFIX=/opt/ompl_humble \
-DOMPL_BUILD_DEMOS=OFF \ # 关闭示例节省编译时间
-DPYTHON_EXECUTABLE=/usr/bin/python3
在OMPL源码中添加自定义规划器时,文件放置位置很有讲究。以扩展RRTConnect为例:
code复制ompl_src/ompl
└── src
└── ompl
└── geometric
└── planners
└── rrt
├── MyCustomRRTConnect.h
└── src
└── MyCustomRRTConnect.cpp
这种布局有两个好处:
GLOB_RECURSE自动收集源文件,无需修改构建配置先来看一个能快速跑通的基础实现。头文件主要做三件事:
cpp复制// MyCustomRRTConnect.h
#pragma once
#include "ompl/geometric/planners/rrt/RRTConnect.h"
namespace ompl {
namespace geometric {
class MyCustomRRTConnect : public RRTConnect {
public:
MyCustomRRTConnect(const base::SpaceInformationPtr &si);
// 必须重写的关键方法
base::PlannerStatus solve(
const base::PlannerTerminationCondition &ptc) override;
// 自定义参数示例
void setMaxExtensionSteps(unsigned int steps);
};
} // namespace geometric
} // namespace ompl
对应的CPP文件建议分阶段实现:
cpp复制// 第一阶段:直接复用父类逻辑
base::PlannerStatus MyCustomRRTConnect::solve(
const base::PlannerTerminationCondition &ptc)
{
return RRTConnect::solve(ptc); // 先保证能运行
}
// 第二阶段:逐步添加自定义逻辑
base::PlannerStatus MyCustomRRTConnect::solve(...) {
// 添加预处理步骤
preprocess();
// 带条件地调用父类方法
if (use_heuristic_) {
return enhancedSolve(ptc);
} else {
return RRTConnect::solve(ptc);
}
}
编译安装后,务必检查动态库是否包含你的类:
bash复制nm -C /opt/ompl_humble/lib/libompl.so | grep MyCustomRRTConnect
正常应该看到类似输出:
code复制0000000000a7b8a0 T ompl::geometric::MyCustomRRTConnect::solve(...)
0000000000a7b6c0 T ompl::geometric::MyCustomRRTConnect::MyCustomRRTConnect(...)
MoveIt2通过planning_context_manager.cpp管理规划器注册,需要修改两处:
cpp复制#include <ompl/geometric/planners/rrt/MyCustomRRTConnect.h>
registerDefaultPlanners()中添加:cpp复制registerPlannerAllocatorHelper<og::MyCustomRRTConnect>(
"geometric::MyCustomRRTConnect");
编译时只需单独构建ompl插件包:
bash复制colcon build --packages-select moveit_planners_ompl
如果遇到链接错误,可能是环境变量问题。检查以下两项:
bash复制# 确保找到自定义OMPL版本
echo $CMAKE_PREFIX_PATH | grep ompl_humble
# 检查库路径
ldd install/moveit_planners_ompl/lib/libmoveit_ompl_interface.so | grep ompl
在机械臂配置包的ompl_planning.yaml中需要添加:
yaml复制planner_configs:
MyCustomRRTConnect:
type: geometric::MyCustomRRTConnect
range: 0.1 # 基础步长
max_extension_steps: 50 # 自定义参数
heuristic_weight: 0.8 # 引导采样权重
然后关联到规划组:
yaml复制arm_group:
planner_configs: [SBLkConfigDefault, RRTConnectkConfigDefault, MyCustomRRTConnect]
重要提醒:修改YAML后必须重新编译安装配置包!因为MoveIt2运行时读取的是install/.../config/下的文件。验证方法:
bash复制grep -r "MyCustomRRTConnect" install/your_robot_config/
创建init_custom_env.sh确保环境纯净:
bash复制#!/bin/bash
# 清除可能干扰的环境
unset LD_LIBRARY_PATH
conda deactivate 2>/dev/null
# 加载自定义OMPL
export CMAKE_PREFIX_PATH=/opt/ompl_humble:$CMAKE_PREFIX_PATH
export LD_LIBRARY_PATH=/opt/ompl_humble/lib:$LD_LIBRARY_PATH
# 加载MoveIt2和机械臂工作空间
source ~/ws_moveit2/install/setup.bash
source ~/robot_ws/install/setup.bash
bash复制nm -C $(ros2 pkg prefix moveit_planners_ompl)/lib/libmoveit_ompl_interface.so | grep MyCustom
bash复制ros2 param get /move_group arm_group.planner_configs
当自定义规划器没有按预期工作时,可以采取以下排查策略:
在规划器代码中添加ROS2风格日志:
cpp复制#include <rclcpp/logging.hpp>
RCLCPP_INFO(ompl::msg::getLogger(),
"Custom planner started with %d rounds", numRounds_);
需要先在OMPL编译选项中启用ROS支持:
bash复制cmake -DOMPL_BUILD_ROS=ON ...
使用OMPL内置的规划统计功能:
cpp复制// 在solve()方法末尾添加:
stats_.totalPlanningTime = time::now() - startTime;
statistics.properties["custom_metric"] = myCustomMetric;
然后在MoveIt中通过/move_group/ompl_statistics话题获取数据。
通过ROS2参数回调实现运行时调整:
cpp复制// 在规划器类中添加:
void parametersCallback(
const std::vector<rclcpp::Parameter> ¶meters)
{
for (const auto ¶m : parameters) {
if (param.get_name() == "heuristic_weight") {
heuristic_weight_ = param.as_double();
}
}
}
// 在MoveIt插件中注册:
declare_parameter("heuristic_weight", 0.5);
parameters_callback_handle_ = add_on_set_parameters_callback(
std::bind(&MyCustomRRTConnect::parametersCallback, this, _1));
实现一个支持算法组合的复合规划器:
cpp复制class HybridPlanner : public base::Planner {
public:
void addPlanner(const base::PlannerPtr &planner) {
planners_.push_back(planner);
}
base::PlannerStatus solve(...) override {
for (auto &planner : planners_) {
auto status = planner->solve(ptc);
if (status) return status;
}
return base::PlannerStatus::TIMEOUT;
}
};
这种架构特别适合需要fallback策略的场景。