
1. 从一次无人机坠机谈谈硬实时被误解的那些事先讲个真实案例。前几年我参与过一个工业级无人机的飞控选型评估团队里一位刚从嵌入式培训学校出来的同事坚持认为只要在STM32H7上跑一个带抢占式调度的FreeRTOS、把任务优先级配好系统就是硬实时了。他甚至在方案评审会上说中断延迟我测过最坏情况也就3微秒这还不够硬吗我当时没直接反驳而是让他把在最高优先级任务里加一个长时间的浮点运算、同时开启Cache、再挂一个DMA搬运任务看看中断响应时间会变成多少。结果测出来的最坏延迟从3微秒直接彪到400多微秒。大家对这个数字可能没有直观概念——对于一架时速100公里的穿越机400微秒意味着它在空间上已经多走了约1.1厘米如果这是一次需要精确到毫米级的视觉定位修正姿态已经偏了。这个案例很好地引出了本文的核心问题飞控计算机里所谓的硬实时和软实时差的远远不止一个延迟数字。很多人在做飞控系统设计时把实时性当成一个快慢问题来理解但实际上它本质上是确定性determinism问题。这篇文章我打算从飞控计算机的实际使用场景出发把硬实时与软实时在调度模型、失败后果、系统架构、验证方法上的差异全部拆开讲透最后给出我在实际工程中采用的混合设计思路和配置参考。内容比较长但每一节都是能直接落到代码和配置上的干货。适合的读者包括正在做飞控系统选型的嵌入式工程师、无人机的开源飞控二次开发者、对实时操作系统有基础认知但想深入理解确定性内涵的研究生以及准备把普通的Linux板卡引入飞控架构但心存疑虑的架构师。2. 硬实时与软实时的分水岭不是快而是可证明在进入飞控计算机的细节之前必须先把概念层面的东西掰清楚。很多介绍实时系统的文章会把硬实时定义为响应时间必须在一个明确的截止时间deadline之前完成这本身没错但在工程实践里这句话实在太容易被误读。我用一个通俗类比来解释假设你是一个餐厅厨师软实时的要求是顾客饿了就赶紧上菜最好在10分钟内但偶尔15分钟也能接受硬实时的要求是这道菜必须在12点整出锅因为包间里的客人要在12点整给领导敬酒晚一秒钟这桌饭就砸了。看出来了吗软实时在乎的是平均体验硬实时在乎的是最坏情况的保证。把这个类比翻译成飞控系统的语言硬实时Hard Real-Time飞控计算机必须保证所有高优先级控制任务在确定的最坏执行时间WCETWorst-Case Execution Time内完成这个上界不是靠大概率保证的而是通过程序分析、硬件特性和调度算法计算出来的。一旦超时后果是物理性的——姿态失控、飞机坠毁。软实时Soft Real-Time系统允许偶尔错过截止时间只要任务在统计意义上能够及时完成。比如飞控里的遥测数据记录任务偶尔晚几十毫秒写入SD卡顶多丢一帧日志不影响飞行安全。这里有个关键的教训不能以延迟多小来区分硬实时和软实时。一个系统即使平均延迟只有1微秒如果最坏情况下可能被某个高阶中断、Cache Miss或者总线竞争拖到几十毫秒它依然不是硬实时的。反过来一个系统的平均延迟有50微秒但通过时间触发调度Time-Triggered精确锁定了最坏执行时间那它在飞控里就是硬实时的。这也就是为什么我在评估飞控实时性的时候从来没有把跑得多快放在第一位而是先去问一个问题最坏情况你有没有算过用的是测量法testing-based还是静态分析法static analysis测量法就是写一个测试程序反复触发中断看最大延迟。这种方法的问题是它只能覆盖到你触发的那些场景Cache状态、总线仲裁优先级、DMA占用的组合数量几乎是无限的你测到的不是上界只是到目前为止见过的最大值。静态分析法则会结合指令集模拟、Cache行为建模、调度分析得出一个数学上的上界可靠得多但分析过程复杂对处理器硬件的细节要求高。飞控里但凡涉及安全级的任务尤其是姿态控制和电机输出我都要求采用静态分析结果作为截止时间设计的依据。飞控系统中的典型硬实时任务包括姿态解算以固定频率如1kHz读取IMU数据并解算姿态控制律计算根据姿态误差计算电机控制量电机PWM输出必须在严格相位对齐的时间点更新占空比传感器数据采集的触发时刻IMU的采样时刻和控制律计算时刻之间的延迟必须恒定典型的软实时任务则包括遥测数据下行黑匣子日志记录地面站图像传输航线规划和重规划非紧急情况下在飞控计算机的设计中硬实时任务是驾驶舱软实时任务是客厅。驾驶舱里每一个操作都有硬性的时间约束客厅里即便偶尔拖延也不致命。3. 飞控调度架构的深层差异时间触发 vs 事件紧急触发确定了概念下一步是看硬实时和软实时在飞控系统调度架构上的具体体现。这个差异比大多数嵌入式开发者以为的要大得多。3.1 硬实时的经典范式时间触发调度航天和航空飞控领域最经典的硬实时范式是时间触发调度Time-TriggeredTT代表是TTEthernetTime-Triggered Ethernet和TTPTime-Triggered Protocol。时间触发的核心思想是所有任务和消息的启动时刻在离线设计阶段就被精确排定在了一个周期性的调度表里。类比一下时间触发就像交响乐团的指挥每一个乐手在哪个小节进入早就在乐谱上写死了乐团不会因为某个乐手状态好就提前演奏。每个任务在固定的相位开始执行在下一个固定的相位之前完成。这带来两个巨大的好处可确定性因为调度表是固定的不需要运行时的任务优先级仲裁任务之间没有竞争关系。CPU在每个时间点执行什么都是预先知道的。这让最坏情况分析变得非常可行——你只需要分析每个任务的WCET再排进调度表后面的事就是机械性的。相位同步传感器采集、控制律计算、执行器输出的时间相位是精确对齐的。比如IMU在t0时刻采样控制律在t0.4ms时刻开始计算PWM在t0.8ms时刻刷新三者之间延迟恒定控制环路的相位裕度不会因为调度抖动而恶化。在飞控领域这个范式的好处怎么强调都不为过。飞行控制的稳定性依赖于系统的延迟模型。如果控制任务的调度抖动过大jitter在频域上就相当于引入了不确定的相位噪声会吃掉控制器的相位裕度严重的会引起高频振荡甚至发散。飞控计算机中的时间触发调度的实现通常不依赖普通RTOS的优先级抢占而是依赖静态调度表一个循环任务表系统按固定的时间步进如1kHz对应的1ms周期依次执行。调度表中的每个任务都要保证在分配给它的时间窗口内完成如果某个任务超时了怎么办一般有看门狗策略硬件看门狗强制复位或者切换到冗余通道。3.2 软实时的典型范式优先级抢占 事件驱动软实时系统的代表是通用RTOS上常见的**优先级抢占调度Preemptive Priority Scheduling**加上事件驱动模型。FreeRTOS、RT-Thread、主流的VxWorks、以及带PREEMPT_RT补丁的Linux都属于这种范式。这种模型里系统不是一个静态的表而是一组等待事件的线程/任务。事件来了比如姿态传感器数据就绪调度器根据优先级决定谁先运行。高优先级任务可以抢占低优先级任务。这种模型灵活性强适合处理突发性的事件比如遥控器信号丢失需要触发返航逻辑但代价是运行时的调度决策引入了不确定的排队延迟、上下文切换开销、优先级翻转概率。用数学语言说这类调度的最坏情况响应时间分析需要把所有高优先级任务的最坏执行时间、资源锁等待时间叠加起来而且还要考虑中断和DMA这些隐藏的高优先级执行者。我之前在一个飞控项目里就踩过优先级翻转的坑遥测DMA中断、姿态控制任务、以及对一个共享CAN外设的互斥访问之间形成了经典的优先级翻转链导致姿态任务在毫秒级周期里偶尔被拖慢了1微秒。用逻辑分析仪抓了三天才找到原因后来把互斥锁改成了无锁环形缓冲才解决。3.3 飞控里最常见的混合架构实际工程里很少有飞控计算机是纯时间触发或者纯事件驱动的。以我参与过的某款双余度飞控为例架构是这样的内回路姿态/角速率环时间触发。IMU数据通过DMA以1kHz频率写入双缓冲由一个定时器在固定相位唤醒控制任务任务使用无锁数据访问执行完在固定时刻刷新PWM。外回路位置/速度环优先级抢占事件驱动。订阅IMU数据和GPS数据融合后更新位置环路对时间要求相对宽松甚至在GPS信号丢失时只需要保持不崩即可。决策层故障处理、模式切换事件驱动但通过独立的监控任务watchdog任务兜底。模式切换的延迟允许有几十毫秒级别的非确定性但切换动作本身是受状态机约束的不会因为延迟造成控制律发散。这种混合架构的本质是硬实时任务放在一个被严格隔离的确定性岛内软实时任务在这个岛之外自由竞争。隔离不是概念层面的而是通过物理手段如独立的CPU核、独立的DMA通道、内存隔离、时间窗口隔离做出来的。4. 失败后果与系统设计取向一个错误导致的物理灾难边界硬实时和软实时最根本的差异不在调度技术而在失败后的物理后果。这是飞控领域判断要不要用硬实时的最高标准。4.1 硬实时任务的失败模式硬实时任务错过截止时间的后果通常是灾难性的。飞控里的典型例子姿态控制任务在某个控制周期内没有完成计算导致控制输出保持上一周期的指令。如果下一个周期恢复还可以接受但如果连续三个周期都没能刷新输出飞机的姿态会在几毫秒级别内出现不可控偏转。电机PWM信号的刷新时刻偏离了预期相位导致多个电机的输出力矩在空间上产生交叉耦合飞行器出现轻微抖动甚至振荡。对于没有充分相位裕度的小型无人机这种高频振荡通常意味着瞬间失控。传感器采样时刻与控制计算时刻之间的抖动会导致控制环路的有效采样时间模糊化在高速机动时引入几度的姿态误差。这就是为什么硬实时任务在飞控设计中要部署在确定性环境中它们的时间行为必须严格符合预先的分析模型哪怕出现一个微小的偏差后果也是物理性的。4.2 软实时任务的失败模式软实时任务错过截止时间的后果通常是非灾难性的但依然会影响系统性能遥测数据丢一帧不影响飞行。GPS融合任务被延迟50ms位置估计精度略微下降但仍然稳定收敛。日志写入被延迟可能丢几百条记录但飞控的实时控制环路不影响。这个非灾难性指的是不会直接导致坠机但并不是说可以完全忽视。我见过一个因为日志记录任务频繁抢占姿态控制任务导致姿态控制任务偶尔超时并触发保护机制的炸机案例——一个本身是软实时的任务如果占用的资源CPU时间、总线带宽、缓存干扰了硬实时任务它实际上变成了系统安全的威胁。所以在飞控系统中处理软实时任务不能只考虑它自己的截止时间还要考虑它对硬实时任务的干扰上界。4.3 余度设计与表决机制中的实时性要求说到失败后果不得不展开讲一下飞控里余度设计和表决机制对实时性的影响。这是很多人容易忽略的维度。在双余度或三余度飞控中多个飞控通道分别计算姿态和控制量然后通过**表决器voter**选出一个有效的输出。表决的前提是各个通道的输出必须在时间上可比。如果通道A的输出在t0时刻生成而通道B的输出在t0.5ms时刻生成表决器需要认为这两个输出对应的是同一个控制周期否则会把两个周期的控制量混在一起投票结果是灾难性的。这要求每个通道的任务调度必须是周期对齐的最好采用一种称作本地表决窗口的机制每个通道在每个控制周期内在自己的时间窗口内完成计算并把输出送到表决器表决器在窗口结束时统一比较。如果某个通道没有在窗口内完成它的输出会被标记为无效而不是参与表决。这个机制下即使某个通道出现了随机硬件故障导致计算超时表决器也能正确识别并隔离故障通道。更深一层三余度系统还要求交叉通道的数据传输Cross-Channel Data TransferCCDT具有确定的延迟。假设通道A在t0时刻采样通道B在t0.2ms时刻采样两条通道之间交换数据后各自进行表决逻辑。如果信道的传输延迟不确定表决逻辑就会引入表决延迟导致每个通道的控制律计算相位不一致。时间触发总线如TTEthernet之所以在飞控中备受青睐恰恰因为它能提供全局时钟同步和确定的消息传输时延让每个通道的采样、计算、交换、表决都能在全局时基上对齐。我曾在一个项目里对比过CAN总线和TTEthernet在三余度飞控交叉通道数据传输中的表现。CAN由于事件触发、带冲突检测在高负载下的数据到达时间抖动很大几十微秒到上百微秒做交叉表决时需要额外加时间戳补偿逻辑TTEthernet则把所有消息时隙化抖动控制在亚微秒级。最终我们为了保住确认性果断选择了TTEthernet。代价是硬件成本上升、配置复杂度大幅增加但在三余度架构里这一点代价换来的是表决机制的可信度。5. 系统验证的差异跑1000次测试通过不等于硬实时很多开发者分不清硬实时和软实时本质上是因为他们不清楚这两种系统在验证方法上的截然不同。软实时系统可以用统计测试来验证——跑一千次一万次看延迟分布只要绝大多数在阈值内十几个异常点可以当作可接受的偶发事件。硬实时系统则只能靠形式化分析和充分的穷举性测试来验证统计平均没有意义你必须证明最坏情况就是这么长没有例外。以飞控中典型的硬实时任务验证为例通常至少需要做以下几件事最坏执行时间WCET分析使用静态分析工具如aiT、Otawa对目标处理器的指令时序进行建模推导任务代码的理论最大执行时间。结合Cache访问模式分析LRU/随机替换策略确定任务在最冷Cache时的开销。分析中断嵌套和DMA抢占对CPU周期的贡献。调度分析对于时间触发系统验证静态调度表中所有任务时间窗的可行性和间隔满足要求。对于优先级抢占系统使用RMARate Monotonic Analysis的可调度性判定公式任务集在特定条件下满足Σ(Ci/Ti) ≤ n(2^(1/n) - 1)才可判定为可调度。一旦涉及阻塞资源共享还得引入优先级天花板协议Priority Ceiling Protocol的分析模型。硬件在环HIL测试将飞控计算机接上惯性仿真器、控制对象模型跑故障注入场景如某个传感器信号丢失、CAN总线堵塞、CPU主频降级、内存错误注入观察任务是否都能在最坏时限内完成。重点记录系统的迟延上界maximum lateness、截止时间错失次数deadline miss count。长时间压力测试不是跑一小时而是连续72小时以上、在温度循环、振动环境、供电波动下重复运行典型任务和边界任务捕捉低概率的调度异常。而软实时系统的验证就简单得多只需要统计任务响应时间分布设定一个可容忍的截止时间错失率比如低于0.1%然后通过压力测试验证分布稳定即可。在实际飞控项目的审厂和适航沟通中经常被问到的问题就是你怎么证明你的系统是硬实时的。如果回答说我测过很多次没有超时评审专家通常会追问你测了多少次覆盖了哪些场景你如何确定没有覆盖到的场景不会超时 而如果回答我做了WCET分析和RMA可调度性分析结论是任务集在最坏情况下可在X毫秒内完成这个说服力就完全不一样了。这也是为什么在很多航空飞控系统中我们倾向于使用带有形式化验证工具链的RTOS如VxWorks 653、PikeOS而不是开源的FreeRTOS——不是说FreeRTOS实时性不好而是它缺少完整的WCET分析和调度验证工具链这让硬实时四个字没法从工程上自证。6. 选型与实现在飞控ARM处理器上如何为任务配置实时属性前面讲了这么多概念和架构这一节我落脚到具体实现在常见的飞控计算机上比如NXP i.MX8、STM32H7、或带AMPAsymmetric Multiprocessing的异构平台到底怎么为不同的任务配置实时属性这里我给出我实际用过的策略。6.1 明确任务分类表在设计飞控软件之初我们就要做一张任务分类表明确每个任务的实时属性、周期、截止时间、最坏执行时间和允许错失率。这个表早于任何代码编写存在。以下是一个实际案例任务名称实时属性周期ms截止时间ms允许错失率关键性级别IMU采样与姿态解算硬实时10.50安全级内回路控制律角速率硬实时10.80安全级外回路位置控制软实时10100.5%一般级GPS数据融合软实时10152%一般级遥测数据打包发送软实时10505%低关键性黑匣子记录软实时1002005%低关键性故障检测与冗余切换硬实时10.50安全级注意这个表格里的截止时间都不是一个周期时间。比如IMU采样的周期是1ms但截止时间是0.5ms意味着采样任务必须在半个周期内完成给剩余的控制律计算留出时间。实际设计时我会把硬实时任务占用的CPU时间预算控制在总CPU时间的60%以内留出40%的余量给软实时和其他偶发负载防止一个软实时任务突然占用过多CPU把硬实时挤爆了。6.2 硬实时任务的设计要点硬实时任务在设计上有一些可落实的原则我逐条说使用静态或伪静态内存分配不在任务内部使用malloc/free防止堆碎片导致的分配延迟。飞控的CCMCore Coupled Memory或内部SRAM中为每个硬实时任务预先分配好固定内存块。禁止在硬实时任务内做阻塞性IO打印日志、访问文件系统这类操作统统移到软实时任务里硬实时任务只通过无锁环形缓冲区向软实时任务传递数据。使用中断响应而不是轮询IMU数据就绪时通过硬件中断或DMA完成通知触发任务避免CPU空转判断状态位。为硬实时任务单独建立中断优先级在STM32H7的NVIC中把IMU触发中断放在组最高优先级并且关闭组内其他可嵌套中断保证IMU中断的响应不会被其他外设中断延迟。缓存锁定Cache Locking对于时间确定性要求极高的代码和数据段可以使用硬件提供的Cache锁定功能把关键代码和常驻数据锁定在Cache里消除Cache Miss带来的最坏执行时间波动。i.MX8等应用处理器通常支持类似的缓存锁定功能但配置极其复杂需要在启动早期进行页表设置和MMU配置。禁用分支预测导致的不确定性现代处理器中的分支预测器会让同一段代码在不同输入下的执行时间产生巨大差异。在硬实时任务里尽量减少数据依赖的分支必要时采用__builtin_expect或干脆用不回应的短分支保持执行时间的平坦。6.3 软实时任务的隔离与约束软实时任务虽然不需要确定性但必须接受两类约束第一是时间预算约束。所有软实时任务累计的CPU占用率必须留出明确的余量。比如在任务启动时计算周期内的平均CPU占用率一旦超过预设值如30%低优先级的软实时任务将被动态降频或丢弃部分工作。第二是资源访问约束。软实时任务访问共享外设比如UART、CAN、SPI时不能在持有锁的情况下被硬实时任务多次等待。我的做法是软实时任务使用无中断禁止的互斥机制如Priority Ceiling或者干脆把对硬实时任务有影响的外设访问全部放到硬实时任务的周期外执行。以我最近用i.MX8M Plus做的一版飞控为例核心配置是4个Cortex-A53中1个核跑Linux负责视觉导航、地面站通信、日志另外1个核跑带RT-PREEMPT补丁的Linux或裸机RTOS负责外回路控制、故障处理。A53核和M7核之间通过共享内存Mailbox通信确保数据交换不依赖Linux的实时性。M7核上跑时间触发的硬实时任务姿态解算1kHz 控制律1kHz 故障检测1kHz任务调度表提前排好使用M7内部TCMTightly Coupled Memory存放关键代码和数据。软实时任务GPS融合、位置环、遥测打包在A53核上跑通过信号量向M7发起非实时请求。关键日志和调试输出通过UART DMA方式输出不影响实时任务的执行。这个架构跑下来的实际效果M7核上姿态解算任务的调度抖动低于1微秒PWM刷新时刻在全天候运行中保持恒定在0.2微秒以内A53核上的Linux任务即便高负载也不影响M7核上的硬实时任务执行。这个隔离效果是我用普通单核RTOS无论如何都做不到的。7. 聊聊那些打着实时旗号的RTOS和Linux实时化方案最后这一节我从工程经验的角度盘点一下飞控领域常见实时化方案的适用性帮助大家选型时少走弯路。7.1 FreeRTOS/RT-Thread真硬实时吗FreeRTOS是优先级抢占式调度从RTOS内核本身来说它完全可以用于硬实时任务。难点在于它本身不提供WCET分析工具最坏情况由工程师自己推。部分BSP的外设驱动如TouchGFX、USB、LWIP协议栈会引入不可控的重负载中断或长临界区导致硬实时任务的延迟上界不可控。堆内存分配、printf重定向等库函数可能引入不确定延迟。如果你问我FreeRTOS能不能做飞控的硬实时任务我的回答是可以但要非常克制。把硬实时任务缩减到极小的关键集合所有外设访问都封装成固定时间的DMA操作明确关闭任何可能引入长临界区的功能。这样FreeRTOS可以胜任小型无人机飞控但代价是需要大量的人工分析。我在多个开源飞控比如PX4、ArduPilot的调度策略中也看到同样的影子PX4的workqueue模型本质上是优先级抢占但它把速率最快的姿态控制任务放在独立的高优先级线程中其他任务放到低优先级workqueue里这个设计思路跟混合架构的内核逻辑是一样的。7.2 VxWorks、PikeOS等商用RTOS硬实时的正统选择商用RTOSVxWorks 653、PikeOS通常带完整的调度分析工具链支持ARINC 653标准的空间/时间分区隔离适合航空级飞控。它们提供的技术保障包括应用之间内存隔离不是MMU保护那种隔离而是分区概念每个分区占用固定的时间窗口这个窗口单靠RTOS调度器就可以锁定不依赖硬件Cache锁定。在需要适航认证的飞控项目中这类RTOS几乎是唯一选项。代价是授权费用高、开发环境封闭、学习曲线陡峭。7.3 Linux RT-PREEMPT是软实时别当硬实时用这几年很多飞控尝试用带RT-PREEMPT补丁的Linux跑控制任务。RT-PREEMPT大幅减少了内核的不可抢占区间在大多数场景下可以把调度延迟压到几十微秒但是Linux的中断下半部softirq、内存管理页面回收、KSM、以及文件系统操作仍然存在非常长的不可阻塞区间。极端情况下比如内存压力大触发回收延迟可能达到毫秒级。Linux的进程调度不是为硬实时设计的即使设置SCHED_FIFO也会有内核功能如rcu、定时器的存在导致非确定性。我的判断是RT-PREEMPT适合跑飞控里的软实时任务比如视觉避障、路径规划、地面站通信这类对极端最坏情况不敏感的工作负载。如果一定要用它跑姿态控制必须在系统层面做完备的延迟上界测试并加上独立的硬件看门狗兜底。我甚至见过一个团队在RT-PREEMPT Linux上通过核心隔离isolcpus affinization 禁调度器tick的配置把临界任务延迟控制在了10微秒级别但那是极其特化的场景不具代表性。7.4 我能给的最终选型建议飞控的安全级姿态控制必须硬实时。有人问FPGA软核和单片机哪个好我的建议是如果你没有把握做完整的WCET分析干脆用FPGA实现硬逻辑控制律这比在MCU上纠结实时性更稳妥。飞控的外回路控制软实时即可。用带RT-PREEMPT的Linux或者普通RTOS的低优先级任务实现配合一定的容错逻辑即可。飞控的任务调度核心如果做三余度或更高安全等级的飞控直接考虑时间触发架构分区隔离ARINC 653。如果做消费级或工业级无人机混合架构M7时间触发Linux软实时已经足够成本和复杂度都可控。我在实际项目里最常说的一句话是做飞控系统设计先做任务分类表和WCET分析再选平台最后才写代码。顺序反了后面全是在填坑。这句话也送给读到这里的你。