ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MoveIt集成Ruckig实现机械臂时间最优轨迹规划与平滑控制

MoveIt集成Ruckig实现机械臂时间最优轨迹规划与平滑控制 如果你玩过一段时间MoveIt大概率碰到过这种场景路径规划器明明避开了所有障碍轨迹可视化得很顺滑但机械臂真正跑起来却一顿一顿的尤其在换向、减速的瞬间末端明显颤一下。我最近调一台六轴机械臂被这个问题折磨了好几天最后发现根子在时间参数化上换用Ruckig做机械臂时间最优运动规划之后整个动作顺滑了不止一个档次。这篇文章就结合我自己的实测经历说说怎么把Ruckig算法接进MoveIt让它真正跑出时间最优轨迹顺便把配置过程中那些文档里查不到的坑都摊开讲。1. 为什么换掉默认时间参数化器三个让我难受的现场1.1 第一个现场速度曲线在中间点来回震荡先说项目背景。我手里的这台六轴机械臂最初用MoveIt的OMPL做规划返回的路径本身没问题避障、无碰撞、路径长度也合理。但每次轨迹下发之后机械臂总会在一段路径的中间位置出现明显的速度波动听起来像是关节电机在轻微的“忽快忽慢”。我在Rviz里打开Trajectory Display把速度曲线调出来一看问题一目了然速度曲线不是平滑的梯形或钟形而是像锯齿一样来回跳。问题出在时间参数化上。MoveIt规划出来的是纯路径点也就是一串关节位置序列它不包含“什么时候到达”和“以多快的速度经过”这些信息。MoveIt需要用一个时间参数化器给这些路径点补上时间戳同时计算出每个点的速度和加速度。默认配置里常用的是IterativeSplineParameterization或者TOTGTime-Optimal Trajectory Generation它们各有各的毛病。IterativeSpline生成的速度曲线很圆润但因为用了样条插值会在多个路径点之间“过度拟合”导致速度波动TOTG理论上是时间最优可在某些复杂路径上容易出现数值不稳定速度曲线在个别路径点附近直接冲高再掉下来。这种锯齿状速度曲线对真实电机的伤害很大。伺服驱动器虽然能跟踪位置但速度指令频繁跳变意味着扭矩跟着频繁跳变机械臂结构本身就存在弹性一来二去末端就会抖。我做视觉引导抓取的时候相机就装在末端这一抖直接导致图像模糊定位精度掉了好几个毫米。1.2 第二个现场规划耗时拖慢了整个节拍另一个让我难受的点是规划耗时。我的项目是机器人上下料节拍要求比较高单次运动从“给出目标位姿”到“轨迹下发执行”最好控制在200ms以内。但TOTG在做时间参数化的时候经常需要迭代计算整条路径的可行时间复杂路径下这一项就能吃掉80到100ms整条规划链路跑下来经常超过300ms。对于实验室演示来说不算什么但在产线上这就是实打实的节拍损失。我当时一度怀疑是路径点太多导致的试着降低OMPL的路径插值密度但路点变少之后避障路径变得更粗糙后续时间参数化的质量反而下降。后来我查资料发现时间参数化本身也是一个搜索过程TOTG要处理大量边界条件计算复杂度并不低不是简单“加个时间戳”那么简单。1.3 第三个现场起始段和终止段的速度总是对不上第三个问题更隐蔽。项目里机械臂经常要从“运动中的某一时刻”接管轨迹也就是上一段轨迹刚结束下一段轨迹紧接着执行。MoveIt默认时间参数化器大部分假设起始速度和终止速度都是0但两段轨迹交接的时候机械臂实际上并没有完全停下来。TOTG在这种情况下会强行把起点速度置零结果就是机械臂先减速到0再加速启动既浪费时间又产生冲击。这三个问题叠加在一起我决定换方案。Ruckig就是在那个时候被我盯上的。它本身是一个开源的时间最优轨迹生成库专门解决“在速度、加速度约束下用最短时间从当前状态运动到目标状态”这个问题最关键的是它支持非零起始速度和终止速度并且计算速度快到可以在毫秒级完成。MoveIt在1.1之后的版本里已经把它集成成了trajectory_processing插件可以直接替代默认的时间参数化器使用。2. 把Ruckig接进MoveIt从plugin配置到验证生效2.1 依赖安装与版本确认如果只想用MoveIt自带的方式接入Ruckig不需要额外安装第三方库因为MoveIt core里已经包含了Ruckig的集成实现。但前提是你的MoveIt版本足够新。我所在的环境是ROS Noetic MoveIt 1.1.x这个版本里已经有Ruckig相关头文件了。先确认一下你本地的MoveIt版本rosversion moveit_core如果版本比较老建议先升级到1.1.5以上。老版本里Ruckig集成还不稳定有些甚至没有编译进去。确认版本之后再检查对应的头文件是否存在rospack find moveit_core ls $(rospack find moveit_core)/include/moveit/trajectory_processing/能看到ruckig_smoother.h就说明MoveIt已经编译了Ruckig支持。如果你是自己从源码编译的MoveIt编译的时候留意一下INCLUDE_RUCKIG之类的CMake开关默认应该是开启的。2.2 move_group的trajectory_processing配置MoveIt接入Ruckig的入口是move_group节点中的trajectory_processing参数。这个参数维护了一个时间参数化插件列表MoveIt在规划流程中的AddTimeParameterization规划请求适配器会从列表里选择插件对OMPL生成的原始路径做时间参数化处理。官方推荐的做法是在move_group.launch里加一段rosparam。下面是我在ROS 1环境里的写法rosparam paramtrajectory_processing - plugin: moveit_core/RuckigPTPSmoother parameters: max_velocity_scaling_factor: 0.5 max_acceleration_scaling_factor: 0.5 /rosparam如果你用的是MoveIt 2配置结构稍微有点区别但核心思路一样都是注册一个moveit_core/RuckigPTPSmoother插件。我后来在ROS 2项目里用的是这样的写法trajectory_processing: ruckig_ptp_1: plugin: moveit_core/RuckigPTPSmoother parameters: max_velocity_scaling_factor: 0.5 max_acceleration_scaling_factor: 0.5这里有两个关键参数max_velocity_scaling_factor和max_acceleration_scaling_factor。它们的作用是告诉Ruckig相对于机器人的关节极限当前任务最多允许用多少比例的速度和加速度。0.5表示以关节最大速度和最大加速度的50%为约束条件来计算轨迹。这两个参数直接影响轨迹总时长和平滑度后面我会专门讲怎么调。配好之后理论上MoveIt的所有plan结果都会自动经过Ruckig处理。但这里有个很容易踩的坑如果move_group节点里还加载了默认的trajectory_processing列表或者planning_request_adapter顺序不对Ruckig可能根本不会被调用。2.3 验证Ruckig是否真的生效确认Ruckig生效这件事我一开始走了弯路光靠肉眼观察机械臂动作很难判断是不是Ruckig在起作用。后来我写了个Python脚本直接在MoveIt的plan结果上检查轨迹点的时间戳、速度、加速度。from moveit_commander import MoveGroupCommander group MoveGroupCommander(manipulator) group.set_goal_tolerance(0.01) plan group.plan() for i, point in enumerate(plan.joint_trajectory.points): t point.time_from_start.to_sec() v point.velocities a point.accelerations v_max max(abs(x) for x in v) if v else 0.0 a_max max(abs(x) for x in a) if a else 0.0 print(f[{i}] t{t:.3f}s v_max{v_max:.3f} a_max{a_max:.3f})如果Ruckig生效打印结果里相邻两个路径点的time_from_start间隔会非常紧凑而且速度值会明显贴着某个上限跑而不是像IterativeSpline那样平滑散开。正常的Ruckig轨迹中关节速度会先快速上升、保持一段、再快速下降形成一个很典型的“梯形速度曲线”这基本可以确认时间参数化器已经替换成功。另外也可以检查move_group的启动日志看有没有加载Ruckig插件的记录。在move_group.launch终端里搜索Ruckig关键字能看到类似“Loaded RuckigPTP smoother”的日志这说明插件已经被move_group管理起来了。3. 在MoveIt里用Ruckig生成时间最优轨迹代码与参数调优3.1 方式一通过规划请求适配器自动生效如果你在move_group.launch里配置好了Ruckig插件那么无论你用MoveIt Commander、MoveGroupInterface还是直接调用plan()接口返回的轨迹都已经自动做了Ruckig时间参数化。这里有个细节MoveIt的AddTimeParameterization适配器会从trajectory_processing参数列表里选一个插件来用。如果列表里同时有多个插件默认会选第一个。所以如果你不想完全替换可以把Ruckig放在列表最前面或者干脆只保留Ruckig一个插件。我实际项目里的状态是只保留Ruckig。原因很简单既然决定用Ruckig就不想让MoveIt再有机会偷偷用回别的参数化器。配置越简单排查问题时越省心。3.2 方式二在C里手动调用RuckigPTP做轨迹后处理有些场景不适合自动适配器比如你想对一条已经生成好的RobotTrajectory重新做时间参数化或者想对比不同参数化器的效果。这时可以在C里手动调用RuckigPTP。下面是我在项目里用过的代码骨架以MoveIt 1.1.x为例#include moveit/robot_model_loader/robot_model_loader.h #include moveit/trajectory_processing/ruckig_smoother.h robot_model_loader::RobotModelLoader loader(robot_description); moveit::core::RobotModelPtr model loader.getModel(); robot_trajectory::RobotTrajectory traj(model, manipulator); // 这里省略填充路径点的代码通常是从OMPL规划结果中拷贝而来 // for (auto point : raw_plan.joint_trajectory.points) { ... } trajectory_processing::RuckigPTP smoother(model, manipulator); double vel_scale 0.5; double acc_scale 0.5; bool ok smoother.computeTimeStamps(traj, vel_scale, acc_scale); if (!ok) { ROS_ERROR(Ruckig smoothing failed); }头文件ruckig_smoother.h在MoveIt不同版本里的类名和构造函数可能略有差异有些版本叫RuckigPTP有些版本叫RuckigPTPSmoother参数列表也可能不一样。我建议动手前先打开头文件确认一下具体声明不要想当然照抄否则编译报错会浪费不少时间。3.3 max_velocity_scaling_factor / max_acceleration_scaling_factor怎么选这两个参数是整个Ruckig配置里最有门道的地方。很多人第一次用的时候觉得“时间最优”就是把缩放因子都设成1.0让机械臂跑得越快越好。我在仿真里也这么干过结果轨迹总时间确实短了但机械臂在路径点附近出现了明显的冲击末端振动幅度比TOTG还大。原因是这样的Ruckig的时间最优是在你给定的约束边界内做到时间最短。当速度缩放和加速度缩放都设为1.0时机械臂所有关节都在各自的物理极限边缘工作加减速过程极其激进任何机械结构的柔性都会被放大。真实机械臂并不是一个完美的刚性体减速机间隙、连杆弹性、伺服跟踪误差都会在极限加速度下被激发出来。我的建议是仿真阶段想验证算法极限可以用0.8到1.0试一试看看轨迹形态是否合理。真机运行时从0.3开始稳定之后再逐步往上加每次加0.05左右。如果项目对节拍没有极端要求固定在0.4到0.6之间是最稳的。注意max_velocity_scaling_factor和max_acceleration_scaling_factor可以分开设置。在有些场景下比如负载比较大时我会把加速度缩放设得比速度缩放更低比如速度0.7、加速度0.4。因为加速度直接决定关节扭矩负载增大时先保加速度速度可以适当放开。4. 避坑指南实测中反复踩过的五个坑4.1 坑一配置文件写了但没生效我刚开始配Ruckig时在move_group.launch里加了trajectory_processing配置然后直接用MoveIt Commander规划结果机械臂跑起来还是原来的味道一点变化都没有。排查了很久最后发现问题出在planning_request_adapter参数上。move_group在规划时会按顺序执行一系列规划请求适配器。AddTimeParameterization负责给路径加时间参数但它可能被放在了一个很靠前的位置之后又有别的适配器对轨迹做了后续处理覆盖了Ruckig的时间参数化结果。另外有些默认的move_group配置里trajectory_processing参数是从外部的yaml文件加载的move_group.launch里的rosparam被整体覆盖了自然也不会生效。排查方法很简单先确认参数到底加载了没有rosparam get /move_group/trajectory_processing如果输出为空或者还是原来的插件列表说明launch里的写法有问题。再查看当前生效的适配器列表rosparam get /move_group/planning_request_adapter确认AddTimeParameterization在列表中并且排在合适的位置。通常它应该放在一系列FixStart/FixGoal适配器之后、最后一步执行。我最终把Ruckig配置单独写到一个yaml文件里然后在launch中显式加载不再依赖内联rosparam问题才彻底解决。4.2 坑二joint_limits没有加速度限制Ruckig直接罢工另一个让我印象深刻的坑发生在把Ruckig迁移到另一台机械臂的时候。那台机械臂的URDF模型只有关节位置和速度限制max_acceleration没有定义。MoveIt生成joint_limits.yaml时加速度限制字段是缺失的。结果Ruckig在初始化的时候拿不到有效的加速度上限生成的轨迹时间非常短速度曲线干脆变成了方波机械臂在仿真里差点把关节甩脱。这个问题的根子在于URDF本身主要定义运动学参数速度限制和加速度限制通常在moveit_config的joint_limits.yaml里补充。但很多机械臂供应商不重视加速度限制导致默认配置里这一项是空的。解决办法是给每个关节补上合理的加速度限制。数值可以参考电机手册上的空载角加速度或者先粗略估计一个值然后在仿真和真机上调试。比如我的六轴臂关节1转速比较暴力加速度限制给到6.28 rad/s²末端关节轻巧给到15 rad/s²左右。补齐之后Ruckig的轨迹马上就正常了。4.3 坑三“时间最优”不等于“柔顺”真机上容易抖这个坑我在前面已经提过但值得单独说。Ruckig生成的轨迹在数学上是最优的但机械臂执行起来不一定舒服。时间最优意味着它会在约束边界处“贴边跑”速度转换点非常尖锐。尤其在多段路径连接的地方关节扭矩方向突变机械结构会因为这个冲击产生振动。我在真机上跑过一次缩放因子0.9的Ruckig轨迹动作确实快但末端执行器在几个换向点上的振动非常明显甚至会发出“咔咔”的声响。后来我把加速度缩放降到0.5速度缩放保持0.8振动明显减弱运动总时间只增加了约20%。对于大多数应用来说用20%的时间换机械结构的稳定非常划算。如果你遇到真机抖动优先降低max_acceleration_scaling_factor而不是速度缩放。加速度是抖动的直接来源速度缩放影响的主要是运动时间。4.4 坑四Gazebo仿真频率低导致轨迹跟踪失真很多人在Gazebo里测试Ruckig轨迹发现机械臂并没有像Rviz里看到的那么平顺反而有点飘。这个锅不能全让Ruckig背更多是Gazebo仿真步长和轨迹发布频率不匹配的问题。Gazebo默认的物理更新频率通常是1000Hz但MoveIt发布的轨迹点间隔一般在几十毫秒级别。机器人控制器在接收轨迹后需要在两个路径点之间做插值如果插值算法不够顺滑或者仿真器侧的控制周期太慢就会出现机械臂“一顿一顿”的现象。我的解决方法是把MoveIt的轨迹点发布频率调低一些让控制器有足够的时间在两个路径点之间平稳插值。具体来说在move_group.launch里可以增加trajectory_execution相关配置让FollowJointTrajectoryAction服务端的插值器接管平滑工作。另外把Gazebo的updateRate适当调高到1000Hz以上也能缓解跟踪问题。4.5 坑五MoveIt里的Ruckig并没有真正传jerk限制进去Ruckig核心库本身是支持加加速度jerk限制的这也是它生成S形速度曲线的能力来源。但我在查阅MoveIt集成代码时发现MoveIt的RuckigPTP插件目前主要读取的是速度限制和加速度限制并不会自动从URDF或joint_limits里读取jerk限制。也就是说我通过MoveIt配置的Ruckig实际上跑的是无jerk限制的版本速度曲线是梯形而不是S形。这个信息在官方文档里写得很隐晦我一开始也没注意直到发现轨迹速度曲线始终是梯形关节加速度存在突变才意识到jerk限制根本没传进去。如果你的应用对机械臂的冲击控制有严格要求比如精密装配或者打磨需要自己改Ruckig的调用代码手动在输入参数里设置每个关节的max_jerk值或者直接在控制器层面对运动指令做二次滤波。5. 效果对比与项目后续改进方向5.1 数据对比规划耗时与运动时间的直观差异为了让大家有个直观感受我把自己项目里同一段PTP运动分别用IterativeSpline、TOTG和Ruckig跑了一遍记录了三组关键指标。说明一下这个数据只代表我调试的这台六轴机械臂不同机器人差异会很大但趋势是通用的。指标IterativeSplineTOTGRuckigscaling0.6规划耗时约45ms约85ms约12ms轨迹总时间4.7s3.4s2.9s速度曲线的明显跳变次数1次3次0次路径点平均间隔较均匀不均匀较紧凑Ruckig在规划耗时上的优势非常明显12ms对85ms差了7倍。这在高节拍应用里是决定性的优势。轨迹总时间也有提升但前提是缩放因子设置得当如果设成0.4以下Ruckig的总时间未必比TOTG短多少但平滑度依然优于TOTG。5.2 不同缩放因子对运动时间的影响我进一步测了不同缩放因子对同一段运动总时间的影响供大家选参时参考速度/加速度缩放因子轨迹总时间主观感受1.02.1s速度快末端冲击明显0.62.9s比较激进真机需要谨慎0.34.6s很柔和适合高精度任务看到1.0和0.3之间差了2.5秒很多人会纠结要不要用1.0。从我实操经验来看除非机械臂刚性和伺服性能极强否则不建议把加速度缩放拉满。Ruckig的意义是“不浪费物理能力”而不是“把设备往极限逼”。先求稳再求快是时间最优轨迹落地时最核心的价值观。5.3 后续改进方向Ruckig只解决了时间参数化层面的问题后续要做的事情还有很多。我在项目里的下一步方向有两个一个是把Ruckig和笛卡尔路径结合起来也就是对computeCartesianPath得到的路径做同样的时间参数化让末端在笛卡尔空间里保持时间最优另一个是研究Ruckig OTG模式实现在线轨迹生成让机械臂在运动过程中根据视觉反馈实时调整目标而不用停下来重新规划。这两个方向都值得深入探索。最后分享一个我个人的使用习惯不管仿真还是真机我都不会直接把缩放因子拉满。先在仿真里用0.5跑通流程观察轨迹形态和速度曲线再渐进式提高。之前有一次图省事设成1.0机械臂末端在换向点直接把固定支架打晃了从那以后我再也没敢“满配”跑过。时间最优是个好东西但落地的时候一定要留出工程余量。
返回列表