资讯动态

麦克纳姆轮底盘开环控制与PS2遥控实战:从运动学解算到四轮全向移动

发布时间:2026/9/9 5:43:44 来源:尧图企业网站定制
简介基于STM32F103ZET6的麦克纳姆轮底盘开环控制方案面向STM32学习者和机器人爱好者解决PS2手柄遥控底盘移动的问题。资源采用PS2手柄作为输入搭配L298N驱动板包含控制原理图与完整代码可以直接在Keil工程中打开使用。压缩包共212个文件以.c/.h源文件、编译生成的.o/.crf/.hex/.axf等Keil工程文件为主另有原理图png与说明文档txt/htm整体仅7.26MB目录结构直观便于快速定位关键代码。项目中的PS2驱动代码经实测可用能完成底盘前后左右等基本控制但存在轻微延迟适合不苛求实时响应的入门应用作者还注明另有低延迟版本可另行下载并留有联系方式供技术交流。目前已有845人学习下载对想理解PS2通信协议、L298N电机驱动以及开环底盘控制逻辑的学习者而言是一份完整的参考资料。 做麦克纳姆轮底盘这类移动平台圈内有个普遍共识第一次调通四个轮子真的能朝任意方向跑起来的那一刻远比什么性能参数都上瘾。但真正把“麦克纳姆轮底盘开环PS2控制”这套组合做扎实很多人卡在了运动学解算和遥控信号处理上——不是车不走就是走起来像喝醉了酒或者转向时原地打转。这篇文章是我自己从零搭过、拆过、改过的完整记录。以开环控制为基础PS2手柄做遥控输入重点讲清楚麦克纳姆轮的运动学解算怎么做、PS2通信怎么接、整车怎么从代码到落地跑起来。适合正在做竞赛小车、机器人课程项目、或者单纯想折腾一台全向移动底盘的朋友参考也适合手里已经有一套底盘但四个轮子“各跑各的”的人排查问题。1. 麦克纳姆轮底盘的核心原理与方案选型1.1 麦克纳姆轮为什么能“横着走”辊子与受力方向先打消一个常见误解麦克纳姆轮不是每个轮子都能自己转向。真正让它实现全向移动的是轮毂外圈斜装的一排小辊子。标准麦克纳姆轮上每个辊子与轮毂轴线成45度角辊子是被动转动的不提供驱动力。这样一来轮子转动时地面的摩擦力沿辊子轴线方向分解实际产生的驱动力并不在轮子正前方而是斜45度方向。把四个这样的轮子按一定布局装在底盘四角每个轮子都给一个不同方向和速度的旋转指令四个斜向力合成起来就能让底盘实现前后、左右、斜向、原地旋转等自由度。打个不太严谨但好记的比方每个麦克纳姆轮像是一个“单向滑靴”四个滑靴朝不同方向使劲最终车身就往合力方向走。这里有个核心参数需要注意辊子角度。默认选45度也有30度或更极端的设计但STM32、Arduino配套的教材、开源库、轮毂供应商默认都是45度没特殊需求别乱换。改装轮子前先看辊子是否完好、轴承是否顺滑不然解算做得再准轮子本身在打滑或卡滞整车表现照样不正常。1.2 开环控制舍掉反馈换来简单与响应开环控制的意思是系统只按给定指令输出不关心轮子实际转速是否达到预期。类比开车时只踩油门不看速度表你给信号车能走但实际速度和负载有关。在麦克纳姆轮底盘上开环带来的最大问题就是四个轮子负载不均匀时实际转速会不一致导致跑偏。那为什么还要选开环两个原因其一入门和调试阶段开环足够验证运动学解算是否正确先把方向搞对再谈闭环精度其二开环省掉编码器、电机驱动板的电流环配置大幅降低成本和代码复杂度用一块Arduino或者入门级STM32就能跑起来。我个人的建议是开环适合18V以下的中小型底盘、室内平路场景。如果后续要上载重、跑户外不平地面就得加编码器做闭环了。但开环做得好不好直接影响后续加闭环的效果——运动学模型错了闭环也救不回来。1.3 遥控方案对比为什么是PS2手柄市面常见的遥控方式有蓝牙串口模块、2.4G无线模块、普通航模遥控器、PS2手柄。我最终选PS2手柄核心原因是性价比和手感。PS2手柄二手拆机或国产兼容加上接收器成本二三十块钱却提供两个模拟摇杆、十几个按键SPI协议通信抗干扰能力也不错。相比蓝牙串口PS2不需要配对和AT指令配置上电几秒就能用相比航模遥控器PS2接收器直接输出数字量不需要额外接PPM解码电路。当然PS2方案也有它的坑比如接收器供电电压和逻辑电平问题、部分国产手柄协议兼容性等这些后面专门讲。总的来说PS2手柄开环底盘是一套低成本、验证运动控制逻辑的黄金组合。2. 运动学解算让四个轮子听懂摇杆2.1 坐标系约定与速度分解麦克纳姆轮底盘的全向移动本质上是将底盘期望的合速度分解到四个轮子的线速度。首先约定坐标系x轴车身正前方y轴车身正左方逆时针旋转方向为正底盘需要三个输入量vx前后速度、vy左右速度、ω旋转角速度。这三个量从哪里来PS2手柄的左摇杆控制vx和vy右摇杆控制ω这是最常见的映射方式。以底盘几何中心为原点四个轮子到中心的距离在x和y方向的分量分别记为l前后方向半间距和w左右方向半间距。这两个值直接影响解算公式中的系数所以要量准。我自己的底盘轮距约220mm轴距约200mm对应l≈110mmw≈100mm代入后面的公式就能算。2.2 逆运动学公式与四轮速度计算麦克纳姆轮逆运动学解算即已知vx、vy、ω求每个轮子的速度。对于X型布局左前、右前、左后、右后四个轮子的速度公式如下统一单位角速度符号通常取逆时针为正v1左前 vx - vy - ω × (l w) v2右前 vx vy ω × (l w) v3左后 vx vy - ω × (l w) v4右后 vx - vy ω × (l w)注意不同文章可能因为坐标系方向或辊子安装方向不同符号会有差异。更重要的是轮子安装方向的A/B轮问题麦克纳姆轮分A轮辊子左上到右下和B轮辊子右上到左下。常见的X型安装布局下左前、右后用A轮右前、左后用B轮。装反了整车的运动方向会完全相反或者扭起来。解算公式里的vx、vy、ω单位建议统一。实际代码里摇杆输入的范围是-128到127我会直接把这个范围映射到PWM输出范围所以公式里的vx、vy、ω也用了“-127到127”的整数域这样省去浮点运算在Arduino这类单片机上跑更高效。注意公式中的ω乘以(lw)后量纲是速度。如果ω的取值范围也定在-127到127那么旋转速度和平移速度的“权重”实际上由(lw)这个系数决定整车转向快慢和直线快慢的比例就体现在这里。调车时如果发现转向太猛或太弱优先调ω的缩放系数而不是改死区。2.3 摇杆信号映射与死区处理PS2手柄的模拟摇杆中位输出理论上在128附近实际会有偏移。如果不处理死区手柄回中后摇杆读数可能停在120到135之间导致车辆缓慢漂移。这在开环系统里尤其明显——因为没有反馈修正。我的处理方式是设定dead zone读数与128的差小于15约12%时按0处理。这样既能容忍摇杆磨损和中位偏移又不会让刚推一点摇杆时毫无反应。死区过大也会影响操控细腻度我用15这个值在多数PS2兼容手柄上测下来手感比较平衡。另外摇杆方向通常和期望运动方向一致左摇杆向上推vx为负因为约定前为正而手柄往上推代表后退这个映射关系最好用代码注释写清楚不然后续调方向会把自己绕晕。建议先在代码里把摇杆值打印出来确认真实读数范围再定映射。3. 硬件搭建与PS2遥控配置3.1 底盘选型与供电设计附清单开环底盘对机械精度要求不算极端但轮子安装孔位要正、四个轮子触地压力要尽量一致。我用的底盘是网上常见的那种铝合金板四支麦克纳姆轮单个轮子直径约60mm搭配TT电机或者带减速箱的N20电机都可以。若负载小、室内跑TT电机的扭矩够用但精度一般N20电机更稳但转速可能偏慢。关键还是看减速比和额定电压。供电是整个项目最容易翻车的一环。四个电机同时堵转或启动瞬间电流可能冲到5A以上主控板一旦被拉低电压就会重启PS2接收器也会掉线。我用了7.4V 2S锂电直接给电机驱动板如TB6612或L298N供电再通过降压模块输出5V给Arduino和PS2接收器。这里要特别强调PS2接收器必须由稳定的5V供电不能直接接在电机电源上否则电机启动瞬间的毛刺会让它直接断连。我在实际调试中最初用的是一块USB供电的Arduino结果一推杆电机一转上位机就掉串口后来才意识到是电源问题换成独立降压供电后整机稳定了很多。3.2 PS2接收器接线与SPI通信要点PS2接收器引脚一般有VCC、GND、DAT、CMD、SEL、CLK共6个。其中DAT、CMD、CLK、SEL这四根线和主控的SPI或模拟SPI相连。Arduino Uno建议接法如下PS2接收器引脚接到主控引脚VCC5V必须稳定GNDGNDDAT数字引脚12SPI MISOCMD数字引脚11SPI MOSISEL数字引脚10SPI SSCLK数字引脚13SPI SCK用Arduino的PS2X库时初始化通常写成#include PS2X_lib.h PS2X ps2x; void setup() { ps2x.config_gamepad(10, 11, 12, 13, true, false); }config_gamepad的四个引脚参数依次是SEL、CMD、DAT、CLK和接线顺序不同容易写反。我初次接的时候就是这里搞反导致手柄数据全是255踩过坑。3.3 装配与轮系X型布局检查轮系布局直接决定运动学公式是否成立。麦克纳姆轮安装前先区分A轮和B轮。一种快速判断方法把单个轮子放在平地上用手沿车身横向推一下如果轮子本身很自然地“往斜前方滑”那就能看出辊子朝向。普通轮子是被推着直走的麦克纳姆轮是斜着走的方向和辊子朝向有关。安装完成后的检查方法抬起底盘用手给四个轮子同时同方向转动观察四个轮子的辊子触地方向是否形成一个X型。上电后下发一个“直行后退”指令观察车是否走直线。如果车斜着走大概率是某个轮子的A/B装反或者运动学公式中对应位置的符号错了。轮子装好后确认四个轮子都能自由转动、辊子滚动顺畅。麦克纳姆轮对地面平整度要求高地面上有石子或台阶都会让它颠簸甚至打滑这也是开环系统无反馈时表现“像抽风”的常见原因之一。4. 代码实现从解算到出轮4.1 代码结构解算、通信、输出三层分离写代码之前先把结构想清楚别一股脑全塞在loop里。我的代码分三层输入层读取PS2手柄摇杆、按键状态转换为vx、vy、ω期望值解算层根据运动学公式把vx、vy、ω换算成四个轮子的目标速度值输出层把目标速度映射为PWM占空比和方向电平驱动电机分层的好处是后续升级闭环时只需要改输出层或者把解算层的结果送入PID控制器其他部分不用大改。即使现在不做闭环分层结构调试起来也清晰得多比如某个轮子反了直接在输出层反过来就行不用翻解算代码。4.2 核心代码实操PS2初始化 运动学解算 PWM输出下面给一个Arduino环境的核心代码框架。这个框架我实测过用的是TB6612驱动左右各一路PWM控制速度方向由两个IO口控制。#include PS2X_lib.h #define PS2_DAT 12 #define PS2_CMD 11 #define PS2_SEL 10 #define PS2_CLK 13 // TB6612引脚 #define AIN1 2 #define AIN2 3 #define BIN1 4 #define BIN2 5 #define PWMA 6 #define PWMB 9 PS2X ps2x; int LX, LY, RX; int vx, vy, w; int deadZone 15; // 底盘尺寸参数单位mm经过换算后的综合系数 // 这里不使用真实毫米而是把摇杆范围-127~127直接映射为输入范围 // l w 根据实际底盘大约为210mm但要缩放成与摇杆匹配的一个系数 float scaleRotate 1.0; // 转向灵敏度系数实际可用0.5~1.2调整 void setup() { Serial.begin(9600); pinMode(AIN1, OUTPUT); pinMode(AIN2, OUTPUT); pinMode(BIN1, OUTPUT); pinMode(BIN2, OUTPUT); pinMode(PWMA, OUTPUT); pinMode(PWMB, OUTPUT); // 第三个参数是configure旋钮模式我习惯设true // 如果发现手柄读不到数据先尝试调低SPI时钟第四参数 int err ps2x.config_gamepad(PS2_SEL, PS2_CMD, PS2_DAT, PS2_CLK, true, false); if (err ! 0) { Serial.print(PS2 init error: ); Serial.println(err); } } void loop() { ps2x.read_gamepad(false, false); LX ps2x.Analog(PSS_LX); LY ps2x.Analog(PSS_LY); RX ps2x.Analog(PSS_RX); // 摇杆范围0~255中位128转换到-127~127 vx map(LY, 0, 255, 127, -127); vy map(LX, 0, 255, -127, 127); w map(RX, 0, 255, -127, 127); // 死区处理 if (abs(vx) deadZone) vx 0; if (abs(vy) deadZone) vy 0; if (abs(w) deadZone) w 0; // 运动学解算 // 注意如果发现轮子方向反了把对应项的符号反一下 float v1 vx - vy - w * scaleRotate; float v2 vx vy w * scaleRotate; float v3 vx vy - w * scaleRotate; float v4 vx - vy w * scaleRotate; // 输出到电机 setMotor(AIN1, AIN2, PWMA, v1); setMotor(BIN1, BIN2, PWMB, v2); // 实际四个电机需要两路PWM方向控制或者四路PWM // 我这里只展示一路的控制逻辑四个轮子逐一对应即可 } void setMotor(int in1, int in2, int pwmPin, int speedValue) { speedValue constrain(speedValue, -255, 255); bool forward speedValue 0; digitalWrite(in1, forward ? HIGH : LOW); digitalWrite(in2, forward ? LOW : HIGH); analogWrite(pwmPin, abs(speedValue)); }真实项目中四个电机要四路PWM比如用TB6612FNG的双路输出两块驱动板分别带左右各两个轮。上面代码只展示单路控制逻辑展开后每个轮子都有自己的一组in1、in2、PWM引脚。4.3 转向模式设计原地转、差速转、全向移动PS2右摇杆控制ω默认是原地旋转模式vx和vy都为0时ω不为0则底盘绕中心原地旋转。这种模式适合狭小空间但很多新手觉得太灵敏一推就转得飞快。解决办法是给ω加一个平方映射或线性缩放让轻微推杆时转向更柔和。还有一种更自然的操控模式叫“差速转”车子在前进的同时叠加旋转右摇杆控制转向幅度左摇杆控制前进方向。这种模式下车身会根据转向角度自动画弧线适合竞速类场景。实现方式很简单在解算前把ω按一定比例叠加到vx、vy上。但开环下这种模式需要谨慎调比例否则容易出现推杆过猛导致的甩尾。我的习惯是做个模式切换按键比如按下SELECT切换一套代码里同时保留“全向模式”和“竞速模式”。全向模式下摇杆直接映射vx、vy、ω竞速模式下右摇杆控制ω左摇杆控制前进后退和左右平移但平移优先级降低保证转弯顺滑。5. 常见问题与排查技巧实录5.1 轮子打滑、机身抖动现象启动瞬间轮子有动作但车身不走或者低速行驶时一顿一顿的。排查顺序检查辊子是否卡住。麦克纳姆轮用久了辊子轴承里容易进灰或者辊子磨损后转动不顺这时轮子在地面上是“蹭”而不是“滚”。检查PWM输出频率。如果用的是analogWrite默认的490Hz左右部分电机和驱动板在低速时扭矩输出不线性抖动明显。可以改用更高频率PWM比如用定时器配置到20kHz以上再看效果。检查驱动板使能脚是否接对。TB6612如果不拉高使能脚输出是无效的电机就在原地嗡嗡响但不出力。开环系统对机械阻力差异非常敏感。我遇到过换了新轮子后四个轮子里有两个明显“紧”结果车速一慢就往一边偏后来换了轴承才好。5.2 跑偏与四轮速度不一致现象直行指令下车不走直线而是偏向一侧。开环下跑偏很难完全消除但能改善到可接受范围。首先要排除机戒问题四个轮子触地是否一致底盘四角高度是否一样有的底盘用尼龙柱固定时间长了会变形导致某个轮子悬空那这个轮子彻底不参与运动整车只能歪着走。然后是代码层面检查运动学公式里的符号。如果四个轮子速度都是正向但车体在转那是某一侧轮子的方向接反了直接把对应轮子的方向逻辑取反即可。如果车斜着走大概率是轮系布局和公式不匹配。最后如果以上都没问题再考虑加软修正比如在代码里给每个轮子一个独立的scale因子手动校准到直行。开环方案的跑偏可以用这种“每个轮子人工微调占空比”的方式压到肉眼看不太出来的程度。5.3 PS2手柄断连、无响应现象开始时正常跑一阵后手柄失效或者一上电就连接不上。我总结的三大主因供电脏。电机启停瞬间电源毛刺直接灌进PS2接收器的VCC导致它丢数据。解决方法是PS2接收器的VCC独立走线并且加一个100uF左右电容。SIGNAL线过长或接触不良。PS2接收器到主控的排线不要超过15cm尽量用短杜邦线太长会增大信号干扰。另外杜邦线频繁插拔容易虚接我后来直接焊死在PCB上。库的初始化问题。PS2X库有两个版本的兼容问题有的手柄要求config_gamepad第四参数时钟速度设为false低速有的则必须为true。遇到初始化报错试试切换这个参数。实测下来PS2手柄连接成功后只要供电稳定跑一整个下午都不会断。如果频繁断九成是电源问题别先怀疑手柄。5.4 供电不足与电流跌落现象电机全速前进时主控重启或者启动瞬间车没反应过一会儿才缓缓动起来。这个问题的本质是锂电内阻和降压模块瞬时响应不够。直接用2S锂电供电时7.4V经过电机驱动板给电机理论上没问题。但Arduino的5V如果是从驱动板的5V输出引出来的而驱动板又是线性稳压电机大电流时热量和压降都会很严重。我的最终方案是电机独立供电主控和PS2接收器走独立的5V降压模块比如MP1584或AMS1117方案小模块。哪怕电池电压掉到6.5V主控仍然稳定PS2不重启。这也是整个项目中回报最高的一项改动——如果只能给读者留一条经验我会选这一条。6. 实测心得与后续扩展方向做完这套开环底盘后我最大的感受是麦克纳姆轮的运动学公式并不复杂真正让整车“听话”功夫全在细节——轮子装配正不正、PS2引脚接没接对、供电扛不扛得住大电流、死区调得合不合理。如果您接下来想继续玩下去我认为最值得扩展的方向是加编码器和IMU做闭环。开环底盘在平地上摆摆造型、跑跑路线演示完全够用但一旦要精确走固定轨迹、或者载重上坡轮子打滑导致的实际位移和期望位移偏差会被累计放大。加了编码器后可以按PID闭环控制每个轮子的实际转速再加个MPU6050做转向角度反馈整车的可玩性和精度立刻上一个台阶。最后分享一个小技巧调试开环底盘时找个手机水平尺贴在底盘中心观察车身在不平地面上是否水平会比肉眼看来得准得多。底盘一旦翘角不管代码怎么写运动学模型都是错的——先把机械找平再谈算力与控制。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价