跳转到内容

嵌入式控制系统设计/从失败中学习

来自维基教科书,开放世界中的开放书籍

工程师必须牢记,他们创建的每台设备最终都会失效。应尽一切手段避免灾难性故障,因为这会导致人员受伤甚至死亡。工程故障的一些主要原因是知识不足、低估影响以及无知或疏忽。

本章列举了一些灾难性故障的例子。通过研究这些例子,工程师可以学习如何在自己的设计中避免故障。

工程失败的例子

[编辑 | 编辑源代码]

爱国者

[编辑 | 编辑源代码]

1991 年 2 月 25 日,在海湾战争期间,一枚驻扎在沙特阿拉伯达兰的美军爱国者导弹未能拦截一枚来袭的伊拉克飞毛腿导弹 [1]。这次失败导致 28 名美军士兵死亡。

问题的根源在于负责追踪来袭飞毛腿导弹的计算机。这台计算机以十分之一秒为单位将时间存储为整数,并将时间存储在长度为 24 位的寄存器中。之后,将这个值乘以一个 24 位的定点表示的 0.1,以获得一个 48 位浮点表示的以秒为单位的时间值。

故障是由 2 进制表示的 24 位 0.1 引起的。0.1 在 2 进制中的表示是非终止的:0.0001100110011001100110011001100.... 爱国者导弹中的 24 位寄存器存储了 0.00011001100110011001100,引入了 0.0000000000000000000000011001100... 的二进制误差,大约为 0.000000095 十进制。这意味着时间的准确性降低了 0.0001%。

然而,导弹的追踪并不依赖于绝对时钟时间,而是依赖于两次不同雷达脉冲之间的时间差。由于这个时间差非常小,0.0001% 的误差实际上是微不足道的。这无法解释爱国者导弹的偏离,但还有第二个问题。

计算机使用的软件是在 20 年前用汇编语言编写的。为了应对该系统最初并非为其设计的高速弹道导弹,软件经过多次更新。其中一个更新包含一个子程序,可以更精确地将时钟时间转换为浮点数。然而,该子程序并没有插入到它需要的每个地方。因此,由于一个雷达脉冲的不太精确的截断时间减去另一个雷达脉冲的更精确的时间,误差不再抵消。

飞毛腿导弹到达时,计算机已经运行了 100 个小时,因此计算出的经过时间误差为 0.000000095×100×60×60×10=0.34 秒。飞毛腿导弹的速度约为每秒 1676 米,因此在这个误差时间内会移动超过半公里。这足以使来袭的飞毛腿导弹位于爱国者导弹的“射程范围”之外。

解决方案

[编辑 | 编辑源代码]

这个问题的解决方案在事故发生之前就已经提出。通过使用更多位来提高时间表示的精度。然而,这个解决方案只是避免了问题发生,并没有解决问题。如果计算机在几年内没有重启,相同的故障仍然可能发生。

有一个更好的方法。只需测量两次雷达脉冲之间的相对时间。时钟应始终在第一个脉冲时复位,并在第二个脉冲时停止。这避免了两个几乎相同的大的数字之间的减法,以及它的数值问题。

  • 应该重写一个算法,该算法使用大型数字进行减法以避免这种数值精度损失。
  • 在设计中分析用于表示物理参数(例如时间)的位数非常重要。
  • 系统设计者需要对所使用的所有组件进行非常详细的规格说明。

火星探路者

[编辑 | 编辑源代码]

1997 年 7 月 4 日,火星探路者成功降落在火星表面。然而,在执行任务开始后的几天,由于多次系统复位,探路者开始丢失信息。该问题的根源在于探路者使用的嵌入式系统内核。

航天器的任务以线程的形式执行,这些线程具有优先级,反映了每个任务的紧急程度。线程有 3 种类型

  • 高优先级线程,例如总线管理任务。
  • 中等优先级线程,例如通信任务。
  • 低优先级线程,例如气象数据收集任务。

在执行总线管理任务期间,它会将数据进出信息总线(连接航天器所有组件的总线)。对该总线的访问与互斥锁同步。气象数据收集任务使用信息总线发布其数据。在发布时,它会获取一个互斥锁,写入总线,然后释放该互斥锁。使用互斥锁可确保每个总线线程等待前一个线程完成。

通常,具有优先级的线程系统和使用互斥锁的总线管理工作得很好。但是,会发生一个称为优先级反转的问题。假设当高优先级总线管理任务因等待低优先级气象数据线程而被阻塞时,调度了一个中等优先级的通信任务。通信任务的优先级高于气象任务,将阻止气象任务运行,从而阻止被阻塞的总线管理任务运行。由于通信任务是一个长时间运行的任务,一个看门狗定时器最终会超时,注意到数据总线任务已经有一段时间没有执行了。看门狗任务将得出结论,认为发生了一些错误,并启动系统总重置。此重置类似于家用电脑的重启;它清除寄存器,以便系统可以无错误地重新启动。

解决方案

[编辑 | 编辑源代码]

这个问题有几个解决方案。其中之一称为优先级继承。气象线程将继承总线管理任务的优先级,使其被安排的优先级高于通信任务。

幸运的是,VxWorks 包含一个 C 语言解释器,旨在允许开发人员在系统调试期间动态输入 C 表达式和函数以执行。在将正确的程序上传到航天器以更改其软件后,问题得到了解决。

重要的是要认识到优先级继承不是解决此问题的最佳方案。在复杂的系统中,数十个总线任务相互交互,所有任务最终将获得最高优先级,问题仍然存在。然而,在探路者的情况下,它就足够了。

  • 使用优先级协调任务不是最佳方法。这种间接方式将所有责任都交给了调度器。最好将协调纳入系统本身[2].
  • 当发生不可预见的错误时,提供干预支持很重要。
    • 看门狗:每个 ECS 都应该包含一个看门狗任务,因为系统块永远不能被排除。
    • 现场调试设施:如果没有在现场修改系统的能力,问题就无法得到纠正。
  • 不可能完全测试每种情况。如果时间(协调)很重要,字节数很重要……,调试总是可能需要的。
  • 好的理论很重要:解决此问题的理论解决方案早在几年前就发表在论文中。
  • 正确性很重要:工程师最初的分析“数据总线任务执行非常频繁且时间紧迫——我们不应该花额外的时间来执行优先级继承”是错误的。正是这种时间紧迫且重要的场景,正确性非常重要,即使需要一些额外的性能成本。


阿丽亚娜 5 号 501 航班

[编辑 | 编辑源代码]

阿丽亚娜 5 号 501 航班于 1996 年 6 月 4 日发射,是欧洲第一次不成功的测试飞行。由于控制软件故障,火箭在发射后 37 秒偏离了飞行轨迹,并被其自动自毁系统摧毁,当时高气动力导致运载工具核心解体。这是历史上最臭名昭著的计算机错误之一。阿丽亚娜 5 号首飞突然改变飞行轨迹的原因是什么?当然,在没有进行模拟的情况下重复使用与之前阿丽亚娜 4 号项目类似的软件代码是一个重大错误。

由于一个称为 BH(水平偏差)的内部对齐函数结果的意外高值,发生了操作数错误,该结果与平台感测到的水平速度有关。该值计算为随时间推移的对齐精度的指示器。BH 的值远高于预期,因为阿丽亚娜 5 号的早期轨迹与阿丽亚娜 4 号不同,导致水平速度值明显更高。从64 位浮点数16 位 有符号整数的执行数据转换期间发生了内部软件异常。转换的浮点数的值大于 16 位有符号整数可以表示的值。这导致操作数错误,导致机载计算机发送诊断数据而不是实际飞行数据。此诊断数据命令喷嘴突然偏转,并引起高迎角。(有关更详细的信息,请参阅阿丽亚娜 5 号报告

解决方案

[编辑 | 编辑源代码]

整个轨迹的模拟(静态代码分析)应该会立即指出溢出错误。此步骤应该在设计过程中的架构设计或实现(原型制作)阶段执行。

此示例说明了数据溢出软件故障的严重后果,其中机载计算机仅接收诊断信息而不是实际飞行数据。虽然阿丽亚娜 5 号的故障部件有备份系统,但由于备份系统使用了相同的软件代码,因此无法防止故障。在 501 航班事故发生后,人们吸取了一些教训

  • 准备一个测试设施,包括尽可能多的真实设备,注入真实的输入数据,并对整个系统进行完整的闭环测试:虽然一个(软件)子系统在以前的嵌入式控制系统中运行良好,但它仍然可能作为新嵌入式系统中的子系统而失败,即:始终质疑子系统的可重用性。在本例中,软件在阿丽亚娜 5 号上失败,而在之前的阿丽亚娜 4 号火箭上运行良好!事故发生后,静态代码分析痛苦地显示了溢出。

另请参阅#Therac-25,了解另一个由于重复使用软件而导致的故障。

  • 传感器必须在任何情况下都能返回可靠的数据:在本例中,IRS(惯性参考系统)由于 16 位有符号整数值的溢出而返回诊断数据而不是实际飞行数据。在某些情况下,传感器最好什么也不返回(或仅返回警告信号),而不是返回不可靠的数据。
  • 与复杂计算系统相关的巨大风险引起了公众、政界人士和高管的注意,导致人们更加支持确保安全关键系统可靠性的研究。随后对阿丽亚娜代码的自动分析是通过抽象解释进行大规模静态代码分析的第一个例子。

注意:在实践中,安全性、经济性(环境)……方面之间始终存在权衡。例如:在上面的例子中,传感器是安全地将宇航员送回地球的关键组件,而作为传送带系统一部分的传感器对生命威胁要小得多。通常,安全性在航空、航空航天和核能领域具有最高优先级!


挑战者号航天飞机

[编辑 | 编辑源代码]

在第 10 次太空飞行中,挑战者号航天飞机在发射后 73 秒解体。7 名宇航员遇难。这次事故的原因不仅是机械性的,也是心理性的,即所谓的群体思维

鉴于美国国家航空航天局 (NASA) 的成功历史,NASA 团队的官员决定在无视工程师的警告的情况下发射航天器,他们有一种不可战胜的感觉。该团队长期紧密合作,他们承受着巨大压力,要求在没有进一步延迟的情况下发射航天飞机,因此他们集体同意发射决定。然而,工程师们知道推进剂火箭的橡胶密封件(一个O 型环)在冰点温度下无法正常工作。1986 年 1 月 28 日的低温导致 O 型环泄漏热流,因此是造成 7 人死亡的可以避免的原因。

这个例子展示了一种硬件故障模式:温度对材料变形的影響。在大多数情况下,系统的所有组件都可以完美地协同工作,但在恶劣环境条件下会发生故障,例如本例中的冰点条件。
另一个重要的结论浮出水面。糟糕的设计并不总是故障的唯一原因。心理因素也很重要。这就是工程师在任何情况下都必须保持批判性,不要屈服于同伴压力的地方。特别是在使用嵌入式控制系统的阶段,操作员必须在启动系统之前检查环境条件是否在设计特性范围内。由于此类心理因素造成的故障往往难以克服,可以被视为操作性故障原因


腐蚀灾难

[编辑 | 编辑源代码]

腐蚀通常是一种难以克服的硬件故障模式。很多时候,灾难发生在材料因腐蚀而坍塌时。下面介绍了其中两个。

埃里卡号是一艘 1975 年建造的油轮,1999 年 12 月 13 日在法国海岸 70 公里处沉没。数吨重油最终流入大海,造成世界上最严重的生态灾难之一。事故的经济后果在该地区得到了深刻的体现:旅游收入下降、捕鱼收入损失以及对包括牡蛎和螃蟹在内的海产品的贸易禁令加剧了人们的不适。

至少从 1994 年起,埃里卡号的腐蚀问题就已显而易见。此外,它的消防系统和惰性气体系统存在许多缺陷,造成爆炸风险。劳埃德船级社报道称,在事故发生前几周,人们发现了严重的腐蚀。然而,并没有采取任何立即的补救措施。

1988 年 4 月 28 日,执行该航班的阿罗哈波音 737 飞机在飞行中发生爆炸性减压后遭到严重损坏,但能够安全降落在毛伊岛的卡胡鲁伊机场。一名空姐不幸遇难,她被吹出了飞机,另外 65 名乘客和机组人员受伤。在飞机上,发现了多个部位存在疲劳损伤,导致结构性破坏。国家运输安全委员会在 1989 年发布的调查报告将事故归咎于运营商的维护计划未能发现腐蚀损伤。由于老旧飞机通常不会被销毁,很可能最终会在另一家运营商手中继续服役,因此工程师需要充分了解老旧飞机的安全问题,并需要持续地实施安全计划。

在产品测试阶段,老化效应和材料腐蚀不可见,但必须不可避免地予以考虑!在设计大型嵌入式控制系统时,工程师应该具有长远的眼光,因此应从公司设计流程中的不同角度进行考虑。售后服务、维护和测试可以作为大型嵌入式系统的解决方案,可以防止公司长期声誉受损。






案例研究:Therac-25

[编辑 | 编辑源代码]

Therac-25 是一款计算机控制的放射治疗机,在 1985 年至 1987 年期间,它严重超剂量照射了 6 人。其中 3 人死亡。对 Therac-25 的案例研究将揭示以下设计难题

  • 软件可能会发生故障
  • 测试可能很困难,但必不可少
  • 软件重用可能很危险
  • 安全性与可用性是相互矛盾的设计标准

在本例中,错误在于重用和信任旧软件。这通常是在高级设计中犯的错误。

本文将首先讨论该机器的背景。在接下来的部分中,将描述一些相关的意外事故。接下来,本文将重点介绍与事故相关的某些软件问题。在最后的部分中,将讨论从事故中可以吸取的一些工程教训。

Therac-25 由加拿大原子能有限公司 (AECL) 和 CGR 生产,是一种医疗线性加速器。这类加速器产生高能束流,以便在对周围健康组织的影响最小的情况下破坏肿瘤。Therac-6 只能产生X 射线,而 Therac-20 是一种双模式(X 射线或电子束)加速器,是 Therac-25 的前身。这两台机器都是 CGR 老式机器的版本,这些机器已经具有临床使用历史,并增加了小型计算机。它们都具有有限的软件功能:计算机只是为现有硬件增加了便利性。老式机器的硬件安全功能得以保留。

几年后,AECL 开发了一种新的加速概念(“双程”),它需要的空间更少,使用更方便,生产成本更低。这种概念应用于 Therac-25 双模式线性加速器。

在每种模式(电子或 X 射线)下,用于创建合适束流的必要工具都安装在转台上,转台旋转设备进入束流。因此,Therac-25 的正常运行取决于转台的位置。在 X 射线模式下,安装在转台上的一个束流整形器用于创建均匀的治疗场。为了产生与电子模式输出相当的输出,这需要很高的输入剂量率。转台位置错误会导致整形器位置错误,从而导致高输出剂量。显然,在使用双模式机器时,这是一个严重的危害。

虽然 Therac-25 受与 Therac-6 和 Therac-20 相同的计算机控制,但它们之间也存在一些差异。Therac-6 和 Therac-20 的硬件能够独立运行(例如,机械联锁,确保安全运行)。然而,Therac-25 的软件承担了更多维护安全方面的责任:机器更多地依赖于软件,而不是硬件安全机制。在 Therac-25 的软件设计中,Therac-6 的设计特性和模块被重复使用。该软件还包含一些 Therac-20 例程。

在 Therac-25 中,软件的任务是控制和检查转台的位置。然而,该软件并不完美,导致 6 人接受了大量超剂量照射。这在意外事故部分中有更详细的解释。

当软件检测到错误时,机器可以通过两种方式关闭

  • 治疗暂停:需要重置机器才能重启
  • 治疗暂停:只需要一个按键命令即可重启机器。

当发生“治疗暂停”时,操作员只需按“P”键“继续”即可恢复治疗,无需重新输入治疗数据。如果该功能被调用 5 次,机器将自动暂停治疗。

意外事故

[编辑 | 编辑源代码]

1985 年 7 月,安大略省(加拿大)

Therac-25 激活后约 5 秒,机器关闭。它显示“治疗暂停”,并且没有进行剂量照射。操作员尝试通过按下“P”键进行第二次治疗,但机器连续关闭了 4 次,直到进入“治疗暂停”。治疗结束后,患者抱怨治疗区域有灼痛感。实际上,患者接受了大量的超剂量照射,约为预期剂量的 100 倍。正常的单次治疗剂量约为 200 拉德。如果对全身照射 1000 拉德的剂量,甚至会致命。

在调查这起事故后,AECL 发现了一些弱点和机械问题,但无法重现发生的故障。随后,AECL 对一些机制进行了重新设计,并修改了软件以解决这些问题。在这些改进之后,AECL 声称“对新解决方案的风险率分析表明,与旧系统相比,其风险率至少提高了五个数量级”。然而,风险分析似乎没有考虑计算机故障。因此,发生了更多事故。


1986 年 3 月,东德克萨斯癌症中心 (ETCC)

操作员对机器有丰富的操作经验,可以快速输入处方数据。她想输入“e”(代表电子模式),但她错误地按下了“x”键(代表 X 射线)。为了纠正错误,她使用了“向上键”,并快速更改了模式输入。其他治疗参数保持不变。为了开始治疗,她按下了“B 键”(光束开启)。片刻后,机器关闭并显示错误信息:“故障 54”,该信息未在机器手册中解释或提及。机器进入“治疗暂停”状态,这表明存在低优先级问题。机器显示剂量不足,因此操作员按下了“P 键”以继续治疗。机器再次关闭,并显示故障 54 错误。病人抱怨他感到像触电一样,并立即接受检查。然而,医生没有怀疑到任何严重问题。实际上,病人接受了大量的过量照射。事后估计,实际剂量为 16500 到 25000 勒德。五个月后,病人死于过量照射并发症。


1986 年 4 月,东德克萨斯癌症中心 (ETCC)

在 ETCC 首次事故发生三周后,再次发生类似事故。同一操作员发现模式输入错误,并使用“向上键”进行更正。机器再次显示故障 54 错误。这名病人于事故发生三周后死于过量照射。ETTC 物理学家在第二次事故发生后立即将机器停用,并自行调查了该错误。记住自己操作步骤的操作员与他一起进行了调查。经过大量努力,他们成功地重现了故障 54 错误。重现该错误的关键因素是数据输入速度:如果数据输入速度过快,就会出现错误。

Therac-20 中存在相同的计算机错误。但由于 Therac-20 具有独立的硬件保护电路,因此该问题只是一个麻烦。

我们将研究软件的一小部分,但这足以展示整体设计缺陷。

事故中涉及的两个基本错误是

  • 糟糕的软件工程实践
  • 构建依赖软件进行安全操作的机器


竞争条件 可能是由于多任务实现造成的。以下原因说明了为什么

  • 允许并发访问共享内存
  • 除了存储在共享变量中的数据之外,没有真正的同步
  • 共享变量的“测试”和“设置”不能分成两个单独的操作。

这些竞争条件在事故中发挥了重要作用。

图 1:导致 ETCC 事故的任务和子例程(受 Nancy Leveson 启发)


ETCC 事故的软件错误

图 1 说明了治疗过程。治疗监控器 (Treat) 根据 Tphase 变量的值控制八个治疗操作阶段或子例程。子例程 Datent(数据输入)和键盘处理程序任务使用共享变量(数据输入完成标志)相互通信。数据输入完成后,键盘处理程序任务会更改该共享变量的值,指示 Datent 更改 Tphase 的值。Datent 子例程退出返回到 Treat,Treat 将重新调度自己以启动下一个子例程。

另一个共享变量 MEOS 包含操作员指定的模式和能量等级。MEOS 由 2 个字节组成:一个低位字节用于设置转台,一个高位字节用于 Datent 设置多个操作参数。

操作员被迫通过数据输入过程输入模式和能量等级,但他们可以在之后编辑数据。当操作员在键盘处理程序任务设置“数据输入完成”标志后更改 MEOS 中的数据时,就会出现问题。Datent 此时已经退出,因此不会检测到更改。然而,转台是根据 MEOS 的低位字节设置的,因此可能与高位字节中的信息不一致。这种不一致显然无法被软件检测到。

Datent 通过高位字节信息完成操作参数设置后,会调用子例程 Magnet,用于设置转台的弯曲磁铁。以下伪代码展示了软件的相关部分(摘自 Nancy Leveson 的论文,第 27 页)

Datent


if mode/energy specified then
  begin calculate table index
    repeat
      fetch parameter
      output parameter
      point to next parameter
    until all parameters set
    call Magnet
    if mode/energy changed then return
  end
if data entry is complete then set Tphase to 3
if data is not complete then
  if reset commando entered then set Tphase to 0
return

Magnet


Set bending magnet flag
repeat
  Set next magnet
  call Ptime
  if mode/energy has changed then exit
until hysteresis delay has expired
Clear bending magnet flag
return

Ptime


repeat
  if bending magnet flag is set then
    if editing taking place then
      if mode/energy has changed then exit
until hysteresis delay has expired
Clear bending magnet flag
return

设置磁铁大约需要 8 秒。子例程 Ptime 用于引入此延迟。Ptime 被多次调用,因为需要设置多个磁铁。为了指示正在设置弯曲磁铁,在进入 Magnet 时会初始化一个标志。此标志在 Ptime 结束时被清除。当提交编辑请求时,键盘处理程序会设置一个共享变量,该变量由 Ptime 检查。如果存在编辑,Ptime 会退出到 Magnet,Magnet 然后退出到 Datent。但是,此共享变量仅在设置弯曲磁铁标志时被检查。但此标志在第一次执行 Ptime 后被清除。因此,在第一次通过 Ptime 后执行的任何编辑都不会被注意到!在 ETCC 事故中,由于编辑是在 8 秒内完成的,因此发生了错误。因此,Datent 永远无法检测到更改。通过在所有磁铁设置完毕时(在 Magnet 结束时)清除弯曲磁铁标志,部分解决了这个问题。

我们可以从这些事故中吸取一些教训。

  • 不要忽视或过度依赖软件
    工程师不能假设问题是由硬件引起的。需要进行软件级别的广泛测试和安全分析,而不仅仅是系统级别的分析。每个安全关键系统都应具有事故分析程序,这些程序应在即将发生可能导致事故的问题时应用。


  • 谨慎使用软件重用
    软件重用并不意味着安全,因为已经进行了广泛的测试。在许多情况下,修改甚至重写整个软件可能更安全,因为每次新开发都会引入自己的(新的)错误。


  • 针对最坏情况进行设计
    正确检测和指示错误(向操作员)至关重要,以便采取正确的步骤。从一开始就在软件中设计错误检测代码,是获取有关错误的正确信息的一种方法。在进行权衡决策时,必须优先考虑自检、错误检测和错误处理功能。

参考资料

[编辑 | 编辑源代码]

实时同步方法。在 IEEE 计算机学报,第 39 卷,第 1175-1185 页,1990 年 9 月。

华夏公益教科书