
智能车竞赛里的“蚂蚁搬家组”听名字就知道不是单纯跑圈速的项目。它要求小车在限定场地里把散落在各处的物料搬运到目标区域既考机械结构又考多任务调度更考验两套主控之间怎么配合。我今年带队做的方案是NXP微控制器上跑MicroPython做上层决策STC单片机做底层执行两个人分工干一个活这里面的协同策略、通信协议、时序控制踩过的坑和最终稳定的参数都值得拿出来聊聊。如果你正在备赛蚂蚁搬家组或者打算用MicroPython做主控、用STC做下位机做这类多任务搬运车这篇应该能帮你少走不少弯路。1. 竞赛任务与整体架构拆解1.1 蚂蚁搬家组到底在比什么蚂蚁搬家组的核心任务说白了就是让小车在限定时间内把场地里的“物料”从初始位置搬到指定的投放区域。听起来简单但真上场就会发现麻烦。物料一般有多个散布在不同区域有的轻有的重场地里还会有障碍物搬运顺序如果不对就会出现小车被自己堵住、投放区堆叠不稳、时间白白浪费的情况。我们这届的规则大致是这样场地是一个4米乘3米的矩形区域四角各有一个物料堆放点中间偏左有一个目标投放区。物料是标准尺寸的木块重量差别不大但摆放方向随机有的平躺有的立着。小车需要从启动区出发依次去四个堆放点取料运到投放区最终投放区里物料数量越多、摆放越整齐得分越高。时间限制是90秒超时扣分。这个规则有一个隐藏难点90秒内完成四个点的搬运单靠一辆车从头跑到尾路线来回折返时间非常紧张。所以很多队伍做的是“双车配合”或者“单车多任务”我们最终选择了单车方案但要在一辆车上同时处理视觉识别、路径规划、机械臂动作、底盘运动四件事。如果只用一块主控理论上也能跑但MicroPython上层逻辑和底层电机控制混在一起实时性很难保证。STC单片机虽然性能不算强但做PWM输出、编码器计数、舵机控制这类硬实时任务非常稳。所以我们的架构从一开始就定成了“NXP做大脑STC做小脑”。1.2 为什么选择NXP-MicroPython加STC双主控方案很多队伍会用OpenMV加STM32的组合或者直接用K210跑深度学习。我们选择NXP加MicroPython主要基于三个考虑。第一NXP的微控制器在跑MicroPython时性能足够。我们用的是NXP的RT系列主频高、内存大MicroPython解释器跑起来不卡顿调用OpenMV兼容的视觉库做颜色识别、色块定位完全没有压力。而且MicroPython的代码迭代速度极快比赛前夜改策略、调阈值直接改.py文件重新上传就行比编译烧录C代码省太多时间。第二STC的单片机处理底层实时控制非常合适。STC的8位单片机虽然资源有限但我们只需要让它干几件固定的事读编码器、输出PWM、控制舵机角度、执行简单的运动指令。这些任务逻辑固定、对时间要求严格用寄存器操作或者简单的C语言就能写得非常高效。更关键的是STC单片机启动快、复位电路简单、抗干扰能力在工业级场景里验证过比赛现场电磁环境复杂它反而比高主频的ARM更皮实。第三双控制器的分工能最大限度发挥各自优势。MicroPython擅长“想”STC擅长“做”。视觉识别结果、路径规划指令通过串口下发给STCSTC根据当前运动状态判断什么时候执行、怎么执行这样即便上层出现卡顿底层也能安全刹车不会冲出边界。1.3 系统总体架构与数据流我们这辆车的完整控制链路是这样的NXP微控制器作为主控外接摄像头采集图像跑MicroPython视觉算法识别物料位置、目标区域位置、障碍物位置STC单片机作为执行控制板连接两个直流电机驱动底盘、一个舵机控制机械爪、两个编码器测速、一个超声波模块近距避障。数据流方向很明确NXP根据图像计算出物料坐标再结合车辆当前坐标做路径规划生成一条由“直行、转弯、停车、取料、投放”组成的指令序列通过串口把指令打包发给STC。STC收到指令后解析成具体的PWM占空比、舵机角度然后闭环控制电机转速完成动作。这里有一个容易忽略的点NXP和STC之间不是简单的“主从命令”关系而是有一个状态同步机制。NXP每次下发指令时会附带指令IDSTC执行完主动回传一个ACKNXP收到ACK才继续发下一条指令。这个机制让MicroPython端能准确知道小车到底走到哪一步了不会出现上层以为已经到点、底层还在半路的情况。2. 硬件选型与电路设计的坑2.1 主控板选择NXP系列跑MicroPython的体验我们用的是NXP的i.MX RT1052这块板子跑MicroPython有两点优势一是算力足够Cortex-M7内核600MHz主频处理VGA分辨率的摄像头图像完全够用二是外设接口丰富有多个UART、SPI、I2C方便和摄像头、传感器连接。但用RT1052跑MicroPython也有需要适应的地方。首先是电源管理RT1052核心电压是1.2V板载稳压如果滤波不好摄像头采集瞬间电流波动会导致MCU复位。我们后来单独给摄像头供电用了一个低压差稳压芯片并加大容量钽电容才彻底解决问题。其次是MicroPython固件的定制官方固件有些外设驱动没有完全开放比如硬件定时器的一些高级功能。我们直接修改了固件源码把摄像头DMA传输的缓冲区做大减少图像传输时间。这个操作需要一点嵌入式基础但对比赛车辆的性能提升是质的飞跃图像帧率从15帧提升到了30帧。如果你不想折腾固件也可以用NXP的LPC系列MicroPython支持更完善性能也够用。但要注意视觉识别算法一旦复杂内存不够就会频繁GC垃圾回收出现卡顿。所以选型时宁可选高配也不要省那几十块钱。2.2 STC单片机承担了哪些底层工作STC单片机在这辆车里干的活我用一句话总结“不让上层操心‘手怎么动’。”它主要负责四件事第一电机闭环控制。两个直流电机分别驱动左右轮通过编码器实时读取转速STC用PID算法调整PWM占空比让车轮转速稳定在目标值。这个闭环控制周期是1msMicroPython根本做不到这么稳定的实时响应。第二舵机控制。机械爪的张开、闭合、调整角度都由STC产生固定周期的PWM信号控制。舵机对脉冲宽度非常敏感时间抖动超过20微秒就会出现明显抖动STC用定时器中断产生PWM精度到微秒级。第三超声波避障。超声波传感器测距需要精确测量回波时间STC用硬件定时器捕获精度高。检测到障碍物距离小于设定值时STC会直接让电机减速不需要等NXP发命令这是安全兜底的一层。第四指令执行状态机。STC内部维护一个简单的状态机空闲、直行、转弯、取料、放料、急停。每次收到NXP指令状态机切换状态执行对应的动作执行完回传状态码。这里特别要提一下STC的选型我们用的是STC8系列自带硬件PWM和编码器接口集成了比较器外围电路简单。STC8H系列主频可以到24MHz以上指令速度比传统51快很多完全够用。如果你用的是老款STC89C52那还是先升级一下芯片吧跑PID会非常吃力。2.3 电机驱动、传感器与电源分配电机驱动选的是TB6612这个模块最大优势是内阻小、发热低适合电池供电的小车。我们开始时用过L298N结果在频繁启停时发热严重电压跌落导致STC复位换了TB6612之后这个问题直接消失。电源分配是双主控方案最容易出问题的地方。NXP的供电、STC的供电、电机驱动供电、舵机供电必须隔离至少也要做到大电流通道和小信号通道严格分开。我们的做法是12V锂电池经过总开关后分成三路一路降压到5V给舵机一路降压到5V给NXP供电板一路直接给电机驱动模块供电STC的5V由NXP板的稳压输出提供避免电机启停时电压波动影响STC。这样做的好处是电机启动瞬间的大电流冲击不会传导到逻辑电路。我们实测过如果不隔离电机急停时STC的复位引脚上会出现几百毫伏的毛刺偶尔就直接复位了后面的状态全乱套。2.4 复位电路与干扰防护STC单片机的复位电路看着简单其实内藏玄机。STC8系列推荐用10微法电容加10千欧电阻构成上电复位电路RESET引脚再接一个0.1微法高频去耦电容。这个去耦电容非常关键它能把电机碳刷产生的射频干扰滤掉。我们比赛调试时遇到一个奇怪现象小车一转弯STC就复位。排查了好几天最后发现是复位引脚线太长和电机线平行走了一段电磁干扰耦合进来导致复位。解决办法是复位线尽量短远离电机线同时在复位引脚对地并联一个100纳法电容再也没有误复位过。另外STC单片机的程序下载口和调试口在比赛时最好用胶带封住防止电磁干扰从排针进入。我们有一次在测试时不小心让金属镊子碰到了下载口的两个引脚当场烧了一片IO教训深刻。3. MicroPython端与STC端的协同软件设计3.1 MicroPython端视觉识别与路径规划的落地MicroPython端的软件结构我们分成了三个模块图像采集与预处理、物料识别与定位、路径规划与指令生成。图像采集用的是摄像头模块采集到的RGB图像先转化成LAB色彩空间再根据物料颜色设定阈值做二值化找到物料色块的最小外接矩形返回中心坐标和宽度高度。这一步是视觉的基石阈值选不好后面全白搭。我们比赛前专门做了阈值标定在三种不同光照下分别采集物料图片取阈值范围的交集确保在不同光线条件下都能稳定识别。路径规划我们没有用复杂的A*算法因为场地相对固定只需要在启动前把四个物料点的坐标标定好运行时根据物料是否被识别到动态生成访问顺序。核心逻辑是贪心算法每次选择距离当前车辆最近的未搬运物料生成一条“旋转对准、直行、停车”的指令序列。这个算法简单可靠调试方便90秒时间也足够跑完四个点。有一个细节很多人会忽略摄像头安装在车头看到的物料坐标是摄像头坐标系下的必须通过标定矩阵转换成车辆坐标系。我们在车模上画了中心标记先让车辆原地旋转记录不同角度下标记在画面中的位置拟合出转换矩阵。这个矩阵写死在代码里效果非常稳。3.2 STC端实时性要求高的底层控制STC端的代码用C语言写结构非常紧凑一个主循环加几个定时器中断。主循环负责解析串口收到的指令、更新状态机定时器中断则负责PID运算、编码器计数、舵机PWM更新。代码里最核心的PID调节器我分享一下实际参数比例系数Kp2.2积分系数Ki0.08微分系数Kd0.5。这个参数是针对我们的车模底盘调出来的不同车模底盘差距会很大但调参的方向是一致的先让Kp从小到大让车能直行不抖再加一点Ki消除稳态误差最后加Kd抑制过冲。STC的串口中断优先级需要仔细设置。我们最初把串口中断优先级设得太低导致指令接收期间PID中断频繁打断帧数据出现字节丢失。后来把串口接收中断优先级提高到仅次于定时器中断并启用了空闲检测数据完整率从90%直接提升到99.9%。需要特别注意STC芯片的EEPROM和Flash操作在程序运行时如果误触发了写操作会卡住主循环导致看门狗复位。我们的代码里把无关引脚全部配置成推挽输出或者高阻输入防止干扰信号误触发内部寄存器操作。3.3 通信协议设计与同步机制NXP和STC之间的串口通信协议看起来简单但设计得好不好直接影响系统的稳定性。我们最终用的是固定长度帧格式帧头、指令类型、数据体、校验和、帧尾共8个字节。数据体里包含指令ID用于同步。整个交互流程是这样的NXP发“Start”指令里面带一个自增的指令编号STC收到后校验通过返回“ACK编号”。NXP只有在收到ACK后才会发送下一条指令。如果500毫秒内没收到ACKNXP认为通信丢失进入安全停车模式。这个同步机制在初期调试时救了我们很多次。有一次摄像头识别漂移NXP误判物料位置下发了一条向左急转的指令。STC执行完后回传ACK但NXP发现视觉坐标和预设边界严重冲突主动发出“急停”指令全车立刻刹车没有冲出场地。如果当时没有同步机制车辆可能已经撞上挡板了。另外协议里还留了4个字节的扩展位用来传递传感器数据、状态码等。比如STC在电机堵转时会上报错误码NXP收到后可以切换策略跳过这个物料点直接去下一个比赛时遇到机械爪卡住木块这个扩展位就发挥了作用。3.4 状态机与任务调度策略双控制器的整体任务调度我们用了一个比较直观的“主从状态机”模型。NXP端状态机包括初始化、扫描场地、前往目标、取料、运输、投料、返回原点、紧急停车。STC端状态机更细致每个动作内部还会细分。举个例子“取料”在NXP端只是一个状态但下发给STC后STC内部会拆解成调整车头方向、低速前进接近、检测到压力传感器触发、舵机合拢、抬升机械爪、后退一段距离。每一步都有独立的超时保护比如舵机合拢超过2秒没有到位STC会放弃本次取料并上报失败。这种双层状态机的好处是上层只管“去哪里、干什么”下层只管“怎么去、怎么干”两边的逻辑都不会太复杂。调试时可以先让STC单独跑用串口调试助手模拟NXP发指令验证底层动作再让NXP单独跑用虚拟的STC回包测试上层决策最后联调时重点盯通信。任务调度上我们还加了一个“策略等级”的概念。比赛倒计时低于20秒时NXP会切换策略不再追求所有物料都搬完而是只去距离最近的一个点宁可少搬一个也别超时扣分。这个策略虽然简单但在测试中至少提升了10%的得分稳定性。4. 搬运策略的核心算法与参数调优4.1 抓取与投放动作的时序控制机械爪的抓取动作看起来就是舵机转个角度但真要稳定抓取时序非常重要。我们的动作序列是靠近物料到距离5厘米停车舵机从张开状态转到闭合状态闭合后等待200毫秒让爪尖完全卡住木块然后抬升到运输高度后退0.3米再转弯。这里有个容易踩的坑抓取前车辆必须完全停稳不能有丝毫漂移。我们最初为了提高速度抓取时不急停微速前进中用爪去“碰”木块结果木块一歪爪就滑了成功率只有60%。后来改成精确停稳再抓虽然单次慢了0.3秒但整体成功率提升到98%综合效率反而更高。投放动作的时序更讲究。投料区可能已经有堆叠的物料机械爪需要在合适的高度松开让木块自由落体到目标位置。我们给STC写了一段缓落逻辑先快速下降到距投放区5厘米高度然后慢速下降用超声波传感器测距距离小于1厘米时松开舵机木块几乎不会跳起。这样投放整齐度得分高很多。4.2 路径规划与防碰撞路径规划我们用的贪心最近点策略但在车辆实际转弯时需要加一个“转弯半径补偿”。小车不是坦克它有最小转弯半径如果直接按理想坐标计算转弯角度实际轨迹会往内侧偏移。我们通过实测在MicroPython的坐标转换里加入了R0.25米的转弯半径补偿转弯后的实际位置误差从15厘米降到了3厘米以内。防碰撞方面除了视觉识别障碍物我们让STC超声波避障的优先级最高。任何状态下只要超声波检测到前方10厘米内有障碍物STC会立即停车并让机械爪保持当前状态避免急停把物料甩飞。这个逻辑在视觉失效时是最后一道防线。还有一个小细节路径中如果遇到目标投放区车辆需要从侧面绕行而不是从投放区上方压过去。否则可能会把已经堆好的物料碰倒。我们在NXP的坐标生成规则里直接禁止了“起点到终点连线穿过投放区”的情况。如果贪心算法给出的直线路径会穿越投放区就在中间插入一个中间点绕一下。4.3 多目标搬运顺序优化搬运顺序看似简单其实对总耗时影响很大。理论上最近的先搬最优但还要考虑“转向代价”。有些物料点虽然距离近但车辆到达后需要转一个非常大的角度才能对准投放区花费的时间反而更多。我们对四个物料点和投放区之间的夹角也做了评估定义了一个“搬运代价”代价 距离 / 平均速度 转角耗时。NXP端在做贪心选择时用这个代价代替纯距离作为排序指标。这个改进让我们整场测试的平均单趟耗时减少了约5秒四趟下来就是20秒非常可观。另外我们保留了一个“最优顺序预设”的功能。在比赛前一天根据场地的实际物料点排布人工指定了一个搬运顺序。如果比赛当天视觉识别一切正常就直接用预设顺序避免临时计算的不确定性。事实证明这个“双保险”很管用因为决赛场地的光线比训练场暗视觉偶尔会抖动但预设顺序不依赖视觉依然能完成任务。4.4 关键参数实测与调优记录分享一组我们最终稳定运行的参数供大家参考参数项初始值最终值调优过程说明物料识别阈值手动标定自适应动态阈值光照变化时自动调整亮度分量识别率从85%升到96%直行PID Kp2.02.2加大后直行偏移角从3度降到1度以内转弯角速度120度/秒95度/秒降低后机械爪振动减小投放精度提升抓取等待时间0.2秒0.3秒等待时间不够导致爪没吃上力木块容易掉超声波避障距离15厘米10厘米太远会影响正常路径太近又刹不住10厘米刚好这些参数不是一次调出来的每改一个都要在场地里跑三轮测试记录成功率和耗时。我建议备赛时把每次测试的参数和结果都记录下来用表格整理这样才能看出趋势。我们就是靠这张参数表在最后三天里把时间从85秒优化到了78秒。5. 联调中的常见问题与排查记录5.1 通信不稳定、丢帧怎么处理双控制器联调时最让人头疼的问题就是串口丢帧。现象是NXP明明发了指令STC偶尔收不到或者收到的是错误数据。我们排查过程三步走。第一步检查波特率。两个芯片的串口波特率必须严格一致我们用115200误差在0.1%以内。但NXP的MicroPython串口初始化允许配置误差校准STC8系列需要用定时器重装值精确计算差一点就会出现字节错位。第二步检查共地。两个控制板必须共地否则串口信号电平参考点不同会出现随机性错误。我们有一次就是因为NXP板用5V供电、STC板用另一路降压供电导致地电位差达到了0.7V通信乱得像天书。后来用一根粗导线把两个板子的GND短接问题立刻消失。第三步加校验和。就算前两步都对了也不排除极端电磁干扰所以协议里的校验和必须要有。我们用的是8位累加和简单但有效校验失败就丢弃这一帧NXP超时重发。加了校验和之后整个比赛期间没有出现一次因通信错误导致的事故。5.2 STC单片机复位异常的经典原因与对策前面提过复位线干扰再补充一个高发的复位异常原因电源电压跌落。电机启动瞬间电流可能达到2安培如果电源线太细或者电池内阻大STC的供电电压会瞬间掉到4.5V以下触发掉电复位。我们的解决方案有两层。硬件上STC供电端并联一个大容量的电解电容470微法和若干0.1微法陶瓷电容形成低阻抗储能网络。软件上STC开启掉电检测中断在检测到电压低于4.7V时先保存当前状态到内部SRAM再进入复位。复位后启动时读取SRAM恢复到复位前的状态继续执行未完成的动作。另外STC的引脚配置也要注意。所有不用的IO口如果悬空在强电磁干扰下可能产生随机电平触发外部中断或者唤醒等意外行为。我们的做法是把所有未使用引脚初始化为推挽输出低电平这样即使有干扰也只是在低电平附近波动不会触发逻辑跳变。5.3 传感器误触发与抗干扰处理超声波传感器在比赛场地里特别容易误触发因为场地上还有其他队伍的车在跑他们发出的超声波可能被我们接收到。我们做的第一层抗干扰是连续三次测距值都在正负2厘米范围内才认为这个距离有效如果三次中有一次偏差大就重新测一轮。第二层是给超声波加上“发射时间窗”。我们让STC只在收到NXP的“准备避障”指令之后才开始每隔100毫秒发射一次超声波其他时间完全关闭。这样既减少互相干扰也降低功耗。压力传感器用来检测机械爪是否夹到物料。我们用的是薄膜压力传感器贴在爪的内侧。它的问题是信号很微弱容易受到电机电流变化产生的共模干扰。硬件上加了一阶RC低通滤波器截止频率设在50赫兹软件上做了滞后判断压力值超过阈值持续20毫秒才确认夹到物料避免瞬时尖峰误触发。5.4 双控制器的异常恢复策略比赛场上最怕程序跑飞或者状态卡死。我们的做法是给两块主控都加了看门狗。NXP的MicroPython端有内置看门狗主循环每500毫秒喂狗一次如果主循环因为某个死循环卡住系统会自动复位重启。STC端也开启看门狗溢出时间设为1.5秒。但看门狗有个坑复位之后系统状态全丢了车辆可能停在场地中间不知所措。所以我们在NXP端加了一个“恢复模式”复位后先读取内部Flash里存的最近一次目标点然后让车辆原地转一圈重新扫描场地如果识别到目标物料就继续执行如果识别不到就低速驶回启动区等待人工干预。STC端的恢复逻辑就更精细了在状态机进入任何一个动作之前都把当前动作编号和剩余超时时间写入SRAM。复位启动后首先读取SRAM如果发现车辆正在执行某个动作就直接回到该动作的起始位置重新执行最多重试三次。这套机制确保即便出现一次意外复位车也能在5秒内恢复工作而不是原地傻等。6. 比赛现场的经验与最终建议6.1 如何高效调试双控制器的配合如果你也是双主控方案我强烈建议先在电脑上做一个“虚拟上位机”。我们用Python写了一个串口调试工具可以模拟NXP下发指令、接收STC回传状态还能绘制车辆轨迹。这样在NXP代码还没写完时就能先把STC底层的所有动作测一遍。反过来当底层稳定后再单独测NXP。可以在NXP代码里加一个“仿真模式”把视觉识别结果替换成预设的坐标数据让NXP的路径规划算法先跑起来。等到两边都自测通过了再联调。这个流程看起来多花了两天时间但实际上联调阶段遇到问题的时间至少缩短了70%。联调时一定不要一上来就全速跑。先把速度限制在20%跑一个简易流程没问题再逐步加速。我们有次贪快直接上全速结果转弯时机械爪把摄像头线扯掉了摄像头掉下来车还在往前冲撞到挡板消耗了整整半天修复。6.2 赛场上的临场策略调整方法比赛现场和训练场环境总会有差异尤其是光照。我们在NXP端写了一个“一键标定”功能按下车上的按键车辆原地旋转360度采集当前光照下物料的颜色分布自动更新阈值。这个功能救了大命因为决赛场地顶灯偏暖训练场是白炽灯如果没有一键标定物料识别率可能会掉到50%以下。另外记住要给策略留“手动模式”。比如比赛时发现投放区前面有个意料之外的障碍物这时候切到遥控模式用遥控器人工把车开到安全位置再切回自动模式。我们的代码里设计了一个两段拨码开关一个开关切换自动/手动一个切换快/慢速。赛场上有很多突发情况能快速人工接管是最实际的安全保障。6.3 个人体会与可持续优化的方向做完整辆车我最深的体会是双控制器的“协同”不在于两边代码写得多精巧而在于边界划得清不清楚。NXP就是负责“想”STC就是负责“做”两者之间只有一个串口协议谁也不要越界。这个原则让整个系统非常容易定位问题调试效率大大提高。后续如果还有时间我会考虑把NXP端的视觉识别换成轻量级神经网络模型提升对不规则物料的适应能力STC端则可以考虑引入闭环的机械爪力控进一步提升抓取成功率。蚂蚁搬家组这个项目表面上比的是搬运速度实际上比的是系统工程能力——把视觉、控制、通信、机械整合在一起还能稳定地跑完三分钟这本身就是一件很有成就感的事。希望这篇分享能给正在备赛的你带来一些参考下次场地见。