一、摘要
在对像自动驾驶系统之类的车辆中的软件密集型系统的开发中,缺陷通常只在对物理车辆进行试验时才被识别。与仿真环境相比,物理执行的操作无法暂停和调试关键代码段,也无法重现和重复错误的试验。此外,汽车内部的发展空间和能力受到限制。因此,最好离线分析在物理执行期间观察到的故障,并在模拟环境中再现故障试验。在模拟环境中进行重复是一项耗时的工作,但是对于将软件组件推向表现出错误行为的状态是必需的。
本文提出了一种在仿真环境中通过对软件系统的精确性进行序列化来重新执行故障状态的方法,并总结了该方法的实践经验。
二、出现的问题
自动驾驶的复杂性使越来越大的软件系统需要处理所有可能的情况。软件系统大小的增加会导致在运行时发生更多可能的错误,从而增加了用于测试和调试的时间。测试这种系统时,通常现在仿真环境中实现和测试软件组件的期望行为。但当系统部署到实际车辆上时,这种方法不再可行。此外,在大多数情况下,开发人员可能会在不调试所有程序的情况下完成整个系统的测试。因此,有必要收集数据以便在专家控制的环境中重现故障。
计时是很大的一部分,可能会改变内部状态。例如,结果可能取决于传入数据的顺序和时间。由于线程、文件访问等原因,此顺序和时间还会受到某些操作系统的影响。因此,需要一种方法来尽可能重现缺陷发生时的状况,并消除车内计算机硬件和操作系统的影响。
三、解决方法—序列化和反序列化软件组件
我们提出了一个框架,用于序列化软件组件的内部状态,以实现所呈现的大多数影响的解耦,这些影响会导致软件组件在一个周期内发生故障。
本文介绍的方法:
1、允许随时准确地再现软件组件的内部状态
2、只需要一个时刻的数据即可重现错误,因为在此时刻之前无需重播序列
3、通过取消通讯和循环执行来防止时序的影响
4、允许快速查找明显导致错误的执行
5、比捕获信号迹线需要更少的磁盘空间和开销
将软件系统的状态转移到离线模拟环境的过程需要两部分:
1、一种存储和恢复软件系统状态的方法
1.1、选择序列化格式
存储软件组件的状态意味着对其进行序列化。本文介绍的调试方法已应用于用C++编写的组件。此外,整个软件组件比通常通过网络连接传输的数据更为复杂,boost序列化就支持复杂的嵌套数据结构。它旨在持久化数据结构并在“另一个程序上下文”中重新创建它们。使用Boost序列化,可以使用命令将任何C++类序列化:
1.2、如何注释源代码以进行序列化
选择在头文件中声明序列化,但在单独的文件中定义他们的序列化方法,这种方法只需要添加三行代码,编译的增加也可以忽略不计。这三行代码包括:头文件、使用boost宏声明要导出的类、使用可扩展的自定义宏声明序列化。这种方法可以在单独的cpp文件中实现。
1.3、如何处理开发过程
创建初始序列化代码后,必须在开发过程中对其进行维护。其中有两个挑战。一个是计划系统不同版本之间的兼容性,Boost序列化通过版本属性解决了这个挑战,此属性允许序列化组件的早期版本,并将其反序列化为更高版本;要解决的第二个挑战是不正确的序列化功能,其中导致序列化功能崩溃的缺陷会自我发现,导致序列化不完整的缺陷,可以通过用测试套件比较有无序列化的自动驾驶系统的行为来检测。
1.4、何时以及如何触发序列化
我们有3中不同的序列化触发器:手动触发序列,可用于存储测试驱动程序认为值得进一步分析的状态。;序列化每次都会记录一条错误信息,如果系统识别出某些问题不起作用,则可以将记录的错误消息用作触发器;每个周期之前序列化。
2、离线模拟环境,用于分析存储的软件状态
2.1、轨迹规划器调试环境
我们主要使用一个独立的环境来重构Trajectory Planner的序列化软件组件。使用图形用户界面定义输入,每个软件组件都使用日志记录和可视化界面来发出有关其计算的信息。在独立环境中,用户可以精确地控制轨迹规划器的输入,包括模拟时钟的时间,并触发另一个周期的执行。由于受控环境和调试编译的存在,软件工程师可以使用标准调试软件逐步执行轨迹规划器执行的每一行代码。
2.2、模拟
在某些情况下,需要对特定情况进行全面分析。例如,如果计划路径与驱动路径之间的偏差异常大,则可能有必要分析在模拟环境中建模的传感器和执行器的已知不准确性是否可以解释这些偏差。在这种情况下,整个计划系统将在模拟环境中反序列化,而不是单个组件。
四、实践经验
本文介绍的调试概念应用于了一个奥迪的项目。在这个项目中,开发了一个计划和控制系统。对于每个项目,都有一个集成团队来执行由团队开发的计划和控制系统以及其他团队的组件。如果集成团队遇到一些不需要的行为,它会在bug跟踪工具中提交一个票据,并附加规划和控制系统生成的日志消息。通过基于序列化的规划组件重新执行问题情况,解决了几个问题。
除了错误报告,还在实车上执行了测试。对于每个测试日,我们存储序列化的计划组件并分析异常行为。这种不正常的行为不仅包括错误的行为,而且还包括我们未曾期望并且需要了解的模式。这将会发现影响规划组件性能但未导致明显错误行为的缺陷。
1、仿真结果
为验证本文提出的概念而进行的第一个测试是在模拟环境中运行它。 在仿真过程中,将在每个周期中对轨迹规划器进行序列化,所有的轨迹规划器输入都用ADTF HarddiskRecorde存储为一个信号轨迹。在七分钟的模拟中,Trailjectory Planner执行了多次停车操作。此时,ADTF信号跟踪需要812 MB的存储空间,而所有序列化的“轨迹规划”组件仅需要10 MB的存储空间。 单个“轨迹规划”实例的最大大小为0.016 MB。 因此,该实验证明了序列化概念比信号跟踪需要更少的存储空间。
2、案例研究
图2-6说明了从软件错误中找到故障并修复该故障的过程。




在进行集成测试时,集成团队注意到汽车行驶的平顺性不如平时。因此,他们将相应的日志文件发送给开发计划和控制组件的团队,以进行进一步的分析。我们首先从记录中包括的记录的加速度数据中识别出不舒适的驾驶情况,如图3所示。它描述了测得的加速度和由计划组件计算的加速度不一致。




分析计划组件在检测到的时间点写入的日志消息,表明计划组件假设错误的初始加速度,如图4所示。




然后从序列化的计划组件中,加载发出可疑日志消息的循环之前序列化的组件。该文件在图2所示的独立环境中反序列化。它可视化计划组件的当前状态,包括其当前位置,当前参考路径和接下来几秒钟的计划。
最后,使用附带的调试工具逐步执行计划周期,最终得到如图5所示的代码片段。所显示的局部变量值指示插值函数未返回预期结果。


在更正了相应的代码片段之后,可以使用更正后的计划组件执行相同的序列化状态。



五、讨论及结论
在本文中,我们演示了一种在离线测试环境中调试物理车辆试驾过程中发生的故障的方法。使用boost C ++库的序列化功能,我们可以完全保存并重新加载系统状态,然后重现观察到的错误,并在源代码中找到缺陷。
为整个计划系统维护序列化代码的初始和持续维护已被证明是有益的,因为测试驱动器主要用于数据采集和参数优化。此外,由于在测试驱动过程中持续保留最大的信息量以供专家进行进一步分析,从而避免了额外的测试驱动,从而加快了开发周期。
当前,序列化概念用于在类似PC的硬件上运行的自动驾驶汽车的计划组件。 目标是为在快速原型实时硬件上执行的控制系统实现类似的概念,这将带来更严格的限制。