一次SBUS协议航模遥控器不跟手问题的解决。中断线程化,串口分层架构

个人的机器人底盘使用sbus协议航模遥控器,主要依靠了DMA加溢满中断,但是因为早期技术能力问题,出现了机器人指令遵循延迟“不跟手”现象,经过后续个人学习后,排查出了问题并根据具体需求进行了解耦和环形缓冲区构建。

错误思路

一年前,需要做一个遥控器控制收帧,一开始依靠DMA加空闲中断,但是传来的数据比较随机,长度,完整性都无法确定,当时的思路比较局限,只会直接处理收到的数据,当时不知道可以建立缓冲区处理
于是就发现接收到的数据处理时要么长度不对,要么混进了被截断的数据,根本无法使用。
于是想了个办法,用一个巨大的接收区(两倍指令长度)加DMA溢满中断,这样只要经过处理,百分百能受到一条完整的数据包。看上去很美好,但是发现实际上遥控器一点也不跟手,当时也没想明白怎么回事。后来也就没管了。能跑就行
近期解决其他问题时复盘了一下,也是发现了问题的所在:

  1. 溢满中断有吞指令的风险
  2. 两倍指令长的接收区,会导致收到两帧有一帧被截断而丢掉,而且被丢掉的一定是最新受到的指令,而遵循上一条指令,所以会导致不跟手
  3. 为了显示收到消息的状态,在DMA的中断回调函数里加了一个阻塞的SPI和w2812灯珠通信,严重影响稳定性和cpu效率(而且还顺带解决了控制任务的延时会时不时超时的问题)
  4. 空闲中断消息断断续续的问题可以通过建立环形缓冲区之类的方法解决

现状分析

用飞书画板绘制出了出问题的串口处理流程,可以直观的分析出来犯的错误
image

实验验证

通过几个实验可以测量出具体的情况:

实验一 测量SUBS接收情况和中断处理的时间

实验方法:

  1. 硬件测量法:首先打算通过中断结束前后反转gpio的电平并用逻辑分析仪测量波形周期。考虑到需要额外接线和配置gpio输出,然而板子的预留的gpio资源有限,因此pass~
  2. 软件测量法1:使用tick计算中断时间间隔,然而普通tick精度不够,分辨率只有1ms(1khz),不能满足测量需求。
  3. 软件测量法2:最终使用DWT的周期计数器(我这边板子是480Mhz,绝对够用了)之后经过换算得到us,可以测量中断时间,并且因为是软件方案,可以直接打印输出以及平均值,峰峰值。

实验结果:测得中断的处理时间峰值来到了惊人的904 µs,而平均值也有459 µs
实验结论:中断处理时间确实太长了,这样的中断处理时间是严重危害实时性的,特别是在我这个时刻需要计算平衡的轮腿机器人里面,简直就是一坨shit。

实验二 确定是是否是spi发送和通道解析影响

实验方法:以实验一数据为基线,测量去掉spi发送和通道解析,改为设置标志位。
实验结果:测得中断的处理时间峰值11.9 µs,平均值7 µs
实验结论:来到了正常的范围,确实是二者的影响

实验三 验证避免丢帧的可行性

实验方法:改变收帧方式,逐个字节接收并处理,观察每秒钟处理sbus帧的数量
实验结果:原代码处理数量:平均36帧每秒 修改后:平均72帧每秒 接收机约14ms收到一次数据 每帧之间隔14ms
实验结论:确实是正好丢了一半的数据.....

修改方案

关于中断:
采用中断线程化的方法,在中断里只设置标志位,中断的内容的执行放在线程里完成,避免干扰实时性

关于收帧:
根据实验可知,由于每帧之间隔14ms,空闲中断已经可以满足接收要求
但是未来将会抛弃航模遥控器,将底盘改为下位机,控制命令由上位机发送
因此有必要将遥控器与串口通信层解耦,并采用环形缓冲区应对不定长数据

最终产出

在飞书画板里设计了如下的新架构,以中断唤醒任务的通知驱动制,中断线程化,将协议和串口解耦。能够解决遥控器不跟手的问题,FPS达到了72帧的无丢帧,并且中断处理时间在12us内,解决了痛点,并为未来的RL强化学习部署预留了接口。(简化后的主要逻辑如下,日志相关的省略)
不足之处:

  1. 尚未进行强化学习的部署,不知其是否需要积分等环节,具体处理链路可能需要在实际部署时修改
    image
posted @ 2026-10-09 17:30  Hynaya  阅读(8)  评论(0)    收藏  举报