ARTICLE DETAIL

资讯详情

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

麦克纳姆轮与全向轮运动学逆解:从公式到代码实现调试指南

麦克纳姆轮与全向轮运动学逆解:从公式到代码实现调试指南 1. 为什么先聊运动学逆解从一台原地画葫芦的底盘说起有段时间我在调一台四麦克纳姆轮小车代码逻辑看起来没毛病给前进指令它也确实往前跑但跑着跑着就歪原地旋转时轮子动作混乱得像在跳踢踏舞。最后发现根本不是电机或驱动板的问题而是我直接把“前进 四个轮子一起正转”这个直觉写进了代码完全绕过了运动学解算。这个锅得由“底盘运动学”来背。麦克纳姆轮和全向轮这两种轮子外形和原理各不相同但底盘层面它们都属于“全向移动平台”。区别在于普通差速底盘只有两个自由度前后和旋转耦合在一起想横移只能靠多次调整而全向底盘天生支持前后、左右、旋转三个自由度的独立控制。但“天生支持”不代表直接给电机发PWM就能做到。你给遥控器一个“向右平移”的指令轮子各自应该转多快、往哪个方向转这就是运动学逆解要回答的问题。所谓逆解就是从“我想要底盘怎么运动”反推出“每个轮子该怎么转”。与之相对的正解是从轮子转速反推底盘速度主要用于里程计和打滑检测两者是一对镜像关系。这篇文章主要围绕三件事展开全向轮和麦克纳姆轮的运动学模型怎么建立、逆解公式怎么一步步推导出来、代码层怎么实现并处理调参和机械误差带来的各种坑。适合正在做机器人竞赛底盘、AGV小车、ROS机器人底盘的开发者参考哪怕你之前完全没接触过运动学跟着走一遍也能把底盘跑直。2. 两种轮子的差异与底盘建模思路2.1 麦克纳姆轮和全向轮长得像性格差很多很多人分不清全向轮和麦克纳姆轮确实从底盘控制角度来看逆解公式的推导思路高度相似但轮子本身的工作原理差别明显。全向轮也叫瑞典轮或正交轮结构是一圈小辊子从动轮垂直于主轮旋转轴方向分布轮子滚动时辊子会在接触地面处产生侧向滑动所以全向轮能够实现侧向的“被动滑动”。它最大的特点是对安装角度和辊子方向要求不那么苛刻常见有三轮全向底盘和四轮全向底盘。麦克纳姆轮则是在轮毂外圈斜向安装了一排纺锤形辊子辊子轴线与主轮轴线呈45度角。正因为辊子与地面接触时会产生沿辊子轴线方向的滑动分力麦克纳姆轮才能实现全向移动。但麦克纳姆轮有一个“方向性”的问题左旋轮和右旋轮必须成对使用安装时还要注意辊子朝向形成的“V”形或“X”形布局装反了底盘会左右乱飘永远跑不直。从工程角度说全向轮结构简单、价格便宜承载能力和抗冲击性能更好麦克纳姆轮操控灵活、摩擦更均匀但辊子容易磨损对地面平整度比较敏感价格也更高。全向轮多用于对成本敏感的竞赛底盘和轻型AGV麦克纳姆轮则是重载AGV、服务机器人、仓储搬运机器人的常客。2.2 从“三自由度”理解底盘运动学模型以麦克纳姆轮为例底盘在水平面上的运动可以分解成三个独立的自由度沿底盘坐标系X轴的前后移动、沿Y轴的左右移动、绕Z轴的旋转。底盘坐标系通常固定在小车本体上X朝前Y朝左Z朝上旋转角速度以逆时针为正。注意这里的X/Y/Z描述的是底盘坐标系不是世界坐标系。这两个概念如果不分清楚后面调试会出现很隐蔽的bug。比如你按世界坐标系的“向前”给指令但底盘已经旋转了90度此时底盘坐标系下的“向前”和世界坐标系下的“向前”完全是两个方向。麦克纳姆轮逆解通常默认输入量已经是底盘坐标系下的速度如果你从遥控器或导航模块拿到的速度是世界坐标系下的值需要先做坐标变换再喂给逆解函数。如果只有两个输入量比如常见的“前后转向”运动学方程会欠定也就是解不出来唯一解这也是很多入门代码里麦克纳姆轮“跑不直”的根本原因之一。建模之前还需要约定几个物理量。以四轮麦克纳姆轮底盘为例底盘前后轮距轴距记为a左右轮距记为b单位米轮子半径记为r单位米四个轮子转速分别记为ω1、ω2、ω3、ω4单位rad/s弧度每秒底盘目标速度为 (vx, vy, ωz)速度单位m/s角速度单位rad/s有了这些符号约定就可以开始推公式了。3. 运动学逆解推导从速度分解到矩阵方程3.1 先把“轮子速度”拆成“底盘速度”麦克纳姆轮底盘的逆解推导最核心的一个思想是把每个轮子与地面的接触点的运动分解为底盘平移运动在轮子处产生的速度加上底盘旋转运动在该点处产生的线速度两者叠加。底盘绕Z轴旋转时轮子接触点相对底盘中心有一个旋转半径这个半径就是轮子位置矢量的长度。根据刚体运动学某点的线速度等于角速度叉乘位置矢量对于二维平面里的底盘这个叉乘结果是一个与位置矢量垂直、大小为ωz乘以距离的速度向量。以右前轮为例假设它相对底盘中心的坐标为(a/2, -b/2)X向前、Y向左右前轮Y坐标为负底盘旋转产生的速度分量为(-ωz * (-b/2), ωz * (a/2)) (ωz * b/2, ωz * a/2)注意这里正负号非常容易出错必须结合右手定则仔细核对。再加上平移运动贡献的(vx, vy)右前轮接触点的绝对速度是(vx ωz·b/2, vy ωz·a/2)同理可推其他三个轮子接触点的绝对速度我这里直接给出结论因为推导过程完全相同只是坐标正负号变化左前轮坐标-a/2, b/2(vx - ωz·b/2, vy ωz·a/2)左后轮坐标-a/2, -b/2(vx - ωz·b/2, vy - ωz·a/2)右后轮坐标a/2, -b/2(vx ωz·b/2, vy - ωz·a/2)这四个速度向量就是每个轮子必须实现的合成速度。接下来的任务是把这个合成速度映射到轮子的驱动方向和辊子滑动方向上。3.2 瑞子角度与速度映射为什么要取45度麦克纳姆轮的辊子轴线与轮子主轴线呈45度角。设轮子前进方向主轮滚动方向为u轴辊子轴线方向为v轴地面接触点的合成速度可以沿这两个方向分解。轮子能直接驱动的只有u方向的分量v方向的分量被辊子以滚动方式“吸收”掉。设轮子的驱动线速度为v_wheelu沿轮子前进方向则合成速度在u方向的分量就等于v_wheelu。而合成速度在u方向的分量 合成速度的x分量乘以cos45度加上合成速度的y分量乘以sin45度具体符号取决于辊子安装方向。这里有个细节非常关键麦克纳姆轮分为左旋轮辊子从左上到右下和右旋轮辊子从右上到左下左前轮和右后轮一组右前轮和左后轮一组。安装时如果从底盘上方看同侧两个轮子的辊子朝向呈“V”形或“X”形。公式里的符号变化正是由这个“辊子朝向”决定的。推导过程中关键是利用回代法给一个条件vx、vy、ωz求一组ω1~ω4使得底盘产生期望的运动。整个推导过程都建立在“轮子不打滑”的理想假设上实际中辊子与地面的摩擦损耗、地面平整度等都会造成偏差但逆解公式作为前馈控制输入依然是必须的误差部分由闭环控制去补偿。以下直接给出四轮麦克纳姆轮常用的逆解公式设所有轮子半径相同均为rω1左前轮 (1/r) · (vx - vy - ωz·(a b))ω2右前轮 (1/r) · (vx vy ωz·(a b))ω3左后轮 (1/r) · (vx vy - ωz·(a b))ω4右后轮 (1/r) · (vx - vy ωz·(a b))注意这里的正负号受轮子安装方向、辊子朝向、坐标系定义影响可能和某些资料不完全一样。工程上常见做法是先选一个约定推导出对应公式如果实测发现方向反了就调整符号比拿到公式直接抄要靠谱。3.3 三轮全向轮的推导另一种全向方案三轮全向轮底盘的推导思路类似但轮子安装角不再是0/90度而是互成120度。常见布局三个全向轮按等边三角形分布每个轮子的驱动方向分别指向底盘中心。设三个轮子的驱动方向单位向量分别为d1、d2、d3每个轮子接触点的位置矢量为p1、p2、p3。轮子驱动线速度应等于底盘速度在驱动方向上的投影再加上旋转运动在该点产生的线速度沿驱动方向的分量。公式形式为设轮距中心到每个轮子距离为L轮子半径rv1 -sin(θ)·vx cos(θ)·vy L·ωzv2 -sin(θ 120°)·vx cos(θ 120°)·vy L·ωzv3 -sin(θ 240°)·vx cos(θ 240°)·vy L·ωz其中θ是第一个轮子的安装角度通常取0度或与X轴的夹角实际部署时依据底盘布局具体标定。全向轮辊子方向与轮子驱动方向垂直所以速度分解时只有驱动方向的分量需要关注辊子方向的被动速度不会产生驱动力——这正是全向轮“少了一个约束”的体现。全向轮推导中L从底盘中心到轮子接触点的距离是重要的标定参数如果这个值量得不准旋转运动会明显偏大或偏小。有些三轮底盘的三个轮子并不在同一个切圆上需要用几何关系分别求出每个轮子的旋转力臂长度不能直接套用统一L。四轮全向轮底盘也常见推导思路类似但四个轮子的驱动方向呈90度间隔公式更接近麦克纳姆轮的对称结构。只是四轮全向轮存在“超静定”问题四个轮子同时着地时地面约束过多容易产生轮子之间互相干涉、内耗的情况。实际应用四轮全向底盘时通常会在悬挂系统上做文章比如使用浮动结构保证四个轮子始终贴地。4. 从公式到代码逆解函数的工程化实现4.1 逆解函数的基本结构有了逆解公式代码实现并不复杂。核心是写一个函数输入是底盘目标速度输出是四个轮子的转速。关键点在于接口设计和单位统一。真实工程中电机转速单位一般有两种rad/s弧度每秒和RPM转每分钟而PWM占空比或者速度控制指令往往又映射到某个线性比例上。如果单位不一致会出现速度指令完全乱套的情况。我习惯在逆解函数内统一使用rad/s在进入电机驱动层再做单位转换typedef struct { float vx; // 前进速度单位 m/s float vy; // 左右速度单位 m/s float wz; // 旋转角速度单位 rad/s逆时针为正 } ChassisVelocity; typedef struct { float w1; // 左前轮角速度单位 rad/s float w2; // 右前轮角速度单位 rad/s float w3; // 左后轮角速度单位 rad/s float w4; // 右后轮角速度单位 rad/s } WheelSpeed; WheelSpeed InverseKinematicsMacanum(ChassisVelocity cmd, float wheel_radius, float wheel_base, float wheel_track) { WheelSpeed out; float k 1.0f / wheel_radius; out.w1 k * (cmd.vx - cmd.vy - cmd.wz * (wheel_base wheel_track)); out.w2 k * (cmd.vx cmd.vy cmd.wz * (wheel_base wheel_track)); out.w3 k * (cmd.vx cmd.vy - cmd.wz * (wheel_base wheel_track)); out.w4 k * (cmd.vx - cmd.vy cmd.wz * (wheel_base wheel_track)); return out; }以上代码里wheel_base是轴距前后轮距离即2awheel_track是轮距左右轮距离即2b公式中用的是ab换算成整车参数就是(wheel_base wheel_track) / 2但看上面代码可以发现已经把2a和2b代入化简轮距轴距直接用整车长度。这里有个特别容易踩的坑wheel_base和wheel_track如果填成“单侧距离”而不是“整车距离”算出来的旋转项会直接小了一半底盘旋转速度明显不足。很多入门代码出问题就出在这个参数定义上不是公式错了而是“这里的a到底是多少”没对齐。对于三轮全向轮代码结构类似只是输入输出从四个轮子变成三个且驱动方向需要额外传入安装角度数组。4.2 限幅、方向映射和死区处理逆解算出来的轮子转速是理论值实际发给电机前还必须做三件事。第一限幅。电机有最高转速逆解输出超过上限时必须整体缩放而不是简单截断。如果单独截断底盘在朝某个方向高速运动同时旋转时个别轮子会“丢失”部分速度指令导致实际运动方向偏离预期。正确处理方式先求出四个轮子转速中的最大值如果超过电机最大转速把所有四个转速统一乘以缩放系数。float max_speed fmax(fabs(w1), fmax(fabs(w2), fmax(fabs(w3), fabs(w4)))); if (max_speed MAX_WHEEL_SPEED) { float scale MAX_WHEEL_SPEED / max_speed; w1 * scale; w2 * scale; w3 * scale; w4 * scale; }第二方向映射。逆解输出的正负号对应轮子正反转但不同电机驱动板对方向的定义可能相反。有的驱动板正PWM为正转有的相反所以通常用“PWM绝对值 方向引脚高低电平”组合控制逆解负责算出符号驱动层负责把符号解释成方向引脚状态。第三死区处理。实际电机在低速下存在静摩擦死区逆解输出接近0的转速指令可能被摩擦力吃掉底盘无法微动。常见做法是设定一个最小有效转速阈值低于阈值的直接置0高于阈值的再映射为实际PWM。但这个阈值不能过大否则底盘低速操控会变得一顿一顿。4.3 逆解代码的测试基准代码写完以后建议先用一组已知速度值做“脑算验证”确认公式和代码没有低级错误。比如期望底盘纯前进(vx1, vy0, ωz0)代入麦克纳姆轮公式四个轮子转速应该完全相同都是vx/r底盘直线前进。期望纯横移(vx0, vy1, ωz0)此时四个轮子转速方向会呈现两正两负而且符号分布恰好和辊子朝向匹配底盘左右平移。最直观的自检方法是做“原地旋转”测试设置vx0, vy0, ωz某个值四个轮子转速应该是两侧方向相反、同侧方向相同且左右两侧转速方向相反。如果同侧轮子转速方向都不同说明公式正负号有问题需要回到推导检查辊子朝向的方向约定。我最开始调试时先用这些基准测试排查掉了公式正负号和参数定义问题才上地面跑动测试。这一步省下了大量在实物上反复试错的时间。5. 调试实录画葫芦、抖动、飘移分别是什么原因5.1 现象一直行变弧线底盘画葫芦这是最常见的麦克纳姆轮底盘问题。排除机械因素轮子磨损、辊子卡死后优先检查轮子转速是否真的一致。用串口把四个轮子的编码器读数打印出来如果某侧轮子转速偏高或偏低问题一般在轮子直径不一致、电机减速比不同、或者PWM到实际转速的标定系数没有归一化。四轮全向底盘对轮子一致性要求很高我遇到过四个轮子型号相同但新旧程度不同实测转速差异达到5%底盘跑十米就偏出去二三十厘米。这种情况下纯靠运动学逆解无法根治要么换新轮子要么在四个轮子各自的转速闭环里做单独的标定系数校准也就是“轮子半径等效值校准”。5.2 现象二大油门时底盘抖动、电流飙高底盘高速运行时抖动很多人第一反应是PID参数出了问题实际上还有一个隐藏因素运动学逆解的输出和机械结构干涉。具体来说如果四个轮子的转速不满足运动学约束例如某个轮子的辊子已经磨损滑动阻力加大底盘就会处在“内耗”状态——四个轮子你推我搡造成抖动和电流异常。排查方法很简单把底盘抬起来空转测量四个轮子无负载空转电流再压到地面测带载电流两者差值异常大的那个轮子就是“内耗源头”。另外底盘结构刚性不足时运动学约束也会被弹性变形消化掉一部分表现为轻载正常、重载抖动。这类问题靠代码难以完全消除只能通过增加底盘刚性、减小轮子磨损或者引入轮子间的力控制来解决。5.3 现象三低速指令下底盘不走高速才动这也是典型的死区和静摩擦问题。麦克纳姆轮的辊子与地面接触面积小局部压强高低速时静摩擦更大所以死区现象比其他底盘更明显。处理方式除了前面说的最小有效转速阈值还可以在控制周期里加一个“速度前馈补偿”对小速度指令做阶梯式起步先给一个短暂的高PWM脉冲克服静摩擦再降回目标速度。这个脉冲宽度和幅值要靠实测整定一般是目标转速的30%~50%额外补偿持续50到100毫秒。5.4 排查链路一个典型的“跑偏”问题全流程某次实测底盘直线前进时往左偏我按以下链路逐步排查最终定位到问题第一步代码层确认。串口打印四个轮子的转速指令确认逆解输出一致排除代码问题。第二步编码器确认。打印四个轮子实际转速发现右后轮比指令值低8%左右其他三个轮子基本一致排除编码器计数值异常。第三步机械检查。右后轮拆下来检查发现辊子有两颗已经磨损严重转动时有明显卡顿同时该轮轴承锈蚀。第四步更换磨损辊子和轴承后复测转速一致性恢复底盘跑直线不再偏移。这个案例说明运动学逆解只是给出了“理想解”实际系统能不能精确复现这个解取决于轮子状态、电机一致性、地面平整度等一堆现实因素。所以调试时不要一上来就怀疑公式和代码先从最容易验证的机械和传感器环节入手反向排除。6. 参数标定与不易察觉的陷阱6.1 轮距轴距参数一半的跑偏都和它有关麦克纳姆轮逆解公式里旋转项系数是(ab)如果把a和b的单位搞混或者量的是轮子中心距而不是辊子接触点距底盘旋转运动就会出现系统性偏差。用卡尺量轮距、轴距时注意要从轮子与地面的接触中心去量而不是从底盘结构边缘去量。对于带悬挂的底盘轮距会随悬挂压缩量变化需要在设计载荷下测量。6.2 轮子半径的等效值理论半径不等于有效半径麦克纳姆轮辊子外圈并非完美圆弧受压后有效半径会变小直接使用厂家标称半径会导致低速时转速偏大、高速时转速偏小。实际做法是做一个“等效半径标定”让底盘在直线模式下以固定PWM跑一段已知距离测量实际位移反推出等效半径。比如标称半径0.05米的轮子实测1秒钟PWM对应的速度是0.48m/s那等效半径就是0.048米。用这个等效值重建逆解公式直线运行精度会明显提高。我调过的底盘里等效半径和标称半径最大能差到5%~8%对速度精度要求高的场景绝对不能忽略。6.3 底盘坐标系与安装方向陀螺仪数据进来就乱很多底盘不只用编码器闭环还接IMU做角度闭环。这时IMU的安装方向必须和底盘坐标系对齐否则角速度反馈符号反了闭环会越调越震荡。检查方法底盘放在地上手动把底盘绕Z轴逆时针旋转从上方看观察IMU输出的z轴角速度是否为正。如果不是需要翻转坐标映射而不是在代码里加负号因为IMU的航向角还会影响后续坐标变换加负号容易埋雷。6.4 速度指令斜坡与加速度限制直接给逆解函数施加突变的速度指令会让底盘瞬间加速电机过流、底盘点头、轮子打滑都可能出现。工程上会在逆解之前加一个“速度斜率限制器”让vx、vy、ωz各自按设定加速度线性爬升到目标值底盘起步和刹车就平顺多了。加速度上限怎么选主要看底盘重心高度和轮子摩擦系数。重心高、摩擦小的底盘加速度太大会翻车或打滑重心低、摩擦大的底盘可以给更大的加速度。从2m/s²开始整定通常是比较安全的起点。7. 多平台落地从单片机到ROS的一点经验运动学逆解代码的移植性很好本质就是一个纯数学函数不依赖特定硬件。我在STM32上用过C语言实现在ROS小车里用过Python实现在带实时系统的工业控制器里也写过C版本核心逻辑几乎没变变的只是输入输出接口。在STM32这类单片机上注意浮点运算耗时旧款Cortex-M3如果没有FPU一次逆解计算量不大但电机速度环频率如果跑到1kHz再加上限幅、斜坡、PID浮点运算量会明显增加。这时候可以考虑定点化优化或把逆解频率降下来只在速度指令变化时重新计算。在ROS环境下常见做法是写一个节点订阅/cmd_vel话题包含线速度和角速度调用逆解函数转换成四个轮子的速度指令再发布到轮子的速度控制器话题上。注意/cmd_vel里的线速度分量是在移动基座坐标系下的如果你的导航模块输出的是全局坐标系速度需要先做坐标变换否则挪动一下底盘朝向运动方向就乱了。ROS里调试时用rqt_plot同时绘制四个轮子的速度曲线非常方便能直观看到四个轮子是否同步、有没有超调、是否触发了限幅。曾经有一次我调PID时速度曲线出现高频抖动就是通过rqt_plot发现只有某个轮子的编码器反馈异常最终锁定了是编码器线束接触不良。8. 收尾前再补充两个容易忽略的细节第一关于轮子旋转方向的正负号约定每个厂家的驱动器定义都不一样绝对不能默认“正数就是正转”。我见过的驱动板里有的正PWM为正转有的是反的有的用方向引脚控制正反有的用负PWM表示反转。最稳妥的方式是先单独测试每个轮子确认“正PWM”对应的实际转动方向再决定逆解输出要不要翻转符号。第二速度指令的坐标正方向最好做成可配置项。有些遥控器给的“前进”是Y方向有些手柄的推杆向前是负Y方向做成宏定义或配置文件比每换一次遥控器就改一遍代码强得多。同理PID方向、编码器计数方向、IMU安装方向都最好集中放在一个“配置头文件”或者“参数配置文件”里调试时改了哪一项一目了然。做底盘运动学这块最大的体会就是公式本身不复杂但任何一步符号约定、参数单位、机械安装的偏差都会在实物上被放大成很玄学的故障。只要把推导过程吃透、把参数标定做扎实、把调试链路理顺剩下的问题都只是时间问题。
返回列表