基于 STM32F103RCT6 麦轮全向智能小车全套开发教程NRF24L01 无线遥控、MPU6050 角度闭环、OpenMV 视觉循迹完整实现与实战调试踩坑记录作者Rowan-w1用途嵌入式入门练手、智能车基础实训开源仓库见文末适合人群零基础嵌入式新手、想要入门麦轮全向车、遥控车、视觉智能小车的同学一、前言本项目以STM32F103RCT6为主控搭建四轮麦克纳姆轮全向移动智能小车。相较于传统差速小车麦轮小车支持横移、斜向移动、原地自旋运动自由度更高、场景适用性更广。本项目完整实现以下功能NRF24L01无线遥控、编码器测速四轮速度PID闭环、MPU6050航向角度环矫正解决跑偏问题、OpenMV视觉数据通信、OLED实时状态显示、蜂鸣器提示、额外发射机构电机控制等全套功能。本文从硬件架构、运动原理、软件代码、控制逻辑到真实调试踩坑全程记录详细记录所有代码均为实车调试通过零基础新手也能完整复刻。二、硬件介绍2.1 整车结构底盘为「X 型」麦轮布局左前 / 右前 / 左后 / 右后 4 个 45° 斜装轮。每个主动轮由一路直流减速电机驱动并带霍尔编码器用于测速另外有 2 路额外电机5、6 号用于发射机构由独立定时器 TIM8 驱动。2.2 核心器件清单类别型号 / 说明在本项目中的角色主控STM32F103RCT6标准库系统大脑跑控制循环电机驱动L298NM模块×34 路主动轮2个 发射机构1个主动电机带编码器直流减速电机 ×4麦轮全向驱动附件电机直流电机 ×25、6 号发射机构姿态传感器MPU6050I2C航向角 yaw 积分角度环无线遥控NRF24L012.4GSPI遥控器 ↔ 小车双向通信视觉模块OpenMV串口二维码/目标识别数据回传显示0.96 寸 OLEDI2C显示连接状态、视觉结果提示有源蜂鸣器收到视觉数据提示电源锂电池 稳压【自备规格】给电机和 MCU 分别供电接线思路电机驱动板的 IN1/IN2 接普通 GPIO方向PWM 接 TIM1 的 4 路通道编码器 A/B 相接 TIM2~TIM5 的编码器接口MPU6050 走 I2C2PB10/PB11NRF 用软件 SPI 模拟CE/CSN/SCK/MOSI/MISOOpenMV 走 USART3PC10/PC11OLED 走 I2C蜂鸣器接一个 GPIO。2.3 引脚速查本项目实测功能引脚电机 PWMM1~M4PA8/PA9/PA10/PA11TIM1_CH1~4电机方向 INPC0~PC5、PC7、PB15M4_IN1编码器TIM2(PA15/PB3)、TIM3(PA6/7)、TIM4(PB6/7)、TIM5(PA0/1)MPU6050PB10/PB11I2C2NRF24L01CEPC12、CSNPB4、SCKPD2、MOSIPA12、MISOPB14OpenMVPC10(TX)/PC11(RX)USART31152005/6 号电机 PWMPC6(TIM8_CH1)/PC9(TIM8_CH4)三、核心原理3.1 麦轮全向移动原理普通小车靠左右轮差速转向只能沿车头方向运动。麦轮的每个轮毂外圈斜装着可自由滚动的辊子与轮轴成 45°于是每个轮既能提供「沿轮轴」方向力也能提供「垂直轮轴」方向力。合理分配 4 个轮的转速和转向就能合成出任意水平方向的速度分量实现前进 / 后退4 轮同向同速左 / 右平移4 轮按特定正负号组合合成出纯横向力原地旋转左右两侧反向。本项目里 4 个轮的编号与速度分配如下运动模式M1(左前)M2(右前)M3(左后)M4(右后)前进 FORWARDVVVV后退 BACKWARD−V−V−V−V左平移 LEFT−PPP−P右平移 RIGHTP−P−PP左旋转 ROT_L−RR−RR右旋转 ROT_RR−RR−R注意麦轮斜装方向分「O 型 / X 型」左右平移的正负组合会相反。如果你的车平移方向相反把MODE_LEFT/MODE_RIGHT里的符号整体取反即可见第六节。3.2 两级控制闭环重点为了让小车「走得直、移得正」工程里做了两层控制速度环5msTIM7 中断编码器读回实际转速与「目标转速」做增量式 PI输出 PWM 给电机。解决「四个电机转速不一致、轮子打滑」的问题。角度环20msMPU6050 的 Z 轴角速度积分得到航向角yaw与目标航向target_yaw_deg做PD 控制输出一个「差速修正量」叠加到四轮纠正小车跑偏/自转。遥控器指令 → 设定运动模式 → 主循环算四轮目标速度 ↓ 速度环(5ms)编码器反馈 → 增量PI → PWM → 电机 角度环(20ms)MPU6050 → 航向误差 → PD → 叠加差速修正四、开发环境项目版本 / 说明MCUSTM32F103RCT6IDEKeil MDK / 【你用的版本】标准库STM32F10x 标准外设库用到的外设PWMTIM1/TIM8、编码器接口TIM2~5、串口USART3、I2CI2C2、GPIO、定时器中断TIM7五、软件代码实现5.1 初始化配置main.c系统用一个 5ms 的 TIM7 定时中断当「系统心跳」所有周期性任务读编码器、角度环、速度环都挂在它上面主循环while(1)只做收遥控、收 OpenMV、刷新 OLED。intmain(void){NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);Delay_Init();Timer7_Init();// 5ms 系统节拍OLED_Init();MPU6050_Init();Encoder1_Init();Encoder2_Init();Encoder3_Init();Encoder4_Init();// 4个编码器MOTOR_GPIO_Init();TIM1_PWM_Init();// 4 路主动轮 PWMBuzzer_Init();PID_Angle_Init();Motor56_Init();// 5/6 号电机 PWMTIM8NRF24L01_Init();// 默认进接收模式USART3_Init(115200);// OpenMV 视觉串口current_modeMODE_STOP;while(1){HandleNRFCommands();// 收遥控/* 链路看门狗超过 500ms 无指令 → 强制停车 */...ApplyMotionControl();// 按模式算四轮目标速度/* 读 OpenMV 串口、刷新 OLED */}}5.2 电机驱动代码方向用 GPIO 推挽输出IN1/IN2转速用 TIM1 的 PWM 占空比表示。Set_Mecanum()把「带符号的目标速度」翻译成方向 占空比并做 999 限幅voidSet_Mecanum(int16_tM1,int16_tM2,int16_tM3,int16_tM4){uint16_tabs_m1;if(M10){GPIO_SetBits(GPIOC,MOTOR1_IN1_PIN);GPIO_ResetBits(GPIOC,MOTOR1_IN2_PIN);abs_m1(uint16_t)M1;}else{GPIO_ResetBits(GPIOC,MOTOR1_IN1_PIN);GPIO_SetBits(GPIOC,MOTOR1_IN2_PIN);abs_m1(uint16_t)(-M1);}/* M2/M3/M4 同理 ... */TIM1_SetCH1(abs_m1);TIM1_SetCH2(abs_m2);TIM1_SetCH3(abs_m3);TIM1_SetCH4(abs_m4);}PWM 初始化TIM1_PWM_Init72MHz 主频预分频 72-1、周期 1000-1 →1kHz方波占空比 0~999即 0%~99.9%。TIM_TimeBaseInitStructure.TIM_Period1000-1;TIM_TimeBaseInitStructure.TIM_Prescaler72-1;TIM_OCInitStructure.TIM_OCModeTIM_OCMode_PWM1;5.3 遥控解析代码NRF24L01遥控器和车共用江协科技的 NRF 驱动软件 SPI。车上HandleNRFCommands()不断查询是否收到包第 0 字节就是命令1~8staticvoidHandleNRFCommands(void){if(NRF24L01_Receive()1){uint8_tcmdNRF24L01_RxPacket[0];last_cmd_rx_timeTimer7_5ms_Count;// 喂看门狗switch(cmd){case1:current_modeMODE_FORWARD;break;case2:current_modeMODE_STOP;break;case3:current_modeMODE_BACKWARD;break;case4:current_modeMODE_LEFT;break;case5:current_modeMODE_RIGHT;break;case6:current_modeMODE_ROTATE_LEFT;break;case7:current_modeMODE_ROTATE_RIGHT;break;case8:current_modeMODE_CANNON;break;// 触发 5/6 号电机}SendReply();// 收到才应答平时 100% 处于接收响应最快}}小车采用「收到指令才回传」策略平时只听收到命令立刻回一帧应答带 OpenMV 视觉数据 / 心跳状态既省功耗又保证遥控实时性。回传帧有两种类型0x01状态帧M模式 S速度...、0x02OpenMV 文本帧。链路看门狗NRF_LINK_TIMEOUT_MS 500。若超过 500ms 没收到任何指令遥控器没电/超出距离/遮挡主循环强制MODE_STOP并判定断链避免小车「失控狂奔」。这是安全必备逻辑。5.4 调速 / 防抖 / 角度环逻辑速度闭环增量式 PI5ms编码器计数 实际转速与目标做 PI输出 PWM。代码里给每个轮加了trim标定系数——四轮机械/轮径/编码器有差异时会平移漂移哪轮偏快就把它的trim调 1.0重新编译即可无需大改。// pid.c —— 增量式 PI 核心Kp22, Ki3.2, Kd0floaterrortgt-(float)real;s-pid_outputKp*(error-s-last_err)Ki*error;角度闭环PD 死区20ms直行/平移时MPU6050 积分出yaw和目标航向比较PD 输出一个差速量rot左轮rot、右轮-rot纠偏// PID_Angle.cfloaterrtarget_yaw_deg-yaw_deg;if(fabsf(err)ANGLE_DEADBAND)err0;// 死区防小角度抖动floatoutANGLE_KP*errANGLE_KD*(err-last_err);// KD 阻尼抑制过冲模式切换重置切运动模式时调用Speed_PID_Reset()清零积分和输出避免上一模式的 PWM 残留导致切换瞬间失衡漂移。yaw 零偏校准上电静默 0.5s 采一次零偏运行中若检测到持续 100ms 角速度接近 0还会动态微调零偏抵消温漂。六、调试过程 踩坑总结下面都是我在实车调试中真真切切踩过的坑配现象 原因 解决办法建议收藏。坑 1电机不转 / 转一下就停现象下载程序后轮子不动或只是抖一下。原因① 电机驱动板使能脚没拉高② TIM1 是高级定时器必须加TIM_CtrlPWMOutputs(TIM1, ENABLE);否则 PWM 引脚不出波③ M4 的 IN1 原本接在 PC6和 NRF 的 TIM8_CH1 或重映射冲突后来改到PB15才正常。解决检查使能脚、补上高级定时器 PWM 输出使能、排查引脚冲突见motorencoder.c里的 M4 改脚注释。坑 2左右平移时小车「斜着漂」跑偏现象按左移车却往左前方斜冲不是纯横移。原因4 个电机/轮子的「每计数对应地面速度」不一致速度环各自收敛到略不同的稳态合力带了纵向分量。解决① 给每个轮加speed_trim标定系数实测偏快就把trim调小② 平移时叠加一个TRANS_FB_COMP前后补偿量抵消纵向漂移③ 引入角度环在平移时只算「航向修正量」heading_rot叠加到四轮差速里。三者合力才把横移走正。坑 3直行时越走越偏 / 角度环「乱冲失控」现象直行没几秒就明显转向或者偏角越大转得越猛最后甩出去。原因① 角度环只有 P 没有 D偏角大时输出过大且过冲振荡②ANGLE_DIR方向符号反了会形成正反馈越偏越往偏的方向转。解决加微分ANGLE_KD1.5抑制过冲把输出限幅ANGLE_OUT_MAX调小到 35若仍反方向把宏#define ANGLE_DIR -1改成1翻转即可。坑 4航向角 yaw 卡死在 ±180° 边界不动现象车转到接近 ±180° 附近航向角突然「冻住」不再更新角度环失效。原因原来在 yaw 跨过 ±180° 做 wrap 之前先判断「单步增量过大」会把 I2C 偶发误码当成跳变而return丢弃结果 wrap 时机和误码判断冲突把 yaw 卡在边界。解决改成先判单步增量再 wrap单次变化 0.3 rad≈17°/20ms 才丢弃该步这样跨 ±180° 不会被误判为跳变。代码注释里写得很清楚。坑 5增量式 PI 加「死区清零 error」导致抖动现象低速时四轮转速忽快忽慢、整车轻微抖动。原因增量式 PI 里只对error清零却没清last_err下一拍(error - last_err)会算出一个虚假反向突变量而且四路各自收敛到不同稳态速度这正是「转速不同→左下偏」的代码层根因。解决去掉死区里清零 error 的写法死区只用于抑制角度环抖动速度环不这么做。坑 6NRF 偶尔失联、发了没响应现象遥控器偶尔叫不应小车或小车回传失败。原因NRF 通信靠自动重传 ACK一旦 FIFO 满 / 双方配置瞬间不一致 / 引脚接触不良状态寄存器会卡在错误态。解决驱动里在发送超时 / MAX_RT / 状态非法时自动重新初始化一次 NRFNRF24L01_Init()自我恢复同时小车侧有 500ms 链路看门狗兜底停车。另外务必保证收发双方地址、频道(0x02)、速率(2Mbps)、包宽(32) 完全一致且关掉 JTAG 释放 PB4 给 CSN 用。坑 7MPU6050 上电漂移 / I2C 读不出现象不动车航向角也慢慢飘或 I2C 一直WHO_AM_I读不到。原因Z 轴陀螺仪有零偏I2C 用了 PB10/PB11 复用开漏但上拉/速率没配对或中断等事件等待超时。解决上电静默 0.5s 采零偏运行中持续静止时动态修正零偏I2C 速率降到 50kHz、开漏 外部上拉读寄存器加超时跳出。七、项目效果7.1 功能演示文字说明遥控器 8 个按键对应前进 / 停车 / 后退 / 左移 / 右移 / 左转 / 右转 / 发射5、6 号电机动作。直行、平移时角度环介入基本走直线、纯横移不漂移。OpenMV 识别到的二维码/目标信息通过 USART3 传给 STM32再由 NRF 回传给遥控器同时 OLED 显示LNK OK / OpenMV文本 / TX:文本。收到新的视觉数据蜂鸣器「嘀」一声提示相同内容去重避免狂响。遥控器超出距离或没电时小车500ms 自动停车不会乱跑。图片7.2 已实现功能汇总麦轮全向移动前进/后退/左右平移/左右旋转NRF24L01 无线遥控 双向回传四轮编码器测速 速度 PID 闭环MPU6050 航向角闭环防跑偏OpenMV 视觉数据接收与回传OLED 状态显示 蜂鸣器提示链路看门狗自动停车安全5/6 号电机执行机构发射八、总结与后续拓展本次学到的知识点STM32 定时器三件套PWM 输出、编码器接口、定时中断节拍。麦轮运动学四轮转速符号组合 → 合成任意方向速度。闭环控制速度环增量式 PI 角度环PD 死区的双层结构。传感器融合基础陀螺仪角速度积分得航向并做零偏校准。无线通信协议设计命令字节 回传帧 链路看门狗。调试方法论现象 → 日志/数据 → 定位代码层根因 → 针对性修复。后续可升级PID 调优给角度环加积分抗扰、速度环加微分自主避障加超声波/ToF循迹加红外/电磁/摄像头巡线视觉追踪OpenMV 做颜色/形状跟踪小车自动跟随上位机用串口 / 蓝牙把数据发到电脑画曲线调参。九、结束语整个工程已跑通并实测可用代码都放自己的仓库里第一次用CSDN和GitHub如果做的不好可以反馈我看到会更改欢迎开源学习、二次开发。如果文章对你有帮助点个赞 / 收藏支持一下有任何问题接线、PID 调参、NRF 配对等欢迎在评论区留言交流力所能及都会回。工程开源地址1. https://github.com/Rowan-w1/STM32-Mecanum-Car-F103?tabreadme-ov-file2.https://gitee.com/Rowan-w1/stm32-mecanum-car-f103交流方式 评论区留言本文代码均来自实车调试工程转载请注明出处。