Email

contact@futurespace.cn

电话

(86)010-82949816

“测试驱动开发”方法在“星舰”研发中的应用分析

2024年11月,“星舰”(Starship)系统完成了第六飞,标志着第一代“星舰”研制任务已经完成,第二代“星舰”的研制将在2025年启动。从2019年那个简陋的“星跳”(Starhopper)到2024年震撼世界的“筷子回收”,“星舰”不仅是史上运载能力最强的可重复使用运载火箭,其研制效率也是最高的。回顾“星舰”的研制历史,其研制过程具有三个鲜明的特点:一是产品设计是随着飞行试验逐渐递增和优化的;二是通过多型原型机以飞代试,缩短产品研发周期;三是生产线、测试线与产品同步研发。其快速研制模式得到了美国国家航空航天局(NASA)、欧洲航天局(ESA)、美国联邦航空管理局(FAA)等机构的认可。在2021年,美国太空探索技术公司(SpaceX)成为美国“阿尔忒弥斯”(Artemis)登月计划中载人登月舱的唯一中标商,管理水平得到了最高分“杰出”评价。如果说马斯克是在用写代码的方式研制“星舰”,那他采用的还不是传统的软件开发方式,而是“测试驱动开发”这种极限编程方式。“测试驱动开发”是20世纪90年代诞生的一种轻量级软件开发方法,要求在开发功能代码之前先编写测试代码,通过不断测试来推动整个开发的进行。在软件工程中,项目可以分为两类:一种是确定性项目,一切都已经确定,可以使用传统的瀑布式开发流程;另一类是实验性项目,充满不确定性,哪怕只是十分微小的变化也会牵一发而动全身。“测试驱动开发”就是为了第二类项目而提出的解决方案,尤其适合那种需求不确定、复杂性非常高的项目,越复杂的项目越能够享受到“测试驱动开发”的好处。“测试驱动开发”不是一种测试技术,它是一种设计技术,也有人更愿意称它为“测试驱动设计”,因此,本文尝试从软件开发领域中的“测试驱动开发”的视角,分析SpaceX公司通过频繁的“测试”逐步获得“星舰”这个超级复杂系统的设计过程。
1 “测试驱动开发”的内涵
20世纪70年代,瀑布法在软件开发行业得到了广泛应用,一时成为了软件开发的主流,随着软件规模不断扩大和复杂性逐渐提高,瀑布法无法解决复杂项目的各种不足,促使人们寻找更加真实有效的方法,于是90年代诞生了各种轻量级开发方法,这些方法致力于向用户快速提供高价值产品,使得小团队也能够在激烈的市场竞争中存活下来并取得成功,其中就包括了“测试驱动开发”。该方法是一种用来设计代码并调整其结构的技术,促使开发者在代码规模不断增加的过程中依然能够写出简洁的代码并对其所写的代码更有信心。“测试驱动开发”的具体过程为:一是把软件的大目标拆解成多个小目标;二是根据当前小目标,快速新增一个测试;三是运行所有的测试,发现新增的测试不能通过;四是编写刚好够用的代码(最简单的方式)使新增的测试通过;五是运行全部的测试,如果失败则修改代码,直到通过全部测试;六是重构代码;七是循环执行步骤二至步骤六,直到实现软件的大目标。“测试驱动开发”的基本流程如图1所示。让我们深入分析一下,如此简单的工作流程,是怎样把编码、测试、优化三个核心融为一体,以及SpaceX公司是怎样应用于“星舰”的开发过程中。
图片
图1 “测试驱动开发”的基本流程
大目标拆解成小目标步骤一表明“测试驱动开发”方法采用了“增量开发”这种顶层设计理念。“如果目标遥不可及,他们会努力开始完成通往成功道路上的每一个细小步骤”,“测试驱动开发”将这种理念作为一种方法学继承了下来。在“测试驱动开发”过程中,当大目标还不明确的时候,先把大目标拆解成小目标,把大测试拆成小测试,编程问题越艰巨,每个测试覆盖的范围就越小。每通过一个测试,就向成功迈进了一步,再让下一个测试通过,然后再下一个,再再下一个……以此类推,通过测试来推动整个开发的进行。马斯克充分估计到了“星舰”系统的复杂性,因此并没有像猎鹰-1(Falcon-1)火箭研制那样,在设计阶段就确定全部技术方案,然后通过一次飞行试验考核全部的关键技术,而是把整个“星舰”的设计分成了多个小目标:大型箭体结构、着陆支腿回收、级间分离、舵面控制、筷子回收、再入热防护、在轨加注等关键技术。同时他也把“星舰”系统的“发射回收”这个大测试,依照飞行顺序拆分成多个小测试:组合体起飞、级间分离、上面级入轨、助推器回收、上面级回收、在轨加注等关键测试,每通过一个测试,就向“星舰”的全部设计更近了一步。事实证明,“星舰”的复杂度的确值得这样细分,例如,当“星跳”原型机150m回收试验成功后,下一个原型机MK1便具有了完整的“星舰”外形(具有头锥和翼面),随即发现了连地面增压试验都没有通过,于是立刻改成类似储罐外形的SN1~SN4依次通过各种地面试验。同样,苏联N-1火箭(采用30台NK-15发动机并联)虽然首飞时就完成了全部设计,然而直到第四次飞行试验才基本完成一级飞行任务,充分说明了大火箭的研制难度。把小目标转换为小测试步骤二这个环节最考验设计水平,如果在这个“转换”过程中发生了错误,就会导致“即使通过了所有的测试,仍然没有达到预定的目标”的现象,同时也要防止过度测试,即“测试”可以做得像脚手架一样难看但实用。传统航天对每个关键技术都要开展大量的测试,但通常局限于地面测试,但地面终究与真实的飞行环境具有一定差距,谁也无法保证“通过了地面试验,上天就没有问题”,这也是传统航天工程一直以来的难题。SpaceX公司对关键技术的验证,不仅安排地面测试,还通过原型机进行“上天测试”,确保了“通过测试就能达到预定的目标”。例如,为了验证“猛禽”(Raptor)发动机、导航系统、新型着陆支腿等关键技术,设计了简陋的“星跳”,虽然看起来根本不像一枚火箭,但却达到了在空中验证的目的;为了验证上面级的回收技术,设计了被网友戏称“飞天茅台”的原型机进行多次高空试飞(没有头锥和控制翼,这些在低速飞行时没用);为了验证隔热瓦的贴敷性能,不仅通过地面测试,还直接粘到“星跳”、SN系列原型机乃至“货运龙”(CargoDragon)飞船上,在真实的飞行热环境中考核;为了验证控制翼面的效率,不仅进行仿真分析和风洞试验,还直接在全尺寸原型机试飞中收集数据;为了验证级间分离技术,通过组合体原型机的多次试飞进行空中验证;为了验证在轨推进剂加注技术,直接在组合体第三次试飞中搭载头部贮箱到主贮箱的推进剂转移试验。最小集实现步骤四要求用极其简单的方式编写代码,只要满足测试通过即可,不会因为“万一要用到”或“马上就得写”之类的理由去编写多余的代码,或者根据虚构或臆想出来的需求而编写复杂的代码。这种方式能够确保代码简洁、避免冗余设计,进而加速开发过程。传统航天对于一项关键技术通常会同时考虑多种解决方案,然后通过大量的计算和分析选出最合适的方案。而SpaceX公司强调从不完美开始,避免“寻找最优解”而影响整体研制进度。正如马斯克所说:“首要的是一个可行的方案,然后再优化”。例如,当SpaceX公司决定采用不锈钢贮箱时,先使用壁厚为14mm的不锈钢通过“跳虫”原型机测试,然后在全尺寸原型机中把壁厚减到4mm,未来将进一步优化到2mm以内;最初的“星际工厂”使用了低成本、可快速拆卸的帐篷,虽然灰尘飞扬,但是满足了最低的生产装配需求且容易调整;“星舰”的发射台一开始也没有设计导流槽和喷水系统,首飞后才加固底部结构和增加喷水系统;发射塔更是采用了极简设计,易于快速修复。回归测试步骤五表明不仅要通过新增的小测试,而且要通过以往的全部测试,如果测试没有通过,则继续修改代码,直到通过全部测试,因此该阶段的测试可能进行数次。这种“回归测试”,对产品代码提供了一个保护网,任何一个由于代码的改动而导致系统其他部分产生的异常,“回归测试”会立刻通知,而不用担心会产生什么不可预知的副作用,使得代码修改变得异常简单,可以轻松地迎接需求变化或者改善代码的设计。SpaceX依次建造了“星跳”、MK系列、SN系列、S系列原型机,这些原型机不是传统意义的火箭原型,它们只是阶段性的设计结果,一次原型机试飞作为一次“回归测试”,能够快速地对前期开发的全部技术进行真实的考核。预测到一次试飞未必能通过,因此针对当前“回归测试”的原型机通常准备多台,如果第一台原型机试飞没通过,那么立刻改进下一台原型机,并再次试飞,直到通过当前的“回归测试”,并立刻报废剩余的原型机。例如,针对高空飞行试验准备了SN8~SN16原型机,当SN15返回成功时,剩余的SN16立刻报废。SpaceX在每次原型机试飞后都会进行数千项微小的改进,既有地面改进,也有箭上改进;既有新技术,也有以往设计的优化;既有性能极限的探索,也有可靠性的验证。但是SpaceX不用花很多时间来分析这些微小的变化会对其他系统带来哪些负面影响,而是直接通过下一台原型机试飞进行全面而精准的考核。这种回归测试,使得产品技术状态的更改变得非常容易,研制过程不再是“一锤子买卖”,即使完全搞错了方向,后面也有很多机会发现和弥补。例如,当SN-8原型机的自生增压出现问题,SN-9原型机立刻变更为冷氦增压,并还要继续研究更适合的增压方式;为了提高载荷效率,级间分离方式从旋转冷分离改为热分离方式。重构代码步骤六明确给出了“重构”的时机,用来消除重复设计,优化设计结构,而不用像其他软件开发方法中那样没有固定的机会,常常随机地启动“重构”。在重构过程中,不仅要优化产品代码,也要优化测试代码,甚至淘汰过期和重复的测试。淘汰测试通常有两种情况:一是代码已经被重构,这个测试已经被新的测试所取代,也就是“过期了”;二是由于其他的测试涵盖了该测试的内容,因此“重复了”。传统航天在飞行器首飞成功后,才开始优化设计,而且为了确保首飞成功,做了很多冗余设计,因此,在批量化生产之前还需要进行多轮优化和飞行验证。“星舰”在研制过程中就开始不断优化,然后通过持续的原型机试飞进行验证,当“星舰”研制结束时,其性能也将达到最优,可以直接量化生产了。原型机的报废也有两种情况:一是技术方案更改了,该原型机已起不到测试作用,即“过期了”。例如,当MK-1进行贮箱加压测试时,由于焊缝强度不足发生破裂,被认定为不适合飞行时,与MK-1工艺类似的MK-2也直接退役。再比如,当SN8~SN11原型机高空返回时相继坠毁,采用类似设计的SN12~SN14也全部报废了。二是测试目标已经达成,该原型机的测试任务将被下一系列的原型机所覆盖,因此该原型机“重复了”。例如,当SN15进行10km级高空返回试验成功后,与它设计类似的SN16一并退役。
2 “测试驱动开发”的技术要求
自测试“自测试”是要求软件设计人员自己动手编写测试程序。这样的好处有三点:一是不用浪费很多时间去等待别人编写测试程序;二是由于编写测试和编写代码的是相同的程序员,因此降低了理解代码所花费的成本和时间;三是由于测试代码与功能代码紧密相连,当功能代码发生更改时,测试代码也会相应地更新,从而确保软件的稳定性和可靠性。“星际基地”是有史以来全球首个专门用于单一飞行器的商业太空港,集中布局了“星舰”的总装制造、测试和发射回收,优化了周转效率,实现了设计-制造-测试的高度集成。打造合适的测试框架“测试驱动开发”要求团队选择或者打造合适的测试框架,能够对微小的变化提供快速的反应。“测试驱动开发”的倡议者肯特·贝克(KentBeck)和埃里克·伽玛(ErichGamma)共同打造了Java语言的单元测试框架JUnit,正是这个简单而强大的工具,才让更多的程序员更加认可和信赖极限编程,从而掀起了敏捷开发的浪潮。SpaceX使用了数十架原型机作为测试框架,通过工业化、流水线的批量化生产方式来加快反馈速度、降低设计成本。公司从2012年开始研制“猛禽”发动机,到2022年,猛禽-2发动机的产能已经达到每日2台,满足了每台组合体原型机就要安装39台(33+6)发动机的需求;采用易加工不锈钢壳体,使得露天也能完成焊接和组装,事实上,正是在确定不锈钢方案后,“星舰”研发才真正驶入了“快车道”;使用工业级元器件代替航天级元器件、采用低成本隔热瓦等低成本控制策略,进一步降低原型机的成本;“星际工厂”(Starfactory)正在朝着每日生产1台“星舰”的目标不断升级改造。
3 “测试驱动开发”的优点
避免过度设计“测试驱动开发”能够通过持续向客户提供测试用例,在研制过程中就可以根据客户的反馈迅速调整设计,而不用在最后的测试阶段才发现错误而返工,避免了过度设计。传统航天在首飞前就完成了全部设计,如果首飞失败,很多关键技术和硬件因为飞行提前终止而得不到验证,造成了一定的浪费,而且在修改设计时,往往牵一发而动全身,很多前期的详细设计和硬件需要被抛弃或者修改,造成了很多的浪费。研制的系统越复杂,首飞的风险就越大,首飞的完成度就越低,浪费的设计也就越多。N-1火箭的前4次飞行都是完整的原型机,并且都携带了登月飞船,但是都没有完成一子级飞行,浪费了大量的经费和时间;而“星舰”的设计是逐渐完成的,每一架原型机的设计和配置都不会超过当前的测试目标,即使炸毁,也不会过度浪费。因此,对于复杂系统的开发,这种探索性的设计流程比常规流程更有效、更节约成本。降低忧虑传统的编程方式在编写完所有功能代码后,才开始测试,在漫长的编写过程中,让人有一种“不知道什么时候能够做完,担心有一长串的缺陷在等待我”的忧虑。而“测试驱动开发”把大目标拆分成小目标,只需关注当前测试和回归测试,一旦测试通过,可以很确定地看到这段工作已经结束了,及时解决潜在风险点,而不会越积越多,进而摆脱了那种“我看到了这个困难问题的开头,却看不到结局”的恐惧感。因此,“测试驱动开发”也被认为是一种在编程时克服恐惧的方法。传统运载火箭在正式入轨发射前都是没有过试飞的,所有未上天验证过的关键技术都可能是潜在的风险点,设计人员在漫长的研发进程中,竭尽一切可能,提前预防故障,并尽量使用成熟技术以降低首飞的风险性。而预研型号往往就投产了1发或2发,如果首飞失败,那么多年的工作将无法体现,导致研发压力很大,难免对试验成败充满敬畏、对技术状态变化畏首畏尾、对技术创新自闭保守。反观SpaceX把“星舰”这个大目标拆解为多个小目标,每次原型机试飞只需完成当前的试验目标,只有50%的把握也能试飞,即使炸毁也可以欢呼,研发过程更是充满激情、一往无前。生产效率高“测试驱动开发”把“测试”放在了“编码”的前面,即“测试”不仅用来检验代码的正确性,还用来指引工作的方向。一个关于“测试驱动开发”的经典例子是“砌墙”:老师傅砌墙时会先拉线再砌墙,垒砖的时候都是以这根线为基准的,而小师傅或者新手则先砌墙再拉线,如果发现墙不直再进行敲敲打打,流程的不同直接导致了工作效率和制品间的差异。“测试驱动开发”就像老师傅这样,通过先写测试,开发人员可以更加清晰地了解需求,避免不必要的返工,同时利用编译器强大而快速的纠错能力,迅速找到错误,比起人员走查代码,具有“快、准、狠”的优势,显著降低了开发人员的负担和难度,提高了开发效率。实现了“用最少的思考”来开发软件,在“测试驱动开发”过程中,没有任何一项工作是由冥思苦想得来的,这正是“测试驱动开发”的魅力所在。传统航天一直用尽可能多的地面试验来模拟实际飞行工况,试图让上天的问题全部暴露在地面并得到解决,但是系统之间相互作用的复杂性以及天地差异性很难在地面模拟出来,免不了大量的仿真和双想工作。而SpaceX公司用测试引领开发工作,通过一次原型机试飞直接找到设计存在的问题和薄弱环节,避免了大量的地面试验和冥思苦想,降低了研制难度,提高了研发效率。产品可靠性高“测试驱动开发”中,持续的“回归测试”使得“大部分时间里代码处于高质量状态”,产品可靠性高。马斯克在接受采访时说:“偶尔一次飞行成功是可能的,因此我们需要很多次飞行试验找到那些10%、1%的失败之处”。在“星舰”研制过程中,持续的“回归测试”使得通过某次原型机试飞的关键技术,仍然可以在后续的原型机试飞中继续验证,因此,在研制过程中就能拿到可靠性数据。而传统航天在预研阶段的目标是首飞成功,可靠性还需更多的飞行试验进行验证。测试集与代码一起完成在软件开发中,产品代码和测试代码都需要开发,传统软件开发是先开发产品代码,然后再开发测试代码,而“测试驱动开发”则是先开发测试代码,然后再开发产品代码,在编码过程中,就不断运行测试,因为产品代码还没有开发完,所以测试当然会失败,但是一旦测试成功,那么产品代码也就完成了,同时也形成了一套全面的测试集。传统航天在研制新型号时通常采用“预研-批量生产”模式,主要分为“预研”和“批量生产”两个阶段。在预研阶段,投产1~2个原型机,以最小的资金、简化的生产方式打造一款成功的样品;在生产阶段,再重新设计生产线和测试线,主要工作也由研制部门转移到生产部门。SpaceX公司为了降低原型机的成本,在“星舰”研制的同时,就着力打造生产线和测试线,在“星舰”逐渐完善设计的过程中,“星际工厂”也从帐篷逐渐过渡到固定式厂房,内部设施也逐步实现批量化、流水线生产。从组合体的“第一飞”到“第六飞”,由于每次原型机的设计都不是完整的,飞行试验结果注定是失败的,但是每次试验都突破了新目标,离最终的设计更进一步,一旦飞行试验成功,其生产线和测试线也就完善好了,可以直接量化生产。如果以“量产”作为评估节点,那么“测试驱动开发”是“毕其功于一役”,生产线、测试线与产品一起完成。给客户提供信心“测试驱动开发”在研制过程中就可以向客户提供测试用例,便于客户更好地理解和使用产品,也给客户提供了更多的信心。当其他竞标对手还在提交PPT和全尺寸模型的时候,SpaceX则可以直接用“星舰”原型机的试飞展示他们的投标方案。2021年“星舰”10千米级试飞阶段中,NASA的“载人登月系统”(HLS)评标团队几乎每次都现场观摩,亲眼目睹了4个月内四射四败却不断前进的“星舰”研发过程,对后续载人登月舱合同的选定产生了潜移默化的影响。2021年4月17日,NASA正式宣布SpaceX被选为美国“阿尔忒弥斯”登月计划中载人登月舱的唯一中标商。2024年11月19日,“星舰”组合体第六次飞行时,美国太空部队负责人参观了星际基地并观看了发射试验,表明了对“星舰”未来军事应用的兴趣。
4 “测试驱动开发”的局限性
对开发人员技术要求高不仅要求开发人员能够理解“测试驱动开发”这种“测试先行”的理念,还需要掌握“重构”“自测试”“设计模式”等敏捷软件开发技术。马斯克本科毕业时正处于互联网革命的大潮,当时涌现了各种轻量级软件开发方法。马斯克亲身践行了这种轻量级软件开发方法,成功开发了两款互联网软件,获得了初始资本。当他研制“猎鹰”“星链”“星舰”等航天产品时,自然地把各种敏捷开发理念(“迭代”“自测试”“重构”“避免重复设计”“简化设计”等)应用到产品开发过程中,相比传统航天的瀑布式开发模式,敏捷模式的应用使得SpaceX公司迅速崛起并打败了传统航天巨头。“星舰”的设计同样延续了敏捷开发的理念。例如,贮箱的柱段由相同的不锈钢环叠加而成,超重助推器的液氧和甲烷的贮箱完全相同,全箭仅使用一种型号的“猛禽”发动机;绝大多数的隔热瓦具有相同的形状和尺寸,利于批量化生产;通过高内聚、低耦合的模块化设计,既能满足快速装配原型机的需求,也能在原型机报废后便于拆解和复用,进一步降低试验成本。放慢开始的开发速度“测试驱动开发”同时开发功能代码和测试代码,并且在开发过程中频繁地进行回归测试,相较传统开发方式中最后才开发测试代码,会放慢开始的开发速度,特别对于要求快速开发原型系统的场景。但是,如果考虑开发速度需要包含功能和品质两个方面,那么单纯的原型开发速度不能代表整体的开发速度。“测试驱动开发”通过持续的回归测试,减少了后期返工的时间,整个过程都可以高质量地快速进行。SpaceX公司在2019年开始试飞“星跳”时,所有人都准备了漫长的观望,毕竟这个超级复杂运输系统的首个原型机竟如此简陋,根本不像一枚火箭,但是短短5年后就实现了惊人的“筷子回收”,整个过程稳扎稳打、步步为营,虽然“星舰”到目前还没有完成全部设计,但已经赢得了大量的订单和全世界的赞叹。反观采用30台NK-15发动机的N-1火箭,首飞时就完成了全部设计,到第四次飞行试验时,已经突破了最难的发动机群技术和气动外形设计技术,但仍然因前4次失败而终止项目,如果把“首飞”这个大测试再细分成多个小测试,结局会不会不同?
5 结束语
“测试驱动开发”是一种设计技术,更是一种组织所有开发活动的技术,它的价值在于“使团队能够在开发流程中简单而安全地调整技术状态”,因此也被誉为软件开发中的“圣杯”,让所有接触到“测试驱动开发”的人都能如狂风暴雨般地改变自己的开发方式。SpaceX公司通过“测试驱动开发”模式,给出了如何开发“星舰”这种超级复杂的航天运输系统的解决方案,当其他的商业航天公司正在着力打造一款能够吸引投资的火箭时,SpaceX公司已经走在批量化生产“星舰”的道路上了。

上一篇 下一篇