爱吱声

标题: 从丰田车控谈工控编程 [打印本页]

作者: 晨枫    时间: 2014-1-1 11:19
标题: 从丰田车控谈工控编程
本帖最后由 晨枫 于 2014-1-1 21:23 编辑
% z) L" j, _0 ?1 e5 C- i* d2 C! I8 O( I. l! D2 S3 r
意广才疏兄在软件人家开了一个帖子:* F- `  z+ k$ `' i/ J: g
关于丰田车载控制系统问题的分析
/ o8 Y) @5 S; v本想在那里接着讨论的,但要说的东西比较多,就另外开一贴了,意广才疏兄请勿见怪。征求了老兵的意见,说可以在帮外贴,就贴到这里来了。
6 B$ o1 S, {+ \  I, j+ e9 ^$ u! E. t* A# J* s( j
按照转贴的说法,丰田车载控制系统(实际上是动力和刹车控制系统)有如下几大罪状:
9 \8 Y4 l2 z; q$ b0 ?1、模块复杂度过高,不便于查错、修复和扩展,有的已经达到“非可维护程度”了; c+ d. x9 K% c
2、全局变量过多/ {. N- Z/ @9 u2 G8 j7 b
3、违反丰田和行业的编程规范! G4 Q! y0 m3 T( B% z* [  c  Y
4、使用递归
+ V2 d7 C) h/ _' U1 |6 h5、缺乏关键变量出错保护
! W3 y6 B, J6 p: B6、对操作系统的代码没有深度校核,选用的操作系统没有通过认证& \2 R4 c9 B( f+ |
7、关键task死后,公用变量没有保护% M+ Y$ q4 U8 X4 [' v( O9 q0 m
8、内存分配容量不足,位置不合理,堆栈溢出造成任务分配表错乱% u0 K! i$ d5 `: N: p
9、没有用task来监视task,没有用watchdog自动重启系统2 o' E9 @8 K7 B. R; I2 N* O
10、watchdog只用于监视CPU过载,但容许CPU过载1.5秒
' D0 _- S, u! I# N7 X% R% q$ w: Z11、用硬件时钟“喂狗”4 j% ~5 n' D  a
12、Brake Echo Check, u' w! Q0 `: [: g" y6 c) j8 D
13、刹车“触发动作”:change of state?  ^# e9 z! @/ p
14、顶层设计:没有考虑mutually exclusive action的cross check,但后面有提到“刹车优先”# T. y8 `( M1 {9 i' ^3 ~
15、节气门大开导致刹车助力不足
+ p6 _& A  F) f" ~6 Y; F- F" e
7 L( m8 t7 M0 L原帖在这里http://blog.jobbole.com/50915/
  J) \0 r; S! I3 J6 s3 \# e" P' |$ O; Z6 k: o. J" y$ [" s. n& T; c
我会逐条评论这些罪状,不过需要一点时间。顺便说一句,根据维基的介绍,Michael Barr是从事嵌入式系统设计的,但就介绍来看,他做过的嵌入式系统似乎与实时控制无关。不大好判别他究竟对实时控制有多少在行。如果原帖里的评论精确反应了Michael Barr的意图的话,那至少评论者对实时控制的理解有一定的偏差。* `, Z9 d" }+ \0 ~- f

1 M3 \) R2 @2 v2 T7 Y另外,历史上最有名的unintended acceleration案例不是2009-10年的丰田,而是1986年的Audi 5000。有意思的是,那个时代油门和刹车还是全机械的。这个案例差点毁了Audi。
& H: S: G( a& \8 V; O4 |/ i: c" N8 ]1 O7 X/ G1 n: U* j% B6 M7 \9 x/ a0 ~
在评论丰田汽车控制的具体问题之前,先说说个人对一般工业过程控制编码的想法。我对化工过程控制最为熟悉,但想起来别的工业过程也有差不多的问题。最大特点是:这一行是三个绝然不同领域的重叠,所以需要三方面的技能。这三方面就是:过程、控制和编程。8 I" }/ G2 R' P, O
* v5 b6 y* |! X: ~
化工过程本身是一个巨大的领域,里面可以分各种专业。具体到工厂,一比较复杂的聚合物化工厂为例,前段工艺偏重反应、分离,精馏等,需要反应、流体、相变、传热方面的一般知识和牵涉到具体工艺的具体知识,这本身已经是一个无底洞了。更“吃亏”的是,工艺上这可能是两个工程师分管的,但自控支援这一边你一个人要通吃。后段工艺偏重切粒、输送、装车、产品跟踪(工厂在不断转换产品的过程中,到最后装车的时候必须确认谁是谁),需要对具体工艺和操作规程十分熟悉,否则谁到了哪里那是两眼一抹黑,产品发错了车,前面生产再高产优质也白搭。熟谙工艺就好比医生熟谙一般生理和病人具体情况一样,做不到这一点,绝对要开错药。# p  y- Z1 v! C) P4 d

( r" D" M7 P$ P3 D0 q另一方面,自控也是很大的一个领域。有连续控制、断续控制,单变量控制、多变量控制,简单控制(线性、时不变)、复杂控制(非线性、时变),随动控制、干扰抑制,更有无穷无尽的组合情况。还有一个问题是自控不光包括传统自控,还和连锁保护高度交联。两者关系不理顺,要么互相打架,要么过程落入“两不管”的区域,最后无序漂移到被迫启动连锁保护的被动局面。自控也不光是“自”控,还要有机地结合进人机交互。不光要及时、准确、择优(指按照优先等级)报告过程信息,还要及时、可靠、无歧义地执行人工干预。计算机可以按照预设程序自动处理所有已知、已有预案的非正常情况,但对于没有预案的情况,人工判断、人工反应依然是最后一道关。这好比医生对药品的理解和熟练应用,也是必须的。
2 S* Y/ u% x5 u6 }" u+ X
8 N* D$ ?* V6 c! `- c+ N2 o还有一方面当然就是编程了。现代计算机控制系统通常自带很大的功能模块库,对于简单问题,可以直接套,并不需要多少编程。但这就像微软办公室软件一样,虽然提供了大量的信件、报告、电邮、宣传等标准格式,但现实远比标准格式复杂,还是有很多场合需要用户编程。我不是软件工程出身的,对于软件的理解不超过皮毛,基本上只要够用就行。一般来说,软件需要模块化,模块之间的逻辑关系要清晰,模块本身的复杂度要适度,功能增减更应该遵循模块化原则,不要撒胡椒面,到处打补丁。& A5 g! c- \6 i

% ^2 w/ F; k7 x8 f9 a7 d在三个方面都达到专业水平,这当然最好,但实际上不大可能。在理论上,学化工的、学自控的、学软件的都可以编制控制应用,但实际上没有那么简单。学化工的基本上都在大学里滚过4年,学的自控也就是一门课的事情,能记得P、I、D各自代表什么、能干什么用已经不简单了。软件也一样,不能要求比打印一句“hello world”高太多的要求。学自控的稍好,这是因为在北美,化工自控也是在化工系里,一般要学全套化工基础课,然后再加自控课。有的学校是快马加鞭,有的学校则是增加到5年毕业。学自控的编程课也要多一点,有点还要学汇编和数据结构,但更学软件的专业人士还是不能比的。学软件的人对于软件专业没得说,那也是大学4年滚出来的,但要是跟他们说精馏塔板效率、泵曲线、催化剂活性与失活,那就吃力了。罗马不是一天建成的。' b# z- |0 n. ^" {
1 r+ I% H( T! h0 B1 D) f7 L  ?
另一方面,不管是喜欢也好,不喜欢也好,自控在工艺眼里就是后娘养的,自控天生就是“保障”工艺的,主从关系非常明确。这个观念一路向上,所以从公司的人事和投资结构来说,自控是“叫你干啥,你只要说一句保证完成任务就行了,别的都是多余的话。”在这样的心态下,计算机控制的必要性虽然大家都知道,但到了要投入人力、物力资源的时候,第一个要看的“你这个部门的业绩是多少?”工艺的业绩很明确:改变工艺条件或者设备挖潜改造后,今年产量提高xx,合格率提高yy。自控就难了。直接的产品质量控制或者产量最优化控制还好说,大量的保障性控制甚至连锁保护就没法计算了。我常发牢骚,哪一天把所有的控制应用都关了,看产量和质量差别多少,就知道自控的业绩了。但这当然是不可能的。由于从上倒下这样的心态,自控常常处于“你先证明你能干什么,再来提要求”的境地。所以,通常的情况是,计算机控制(尤其是先进版本)的开始不是来自于工艺的要求,而是自控的请战:“瞧啊,这xx技术上得厅堂下得厨房,咱们要不要试试?”回答是:“要钱没有,要机会可以考虑给你一个。”请专业的软件工程师是out of question,所以自控的人自己动手,搞出第一版。如果成功了,然后才有然后。这样不可避免地打上“业余”的烙印。, v/ o( T' l( m7 ]' J; ^: A

) p/ S5 B. g6 |/ P  V1 I, Z) d另一个问题是专业背景。软件工程师对于软件的工具和方法当然最在行,但对于工艺和自控就不一定在行了。在理论上,工艺和自控的人可以编写任务书,但任务书的编写是一门大学问,缺乏对软件工程的基本理解的话,从工艺和自控那边“想”的翻译成软件这边可以照着“做”的,这中间的距离不可以道里计。这有点像请中文系的人来编写产品使用手册。他们对语言的感觉和文章的组织可能非常好,但要工艺和产品这边的人把要写的东西解释清楚,这是非常艰巨的任务。事实上,是工艺和产品人员获得合格的写作技能才是一般通行的做法。
' Y& X6 d# V0 d! ~  M2 ]4 |' ^" u! \+ O0 }  S
出生决定品格,这对软件也是一样的。控制软件很多都出生旁门,这决定了它们业余的特质。控制软件的另一个特质是要求高度可靠。办公室电脑或者家庭电脑出了问题,IT的第一句话通常是:你重新启动过没有?重新启动可以刷新所有状态,大多数情况下,这就强迫系统退出不正常状态,在硬件、软件没有大问题的情况下,除非完全重复导致当前不正常状态的所有步骤(通常这是不可能的),再次出现同样问题的几率很小。采用品牌保证的硬件,大牌软件,通常也避免了大问题的出现。所以重启是合理的做法。但在实时控制系统里,重启是最后才可以考虑的选择。除非有特别的措施作为替补,重启期间的失控是不容许的。
' P! |( P2 n1 w8 C( ^0 ^- n
$ K( D! w0 S8 l% \第二个问题是系统初始化。首先,系统要“理解”当前所有控制动作的位置,一切新的控制指令从当前位置开始,而不是从零开始,否则要出大乱子。所有多层控制回路都要逆向初始化,也是一样的意思。设计正确的控制系统应该打断所有控制回路,从底层向顶层逐级初始化,使当前的控制指令与控制机构的实际状态对应;然后再逐级接通,从顶层向底层发出控制指令。具体说起来,还有internal initialization和external initialization,还有PV tracking,但这说起来太罗嗦了,有兴趣的我们可以展开,否则就按下不表了。不过问题就是,初始化之后,全系统的设定值都可能发生偏移,需要人工或者应用重新校验、核对和调整一遍,才谈得上恢复正常。
$ `% ^- M3 j- s5 W* j0 c" u
& a, _1 D1 f2 S( G) \控制软件不光根据当前状态动作,还根据状态历史作出控制决定。比如说,当前温度高了是一回事,在过去一段时间里,温度一直在迅速升高,这就是另一个问题了,需要紧急处理。相反,当前温度偏高,但过去一段时间里温度已经开始回落,这就可能不需要特别处理,静观其变更好。
1 _) p! S+ ?6 R2 C5 U
9 H  o3 `- P, P, Q2 M5 q另外的初始化问题包括所有控制参数的设定。有的参数是固化的,有的是用暂存动态存放的。控制系统都做定时备份,重启时调用最近的备份,但暂存里动态存放的参数就可能是过时的,也需要校验、核实和调整。0 I# b8 _4 Y- U3 u5 P

2 ]  `+ \) t9 o8 W$ n/ q' q总而言之,控制系统重启是一件大事,不可能轻易就重启的。这一点在后面watchdog的时候还要谈到。: c+ r; P& A' D8 i. P9 V- U
. w2 a* x% {2 |1 z( K
但这样高度可靠性要求的另一面就是:不要轻易改动已知可靠工作的部分,尽量做加法,减法或者重组要特别慎重。参照前面说道的核心软件的“业余”性质,隐患就不难理解了。
/ J& S. ^! I8 k3 W2 f3 X
$ P3 @; Q! x4 o2 M推到重写是有的,但这是非常慎重的事情。最重要的就是:控制软件通常“从属于”工艺项目,项目“不需要”的时候,没有控制软件独善其身的机会;项目需要的时候,时间限制放在那里,不容控制软件独善其身。这和通用软件的环境是不一样的。通用软件自身就是最终产品,除了市场竞争因素外,软件部门是可以自己控制进度和要求的。另一个问题就是,通用软件的开发任务相对单纯,周期也较长,windows不断出补丁,但基本软件要好几年才推出一个新版,尽管近10来年基本上是无事生非,为创新而创新,但这是另外一个话题了。航空航天也是一样,NASA从阿波罗到现在,中间只出过一个航天飞机。洛克希德这些年算高产了,但F-22到F-35,30年里也只是两型战斗机,算上无人机要不超过4-5中。一般工业控制软件则不一样,作为“从属”系统,需要短平快出产品。比如说,丰田一年就要推出好多种汽车。各种丰田汽车的控制系统当然有通用部分,但毕竟还是有很多不同。我不知道丰田的基本软件的身世,但要是有相对“业余”的核心,我一点也不会感到奇怪。
! c! K- x- g  t# T9 r8 {
+ r3 Q5 U" {9 N7 E$ I2 N% j" B控制软件还有一个问题:测试环境。通用软件的测试环境不是一个问题,只有有限人力物力的问题,否则理论上可以测试所有的情况。航空航天不惜工本,先用铁鸟测试,也可以解决大部分问题,尽管F-22试飞中依然出现PIO问题,F-35的软件研发也是问题一大堆。但工业控制软件的测试环境就很有限。有的地方还有仿真系统可以测试,但有些功能仿真系统也测试不了。仿真系统是实际系统的简化,不光在工艺上简化,在控制系统方面也是简化。以化工为例,HYSIS(现在是Honeywell的一部分)用硬件仿真,只有工艺模型用数学模型,控制系统是全套硬件,这样的系统仿真度高,但是成本受不了。Honeywell也用PC作为控制终端,只是在总线上插一块板子,但这块板子就是4万美刀,PC实际上白送。这还是控制系统里最便宜的部分,I/O柜子一个就是1.5万美刀,一个铁柜子啊!林林总总加起来,完全模拟一个化工厂的DCS,等于DCS硬件双倍投资,省掉一点现场接线的投资,这就是没有50万美刀想也不要想的事情,加上过程模型,还有每年的服务协议,这就使大部分公司打退堂鼓了。ABB(现在叫RSI)是软件仿真,省掉很多DCS硬件,但软件与实际DCS的相似度总是差那么一点点。要命的是,有些时候,仿真上通过了,实际一用,毛病来了。可以亡羊补牢的应用还不要紧,一锤子买卖的应用就要命了。有时候,特别关键的应用反而没法测试,只有“实践是检验真理的唯一标准”。问题是有些实践是可遇而不可求的,甚至是极力避免这个“遇”的。比如说,全过程紧急停车的后处理应用,这非常关键,不管保证安全,还减少设备损坏,但如果仿真不能提供100%的答案的话,工艺是不可能为了让你测试应用而给你拉闸的。
7 u6 [/ j+ G$ l, j7 _# J0 N0 p0 k8 q( X, T/ P: Z3 n
这些不是吐糟,而是为了帮助非控制行当但对IT很熟悉的人,了解控制软件的特殊生态环境。有了这样的了解,再来看丰田汽车控制的问题,就比较容易了。6 u* ?- L. ^5 t/ P  v, ]+ ]/ q

+ \. Y& N4 G8 ?3 Z9 J$ W& ]- E: a第一个问题:模块复杂度过高,不便于查错、修复和扩展,有的已经达到“非可维护程度”了
# N9 K4 h: r4 G- Z1 D
+ w, }$ w' D) o  B( Z前面已经谈到,大部分控制软件有一个身世问题,通常出生旁门左道,在结构上不够专业,不够高瞻远瞩,好在功能简单,问题还不大。但随着经验的累积和胃口的增加,控制软件的不断复杂化,这简直就和水往低处流一样必然。
. n4 p- ?# |% k$ U) g- W
# P' l3 @2 b+ ]1 i# a3 e在增加功能的时候,首先考虑的通常是尽量不改动已有部分,只是净增加。这似乎是模块化的好机会,新功能整合到新模块,老功能保留在老模块。但实际上没有那么简单。控制软件的大头通常并不是核心功能部分,而是人机接口和意外处理,姑且称这些为外围功能。对于新功能来说,这部分在很大程度上是重复的。要看原始软件是怎么搭建的,如果核心功能与外围功能本来就分属不同模块,这样模块化还容易些。但考虑到“原始”控制软件大多很小、很简单,模块化程度可能不高,而新增功能只是很小的改动,最大的可能还是“就地”改动,继续享用原有外围功能,而不是另外构建新的模块。否则就要拆分原有模块,根本改动软件结构了。这不仅是编程的工作量,还有重新测试和验收问题。工艺上会不解地问:“只要那么小一个改动,为什么要重起炉灶?”问题也是显然的,这样时间长了,累计改动就积少成多,造成模块复杂度过高。7 G$ H- a8 ?2 g2 _
& O" d( W! ~( a* }! l5 d( K- [
现成软件在什么时候拆分模块,这是一个艺术。太早了,没有business case,吃力不讨好,四处讨骂;太晚了,就有Michael Barr说的问题。但是具体到某一个软件,就怕Machael Barr自己也说不好,“应该”什么时候拆分,尤其是把对项目、成本、工期、风险的影响统统摆在面前的时候。写书、谈理论是容易的,但CEO从来不根据理论或者书来做business决定。
: y5 n+ F1 c4 l+ r2 L$ n3 Y/ ]/ \
! W) r, w) Z& \6 H1 E6 G第二个问题:全局变量过多; v' e: }7 k# Y$ C. |3 ?

# U+ Y3 g! R. s控制软件的对象是物理系统,尤其是大系统。大系统的行为是高度关联的。控制软件在本质上就是要与大系统的很多参数交联,否则就无法有效控制。聚合物工厂的反应器当然有温度控制、浓度控制、转化率控制等等,大这些控制上要“感知”进料情况,下要“感知”火车装车,中间更要“感知”进料纯化、催化剂制备、产物分离、未反应物料循环等等,哪一个环节脱节了,都要相应调整、减产,甚至停车。这一点与办公室软件或者小系统的专用软件有很大的不同。引文中用洗衣机(还是洗碗机?)做例子,全系统才有几个变量?
' c: R# E4 f" C8 F7 ~/ p- @' w2 q# x" [+ I* u4 C. ]  o# p
控制软件的另一个特点是,不仅要使用变量的当前值,还要使用变量的历史值。控制软件是定时反复执行的,比如说,每秒钟或者每分钟(根据控制对象的动态特性决定,慢过程还有每几分钟执行一次的,像精馏塔温度,升高个几度动辄半小时一小时,执行更快也没用;但F-16的尾翼就要每秒60次以上,否则飞机就掉下来了)执行X次,每次都要从头执行到底,或者在中途受控退出(exit)。全局变量有两种,一种是测量值,全系统都有access,这当然是全局变量,控制软件只是“调用”而已;另一种是中间数据暂存。这是因为程序内部的局部变量在每次执行结束后都被清洗掉,只有用全局变量才能在两次(或者多次)执行之间保留数值。这对历史值特别有用。
: F2 w* C/ O  w& w3 G$ X9 y7 U
, f. r; w3 h0 I9 I' Z0 V比如说,当前温度超限了,这当然是坏事,但“坏度”取决于温度在继续升高,还是已经在回落了,还有就是升高或者回落的速率。这就牵涉到历史值。历史值的另一个应用是比较当前状态和过去一段时间里的平均值。系统的工作状态是一直在漂移的,即使是“同一工况”,由于设备损耗、气候因素、物料纯度等等,具体到某一个温度、压力、电动机扭力,每时每刻都会有一点不一样。标准工况参数(SOC,standard operating condition)只是一个参照,具体到今天,这个数据可高可低,都是正常的。但要是要对异常情况监测,就不能用SOC参数比较,而要用当日平均值比较,这又要用到历史值。3 b5 M+ e: Y" l! }1 G" p
( W; P! ], g- f  y: S
Michael Barr说到丰田用了11000个全局变量。听起来这有点多了,但没有研究丰田的实际控制问题之前,我不能下结论这到底是多还是少。我自己的化工厂光测量值就有10万个,加上各种暂存,可能在要加几万。但化工厂和汽车的控制问题不能直接相比,只能说,控制软件“天生”就有大量的全局变量。" s! E* x, A4 S, \# b$ ~' g
/ M, |* B+ w8 |7 v7 R9 k& N0 f
第三个问题:违反丰田和行业的编程规范
2 j$ Q" V0 r1 p( M# T0 ^, X, }  ^$ c4 O" Y3 `8 f
丰田的汽车控制软件违反丰田和行业的编程规范,这是有可能的。这有几个可能:
2 V$ j8 F3 U! W' C1、        核心模块采用了大量legacy成分,也就是“历史流传下来”的东西。已经久经考验的,一般不会轻易放弃。但是当年编程的时候没有按照规范,留下来就一直这个样子了。工业上这叫grandfathering。重新按照规范改写当然可以,但一来需要花时间和投资,二来要重新测试和认证。在很多情况下,这样的重写属于“没事找事”。
6 @/ K8 H% i; s2 ^, q6 ]6 {2 a3 p2、        核心模块不仅采用了legacy成分,而且是“打包固化”的。用科学计算程序举例,历史上流传下来很多FORTRAN程序,用于解算各种复杂科学问题。用了几十年了,可靠性是已经证明了的。但现在的程序环境早就进化了,C、VB、MATLAB或者专用环境,不再能直接使用,但可以把当年FORTRAN打包成DLL之类的东西调用。这样的话,更不可能按照新规范重新编写了。
( X9 O6 W$ s  P: R# ]+ Z$ ]0 T$ X9 H1 b* P' ^; B. a6 w( @
一般计算机出毛病,IT的第一句话就是重启过没有,但工控可不能轻易重启。工业控制要求极端可靠。这包括两方面。第一是系统软件、硬件的无故障间隔,第二则是系统软件、硬件的出错机制已经“透明”。系统软件、硬件的无故障间隔当然是越长越好,但有时深刻理解出错机制更重要。只要能采取适当的“预防性维修”(Preventative Maintenance,简称PM),在故障间隔到期前更换部件,或者重启系统,故障间隔不可怕。但不可预测的出错隐患是大得多的威胁,不知道什么时候就咬你一下。这就是为什么要求高度可靠的应用常常使用貌似老爷的系统,主要是因为这些系统的出错机制已经熟悉,不会有意外。有些工业要求具有可靠性认证,没有足够的运行时间,这也是不可能的到的。例如,直到10年前(现在不知道是不是有改变了),核反应堆的控制如果用Honeywell DCS的话,还是TDC2000,这是80年代初的技术,连80年代后期的TDC3000都不准用。波音777上还是用8086、80286,F-22上使用PowerPC,都是这个道理。& u6 N; b. B6 w/ G. C: K, \

* {; y0 [: K) Q1 L8 Z; R5 _! v既然是老式计算机系统,编程环境陈旧就不奇怪了。不仅陈旧,而且是简化版。像Honeywell TDC3000上使用的CL语言,这是基于FORTRAN 4的简化版,可以IF…THEN…ELSE,但不能做嵌套IF…THEN,也就是说,不能使用' p- E, K7 u/ O4 p. U1 e
IF…THEN4 I* C7 o* B% B) z6 D$ R+ s
        IF…THEN
, A& ^, g  e9 [0 G6 W6 S这样的多层嵌套。这使得结构化的编程很困难,只有用逆逻辑和GOTO模拟与结构化相当的效果,但有时还是不能完全做到。如果丰田的编程环境有类似的问题,达不到规范要求也就不奇怪了。
& n/ i! ^$ c. k. {6 f2 t+ O" p+ _( E8 ?
还是那句话,原则上,规范要求是底线,必须达到;但实际上由于种种情况,不一定能做到。4 M5 n( h" H4 `
4 m# ~5 g- t8 h+ \
第四个问题:使用递归- ]- K, H" l1 O+ M6 n4 `" {% `

% o$ m4 k* y: A) U' _, ~由于实时控制需要使用历史值,有的时候recursion是不可避免的。求数值解的时候,也常用recursion,可能这和文中提到的递归是不一样,那是函数反复调用自身?性质有所不同,但也未必不可控制。设立一个独立的计数,每调用一次就增加一次计数值,这样应该可以控制最多递归的次数,避免无穷递归。控制程序都是定时重复执行的,每一个循环开始的时候,技术需要清零。
( r" e) u; M( Q" b! ]& s8 q
2 }2 v6 c; I! g0 k& O  ^第五个问题:缺乏关键变量出错保护
. d" V2 w# U8 ~9 I8 q1 k( X  Q
+ j6 P) m7 F3 x1 E5 D2 F& U变量保护分几个方面:
% R: B; W- q" i# w0 W, ]9 h1、        发现变量出错,比如bad value,像传感器开路,bit reversal也属于这一类/ ?* H3 }  N5 J, E$ K  M) x4 D9 f% G
2、        变量不合理,比如flat line(数值冻结)、阶跃式数值剧变、脉冲式数值剧变等
( Z' {5 v, u- e) p9 |% M5 K3、        发现出错后如何处理
! q1 I' Z& p4 {% o" }
) i8 r' u2 S3 j传感器开路有专门的检测手段,bit reversal也可以用类似校验码之类的办法,问题是工控的CPU有严格的时间限制,每一个时间段(一般是把一秒分划为256或者多少个等分)里,相关程序模块必须执行完全,不能“溢出”到下一个时间段里,影响其他程序模块的执行。换句话说,程序的复杂度要控制,否则可能出现超时出错(overrun)。
  H% E7 w  ?5 c* M9 w
, c- O. O, v( y! V  {变量不合理的检测更加复杂。数值冻结需要对同一变量检测一段时间才能判定,这当然是移动窗口。阶跃式数值跳变是数值跳变后,一直停留在新的值,没有回落。连续控制作用对这样的跳变做出反应会很危险,大部分情况下这是传感器出错,而不是过程变量真的跳变了,但不采取有效措施检测,控制律是不知道不该对这样的跳变做出反应的。脉冲式跳变更常见,瞬时变化很大,但很快回落。人工控制的话,会对这样的跳变产生怀疑,通常等一等,跳变也就过去了,不至于做出错误反应。但计算机控制就比较死板,要不错误反应需要增加很多复杂逻辑,又多出来“聪明反被聪明误”的问题。总之,没有放之四海而皆准的好办法。4 K: Y1 h* ?& D) d$ s

  x1 ^" G. D  V+ e3 Z发现变量出错后,如何处理,这也是一门学问。常用做法是使用一个固定的无害替代值,还有就是使用最近的有效值。后一个方法较好,但问题是如果变量持续出错,就要出现数值冻结问题。$ K0 c! I# k# {7 P% ?, {
& Y; E+ p6 A7 [) s2 e5 e) k0 s4 v* r( d
所有的变量保护措施都耗用CPU和I/O时间,不加区分都保护的话,CPU和I/O忙不过来。但什么是关键变量很不好确定,常常是以为不关键的变量最后要了你的命。
) ]% s: A; x' ]3 o! X7 E
! x, k2 |- E( s, ~6、对操作系统的代码没有深度校核,选用的操作系统没有通过认证; V; d* D6 B" D; J
# c- U' }- p$ y0 ~
操作系统应该认证。丰田使用未经认证的系统,这是自找麻烦。但使用应用软件的人,很少有本事或者工具对操作系统的代码深度校验,这个要求过分了。美国海军使用Windows NT作为宙斯盾的平台,香港赤腊角机场管理也是,早期曾出现死机问题。他们这样人力物力丰厚的地方都没有能力深度校核操作系统深层代码,一般工业公司就更不可能了。8 T: f( J, }+ l% Z2 i" r& L

, o) U6 {, L) l& Z8 [7、关键进程死后,公用变量没有保护8 P6 e1 F- x( ?
0 s# O& |: ]4 _" \& v$ @
如前所述,控制系统中公用变量和一般程序中有所不同,所有测量量、控制作用都是公用变量,这些公用变量不受关键进程死机的影响,它们和物理变量相连接。另一种公用变量是关键进程的计算结果,但这个数据是公用的,比如化工厂里反应器的转化率。这样的数据一般是放在专用的placeholder里,施主进程不更新,数据就保持不变;施主进程死机了,数据也就冻结了。由于数据本身是合理的(不是乱码,不超限),简单的校验是看不出数据是不是正确。“冻结检测”可以发现数据已经超过一定时间没有变化了,但这不一定可靠,有时候这数据确实就是长时间不变的;延长检测窗口最终是可以发现冻结,但要等很长时间才能确定冻结,发现的时候就太晚了。另一个办法是由施主进程在写入计算结果的时候,同时写入一个“心跳”(heartbeat)信号,现在是0,就写一个1;现在是1,就写一个0。受主进程在读用计算结果的时候,同时检测这个心跳,如果超过一定时间心跳冻结在一个数值,这就是说施主进程死机了。这时有几个办法:一个办法是不用计算结果,但有时这是不可能的;还有一个办法是降低控制增益甚至冻结控制输出,直到信号恢复;要是这不是唯一的输入的话,可以在几个同质输入中调整加权,降低冻结信号的加权,维持控制。3 F8 L- C+ ^, n
8 J. R8 W7 x- b* J* u
8、内存分配容量不足,位置不合理,堆栈溢出造成任务分配表错乱
, I9 b8 y8 r+ I0 S) B6 X- b0 n; v4 n7 r8 Y- |7 ^
对于这一段,很怀疑原作者对于堆栈溢出理解错误:“丰田的系统里,正好有这么两块相邻的内存块。第一块被称为“堆栈(Stack)”,这是所有Task存储它们运行状态的地方,大小为4KB。与之相邻的地 方储存了操作系统进行任务分配的记录。那么可以想象,如果很多Task给堆栈里写入太多东西,超过4KB,那么就会错误地写入与之相邻的任务分配表。这种 错误被称为“堆栈溢出”。”这样的指针管理错误实在太低级。堆栈是后进先出的,如果中断层次超过堆栈尺寸,最近的中断地址就没地方放了,程序就执行不下去了,这就是我理解的堆栈溢出,但绝不是把数据存到相邻的内存区。丰田很可能堆栈区没有设计有足够的尺寸,递归更加使得堆栈问题无法控制,但溢出到相邻内存应该是误解。' q8 {/ l) \; r9 @" x$ d+ V9 }
& r+ r8 b1 \# L$ u: j; L7 a0 e: I
9、没有用task来监视task,没有用watchdog自动重启系统
0 m) F: Z9 r1 I7 s9 ?6 g( N
1 X6 a$ }, W& |( U* ~用task来监视task,这就是看门狗。看门狗是实时程序中常用的东西,但看门狗最不能做的事情就是自动重启系统。系统如果当机,不弄清楚是怎么进入这个当机状态的,直接重启,很可能造成无限重启,问题更大。另外,看门狗要看性质,有时不是系统全面当机,只是局部功能死机,自动重启就是下下策。如前所述,计算机控制系统重启可不是PC机重启,这也是办公室IT和自控IT思维的关键差别。如果办公室PC出了毛病,哪怕还没有死机,IT支持经常首先要你重启一下,如果毛病没有了,就当从来没有出现过这毛病;如果毛病还在,再进一步查找。这是办公室IT的标准程序。但控制系统是24/7/365的,没有天大的事情,决不能贸然重启。不光重启期间全过程要失控,重启后的初始化更要火烛小心,否则就真的搬起石头砸自己的脚了。在工业工程中,看门狗用于提醒操作工或者系统人员:出现异常,这时就要人工按预案补偿、修复。对于不可能人工干预的情况,比如汽车的发动机-刹车控制系统,这就要考虑多余度备份,必要的时候保留模拟控制通道,保持简化的基本控制。丰田在看门狗“叫唤”的时候是怎么做的,我不知道,但自动重启是要不得的。
" \, L) U$ e) d& x, ^0 j$ g. h/ p6 H/ m; Q6 }# T/ b: z) z# j9 f
10、watchdog只用于监视CPU过载,但容许CPU过载1.5秒% P5 J  ~; @" ^' c! s7 O2 A; G" G

  L2 {+ {5 Z0 @$ m$ oCPU过载是不好的,但也要具体情况具体分析。实时系统的特点是程序按固定周期反复执行。最起码的要求是在指定周期内要完成所有计算和I/O。但具体分起来,前台(foreground)程序必须在指定周期内必须完成,否则就是overrun错误;累计overrun到一定程度,系统就认定已经无法再完成任务,或者选择性地关闭“坏程序”,但也可能的是全面崩溃。后台(background)程序则容许见缝插针,在前台程序执行空闲的间隙执行,一个周期完不成,就“溢出”到下一个周期。要是连续几个周期都完不成,那也引起overrun错误。另一方面,计算机虽然是数字式的,非黑即白,但在过载问题上,就和汽车发动机转速红线一样,这是指导性的,但不是硬性的。换句话说,发动机转速经常保持在红线上,发动机肯定要出毛病;但偶尔上红线甚至略为超过一点,并不至于损坏发动机。至于到了红线是否立刻切断油路,这要看应用场合。民用汽车发动机没有太性命交关的应用,到红线就切断油路是妥当的;飞机发动机就不一样了,有时候飞行员就是要争取这关键的几秒钟,否则就撞山了,这是你给他切断油路,还不如直接给他一枪自杀算了。控制系统CPU过载也是一样,由于不到万不得已不宜关机重启,适当容许过载一定的时间并无不妥,当然这个容许时间要小心掌握,在使用中也要时刻关注,要是经常出现进入过载但还不到容许时间极限的情况,这就要警惕了,有重大隐患,不能心存侥幸。
  ^1 W% v0 T: E( d0 y+ E# T9 v" [
: `' b) B6 `8 D3 x; k5 b11、用硬件时钟“喂狗”* @, ~& H- R8 ]4 u% J

9 U( ^6 R4 `( z: C  K9 D7 P" l硬件时钟喂狗是不妥的,这只监视硬件。根据不同系统,要是时钟死了,整机也就死了;心脏停跳了,还要大脑指挥手脚做反应,这就是荒唐了。
' B6 B7 ]. a! Z$ ^
( i) G/ B5 l" S/ d& M12、Brake Echo Check3 F* u! C; D/ K8 _1 b4 m
3 y6 w/ _8 y' {4 t, p( w: G
如果我理解没错的话,brake echo check是根据刹车片位置传感器的反馈来确认刹车动作,这在化工上也常用,叫valve open/close switch,泵机等也有类似传感器。这牵涉到控制和逻辑的基本设计思路:一种是开环,根据控制意图发布指令,但发布指令后并不检查是否完成;另一种是闭环,发布指令后检查系统状态,确认指令得到完成,然后再做下一步。显然,闭环方法更好,但首先需要更多的传感器掌握系统状态,控制逻辑的复杂程度大大提高,而且执行时间更长,因为要等到确认了系统状态才能进入下一步。这也多了一个新的隐患。有的时候,第一次指令发出后,动作没有完成;控制系统根据系统状态反馈,发出下一次指令;几次下来,动作完成了。从控制逻辑的角度来说,万事大吉,达到设计要求了。但这实际上留下了一个隐患:为什么第一次没有完成动作?为什么总是要到第三次才完成?如果能确认原因,这通常引向一个系统的特质,然后针对性地改进设计,可以大大提高执行的可靠性,而不是依靠状态反馈。状态反馈是好东西,但也会掩盖深层问题,忽略了深层问题而依靠状态反馈“擦屁股”,最终是要被“踢屁股”的。有时问题是累计性的,前面几次依靠状态反馈最后完成了,但反复尝试的次数越来越多,最后再试也不管用了,这时就晚了。还有一点:多一个状态反馈,就多一个出错点。如果状态反馈传感器故障,控制动作正确执行了也不管用,控制系统还是“傻等”。开环方法还是闭环方法要看具体应用的关键性,还要看执行机构和传感器的相对可靠性,否则会自己给自己上枷锁。
) R( O) \; ]! D# h  o6 r; ]6 K" i  O0 x( L9 e5 c- E+ g
13、刹车“触发动作”:change of state?  |( u2 O) q0 ^
" l  Q* H. H( }, z3 E9 q+ B
原文中对刹车“触发动作”的描述语焉不详,可能这是latched logic还是momentary logic的差别?前者根据系统状态将控制输出锁定在指定位置,后者则用脉冲信号将输出一次性推到指定位置,然后控制信号回中。两者各有优点。比如汽车车灯就是latched,而照相机快门就是momentary。还有一个可能是指根据系统状态(是0还是1)做出控制动作,还是根据系统状态变化(从0变到1或者从1变到0)做出控制动作。这一段看不懂作者在说什么,不大好评论。4 i1 H7 E  s( s- a+ T. j6 L

+ N: x) V3 A( P7 [7 {14、顶层设计:没有考虑mutually exclusive action的cross check
% }  l3 e$ z; T7 q2 U! I- \* j
) ]) `) M# T: |0 P/ O( H! f原文指责丰田在顶层设计没有考虑mutually exclusive action的cross check,这确是对关键控制动作的基本检查。不过针对不同控制任务,mutually exclusive action集是不同的,有的时候绕了几个圈子后可能发生冲突,要很小心,别把自己绕住了。比如说,驾车时,右脚在油门踏板上,就不能在刹车踏板上,这在通常是对的,但飙车时是可以heel and toe的,也就是在踩刹车的时候同时拖一点油门。这是为了保持发动机转速不掉下来,在出弯的时候可以更快地加速。即使不做heel and toe,驾车人也可能错误地左脚踩刹车,右脚踩油门。对于开惯手动车的人来说,如果新换上自动车,还不习惯,左脚“找不到”离合器的时候,偏一点踩到刹车上,这是很可能出现的错误。如果控制系统死板地认定刹车和油门不可能同时踩下,而对控制逻辑做相应的设计,这就要出大问题。作者后面提到“刹车优先”,在刹车踏板踩下时,无条件关闭节气门,切断动力。认为如果这一点做到,即使上述mutually exclusive action没有做到,也可以保证安全。同样的问题,这不能解决heel and toe的问题,也容易在意外同时踩下两块踏板时过度减速,造成追尾。如果“刹车优先”在刹车踏板踩到80%以上才启动,可以解决这个问题。
  G" N* a9 y, _* R  v7 G3 _1 T- K6 q. ]4 Y# F5 }" ~7 i
15、节气门大开导致刹车助力不足+ R* e+ B2 Q6 F) E4 a
  _, v% Q( S* z# ~1 }! r  u2 D
这个不知道是Michael Barr的错误还是解读他的人的错误,汽车发动机进气系统的节气门与刹车是两个系统。轿车刹车是独立的液压系统,与发动机进气没有关系。即使是卡车上的气刹车也与发动机进气没有关系。(这一点我的理解有误,刹车的真空助力确实和节气门有关)
9 w$ A& O6 D) A+ c) V8 V" U' n5 `: l8 e9 w, ]+ ]4 s% _# _4 f
丰田的控制编程可能有很多严重问题,相信Michael Burr是有根据的。原文的解读也是花了功夫的,作者似乎是软件业内人士,至少对软件工程很熟悉,但对于实时控制软件的很多基本特点不甚了了,而是套用通用软件的概念,得出的结论就容易有偏差。但是作者的一些观点还是正确的。控制从业人员越来越借助计算机实现控制算法,编程早已不是选择之一,而是主要吃饭家伙。另一方面,控制从业人员的基本训练中,软件工程训练不足。这本来就是一个无底洞。软件发展那么快,几乎所有新颖软件都有控制应用,比如说,HTML已经广泛用于人机界面设计,JAVA也开始大量使用。随着机翼不同软件平台的MES和DCS的进一步整合,未来跨平台应用都会出现。控制从业人员一方面要不断提高自身软件工程的素质,另一方面也依赖软件行业提供更先进、有力的软件环境,帮助、鼓励甚至迫使控制从业人员采用软件工程的原则。隔行如隔山,有软件专业人员编写控制程序就像由药剂师开处方一样,说到底是十八般武艺用错了地方。对于控制软件和应用来说,软件专业人员的贡献更在于提供工具,而不在于具体解决问题,自控还是应该由自控专业人员主导,只有他们才更理解控制问题的特质。
4 I& J9 o/ b# z7 T) L) ~8 r
作者: 清流宰    时间: 2014-1-1 12:11
沙发。
& _: E( k4 q* ^1 h& ^7 ]# j9 X; j犹豫了好半天 2013 最后一晚该看什么,正好发现晨大的文章。
作者: 晨枫    时间: 2014-1-1 12:17
清流宰 发表于 2013-12-31 22:11 " J& J$ A2 A& I) S
沙发。) Z: O; {% C( @3 y2 {, \
犹豫了好半天 2013 最后一晚该看什么,正好发现晨大的文章。

2 X7 D" p6 Y4 N1 L% F$ V! z+ s哈,正好看了催眠,今晚睡得肯定好。新年快乐!
作者: 马鹿    时间: 2014-1-1 12:19
不了解工业控制的写控制程序肯定是不行的。
作者: 晨枫    时间: 2014-1-1 12:34
马鹿 发表于 2013-12-31 22:19
* M1 m% R: V. f( @不了解工业控制的写控制程序肯定是不行的。

+ T# T5 X# b6 l7 T! c; r但是一些软件专业人员看工控人员写的程序“不上档次”,很不满意啊。
作者: 清流宰    时间: 2014-1-1 12:42
晨枫 发表于 2014-1-1 12:17 $ w; `; Z- t$ Q2 c# J
哈,正好看了催眠,今晚睡得肯定好。新年快乐!

+ s$ t4 P, F* i. p3 L哪里有催眠,这也是本行,感慨实多。括而言之,“制造业的艰难”。; F+ A$ m% ^$ ~  h8 {, U7 p
加拿大没出息,有滑向沙特之类快乐的资源国的趋势。; W; z( }. @. a5 V' _
美国惯坏了,找到了赚快钱的方法,并且上瘾。5 [! e2 C, Y, G2 G9 x* Y  F
中国还在爬科技树,热衷制造业。祝福。
作者: 晨枫    时间: 2014-1-1 12:44
清流宰 发表于 2013-12-31 22:42
8 D4 n  ^, y" n, A, w哪里有催眠,这也是本行,感慨实多。括而言之,“制造业的艰难”。
( D' A& l7 w# y& E$ W2 o加拿大没出息,有滑向沙特之类快乐的 ...
  H# b& s$ i% ~. [( G& v' s" W' G
加拿大的石油化工倒是迎来了第二春,传统制造业确实是不行了。
作者: 江城如画里    时间: 2014-1-2 17:43
关于堆栈的讨论:, x, P9 I- h: f, E; S! q
我个人觉得原文说堆栈出问题影响到邻近内存区还是有可能的,我遇到过这样的情况,大概如下:4 m0 H" I5 a6 `6 L4 J
|OS reserved area.......................<----.....stack area|# ~4 ]3 ~; W6 Q$ }
这种系统stack point是倒着长的,即箭头方向。当时的情况就是stack area未分配够,结果几次函数调用后,os reserved area的数据给毁了一部分——这在当时是看不出什么后果的,是到了后面其它函数运行时数据不正常导致系统垮掉了。
作者: 晨枫    时间: 2014-1-2 23:12
江城如画里 发表于 2014-1-2 03:43
4 H' N" H. Z3 {. a关于堆栈的讨论:
! B+ E7 j& n# Z我个人觉得原文说堆栈出问题影响到邻近内存区还是有可能的,我遇到过这样的情况,大概如 ...
- m: o8 x" X( h# W. \% S* E
那为什么不对stack point能放的位置限制在stack area里呢?当然,这样中断就继续不下去了。不过让stack point跑到外面来,也是一样个死,OS reserved area里数据被毁也是死,死法不同,但至少stack overflow明确告警而死,错误信息明确一点,也好以后查错啊。
作者: 江城如画里    时间: 2014-1-3 07:44
晨枫 发表于 2014-1-2 23:12 # V6 y: v+ T, l) W0 D! [* p
那为什么不对stack point能放的位置限制在stack area里呢?当然,这样中断就继续不下去了。不过让stack p ...
3 {' B$ n( X* Y
嗯,理论上说是可以自己计算在当前栈占用了多少空间,但实际上很难做到。
* X1 ^. X9 a) w4 h首先这跟CPU体系结构有关系,尤其是CISC的CPU在这里计算很麻烦,它要判断段的属性(栈段有可能向下长也可能向上长),一会加一会减还要变号很让程序员崩溃;
( |# ]- l  _. o2 x3 a其次部分指令是隐含自己压栈的,比如INT指令,从面上看没有任何压栈操作,但实际上该指令周期内是自动做了压栈的,加上部分CPU有不同保护级,在级间切换时栈段空间也要切换。这一部分也跟CPU有关系。1 S8 ]; `! U5 ^% _! Y
再次,做嵌入式的程序员由于资源受限,有时为了优化会写很多变态代码——比如交换两变量,先push再pop一下,比直接用中间变量交换快且代码短,遇到这种情况再想算清楚栈上用了多少空间几乎成不可能的事了。8 K& t! Y6 O/ L1 G; c3 B; O* d
很多是通过间接手段限制的,就是设立专门的栈段,然后靠段自己的保护机制(段限长不应该突破)越界时出现fault就知道发生了栈溢出。
作者: 晨枫    时间: 2014-1-3 07:53
江城如画里 发表于 2014-1-2 17:44 0 G: A1 z2 `! q. L; _0 I- w
嗯,理论上说是可以自己计算在当前栈占用了多少空间,但实际上很难做到。7 {+ Y( t: ]1 Y/ r5 H! D
首先这跟CPU体系结构有关系,尤 ...
' G$ L/ c% t$ ^# [% }

  F( m7 m3 O  v; y7 F" \8 D3 }嵌入式系统确实受到资源限制问题。你后面说道的保护机制(段限长不得突破)就是我前面说的堆栈溢出保护的意思。我前面没有说清楚,但我们想的是一样的。
作者: 意广才疏    时间: 2014-1-8 20:08
早就想回复晨枫同学的雄文了,奈何年底自己的新东西要上线,有心无力。明后天大概能有点空闲,稍微写一点吧。
作者: 晨枫    时间: 2014-1-10 13:09
意广才疏 发表于 2014-1-8 06:08
' I6 Z' }# x& ^# i3 T1 `$ L0 E& Y# K. j早就想回复晨枫同学的雄文了,奈何年底自己的新东西要上线,有心无力。明后天大概能有点空闲,稍微写一点吧 ...
: j/ H$ i7 Y) V/ ?5 b
期待中。
作者: 沉宝    时间: 2014-1-17 12:51
要说爱坛就是好,大家都是谦谦君子。晨老大的帖子发出这么多天,除了[意广才疏]的那一只靴子悬在半空中外,其它基本上都是附和之声。今天我做恶人,上来拍砖。* ^8 \5 i$ N9 P9 c1 c+ U1 {/ M
1 g: J* H& {1 k" V' D; P8 \
先说晨帖中有价值的一面,晨老大最大的功绩是说清楚了为什么丰田车载控制系统写成了这个样子:时间紧、公司中地位不高(晨语:后娘养的)、历史积累太多改动困难、等等... ... 这些都是非身处一线而不能深刻体会的。但对于Michael Burr法庭证词的主要目的,即丰田车载控制系统是不是reliable software,晨文则或观点错误,或罔顾左右而言他。借用电视剧《潜伏》打个比方,不发生横向联系是地下工作的纪律。晨枫在国军桂系中成功潜伏十八年,对此条纪律有异议,云:我都干了十八年,根据经验有没有这条都没事;桂系中的人事关系特殊,那些对付军统的方法在这里不适用... ... 而被晨枫批驳的原文(以下简称原文)则说的是:组织研究后认定,鉴于晨枫小组未能严格遵守工作纪律,因此不能将晨枫小组视为可靠情报来源。
# f) |* e* W' J6 B
6 m  z2 Q8 j" U" {1 m. R下面逐条评论一下晨文。
( b' ^; ~& C, r: R0 q# U& c  }2 ~. {* N( i: u+ z1 E& }) s
第一个问题:模块复杂度过高,不便于查错、修复和扩展,有的已经达到“非可维护程度”了。5 ~  ]0 H$ }9 o1 O8 I
这点上晨枫其实并不反对Machael Barr,只是在强调:理想很丰满,现实很骨感。
. Q' b+ E- V+ Q+ u; f3 C
4 C, _9 D; K6 Z1 R0 U& e6 F; |第二个问题:全局变量过多。# v8 Q% [* s4 O, @
无论多么深层的子程序,全局变量都可以拿起来就用,很简捷、高效,不是吗?就像[萨苏]主持的节目《国宴》中提到的一件事:周总理直接打电话给礼宾司的一名小职员讨论宴会坐次安排问题。但是这么做绝对是有前提条件的:一是要求负责人有很高的智力水平和责任心。周恩来一杆到底没问题,林彪这么做效果也很好,但到了常凯申那里就是杯具。二是环境恶劣场合不适用,想一想周恩来一个电话打给余成则会怎么样。回到丰田车载控制系统,这两条全占了。环境恶劣是一定的,汽车发动机舱内可没有空调间,火花塞那个高压放电呀一分钟好几千次。至于丰田那帮developer的心智水平,比之于常公那又是差了十万八千里。
: K2 M8 d! K* u0 u* _& _. J- L+ B( o& f2 R; a. S( L, U
所以一般人写程序,最好学余成则们,尽量保持单线联系。而全局变量过多,往往会给各个模块之间带来一些不必要的横向联系。这不仅造成程序运行时结果不可測,就是开发本身也困难重重。设想一下,航天部某研究所承担嫦娥三号任务,一天) P" T0 V$ G5 r2 ~+ b+ D% u
领导:小王,你那月球车遮阳板做得怎么样了?8 B- Q- U0 A  n& W
小王:做好了。1 S  |/ H- \2 \% j" @8 A
领导:很好。抓紧时间赶快测试一下。& v6 I7 q0 g4 q9 n0 Z
小王:月球车的其它部分还没有做完你让我怎么试?
" X9 v* D: s% a: ~+ k" ], Q* u' Y领导:?@#%¥&!!!, Y1 ?* E2 f, G. C

$ z! R, T! `7 r8 P  V# _可笑吗?很多spaghetti程序就是这样,写出来后模块A等模块B,模块B等模块C,根本无法单独测试。等到所以模块都出来后放在一起才发现糊成一锅粥了。4 Q' d& [" a$ s
* I; V+ |5 j7 Q& c
说了怎么多那么我们如何面对晨枫难题,即控制软件所需要的大量数据。这里晨大显然将全局变量(global variable)与全局数据(global data)混在一起了。全局变量可以承载全局数据,但全局数据并不一定依赖于全局变量,没有全局变量一样可以获取全局数据。以C语言为例,可以定义一个结构(struct)涵盖全局数据区的所有数据,然后把这个结构的指针作为参数传递给子程序。如果数据真的很多很复杂,还可以考虑轻量级的in-memory数据库(不是所有的数据库都像Oracle、DB2那样巨无霸),保证酸味(ACID)纯正。
5 |$ O" H; k9 \8 L! X+ n' Q% D" Q/ q6 |: c( m
第三个问题:违反丰田和行业的编程规范。$ {2 t* z9 m  B
没啥好说的。自己定下的编程规范自己不遵守,那么编程规范就是糊弄外人的样子货。
+ e: m& F8 Q* o) R
8 z) t8 X7 @# V! P% O: `# e第四个问题:使用递归。" F0 ?8 v0 `9 a  y; `
递归简单地说就是函数自己调用自己。这种表达很简洁,在自控的理论研究中也多有采用。但在计算机中运行时会大量消耗系统资源。解决方法不是晨大所提出的调用计数器(有限层调用也会消耗不少堆栈资源,而且计数器本身恐怕也要靠全局变量实现),而是应将递归(recursion)问题先转化成迭代(iteration)再写代码。) P9 t2 d& O& l5 R* Z* |4 Q: u+ q

; U# K) Q2 |2 i3 ~" z9 g$ ^* ?第五个问题:缺乏关键变量出错保护。- O! ]% l2 w9 Q# ^* h8 b8 p  J
晨文中的变量保护与原文说的是两回事。晨文讨论的是传感器给我坏数据怎么办;原文关注的是好数据在内存中放着放着自己就坏了怎么办。一般人可能不理解,数据又不是草莓,还能放着自己就长霉不成?这个嘛需要一点内存的小知识。现在计算机的内存基本上都是SDRAM了,速度越来越快,DDR、DDR2、DDR3,一路走高。SDRAM中的D代表的动态(dynamic ),什么意思呢?先来一段小电影: L. c& S) y" K( ]3 O! e8 I/ z# b
(某宣传科)领导:小张,今天的好人好事很多,你都记下来,别落下什么。
. P2 z! d4 @+ Q8 M小张:好的。给我... .../ R( d# C% r" b0 `( m1 p( a6 ^
领导:什么?% U+ v! F; T- h2 x9 x/ G! R
小张:纸呀。另外墨水也用光了。
1 _/ d  x- ~0 S; }领导:奥,现在经费紧张。你先毛笔蘸水写在地板上吧。
- x' }& e) I6 z, w2 b! |1 e小张:那一会水一干不就什么都没了?
/ `/ @3 F2 Q' f  E2 w5 F) l领导:嗯,这是一个问题。这样罢,你趁着水没干之前再抄一遍。0 X0 V* `) X, @, {4 o5 T4 w
小张:@#%¥&!!!# @. V" u/ Z  A/ S, n+ x
) x2 N4 a1 }1 ?6 V8 S1 R" N1 s
现在的内存就是这样,只不过内存的重抄的速度比小张快多了,是毫秒级的,也就是说一秒钟要成百上千次。饶是电子电路很可靠,抄多了也难免抄出错来。这时需要对数据加上校验,而且校验码短了也不行(像2008年款凯美瑞),因为短了能发现错误但改正不了。另外这些都是用硬件实现的。不存在晨文中的耗用CPU和I/O时间的问题。  u1 O9 \. s% G2 }  {
+ \6 N3 A5 p* g0 v) Z; N
第六个问题:对操作系统的代码没有深度审核,选用的操作系统没有通过认证。1 F( |9 l" D: d9 c% B
晨枫承认丰田使用未经认证的系统不应该。但是否对操作系统的代码进行深度审核,取决于任务的关键性和操作系统的复杂性。美国海军使用Windows NT,但火控系统的核心肯定还是固化在电路上,不能Windows一蓝屏导弹连烧火棍都不如。另一方面,丰田使用的操作系统也没有那么复杂,丰田完全雇得起像Michael Burr那样的人对其操作系统进行深度审核,但丰田没有做。
" b/ u4 V2 C7 `! R8 Y  J' `
% G4 N% \. a4 ?先说第八个问题:内存分配容量不足,位置不合理,堆栈溢出造成任务分配表错乱。/ _( `0 W' [8 W2 Z( g4 E4 L0 H+ P
这个问题上十有八九也是晨大错了。如果有时间读一下任何一个系统启动(boot)代码,就会知道刚开始的时候计算机内存里那真是白茫茫的一片,哪里是堆( heap)、哪里是栈 (stack)、哪里是任务分配表全是人指定的。CPU对所有内存一视同仁,并不会每走一步都做边界检查。这就是为什么“堆栈溢出”是病毒入侵的一种常用手段。所谓“堆栈溢出”入侵,指的是说病毒将恶意代码作为数据写到栈上(当然要写过界),然后过一会儿(最常见是函数返回时)CPU把这些过界的数据捡起来当代码执行。龙芯二号能够给压入栈的数据打上不可执行的记号,这已经是超过包括intel在内其它CPU的反病毒技术了。一般CPU的堆栈保护确实像[江城如画里]说的那样,就是设立专门的栈段,然后靠段自己的保护机制进行总量控制。如果设置合理,虽然并不一定能防住病毒入侵时的局部溢出,但至少也不会错误地写入与之相邻的任务分配表而不知。段保护的前提是要求CPU要有MMU(内存管理单元)。可是嵌入式系统不比家用PC机,许多CPU并不具备MMU。这样的CPU行为上更像8086,而不是80386或其后续产品。与之对应软件上要做相应的考虑,比如不能使用常见的linux,而要用它的变种μClinux。无论出于历史原因还是成本考虑,凯美瑞使用不含MMC的嵌入式系统的可能性都很高。不含MMC的系统肯定便宜。至于历史原因嘛,就像晨大所说工业界偏好“貌似老爷的系统”,早期的嵌入式系统大部分不含MMC,丰田用熟了很可能继续用下去。从那个4KB大小的堆栈我们也能看出一些端倪,这玩意儿恐怕不是什么高端大气上档次的东西。; H$ {8 q. C( P0 x3 J3 O0 X

5 c/ J/ j) t. o  b: H/ y7 a1 U问题七、九、十、十一放在一起谈。  A. M8 U. ?% x9 n1 d
上文说到计算机的内存不可靠,那么CPU就百分之百地可靠吗?这里我们要像余成则那样思维:朋友不可靠,父母不可靠,老婆也不可靠,一切都要有后备方案。设想一下CPU中的程序计数器(PC)出了意外会怎么样。不用多了,就是多加一个1对多字节指令也是灾难性的(把我的名字读成冗宝,这还了得)。下面呢,CPU发狂暴走,不知奔向何方,俗语说就是程序飞了。watchdog的作用是把跑飞了的程序圈回来。具体做法:在程序中嵌入对watchdog定时器清零(俗称喂狗)的指令,如果程序出现故障无法在规定的时间里执行清零指令,一旦watchdog定时器计满则触发系统重启。由此可见,用硬件时钟“喂狗”是荒唐的,但不是晨枫所说的那种荒唐。程序跑飞时不是时钟死了,恰恰相反,因为时钟活着程序才能越飞越远。丰田工程师的错误,就像潜伏时组织要求晨枫每月十五号到二十号晚上六点半到七点半之间拨打一个特殊号码,结果晨枫带着智能手机穿越回去搞潜伏。手机上一设定时,电话是每月按时拨出去了,但打电话的目的也就落空了。( Z8 D7 D& U5 W

6 i& B3 w% ]1 f& |5 k所以watchdog是用来应付意外情况的,例如内部出现叛徒,组织遭到严重破坏。潜伏人员工作负担太重的问题应该另外有人负责(第十个问题)。做地下工作,领导的主要任务是搞好组织建设,而不是像Task X那样亲自动手干脏话、湿活(第九个问题)。即使领导出现了意外,也要保护好党员名单,不要让它流失或落入敌手(第七个问题)。
; x. [$ X5 z8 N- a- @: J
! Y( P) |- K$ q/ h) `关于系统重启,晨大反复给我们描述了其可怕的场面。我不禁在想这会不会是晨大为了加强他的论点而故意执片面之词呢。我并不否认一个连续控制系统一切从零开始的种种危险。但重启就只有这样吗?如果说晨大对计算机有可能不熟的话,那么做了那么多年自控对PLC应该再熟不过了。以西门子S7-400为例,启动可分为冷启动、暖启动和热启动:2 `0 G  m8 s! Q' {, G* v
冷启动:所有的数据(过程映象,位存储器、定时器和计数器)都被初始化,包括数据块均被重置为存储在装载存储器(Load memory)中的初始值,与这些数据是否被组态为可保持还是不可保持无关。首先执行启动组织块OB102。
+ {5 f; |3 J$ V& s0 \1 r
8 m8 d' ~$ n5 m( `: m5 `暖启动:从程序开始处以系统数据和用户地址区的初始设置开始进行程序处理(非保持性定时器、计数器和位存储器复位)。保持的标志存储器、定时器和计数器以及数据块的当前值保持(仅当有后备电池)。OB100中的程序执行一次然后循环程序开始执行。6 v% q/ f, M9 @8 k( Y( v* z

1 R9 ~5 ~; ?7 X3 ^* G% l热启动:在程序中断处重新开始执行程序(不复位定时器、计数器和位存储器)。在启动时所有数据(标志存储器、定时器、计数器、过程映像及数据块的当前值)被保持, OB101中的程序执行一次。然后程序从断点处(断电, CPU STOP) 恢复执行。

9 Y6 b* L" t; H) s这里不是讨论PLC,而是说一个嵌入系统的重启也可以类似地分成三类。晨大描述的可怕场面对应的是冷启动,而watchdog触发的重启则很有条件相当于热启动。由此我们也可以再次体会到保护系统关键数据的重要性(第七个问题)。/ k3 z, `- Q9 d6 C
. y% z3 o+ C  f
第十二和第十三个问题
6 X' I& w8 k8 O$ |# s7 d+ ?3 Z7 I6 eBrake Echo Check是以上各种措施失效后的救命手段。那么这个救命手段什么时候能运转起来呢?change of state。丰田工程师的思路,就像奥巴马总统最喜欢唱的那首歌《除了change我们谁也不认》。什么是change呢?司机的脚不在刹车踏板上到踩下刹车是change;踩在刹车上的脚离开刹车踏板也是change ;而踩在刹车上的脚死死地停在刹车踏板上就不是change。所以如果汽车暴冲前你的脚是踩在刹车上的,那么暴冲后你的正确做法是把脚挪开刹车踏板片刻,好让Brake Echo Check发挥作用。我相信,这对舒马赫不难。" L; H1 t, r$ B. Z1 n2 r

  R6 p$ ?& z% w5 Y$ k) e% l第十四个问题
0 F9 U6 t7 o/ Q# k7 n晨大说得太多了。一句话,凯美瑞是家庭车。所有mutually exclusive action的cross check按普通人考虑即可。如果玩不了heel and toe,那人不应该抱怨丰田,而是该另买一辆车。7 |# k3 Z5 G3 s' ~4 A

" L& K9 V3 y0 _; I+ p7 H# B) R第十五个问题:节气门大开导致刹车助力不足。
) e6 y# z7 D  k! P! u6 a8 q, S; b后来晨枫也承认刹车的真空助力确实和节气门有关。谁要还对此持异议,可以自己试一下。把车开上高坡,变速器到N(neutral)档,发动机熄火,汽车借重力加速下滑,到60km/h再开始踩刹车。事先声明,出事与我无关,拒绝追杀。
* q6 M3 o9 V" M. j8 K, C  B1 @# x  D9 K# p1 Z
总结:晨枫对Michael Barr的批评站不住脚。也许化工领域有特殊性,但给嫦娥三号编程会遵照Michael Barr的原则而不是认同晨枫的观念。至于丰田车该站哪一边,大伙看着办吧。
作者: 意广才疏    时间: 2014-1-30 16:46
实在抱歉啊,月头的时候刚刚把自己的新东西上线,以为可以空闲两天,不料当晚就听到坏消息,马上要为保卫整个项目的存活而奋战了 …… 三周下来,结果是奋战失败,项目被砍,自己也要被分流到其他项目去,也算是新年新气象,6 R$ w, J" g9 m: o  L$ B
) F3 D3 E. K* n2 c' a
不过我想指出来的具体问题,沉宝同学貌似已经说完了,重复一遍也无必要。简单说一句吧:我看过很多来自不同行业、有不同背景的技术人员,在需要为自己的领域书写代码的时候,喜欢强调各自领域的特殊性或是复杂性,试图说明某些软工原则在此并不适用。但按照我的观察,那些说法很少成立,而且大多出自对软件工程的不理解。这里晨枫同学对全局变量的说法,算是一个典型的例子。
作者: 晨枫    时间: 2014-1-30 22:07
本帖最后由 晨枫 于 2014-1-30 12:33 编辑   N3 I+ D8 A( {: v5 ^8 Z" R1 ]( w8 L
意广才疏 发表于 2014-1-30 02:46
1 ~$ `! z6 f! j/ r; E4 [# J$ G实在抱歉啊,月头的时候刚刚把自己的新东西上线,以为可以空闲两天,不料当晚就听到坏消息,马上要为保卫整 ...
5 g8 W7 Y0 j0 m( K

5 A0 q; B" S! Q. b; R; P4 X" o" V4 T简单说一句吧:
4 |6 |; @, R# D8 |( `1 ?8 P. ^) R
我看过很多来自不同行业、有不同背景的技术人员,在需要为自己的领域书写代码的时候,喜欢强调各自领域的特殊性或是复杂性,试图说明某些软工原则在此并不适用。但按照我的观察,那些说法很少成立,而且大多出自对软件工程的不理解。这里晨枫同学对全局变量的说法,算是一个典型的例子。
$ }, g8 S: A5 K. f( S0 s

; A, E2 ?6 C! z& }3 ^. E4 A这其实对很多事情都适用。比如说,楼上沉宝同学真的清楚冷启动、暖启动、热启动的实际影响吗?这可不是手册上说的那么简单。我没有质疑软件工程原则适用性原则的意思,我只是对教条主义式地硬套原则有不同看法。在可能的情况下,应该遵照软件工程原则,这不是问题。问题在于对于现有的并不完全符合软件工程原则但已经存在而且有效的复杂软件怎么办的问题。推到重来很好,但不是总是可能的或者现实的。
& f7 S& {0 j( a: [8 G
; c+ P# s0 V7 k: N% }' {对于全局变量,可能我的理解有误。但就DCS而言,不是通常的变量围绕程序而指定,而是反过来,程序围绕变量而编制。不知道我们是不是能够在这一点上统一看法?
作者: 意广才疏    时间: 2014-2-26 12:53
晨枫 发表于 2014-1-30 22:07 ' d+ @( [9 @4 u; \* [
简单说一句吧:
0 p  Y- \* M  }* {7 Y# a$ h
沉宝同学的情况不了解,我自己确实不清楚化工自控里冷、暖、热启动的实际影响,晨枫你若有空闲不妨科普一下?
" x2 b/ l% k$ m& v8 v+ r* K: J/ ]
关于软件工程原则稍微说两句。确实如晨枫你所说,很多时候由于各种现实情况,我们不见得总是能够教科书式地遵守一些软件工程原则。进度压力、平台限制、人员配置情况,这几个算是典型的影响因素。但这并不代表说我们就可以干脆向这些现实情况举手投降。那些原则被总结出来,是由于许多实践证明了遵循这些原则能够带来巨大的好处。所以在充分理解现实的情况下,我们应该做的是尽量创造条件逐步改进这个现实。全系统推倒重来或许不现实,但我们可以设法划清界线,让新系统、新模块能够以比较健康地方式构造起来,老系统或许也能做些有益的改进和重构,而不是直接摊手啥也不做,甚至继续在已经不太健康的系统上继续叠床架屋,把问题越滚越大。你说对吗?
* X6 ]1 H1 F4 x/ j! k" T9 @/ l3 `4 m5 K0 J' Z0 V. O
再来说全局变量这个事。我没干过自控,DCS 程序应该以逻辑还是数据为中心我没有发言权。但不论那种情况,直接使用全局变量都是不合适的:你可以有全局的数据,但并非一定要用任何代码都能随意读写的全局变量来表示和保存。这里沉宝说得很好:: r3 i. T+ y( W9 U3 M0 o
: w; H2 M5 X- M1 u6 h0 s
全局变量可以承载全局数据,但全局数据并不一定依赖于全局变量,没有全局变量一样可以获取全局数据。以C语言为例,可以定义一个结构(struct)涵盖全局数据区的所有数据,然后把这个结构的指针作为参数传递给子程序。如果数据真的很多很复杂,还可以考虑轻量级的in-memory数据库(不是所有的数据库都像Oracle、DB2那样巨无霸),保证酸味(ACID)纯正。

作者: 冰蚁    时间: 2014-2-26 13:05
意广才疏 发表于 2014-2-25 23:53 7 L" K1 ~/ \" {$ e
沉宝同学的情况不了解,我自己确实不清楚化工自控里冷、暖、热启动的实际影响,晨枫你若有空闲不妨科普一 ...

% Y8 L- u+ h8 u; d" @3 c够呛,有益的改进和重构自然很好。但估计多数情况大概就等于推倒重来,而且不知道重来后的 bug 情况如何。




欢迎光临 爱吱声 (http://www.aswetalk.net/bbs/) Powered by Discuz! X3.2