四足机械狗/人形机器人实时软件设计规则
前言
人形机器人软件系统是一个对实时性要求较高的软件系统。它包含低层级运动闭环、运动规划与行为决策、环境感知、人机交互等任务。在这些任务中,有些任务对实时性要求较高,例如,EtherCAT 主站、力矩闭环。有些任务对实时性要求不高,例如,环境感知、人机交互任务。本文将从PREEMPT_RT Linux、操作系统调度策略、CPU内核隔离、内存锁定、进程亲和性绑定方面进行阐述,详细讲解如何构建一个既能满足各任务实时性需求,又能满足功能多样性需求的四足机械狗/人形机器人软件系统。
一、什么是 PREEMPT_RT
PREEMPT_RT(Real-Time Preemption),俗称 RT 补丁,是一套对 Linux 内核进行大规模改造的补丁集。其目标是把标准 Linux 从软实时改造为具备确定性低延迟的实时 Linux,让内核几乎全部代码路径支持抢占。PREEMPT_RT 四大核心改造如下。
1. 自旋锁改造:spinlock → rtmutex(可睡眠锁)
- 普通 spinlock_t 在 PREEMPT_RT 下不再忙等、不再关闭抢占;
- 内部基于rtmutex(实时互斥锁)实现,争锁失败时线程主动让出 CPU;
- 自带优先级继承(PI),缓解优先级反转;
- 极短底层同步代码保留 raw_spinlock_t(真正不可抢占)。
2. 中断线程化(Threaded IRQs)
硬件中断只保留极简上半部(ack 中断);绝大多数中断处理逻辑放到独立内核线程运行。这样做的好处有
- 中断线程拥有优先级,可以被更高优先级实时任务抢占;
- 不再出现 “中断长时间霸占 CPU” 阻塞实时任务。
3. 最大化内核抢占点
普通 CONFIG_PREEMPT:遇到 spinlock 临界区就不能抢占。
PREEMPT_RT:几乎所有内核代码都允许抢占,只有极少数极短底层代码路径禁止抢占。
4. 增强高精度定时器 hrtimer
为周期性控制任务(伺服、运动控制)提供稳定纳秒级定时能力。
二、Linux 四种抢占模式对比
| 配置 | 名称 | 实时能力 | 典型最大延迟 | 适用场景 |
|---|---|---|---|---|
| PREEMPT_NONE | 无抢占 | 最差 | 数 ms~ 几十 ms | 服务器、高性能计算 |
| PREEMPT_VOLUNTARY | 自愿抢占 | 一般 | ~2ms | 桌面发行版默认 |
| PREEMPT | 可抢占内核(低延迟) | 中等 | 几百 μs~1ms | 普通低延迟嵌入式 |
| PREEMPT_RT | 实时抢占内核 | 最优 | 几十 μs(优化后 < 50μs) | 工业控制、机器人、伺服、音频 |
三、SCHED_FIFO 与 SCHED_RR调度策略
SCHED_FIFO 与 SCHED_RR都属于Linux实时调度策略,这两种实时策略能不能用,要看你的 Linux 内核有没有开启对应的抢占选项(PREEMPT_RT 或者 CONFIG_PREEMPT)。
1. SCHED_FIFO(先进先出)
(1) SCHED_FIFO规则
1) 同一优先级下多个任务排队;
2) 一个任务拿到 CPU 后,持续运行直到以下任意一种情况才让出 CPU
- 任务主动阻塞(sleep、等待信号量、I/O);
- 任务主动调用 sched_yield() 放弃 CPU;
- 有更高优先级任务就绪,被抢占;
3) 任务不会因为时间片用完被强制切走,没有时间片概念。
(2) SCHED_FIFO例子
- 优先级90、80两个FIFO任务。90任务就绪,直接抢占80任务。
- 同优先级两个 FIFO 任务 A、B:A 运行后,只要不阻塞,B 永远得不到调度。
(3) SCHED_FIFO适用场景
SCHED_FIFO适用于不希望中途被同优先级任务打断的场景。例如,机器人伺服闭环、EtherCAT 主站、周期性硬实时控制等任务。
(4) C代码设置SCHED_FIFO
#include <sched.h>
void set_realtime_fifo(int prio)
{
struct sched_param sp;
sp.sched_priority = prio;
sched_setscheduler(0, SCHED_FIFO, &sp);
}
2. SCHED_RR(轮询实时,Round-Robin)
(1) SCHED_RR规则
- 跨优先级行为和 FIFO 完全一致:高优先级随时抢占低优先级;
- 同一优先级内部加入时间片机制;
- 同优先级多个任务轮流执行,任务用完时间片后,放到同优先级队列尾部;
- 若任务中途阻塞,再次唤醒后排在队列末尾。
- 默认时间片由内核参数控制 /proc/sys/kernel/sched_rr_timeslice_ms,默认 100ms。
(2) 例子
同优先级 90的RR 任务 A、B:
- A 运行一段时间,时间片耗尽 → 切换 B;
- B 时间片耗尽切回 A;
- 如果此时来了优先级 91 任务,立刻抢占 A/B。
(3) 适用场景
1) 场景 1:多传感器同步采集系统(工业检测平台)
- 设备同时采集:高速相机图像、激光测距、IMU 数据,三类采集线程设置相同实时优先级:
- 采集线程都需要周期性抢占普通任务;
- 没有任何一路采集拥有绝对最高权限,不能允许某一路持续占用 CPU 导致其他传感器丢帧;
- SCHED_RR 依靠时间片轮转,让多路采集线程轮流执行,均衡分配 CPU,防止单个传感器线程长期运行阻塞其余采集任务。
2) 场景 2:多通道实时音频处理(专业声卡、音频工作站)
- 多路音频轨道渲染、音效滤波、混音输出,全部设置为同一实时优先级;
- 如果用 SCHED_FIFO:最先被调度到的轨道线程会一直运行,其他声道饥饿,产生爆音、丢采样;
- 使用 SCHED_RR:同优先级各路音频线程按时间片轮流执行,保证所有声道均衡获得 CPU 时间,避免单一路径独占,保障多路音频同步输出。
四、cpu亲和性
CPU 亲和性(CPU Affinity),就是强制把某个线程 / 进程 “绑定到一个或多个 CPU 核心上运行”,限制内核调度器不能随意把它迁移到其他核心。
1. 为什么迁移 CPU 会带来延迟
线程从 CPU0 切换到 CPU1 时:
- L1/L2 缓存失效,新核心缓存为空,大量 Cache Miss;
- TLB 刷新;
- 跨 NUMA 节点内存访问延迟飙升;
- 引入不可预测的调度抖动。
2. cpu亲和性在Linux中分为两种
- 软亲和性:调度器倾向尽量不迁移,不强制;
- 硬亲和性:通过 API 显式掩码绑定,强制限制可运行的核心集合。
3. CPU 掩码
掩码每一位代表一个核心:
0b0001 → 只能跑在 CPU0
0b0101 → 可以跑在 CPU0、CPU2
4. C代码绑定当前线程到 CPU3
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(3, &mask);
sched_setaffinity(0, sizeof(mask), &mask);
五、内存锁
mlock:把进程虚拟内存锁定在物理内存,禁止内核把这块内存交换(swap)到磁盘。
1. 背景知识
Linux 默认采用虚拟内存 + 按需分页(Demand Paging):
- 程序启动时,只有虚拟地址,物理内存不一定立刻分配;
- 内存不足时,内核会把不常用页面换出到 swap 磁盘;
- 程序访问尚未载入物理内存的地址 → 缺页异常(Page Fault)。内核要从磁盘读取页面,耗时毫秒级甚至十几 ms,对于微秒级实时闭环是致命延迟尖峰。
实时任务最大敌人之一:运行中途触发缺页故障。mlock 就是用来杜绝运行时 page fault。
2. 内存锁两个系统调用
(1) mlock(),锁定指定一段内存区域(堆、全局数组、共享内存)。
int mlock(const void *addr, size_t len);
mlock 做了两件核心事
- 强制所有虚拟页立刻映射到物理内存,主动触发缺页(在初始化阶段一次性完成);
- 标记页面不可被回收、不可 swap out,运行期间不会再产生磁盘 IO 类缺页。
(2) mlockall()
// MCL_CURRENT:锁定当前已经分配的所有内存
// MCL_FUTURE:锁定将来新分配的内存(malloc/mmap)
int mlockall(int flags);
工程实时程序最常用:mlockall(MCL_CURRENT | MCL_FUTURE)
六、四足机械狗/人形机器人实时软件进程隔离规则
以下隔离规则统一用主流工控平台8核CPU举例。
1. 底层运动控制层隔离规则
底层运动控制代表程序有EtherCAT 主站、力矩闭环、全身 WBC 控制器。这类底层运动控制程序应独占CPU3,不与任何其他线程共享核心。调试策略用先进先出SCHED_FIFO调度,不要使用SCHE_RR。进程启动第一时间执行 mlockall(MCL_CURRENT | MCL_FUTURE),防止运行时缺页。线程内部禁止调用阻塞IO、printf大量打印、磁盘写操作。禁止调用ROS、GPU推理等耗时不定的库。EtherCAT网卡中断IRQ手动绑定CPU3。不要把视觉、SLAM任何线程放到CPU3,一旦CPU3出现计算尖峰,容易导致伺服丢周期,机器人摔倒。
2. 运动规划层隔离规则
运动规划层的代表程序有步态规划、平衡控制、状态估计等。这类程序的典型周期为200Hz~500Hz,指定其运行在CPU2。优先级低于伺服闭环,保证当闭环线程需要CPU时,可以立刻抢占规划线程。允许做矩阵运算、QP优化(WBC二次规划)。规划层计算超时只会影响步态流畅性,不会造成电机失控。步态规划可以和状态估计共享CPU2,但是尽量拆分为独立线程,区分优先级。
3. 环境感知层隔离规则
环境感知的代表程序有相机、激光雷达、SLAM等。全部不允许这类程序进入隔离实时核,可指定这类程序运行在CPU1。图像推理、点云处理耗时抖动极大,属于典型“计算时长不可预测任务”。对于这类程序一律使用SCHED_OTHER CFS 调度。如果需要略微降低延迟,可少量使用SCHED_RR策略。感知结果通过共享内存或内存零拷贝传递给运动规划程序。避免让感知线程直接调用运动控制接口。
4. 人机交互层隔离规则
人机交互程序主要有手柄、语音对话、上位机客户端、网页控制端等。对于这类程序可指定在CPU0或CPU4中执行,采用普通分时调度即可。因为,它们只负责解析指令、输出目标姿态/目标速度给运动规划层。交互模块丢失几帧指令只会造成操控卡顿,不影响机器人稳定。此外,网络收发、语音解码存在不确定延迟,不要将这类程序提升至实时优先级。
七、四足机械狗/人形机器人软件进程隔离架构图

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)