1. 扫地机器人双脑架构的由来与核心逻辑
1.1 一个真实场景引发的架构思考
去年冬天,我在一个做扫地机器人方案的朋友那里喝茶,他给我看了一段测试视频:一台正在运行的扫地机器人,因为Linux主控端的一个内存泄漏问题,系统在运行了四十多分钟后突然卡死,而此时机器人的滚刷还在高速旋转,机器却已经失去了对悬崖传感器的响应。虽然最终靠硬件看门狗把整机复位了,但那个“卡死到复位”之间的几秒钟窗口,足以让一台机器从楼梯口冲下去。
这件事让我重新审视了一个在嵌入式圈子里争论已久的话题:扫地机器人到底该不该把安全相关的控制逻辑交给Linux?我的答案很明确——不能,而且永远不能。这不是对Linux有偏见,恰恰相反,Linux在扫地机器人上承担着非常关键的角色,比如SLAM建图、路径规划、WiFi通信、语音交互、OTA升级,这些活儿Linux干得比任何RTOS都漂亮。但一旦涉及到“撞墙前必须停”“悬崖前必须退”“电机堵转必须断”这类安全底线,就必须交给一个独立的、极简的、可预测的MCU来兜底。
这就是双脑架构的核心思想:一个“大脑”负责聪明,一个“小脑”负责安全。聪明的那个可以复杂、可以跑Linux、可以联网、可以升级;安全的那个必须简单、必须跑裸机或RTOS、必须独立供电、必须能在主控完全死机的情况下依然把机器停下来。
1.2 为什么Linux天生不适合做安全兜底
很多人会反驳:Linux有实时补丁(PREEMPT_RT),有看门狗,有各种安全机制,为什么不能做安全控制?我试着从几个维度把这个问题讲透。
第一,Linux的调度不确定性是物理层面的。即使打了RT补丁,Linux内核仍然要处理中断、内存管理、文件系统、网络协议栈等大量非确定性任务。一个高优先级的实时线程,理论上可以被调度器保证在某个时间窗口内执行,但实际上,当内核正在处理一个长时间的不可抢占区(比如某些驱动里的自旋锁),你的安全线程就是叫天天不应。我实测过,在负载较重的情况下,一个标称1ms周期的RT线程,抖动到十几毫秒是常有的事。对于一台以0.3m/s移动的扫地机器人来说,十几毫秒意味着它已经往前走了将近5毫米,如果前面是悬崖,这5毫米可能就是“停住”和“掉下去”的区别。
第二,Linux的启动时间和运行状态不可控。扫地机器人从按下开关到Linux完全启动,通常需要几秒到十几秒。在这段时间里,如果电机驱动已经上电,而安全逻辑还没跑起来,就是一个巨大的风险窗口。而MCU从复位到进入主循环,通常只需要几毫秒到几十毫秒,STM32F0系列甚至能在几毫秒内完成初始化并开始执行安全检测。
第三,Linux的软件栈太厚,失效模式太多。一个跑着Linux的系统,上面有内核、有驱动、有根文件系统、有各种用户态进程、有通信中间件。任何一个环节出问题——内存耗尽、文件系统损坏、某个进程死锁、通信超时——都可能导致安全逻辑无法执行。而一个跑裸机的MCU,代码量可能只有几KB,失效模式屈指可数,甚至可以做到形式化验证。
第四,安全认证的硬性要求。如果扫地机器人要出口到对功能安全有要求的市场,安全相关的控制链路通常需要满足IEC 61508或ISO 13849等标准。让一个几百万行的Linux内核去通过这类认证,成本高到不现实。而一个简单的MCU安全固件,认证路径清晰得多。
所以,双脑架构不是“为了复杂而复杂”,而是被物理规律和工程现实逼出来的最优解。
1.3 双脑架构的典型分工
在我接触过的方案里,双脑架构的分工通常是这样切的:
| 职责 | Linux主控(大脑) | MCU安全核(小脑) |
|---|---|---|
| SLAM建图与定位 | 核心任务 | 不参与 |
| 路径规划与导航 | 核心任务 | 不参与 |
| WiFi/蓝牙通信 | 核心任务 | 不参与 |
| 语音交互 | 核心任务 | 不参与 |
| OTA升级 | 核心任务 | 仅接收安全固件更新 |
| 电机速度指令 | 下发目标速度 | 执行并监控 |
| 悬崖检测 | 上报传感器数据 | 独立判断并急停 |
| 碰撞检测 | 上报传感器数据 | 独立判断并急停 |
| 电机堵转保护 | 不参与 | 独立判断并断电 |
| 电池过放保护 | 上报电量 | 独立判断并切断 |
| 看门狗 | 被监控 | 监控主控 |
这张表的关键在于:MCU不信任Linux下发的任何指令,它只执行经过自己独立判断后认为安全的指令。Linux说“往前跑”,MCU会先看悬崖传感器、看碰撞传感器、看电流检测,确认没问题才真正驱动电机。Linux说“停”,MCU会立即停。Linux什么都不说(死机了),MCU会在超时后主动停。
这就是“安全永远不能交给Linux”的工程含义:安全逻辑必须在一个独立于Linux的、可预测的、极简的硬件上运行,并且拥有最终的执行权。
2. 核心硬件选型与安全核设计要点
2.1 为什么STM32是安全核的常见选择
在扫地机器人双脑架构里,安全核MCU的选型有几个硬性约束:要有足够的GPIO和定时器资源、要有CAN或UART与主控通信、要有ADC做电流检测、要能在恶劣的电机噪声环境下稳定工作、要有足够的安全特性(如独立看门狗、时钟安全系统、CRC校验等)。STM32系列几乎是这个场景下的默认答案。
具体到型号,我见过最多的几类:
- STM32F0系列:成本极低,适合做纯安全兜底,跑裸机,代码量小,响应快。缺点是外设资源少,如果安全逻辑复杂一点就不够用。
- STM32F1系列:经典款,资源适中,社区资料多,适合中小型扫地机器人。
- STM32F4系列:性能强,适合安全核同时要处理一些传感器融合的场景,但成本偏高。
- STM32G0系列:新一代入门款,安全特性比F0更好,有硬件CRC、双看门狗,性价比很高。
- STM32L4系列:低功耗场景,如果安全核需要长期待机监听,可以考虑。
选型时我一般会问几个问题:安全核需要几路PWM输出?需要几路ADC采样?需要几路外部中断?通信接口是UART还是CAN?有没有功能安全认证需求?把这些列清楚,再对照STM32各系列的数据手册选,基本不会错。
2.2 安全核的独立供电与隔离设计
双脑架构里有一个容易被忽视但极其关键的细节:安全核的供电必须独立于主控。我见过一些方案为了省成本,让Linux主控和MCU共用一路DCDC,结果主控端因为负载突变导致电压跌落时,MCU也跟着复位,安全逻辑直接失效。
正确的做法是:安全核用独立的LDO供电,输入直接来自电池或主DCDC的前级。这样即使主控端的电源被拉垮,安全核依然能正常工作。同时,安全核的GPIO与主控之间最好加电平隔离或至少加限流电阻,防止主控端异常时把安全核的IO拉死。
另外,安全核的看门狗必须用独立看门狗(IWDG),时钟源用内部LSI,不依赖主控的任何时钟。这样即使主控把系统时钟搞挂了,安全核的看门狗依然能正常复位。
2.3 通信链路的设计与容错
Linux主控和安全核之间通常用UART或SPI通信,少数用CAN。通信协议的设计有几个原则:
- 心跳机制:主控每隔固定时间(比如50ms)发一次心跳,安全核如果在比如200ms内没收到心跳,就判定主控失联,主动进入安全状态(停电机、报警)。
- 指令校验:每条指令都要带CRC或校验和,防止通信误码导致误动作。
- 指令超时:安全核收到速度指令后,如果在一段时间内没有收到新的指令,就自动减速停车,防止主控死机后电机还在跑。
- 状态回传:安全核要定期把当前的安全状态(是否触发急停、电流是否正常、传感器是否有效)回传给主控,让主控知道底层是否健康。
我踩过的一个坑是:早期方案里主控和安全核用UART通信,波特率设了115200,结果电机PWM一开,通信误码率飙升。后来降到57600,加上硬件流控和CRC,才稳定下来。所以通信速率不是越高越好,要看实际的电磁环境。
3. 安全核固件的核心模块与实现细节
3.1 安全核的主循环设计
安全核的固件架构必须极简。我通常用裸机写一个超级循环,结构大概是:
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC_Init(); MX_TIM_Init(); MX_UART_Init(); IWDG_Init(); safety_state = SAFE_STATE_INIT; while (1) { IWDG_Reload(); // 喂狗 read_sensors(); // 读悬崖、碰撞、电流 check_comm_timeout(); // 检查主控心跳 update_safety_state(); // 更新安全状态机 execute_motor_control(); // 执行电机控制 send_status_to_host(); // 回传状态 } }这个循环的周期必须固定且可预测。我一般会把关键的安全检测放在定时器中断里,比如1ms一次,确保即使主循环被某个慢操作阻塞,安全检测依然能按时执行。
3.2 悬崖检测的独立判断逻辑
悬崖传感器通常是红外或ToF,输出模拟量或数字量。安全核要独立读取这些传感器,并且用独立的阈值判断。这里的关键是:不要用主控下发的阈值,安全核自己有一套固定的、经过标定的阈值。
我通常会在安全核里做两级判断:
- 一级预警:传感器读数超过预警阈值,安全核开始减速,并通知主控。
- 二级急停:传感器读数超过急停阈值,安全核立即切断电机PWM,不管主控在干什么。
急停阈值要留足够的余量。比如悬崖传感器在距离地面5cm时输出2.5V,在3cm时输出3.0V,那急停阈值可以设在2.8V,对应大约4cm的距离。这样即使机器人以最大速度前进,也有足够的反应时间。
3.3 电机堵转与过流保护
电机堵转是扫地机器人最常见的故障之一。安全核要通过电流检测(通常用采样电阻+运放+ADC)实时监控电机电流。当电流超过阈值且持续时间超过比如100ms,就判定为堵转,立即切断电机输出,并通知主控。
这里有个细节:堵转判断不能只看瞬时电流,要看电流的积分或持续时间。因为电机启动瞬间的浪涌电流可能很大,但那是正常的。我通常会用滑动窗口平均或简单的计数器:电流超过阈值时计数器加一,低于阈值时计数器减一,计数器超过某个值就触发保护。
另外,安全核要能独立控制电机驱动器的使能引脚。即使主控还在发PWM,安全核只要把使能拉低,电机就会立即断电。这个使能引脚必须是安全核独占的,主控不能控制。
3.4 安全状态机的设计
安全核内部要有一个清晰的状态机,我一般会设计这几个状态:
- INIT:初始化,电机禁用,等待主控握手。
- STANDBY:待机,电机禁用,心跳正常。
- RUNNING:正常运行,电机使能,持续监控。
- WARNING:预警状态,比如悬崖传感器接近阈值,减速运行。
- EMERGENCY:急停状态,电机立即禁用,等待主控复位或人工干预。
- FAULT:故障状态,比如传感器失效、通信持续超时,电机禁用,需要断电重启。
状态之间的切换条件要明确且互斥。比如从RUNNING到EMERGENCY,只要悬崖急停触发或碰撞触发或电流堵转触发,就立即切换,不经过任何中间状态。从EMERGENCY回到RUNNING,必须主控明确发送复位指令,并且安全核确认所有传感器恢复正常。
4. 常见问题与排查技巧实录
4.1 主控死机后安全核为什么没停
这是双脑架构最常被问到的问题。如果主控死机了,安全核应该通过心跳超时来停车。但实际中可能出现几种情况:
- 心跳超时时间设得太长:比如设了2秒,那主控死机后电机还要跑2秒。我一般建议心跳周期50ms,超时时间200ms,这样主控死机后200ms内电机就会停。
- 安全核也在忙别的:如果安全核的主循环被某个慢操作阻塞,心跳检测没及时执行,也会导致停车延迟。解决办法是把心跳检测放在定时器中断里,或者用独立的硬件看门狗。
- 通信链路物理断开:如果UART线松了,安全核收不到心跳,应该触发超时。但如果安全核的UART接收中断被其他高优先级中断屏蔽了,也可能漏掉超时判断。所以超时判断最好用独立的定时器。
4.2 电机噪声导致传感器误触发
扫地机器人的电机噪声很大,尤其是滚刷电机和风机。这些噪声可能耦合到悬崖传感器或碰撞传感器的信号线上,导致安全核误判。
我常用的解决办法:
- 硬件滤波:在传感器信号线上加RC低通滤波,截止频率根据信号带宽来定。比如悬崖传感器信号变化慢,可以用10Hz截止。
- 软件滤波:在安全核里做滑动平均或中值滤波。比如连续采样5次,去掉最大最小值,取中间3次的平均。
- 阈值滞回:设置两个阈值,比如触发急停用2.8V,恢复用2.5V,避免在阈值附近反复触发。
- 屏蔽时间:在电机启动或换向的瞬间,暂时屏蔽传感器判断,等噪声过去再恢复。但屏蔽时间要尽量短,且不能屏蔽急停逻辑。
4.3 安全核固件升级的安全策略
安全核的固件也需要升级,但升级过程本身就是一个风险窗口。我的做法是:
- 双区备份:安全核的Flash分成两个区,一个运行区,一个备份区。升级时先写备份区,校验通过后再切换。
- 签名校验:升级固件必须带数字签名,安全核用内置的公钥验证签名,防止被篡改。
- 升级期间保持安全:升级过程中,安全核依然要执行安全监控,电机保持禁用状态。
- 回滚机制:如果新固件启动后自检失败,自动回滚到旧固件。这就是热词里提到的“MCU antirollback”思路的反向应用——不是防止回滚,而是保证可回滚。
4.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 主控死机后电机不停 | 心跳超时未触发 | 用示波器看UART心跳信号 | 缩短超时时间,心跳检测放中断 |
| 悬崖传感器误触发 | 电机噪声耦合 | 示波器看传感器信号 | 加RC滤波,软件中值滤波 |
| 安全核频繁复位 | 电源被主控拉垮 | 测安全核供电电压 | 独立LDO供电,加去耦电容 |
| 通信误码率高 | 波特率过高或干扰 | 看UART错误计数 | 降波特率,加CRC,硬件流控 |
| 堵转保护不触发 | 电流阈值过高 | 测实际堵转电流 | 降低阈值,加持续时间判断 |
| 安全核启动慢 | 时钟配置复杂 | 测复位到主循环时间 | 简化时钟,用内部RC |
5. 双脑架构的扩展与演进
5.1 从双脑到三核的演进
在一些高端扫地机器人上,我见过三核架构:一个Linux主控负责导航和交互,一个MCU负责安全兜底,还有一个MCU或DSP负责电机FOC控制。这样分工更细,安全核可以更专注于安全逻辑,电机控制核专注于电流环和速度环。
但三核也带来了新的问题:核间通信更复杂,成本更高,调试更麻烦。我的建议是,如果双核能满足安全需求,就不要上三核。安全核的代码量越小、逻辑越简单,可靠性越高。
5.2 安全核与功能安全认证
如果产品要过功能安全认证,安全核的固件需要按照IEC 61508或ISO 13849的要求来开发。这意味着:
- 需求追溯:每条安全需求都要能追溯到代码和测试用例。
- 代码规范:比如MISRA C,禁止动态内存分配,禁止递归,限制指针使用。
- 单元测试:安全核的每个函数都要有单元测试,覆盖率要求通常很高。
- 故障注入测试:要模拟各种故障(传感器失效、通信中断、电源跌落),验证安全核能正确响应。
- 独立看门狗:看门狗必须独立于安全核的软件,最好用外部看门狗芯片。
这些要求会让开发周期和成本大幅增加,但对于要进入对功能安全有要求的市场的产品,这是必须走的路。
5.3 安全核的调试与测试技巧
安全核的调试和主控不太一样,因为它通常没有操作系统,没有文件系统,没有网络。我常用的调试手段:
- GPIO打点:在关键代码段翻转GPIO,用示波器看时序。这是最直接的方法,能看出安全逻辑的执行时间和顺序。
- 串口打印:安全核留一个调试串口,打印状态机的切换和关键变量。但要注意,打印本身会影响实时性,所以只在调试时开,量产固件要关掉。
- 逻辑分析仪:抓UART、SPI、PWM波形,分析通信协议和电机控制时序。
- 故障注入:手动短接传感器、断开通信线、拉低电源,看安全核的反应。这个测试一定要做,而且要在各种工况下反复做。
我个人的经验是,安全核的测试用例要覆盖所有状态机的切换路径,以及所有可能的故障组合。比如“主控死机+悬崖传感器触发”“通信中断+电机堵转”“电源跌落+看门狗复位”,这些组合场景才是真正考验安全核的地方。
5.4 一个实际项目的参数配置参考
最后分享一个我参与过的扫地机器人双脑架构项目的关键参数,供参考:
| 参数 | 值 | 说明 |
|---|---|---|
| 安全核型号 | STM32G030 | 低成本,带硬件CRC |
| 安全核主频 | 64MHz | 内部RC,不依赖外部晶振 |
| 主循环周期 | 1ms | 定时器中断驱动 |
| 心跳周期 | 50ms | 主控发送 |
| 心跳超时 | 200ms | 安全核判断 |
| 悬崖急停阈值 | 2.8V | 对应约4cm |
| 悬崖预警阈值 | 2.5V | 对应约6cm |
| 堵转电流阈值 | 2.5A | 持续100ms触发 |
| 电机使能控制 | 安全核独占GPIO | 主控无法控制 |
| 看门狗 | 独立看门狗,LSI时钟 | 超时200ms |
| 通信接口 | UART 57600 | 带CRC和硬件流控 |
| 供电 | 独立LDO 3.3V | 输入来自电池前级 |
这套参数在实际测试中表现稳定,主控死机后电机在200ms内停止,悬崖传感器在4cm处可靠触发急停,电机堵转在100ms内切断输出。当然,具体参数要根据实际传感器和电机特性来标定,不能照搬。
双脑架构的核心思想其实可以用一句话概括:让复杂的归复杂,让安全的归安全。Linux负责聪明,MCU负责可靠。两者各司其职,通过清晰的通信协议和独立的安全判断,共同保证扫地机器人在各种异常情况下都不会造成伤害。这个架构不仅适用于扫地机器人,任何需要“智能+安全”双重保障的移动设备,都可以参考这个思路来设计。