RoboMaster 队伍里有个很经典的现象:代码跑不通的时候,大家第一反应是翻控制算法,翻完 PID 再翻状态机,最后发现是底盘上一根 CAN 线松了,或者电池插头氧化得只剩半个接触面。我前后带过几届队伍,踩过的硬件坑比软件坑多得多,而这些东西在官方文档里往往只有一句话带过。这份硬件基础讲义想讲的就是这些"文档里不提、但赛场上致命"的部分——从一块 6S 电池的能量链路,到 CAN 总线的终端电阻,到主控最小系统的上电时序,再到布线与接地的隐性故障源。不管你是刚进队的新人,还是已经能画板子的老队员,这里的内容都能当作一份上电前的对照清单来用。V0.2.1 这个版本号我特意保留着,因为硬件讲义本身就是一份不断打补丁的活文档,每一版都对应着一次真实的炸板或者一次莫名其妙的丢帧。
1. 从一块电池说起:整车能量链路怎么走
1.1 6S 动力电池的电压窗口和内阻账
RoboMaster 的整车能量几乎都来自一块 6S 锂聚合物电池。标称电压 22.2V,满电 25.2V(单芯 4.2V),放电截止一般设在 19.8V(单芯 3.3V)。这三个数字不是随便记的,它们决定了你整车上所有 DC-DC 的工作范围。比如你选了一颗输入范围 7~24V 的降压模块,满电 25.2V 一插上去就直接超压,运气好是保护,运气不好就是冒烟。
更麻烦的是内阻带来的动态压降。一块状态健康的 6S 10000mAh 电池,整体内阻大概在 6~12mΩ 之间,用久了会涨到 20mΩ 以上。假设底盘四轮同时加速,瞬时母线电流拉到 100A,那么 15mΩ 的内阻就会带来 1.5V 的压降,母线瞬间从 24V 掉到 22.5V 以下;如果电池老化到 30mΩ,压降就是 3V,此时你那颗输入下限 20V 的模块就会开始不稳定输出,表现是主控随机复位、图传闪烁。很多人以为是干扰问题,其实是电池内阻问题。
我的做法是给每块电池建一张简单的体检表:编号、购入时间、循环次数、满电静置后的单体压差、大电流负载下的母线跌落到多少。单体压差超过 50mV 的电池就不要放在关键位置用了,扔给地面测试台当耗材。测内阻不需要专业设备,用一个固定阻值的大功率电阻做负载,记录空载和带载时的电压差,除以电流就是近似内阻,误差足够做趋势判断。
注意:锂聚合物电池鼓包、外皮破损、插头处有焦痕的,无论容量还剩多少,一律退役。训练场冒烟和赛场上冒烟是两码事。
1.2 XT60 之后的每一级接触电阻都算在总账里
从电池插头到分电板,中间可能经过 XT60 插头、航模插头转接、分电板的输入焊盘、保险丝座、开关。每一个环节都贡献接触电阻,典型值在 0.5~2mΩ,插拔几百次之后,镀层磨损加上氧化,涨到 5mΩ 也不稀奇。听起来微不足道,但把它们串起来算:电池插头 2mΩ + 转接 3mΩ + 分电板焊点 1mΩ + 保险丝座 3mΩ = 9mΩ。在 100A 的瞬时电流下就是 0.9V 的额外压降,全部转换成热量堆在你的插头和保险丝座上。
实际排查时,我会用最笨但最有效的办法:上电跑一段大电流动作,然后立刻用手背(不是手指)靠近插头和保险丝座感受温度。局部明显发热的位置就是接触电阻大户。红外测温枪更好,但手背在紧急情况下也够用。
提示:接触电阻的排查顺序按"从电池到用电设备"走,越靠前的环节承载电流越大,单位电阻的代价也越高。
1.3 电压分层与功率预算表
整车电压通常分成四层,分层的意义是把大电流路径和小信号路径彻底隔开:
| 电压层 | 典型来源 | 主要负载 | 设计要点 |
|---|---|---|---|
| 24V 直供 | 电池母线 | 底盘电机驱动器、云台电机 | 线径最粗,走线最短,避开信号线 |
| 12V | 降压模块 | 图传、部分工业传感器 | 注意模块的动态响应和纹波 |
| 5V | 降压模块或 BEC | 主控板、舵机、激光 | 舵机堵转电流要单独核算 |
| 3.3V | 板载 LDO 或 DC-DC | MCU 内核、IMU、编码器 | 与数字噪声隔离,靠近负载放置 |
分层之后一定要做功率预算。把所有负载的额定电流和峰值电流列出来,按电压层汇总,再乘 1.3~1.5 的安全系数,这才是你选降压模块的依据。教材上常见的错误是用 LDO 从 24V 降到 5V,此时 LDO 上消耗的功率是(24-5)× 负载电流,带 1A 就是 19W,一颗 TO-220 封装根本扛不住,几秒钟就能闻到味道。压差超过 3V 的场合老老实实上同步整流的 DC-DC 降压模块。
选模块时我看三个参数:输入范围要覆盖 19.8~25.2V 全窗口并留余量;额定输出电流要大于你的峰值负载;纹波在满载下最好低于 50mV。第三个参数最容易被忽略,但纹波大的模块会让你的 ADC 采样抖动、IMU 数据发飘。
2. 电机与驱动器:CAN 总线才是电控的骨架
2.1 M3508、M2006、GM6020 各自该待在哪
RoboMaster 生态里最常见的三类电机,选型逻辑其实是"扭矩密度"和"响应速度"的取舍:
| 型号 | 配套驱动器 | 减速比 | 特点 | 典型位置 |
|---|---|---|---|---|
| M3508 | C620 | 约 1:19 | 扭矩大、转速中等 | 底盘麦轮/全向轮、发射机构 |
| M2006 | C610 | 约 1:36 | 体积小、扭矩小 | 拨弹盘、小行程机构 |
| GM6020 | 内置驱动 | 直驱 | 响应快、精度高、可当舵机用 | 云台偏航/俯仰 |
底盘要的是把电池功率高效地变成地面推力,所以选大扭矩低转速的 M3508;云台要的是毫秒级的姿态跟随,减速箱的间隙会直接变成控制上的死区,所以要用直驱的 GM6020;拨弹这种只需要稳定输出小扭矩的机构,用 M2006 就够,省重量也省电流。
一个容易被忽略的点是减速箱的回程间隙。M3508 在换向的瞬间,角度反馈会有零点几度的"空走",如果你的控制环带宽拉得很高,这个间隙会直接引发高频振荡。我的经验是把速度环的输出限幅收紧一点,同时在换向附近做一段小幅度的死区补偿,实测能明显压掉那种"哒哒哒"的抖动声。
2.2 CAN 物理层:120Ω 终端电阻和线序
CAN 是差分总线,靠 CANH 和 CANL 之间的电压差传数据,天生抗共模干扰,但它的物理层有两个硬约束:第一,1Mbps 波特率下总线总长度不能任性拉长,整车走线尽量控制在几米以内并保持主干笔直;第二,总线的两端各需要一个 120Ω 终端电阻,中间的节点绝对不能加。
我在赛场上见过最多的 CAN 故障,就是终端电阻的问题。有人图省事,只在主控板上装了一个电阻;有人把每个电机的驱动器都当成终端,结果总线上挂了四五个 120Ω。两种情况的表现都是"随机丢帧":电机偶尔响应、偶尔不响应,重启又好一阵,非常容易误判成代码里的 ID 冲突或者发送周期问题。
排查方法很直接:断电后用万用表测 CANH 与 CANL 之间的电阻,正常应该是 60Ω(两个 120Ω 并联)。如果读到 120Ω,说明只有一端有终端;如果读到 40Ω 甚至更低,说明挂了多余的终端;如果读到开路,说明线断了或者接插件没插好。
还有一个细节是线材。双绞线能把差分信号绑在一起,让外部干扰以共模形式进入、在接收端被抵消,普通平行排线做不到这一点。屏蔽层如果要用,记住只在一端接地,两端都接会形成地环路,反而变成天线。
2.3 控制帧与反馈帧的解析细节
DJI 这套电机的 CAN 协议,控制帧用的是 0x200 和 0x1FF 这类标识符,一帧里打包四个电机的电流值,每个是 16 位有符号整数,数值范围 -16384 到 16384 线性对应 -20A 到 20A。反馈帧是 0x201 到 0x204 这种,每个电机一帧,8 个字节里塞了转子机械角度、转速、实际转矩电流和温度。
解析代码写错的地方集中在位拼接和符号扩展上。下面这段是常见的反馈帧解析写法:
typedef struct { uint16_t angle; // 机械角度 0~8191 对应 0~360 度 int16_t speed; // 转速 rpm int16_t current; // 实际转矩电流 uint8_t temp; // 温度 摄氏度 } motor_feedback_t; void parse_can_feedback(const uint8_t *data, motor_feedback_t *out) { out->angle = (uint16_t)((data[0] << 8) | data[1]); out->speed = (int16_t)((data[2] << 8) | data[3]); // 必须转成有符号 out->current = (int16_t)((data[4] << 8) | data[5]); out->temp = data[6]; }speed和current一定要转成有符号,否则负转速会变成一个巨大的正数,你的速度环会瞬间输出满电流,电机直接冲出去。角度用的是 0~8191 的单圈编码值,要得到连续多圈角度得自己维护累计量:比较本次和上次的差值,跳变超过一半量程就判定为跨圈,加或减一圈。
注意:多圈累计的角度变量在电机断电重启后会归零,上位机的姿态解算如果依赖这个角度做绝对定位,一定要在上电时先找机械限位做原点复位。
3. 主控与最小系统:从选板到引脚分配
3.1 开发板选型:官方板、量产板还是自绘板
入门阶段用官方的开发板是最省事的,接口、电源、调试器都现成,你只需要专注在 CAN 收发和串口调试上。等队伍开始追求装机密度、走线整齐和成本控制,就会走到自绘板这一步。
自绘板的收益是布局可控、接口可定制、重量和体积能压缩;代价是你得自己承担所有的硬件风险,包括电源设计、晶振布局、CAN 收发器选型和阻抗匹配。我的建议是先在官方板上跑通全部功能,形成一份稳定的引脚分配表,再把这套分配原封不动地搬到自绘板上,这样出问题时至少能确定软件是好的。
3.2 最小系统必须搞定的四件事
任何一块 MCU 板子,能不能跑起来就看四件事:供电、复位、时钟、下载。
供电部分,3.3V 这一路要干净。每一对电源引脚旁边放一个 100nF 的陶瓷去耦电容,位置尽量贴着引脚,走线尽量短,因为去耦电容的作用就是把高频噪声就近短路到地,线一长,寄生电感就把效果吃掉了。整板再放一到两个 10μF 的储能电容兜底。
复位部分,复位引脚不要悬空,加一个上拉电阻和一个对地的电容,避免上电瞬间的抖动导致 MCU 反复复位。如果复位引脚引到了外部按键或调试接口,记得加 RC 滤波,否则一根长线引过来的干扰就够你查半天。
时钟部分,如果用了外部晶振,负载电容的取值要对着晶振规格书来,一般是 12pF 到 22pF 之间,取大了起振慢、取小了频偏。晶振的走线要尽量短、尽量靠近芯片、下方不要走高速信号线,最好有一块完整的地平面做参考。
下载部分,SWD 接口的 SWCLK 和 SWDIO 线尽量短,必要时串一个小电阻抑制振铃。BOOT 引脚的上下拉电阻别省略,否则你可能怎么都进不了下载模式,还以为是芯片坏了。
3.3 引脚分配表:提前把冲突记下来
引脚分配这件事,我建议在动焊之前先在表格里排完,因为 STM32 这类芯片的外设复用关系非常密集,CAN、UART、SPI、定时器通道经常抢同一组引脚。
| 功能 | 外设 | 引脚 | 备注 |
|---|---|---|---|
| 底盘 CAN | CAN1 | PA11/PA12 | 1Mbps,走线最短 |
| 云台 CAN | CAN2 | PB12/PB13 | 与 CAN1 共用滤波器组 |
| 遥控接收 | UART3 | PB10/PB11 | 注意电平匹配 |
| IMU | SPI1 | PA5/PA6/PA7 | 时钟线尽量短 |
| 调试串口 | UART1 | PA9/PA10 | 一定要预留 |
| 蜂鸣器 | TIM 通道 | PB0 | 用于状态提示 |
这张表最下面那行"调试串口"看起来最没用,实际上最重要。没有调试串口,你只能靠 LED 闪灯数次数来定位问题,效率会低到让人崩溃。另外要注意 CAN1 和 CAN2 共享滤波器资源,用的时候要把 CAN2 的起始滤波器编号配置对,否则会出现"CAN1 收得到、CAN2 收不到"这种诡异现象。
4. 传感器与信号链:把物理量变成可信数据
4.1 IMU 的硬件前提比算法更重要
姿态解算的精度,七成取决于 IMU 的硬件状态,三成才是算法。三个硬件要点:量程、安装刚性、零偏标定。
量程要按实际工况选。云台电机的角速度在快速甩头时能到每秒几百度的量级,如果陀螺仪量程只开到 500dps,一甩头就饱和,数据直接截顶,后面的解算全乱。加速度计量程也是同理,底盘经过减速带时的冲击加速度不小,量程太窄同样是截顶。
安装刚性是最容易吃亏的地方。有人为了让 IMU 少受震动,在下面垫了很软的减震棉,结果 IMU 和机架之间产生了一个低频的机械谐振,姿态数据里多出一段几十赫兹的抖动,滤波器怎么调都调不干净。正确做法是用刚度足够高的方式固定,例如直接锁在结构件上,或者用中等硬度的减震垫,并确保 IMU 与云台之间没有额外的自由度。
零偏标定必须在整机上电、静止的状态下做,标定期间不能有任何震动和温漂干扰。零偏会随温度变化,所以最好做一次温度补偿曲线:把整机放在不同温度环境下静置,记录零偏随温度的变化,运行时按当前温度插值补偿。
4.2 编码器、限位开关和光电门的接线细节
这类开关量传感器的故障几乎都出在电气细节上。机械限位开关接 MCU 输入时,一定要有上拉或下拉电阻确定默认电平,悬空的输入引脚会随环境噪声乱跳。走线超过十几厘米的,再加一个 100nF 的对地电容做硬件去抖。
软件去抖也别省。典型的机械开关抖动时间是 5~20ms,我在初始化阶段会连续采样几十次确认稳定状态,运行中再做几次一致性校验,避免单次误触发把机构送进限位。
光电门用于拨弹盘计数或者发射机构检测时,注意它的输出类型是集电极开路还是推挽,前者需要外接上拉电阻。另外光电门对灰尘和强光敏感,装在拨弹盘旁边的时候要定期清理,赛前一定要擦一遍。
4.3 模拟采样的三个关键:分压、滤波、基准
电池电压检测是最典型的模拟采样场景。以 25.2V 满电为例,MCU 的 ADC 参考电压是 3.3V,那么分压比至少要大于 25.2/3.3 ≈ 7.6,取 1:11 比较稳妥,这样满电时采样点电压是 25.2/11 ≈ 2.29V,留足了余量。
电阻选取要注意两点:阻值不能太大,否则 ADC 采样电容充电时间不够,读数偏低;也不能太小,否则长期挂在电池上会持续耗电。上臂取 100kΩ、下臂取 10kΩ 是比较常用的组合,静态电流约 0.23mA,可以接受。分压点后面并一个 100nF 电容到地,和上臂电阻构成低通滤波。
ADC 基准电压的稳定性直接决定测量精度。用 MCU 内部的 VDD 做基准的话,VDD 本身的波动会全部体现在读数里,所以要么用外部基准芯片,要么在软件里用内部参考电压通道反算实际 VDD,做一次比值补偿。采样软件上我习惯做中值滤波加滑动平均:先采 9 次取中值剔除野点,再对最近若干次结果做平均,读数又稳又不迟钝。
5. 布线、接地与抗干扰:看不见的故障源
5.1 线径和长度是要算的,不是凭感觉挑的
导线有电阻,电阻乘以电流就是压降,压降乘以电流就是发热。粗略估算时可以用铜的电阻率 0.0175 Ω·mm²/m,走线和回线都要算上,公式是:
ΔU = 2 × 0.0175 × L × I / S
其中 L 是单程长度(米),I 是电流(安培),S 是导体截面积(平方毫米)。举个例子,一根 1.5m 长的线要走 20A,允许压降 0.5V,那么需要的截面积是 2 × 0.0175 × 1.5 × 20 / 0.5 = 2.1 mm²,约合 AWG 14 到 AWG 13 之间的线。如果你手上只有 0.75mm² 的线,压降会接近 1.4V,同时线自身要消耗 28W 的功率,很快就会烫手甚至熔皮。
| 截面积 (mm²) | 近似 AWG | 常见用途 |
|---|---|---|
| 0.2 | 24 | 信号线、传感器 |
| 0.5 | 20 | 小功率外设、舵机 |
| 0.75 | 18 | 图传、中等功率模块 |
| 1.5 | 16 | 单电机供电支路 |
| 4.0 | 11 | 电池主回路、分电板输入 |
实际布线时还有两个经验值:大电流线的长度尽量短,能省一半长度就省一半,因为压降和发热都跟长度成正比;信号线和大电流线不要平行长距离走,如果非要交叉,尽量垂直交叉。
5.2 接地拓扑:星形还是就近接地
整车地线处理的核心原则是"大电流回流路径不要穿过小信号区域"。底盘电机的几十安培电流如果从主控板的地平面上流回去,主控的参考地会随着电流波动而波动,表现是 ADC 读数跳动、CAN 偶尔出错。
我采用的做法是单点星形接地:分电板设一个主接地点,电池负极、各电机驱动器负极、各降压模块输入负极全部汇到这个点;降压模块的输出侧地再单独汇总到主控附近的一个信号接地点,两个接地点之间只通过一条粗线连接,不形成多个回路。
屏蔽线的处理也是同一个逻辑。屏蔽层的作用是把耦合进来的干扰导走,如果两端都接地,屏蔽层本身就成了一个环路,反而会感应电流。所以我只在主控这一端接地,另一端悬空或者通过一个小电容接地。
5.3 连接器选型和防呆设计
连接器选错是"看起来很蠢但很常见"的坑,尤其是在振动环境下:
| 连接器 | 载流能力 | 特点 | 适用场景 |
|---|---|---|---|
| XT60 | 大 | 防呆、插拔力强 | 电池主回路 |
| XT30 | 中 | 体积小 | 模块级供电 |
| MR30 | 中 | 三芯、带锁扣感 | 电机支路 |
| XH2.54 | 小 | 有卡扣、便宜 | 传感器、风扇 |
| GH1.25 | 小 | 带锁扣、抗震好 | 板间连接、信号 |
带锁扣的连接器在振动环境里的价值非常高。我在赛场上见过因为用了不带锁扣的杜邦线,跑了几圈之后整排线松脱,主控直接失联。凡是会在颠簸中运动的部位,一律用带锁扣的连接器,插好之后再点一点热熔胶做二次固定,同时把线束用扎带在结构件上做应变消除,让拉力作用在扎带上而不是焊盘上。
6. 上电测试与讲义迭代:把事故挡在赛场外
6.1 上电前的万用表自查清单
新板子或者大改之后的第一次上电,我固定走这套检查:
- 目视检查焊点有没有连锡、虚焊、锡珠,尤其是芯片引脚密集的区域;
- 万用表二极管档测电源输入到地是否短路,正常应该读到几百欧以上的阻值或者二极管压降;
- 测各电压输出对地是否短路,尤其是 5V 和 3.3V 这两路;
- 测 CANH 与 CANL 之间的电阻,确认是 60Ω 左右;
- 确认所有连接器的方向和针脚定义,对照图纸一根一根数,不要凭印象。
第二步和第三步是最省时间的。我见过一次因为一颗锡珠导致 3.3V 对地短路,上电瞬间烧掉了整颗主控芯片,那颗锡珠在放大镜下才看得出来。万用表测一下只要十秒。
6.2 分阶段上电:限流电源是救命的东西
有条件的队伍一定要配一台可调限流电源。第一次上电时把电压设到额定值、电流限制设到很小的值(比如 200mA),观察几件事:电源有没有立刻进入限流状态、有没有明显的温升、电流是不是维持在合理的静态值。如果一上电就冲到限流值,说明有短路,立刻断电排查,此时因为电流被限制住了,器件一般不会被烧穿。
限流通过之后,把电流上限逐步放开,同时观察静态电流。静态电流明显偏大通常意味着某个器件在漏电或者某个模块在异常工作。等所有静态电流都正常了,再插电池上电。
注意:调试阶段不要用大容量电池直接给新板子供电,限流电源能救回很多本来会被烧掉的板子。
6.3 常见故障速查表
| 现象 | 高概率原因 | 排查方向 |
|---|---|---|
| 主控随机复位 | 母线压降大、电池内阻高 | 带载测母线电压、查电池健康度 |
| 电机偶发不响应 | CAN 终端电阻异常 | 断电测 CANH-CANL 电阻 |
| IMU 数据持续抖动 | 安装刚性不足或量程饱和 | 检查固定方式、看原始数据是否截顶 |
| ADC 读数跳动 | 参考电压不稳或滤波不足 | 检查基准、增加中值滤波 |
| 某个模块不工作 | 供电不足、线径过细 | 单独测该模块输入电压 |
| 通信偶发错误 | 信号线与动力线并行走线 | 分离走线、检查屏蔽层接地 |
6.4 版本号的意义:为什么值得记 V0.2.1
把讲义标上版本号,看上去有点形式主义,但对队伍来说很实用。V0.1 是最初的骨架,只有器件清单和大致接线;V0.1.x 这些小版本通常是在补具体的坑,比如某次炸板之后加进去的"限流电源必须使用",某次丢帧之后加进去的"CAN 终端电阻测量方法"。V0.2 则往往对应一次结构性的调整,比如电压分层从三层改成四层,或者把底盘和云台的接地方案彻底重做。
V0.2.1 这种小版本号,记录的是"在上一个结构版本基础上打的补丁"。它值钱的地方在于,当你第二年重新捡起这份文档时,能顺着版本历史看到每一次事故的来龙去脉,而不是面对一堆不知从哪来的规则。我带队的时候有个硬性要求:任何一次赛场故障,无论大小,复盘之后必须在讲义里加一条,并且提升一个小版本号。一个赛季下来,这份讲义会变成队伍最值钱的东西,比任何一次比赛的奖状都耐用。
如果你现在手上正好有一块刚焊好的板子,我建议先别急着上电,把第 6.1 节那份清单打印出来,一项一项勾完再说。这个动作我做了很多次,每次都能勾出点东西来,而每勾出一个,就少一次炸板和一次赛场上手忙脚乱的机会。