ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

汽车电子测试实战:Robot Framework关键字驱动与HIL台架集成

汽车电子测试实战:Robot Framework关键字驱动与HIL台架集成 1. 为什么汽车电子测试圈开始流行 Robot Framework在汽车电子测试这个行当里摸爬滚打这些年测试工具的更迭换代我见了不少。早些年做 ECU 功能验证清一色是用 CAPL 脚本配合 CANoe 搭自动化序列后来有人开始用 Python 直接怼 Vector 的 XL Driver Library再往后 Jenkins 流水线一接大家又开始琢磨怎么把测试用例管理得更规范一些。大概从三四年前开始我陆续在几个量产项目里接触到Robot Framework一开始以为它只是个 Web 自动化工具毕竟它在互联网圈的名声基本绑在 Selenium 上面。但真正用下来才发现这玩意儿在汽车电子测试场景里其实被严重低估了。Robot Framework 是一个基于 Python 的关键字驱动测试自动化框架它的核心思路是把测试逻辑和底层实现彻底解耦。你写测试用例的时候用的是一套接近自然语言的表格语法而具体每一步做什么交给底层的关键字库去实现。这个特性放在汽车电子测试里简直就是量身定做——因为汽车电子的测试层级太杂了从最底层的 CAN/LIN 总线信号到中间层的诊断服务UDS再到应用层的 HIL 台架功能验证每一层用的工具和接口都不一样。如果没有一个统一的调度框架你最后就会陷入 每个测试环境一套脚本谁也不敢动谁 的尴尬局面。这篇文章我打算从实际项目出发把 Robot Framework 在汽车电子测试中的应用拆开讲清楚。包括它的架构设计思路、测试环境怎么搭、CAN 信号和诊断服务怎么封装成关键字、HIL 台架怎么联调、以及我在实际项目中踩过的那些坑。适合已经有一定测试基础、正在寻找自动化测试框架的汽车电子工程师也适合刚入行想了解行业主流工具链的新人。我不会讲太多教科书式的概念重点放在怎么用、为什么这么用、用了会怎样这三件事上。2. Robot Framework 的架构设计思路拆解2.1 关键字驱动为什么比数据驱动更适合汽车电子汽车电子测试有一个很明显的特征测试步骤的复用率极高。比如 唤醒 ECU 这个操作在诊断测试、网络管理测试、刷写测试里都要用到但每次唤醒之后的流程完全不同。如果你用数据驱动的方式把测试数据和测试步骤混在一起那唤醒逻辑就得在每个测试用例里重复实现一遍。而 Robot Framework 的关键字驱动模式允许你先把 唤醒 ECU 封装成一个关键字里面可以包含发送唤醒报文、等待总线响应、检查 ECU 心跳等多个底层操作之后所有测试用例直接调用这个关键字就行。这种分层封装的思路在汽车电子里特别重要因为底层通信协议经常变。今天用 CAN明天可能换 CAN FD后天部分信号走以太网。如果你的测试用例和底层通信直接耦合协议一换几百条用例全得改。但用 Robot Framework 的话你只需要改底层关键字库的实现上层测试用例一个字都不用动。提示关键字驱动不等于没有数据驱动。Robot Framework 同样支持测试模板和数据驱动只是它的核心优势在于把 操作步骤 抽象成了可复用的关键字。2.2 测试库的分层设计原则我在项目里一般把 Robot Framework 的测试库分成三层。第一层是通信层负责和硬件打交道比如 CAN 报文收发、LIN 调度表控制、以太网 SOME/IP 通信。这一层通常用 Python 封装 Vector 的 XL API 或者 Vector CANoe 的 COM 接口把底层 C 或 C 的调用包装成 Python 函数。第二层是服务层把通信层的函数组合成有业务含义的操作比如 发送诊断请求并解析响应、检查 DTC 状态、执行刷写流程。第三层是测试用例层用最接近自然语言的语法描述测试场景比如 验证 ECU 在电压低于 9V 时进入欠压保护状态。这三层分下来好处非常明显。新人来了直接看测试用例层就能明白测什么想了解底层实现再往下翻。而且层与层之间通过接口隔离通信层换实现方式服务层和用例层完全无感知。我见过有些团队把所有逻辑塞在一层里结果就是一条测试用例写了 200 行 Python 代码维护起来简直要命。2.3 与 CI/CD 流水线的天然契合Robot Framework 的输出格式是 XML天生适合对接 Jenkins、GitLab CI 这些持续集成工具。每次代码提交后自动触发测试生成 HTML 报告哪个用例挂了、挂在哪个关键字、当时的日志和截图都清清楚楚。在汽车电子项目里这一点尤其重要因为很多测试是要在 HIL 台架上跑的台架资源有限不可能让每个人本地跑。把 Robot Framework 集成到 CI 流水线后台架可以 24 小时不间断执行回归测试第二天早上来看报告就行。3. 汽车电子测试环境搭建与核心工具选型3.1 硬件在环台架的基本组成在聊软件之前得先把硬件环境说清楚。汽车电子的测试环境一般分三种纯仿真环境没有真实 ECU用 CANoe 的 Simulation 节点模拟、HIL 台架有真实 ECU但负载是模拟的、实车环境。Robot Framework 在这三种环境里都能用但配置方式差别很大。HIL 台架的典型组成是这样的一台实时处理器比如 dSPACE 的 SCALEXIO 或者 Vector 的 VT System跑车辆模型一台工控机跑 CANoe 或者 CANalyzer 做总线监控和诊断被测 ECU 通过线束和台架连接电源由可编程电源提供。Robot Framework 一般跑在工控机上通过 COM 接口或者 TCP/IP 和 CANoe 通信间接控制总线报文收发和诊断服务。这里有个关键点Robot Framework 本身不直接和硬件打交道它只是一个调度框架。真正干活的是它背后的测试库而测试库大部分是 Python 写的通过调用硬件厂商提供的 API 来实现具体操作。所以选型的时候首先要确认硬件厂商的 API 支持 Python 调用。3.2 工具链选型对比工具/库用途优点缺点Vector CANoe总线仿真、诊断、监控功能全面行业标准贵COM 接口偶尔不稳定Vector XL Driver Library直接操作 CAN 硬件轻量响应快需要自己封装协议层python-canPython 操作 CAN 总线开源跨平台对 CAN FD 支持有限udsoncanUDS 诊断协议栈纯 Python易集成需要自己处理底层传输Robot Framework测试调度和报告关键字驱动易读对实时性要求高的场景不适用我个人的建议是如果项目预算充足、团队已经用了 Vector 工具链那就用CANoe COM 接口的方案Robot Framework 通过win32com调用 CANoe 的自动化接口。如果预算有限或者只需要简单的 CAN 收发可以用python-can udsoncan的组合完全开源部署灵活。但要注意 python-can 对 CAN FD 的支持不如 Vector 原生驱动稳定如果测试涉及 CAN FD 高速刷写还是建议用 Vector 的方案。3.3 Robot Framework 安装与基本配置安装本身不复杂但汽车电子项目通常对 Python 版本和依赖库版本有严格要求。我一般用 Python 3.8 或 3.9太新的版本有些硬件厂商的库还没适配。pip install robotframework pip install robotframework-seleniumlibrary # 如果需要 UI 测试 pip install python-can udsoncan # 开源 CAN 和诊断方案 pip install pywin32 # 如果需要调用 CANoe COM 接口安装完之后建一个项目目录结构大概是这样project/ ├── testsuites/ # 测试用例 │ ├── diagnostic.robot │ └── network_management.robot ├── resources/ # 关键字库和变量 │ ├── common_keywords.robot │ └── variables.py ├── libraries/ # 自定义 Python 库 │ ├── CanLibrary.py │ └── DiagnosticLibrary.py └── reports/ # 测试报告输出这个结构不是强制性的但分层清晰方便多人协作。testsuites里只放测试逻辑resources里放公共关键字和变量libraries里放 Python 实现的底层库。注意自定义 Python 库的类名必须和文件名一致否则 Robot Framework 导入时会报错。这个坑我踩过当时排查了半天才发现是文件名大小写的问题。4. 核心测试场景的实操与关键字封装4.1 CAN 通信关键字的设计与实现先看一个最基础的场景通过 Robot Framework 发送和接收 CAN 报文。我用 python-can 来实现底层封装一个 Python 库然后在 Robot Framework 里调用。# libraries/CanLibrary.py import can import time class CanLibrary: def __init__(self): self.bus None def open_can_channel(self, channelcan0, bustypesocketcan, bitrate500000): self.bus can.interface.Bus(channelchannel, bustypebustype, bitratebitrate) return True def send_can_message(self, arbitration_id, data, is_extended_idFalse): msg can.Message( arbitration_idint(arbitration_id, 16), data[int(b, 16) for b in data.split()], is_extended_idis_extended_id ) self.bus.send(msg) time.sleep(0.01) def receive_can_message(self, timeout1.0): msg self.bus.recv(timeouttimeout) if msg: return { id: hex(msg.arbitration_id), data: .join(f{b:02X} for b in msg.data), timestamp: msg.timestamp } return None def close_can_channel(self): if self.bus: self.bus.shutdown()这个库封装好之后在 Robot Framework 里就可以这样写测试用例*** Settings *** Library ../libraries/CanLibrary.py *** Test Cases *** 验证 ECU 上电后发送心跳报文 Open Can Channel can0 socketcan 500000 Send Can Message 0x123 01 02 03 04 05 06 07 08 ${msg} Receive Can Message timeout2.0 Should Not Be Equal ${msg} None Should Be Equal ${msg[id]} 0x123 Close Can Channel这个例子里Open Can Channel、Send Can Message这些关键字都是 Python 库里定义的方法Robot Framework 自动把下划线转成空格读起来就像自然语言。测试报告里会详细记录每一步的执行结果包括发送的具体报文和接收到的响应。4.2 UDS 诊断服务的封装诊断测试是汽车电子测试的重头戏UDS 协议栈的封装比 CAN 通信要复杂得多。我用 udsoncan 这个库来举例它把 ISO 14229 定义的服务都实现了我们只需要处理传输层。# libraries/DiagnosticLibrary.py import udsoncan from udsoncan.connections import PythonIsoTpConnection from udsoncan.client import Client import isotp class DiagnosticLibrary: def __init__(self): self.conn None self.client None def connect_ecu(self, channelcan0, txid0x7E0, rxid0x7E8): tp_addr isotp.Address(isotp.AddressingMode.Normal_11bits, txidtxid, rxidrxid) stack isotp.CanStack(bus..., addresstp_addr) self.conn PythonIsoTpConnection(stack) self.client Client(self.conn, request_timeout5) self.client.open() def read_data_by_identifier(self, did): response self.client.read_data_by_identifier(int(did, 16)) return response.service_data.values[int(did, 16)] def write_data_by_identifier(self, did, data): self.client.write_data_by_identifier(int(did, 16), bytes.fromhex(data)) def read_dtc_information(self): response self.client.get_dtc_by_status_mask(0xFF) return response.service_data.dtcs对应的 Robot Framework 测试用例*** Test Cases *** 读取 ECU 软件版本号 Connect Ecu can0 0x7E0 0x7E8 ${version} Read Data By Identifier 0xF189 Log ECU 软件版本: ${version} Should Not Be Empty ${version} 清除并读取 DTC Connect Ecu can0 0x7E0 0x7E8 Clear Diagnostic Information ${dtcs} Read Dtc Information Length Should Be ${dtcs} 0这种写法的好处是测试工程师不需要懂 Python 和 udsoncan 的细节只要知道 UDS 服务的作用就能看懂测试用例在做什么。而且测试报告里会记录完整的请求和响应数据出了问题直接看日志就行。4.3 HIL 台架联调的关键配置HIL 台架和纯仿真环境最大的区别是被测 ECU 是真实的响应时间、总线负载、电源波动都会影响测试结果。Robot Framework 在 HIL 环境里跑的时候有几个配置项需要特别注意。第一是超时时间。真实 ECU 的响应时间受软件调度影响可能比仿真环境慢很多。Receive Can Message的超时时间不能设太短我一般设 2 到 3 秒关键诊断服务甚至设 5 秒。第二是总线负载控制。Robot Framework 发报文的速度如果太快会把总线负载拉高影响 ECU 正常通信。我在关键字里加了固定的发送间隔一般 10ms 以上具体看总线波特率。第三是电源控制。很多测试需要模拟欠压、过压、掉电恢复等场景这需要可编程电源配合。我一般用 Python 的串口库控制电源封装成Set Power Voltage、Power Cycle ECU这样的关键字。# libraries/PowerSupplyLibrary.py import serial import time class PowerSupplyLibrary: def __init__(self): self.ser None def connect_power_supply(self, portCOM3, baudrate9600): self.ser serial.Serial(port, baudrate, timeout1) def set_voltage(self, voltage): cmd fVOLT {voltage}\r\n.encode() self.ser.write(cmd) time.sleep(0.5) def power_cycle(self, off_time2.0, on_voltage12.0): self.set_voltage(0) time.sleep(off_time) self.set_voltage(on_voltage) time.sleep(1.0)这些关键字和 CAN 通信关键字组合起来就能实现完整的 HIL 测试场景。比如 验证 ECU 在 9V 欠压时停止发送报文恢复 12V 后重新上线 这个用例就是先设电压 9V然后监听总线确认没有报文再设 12V确认报文恢复。实操心得HIL 台架上的电源控制和总线通信一定要加足够的延时。我一开始没加延时结果电源还没稳定就发报文ECU 直接进入故障状态排查了一整天。后来在电源切换后固定等 1 秒问题再没出现过。5. 测试用例的组织与持续集成5.1 测试套件的分层组织汽车电子项目的测试用例数量通常很大一个完整的 ECU 测试套件可能有上千条用例。如果不组织好光是找用例就要花半天。我的做法是按测试类型分目录每个目录下再按功能模块分文件。比如testsuites/ ├── communication/ │ ├── can_communication.robot │ └── lin_communication.robot ├── diagnostic/ │ ├── uds_service_10.robot │ ├── uds_service_22.robot │ └── dtc_handling.robot ├── network_management/ │ └── nm_sleep_wakeup.robot └── flashing/ └── ecu_reprogramming.robot每个.robot文件里用*** Test Cases ***组织具体用例用[Tags]标记用例的优先级和类型。比如smoke标签标记冒烟测试regression标记回归测试CI 流水线可以根据标签选择性执行。5.2 Jenkins 流水线集成Robot Framework 和 Jenkins 的集成非常成熟装个 Robot Framework 插件就行。流水线脚本大概是这样pipeline { agent { label hil_bench } stages { stage(Checkout) { steps { git http://your-git-server/ecu-test.git } } stage(Run Smoke Tests) { steps { bat robot --include smoke --outputdir reports testsuites/ } } stage(Run Regression Tests) { steps { bat robot --include regression --outputdir reports testsuites/ } } } post { always { robot outputPath: reports/, reportFileName: report.html, logFileName: log.html } } }这个流水线跑起来之后每次提交代码自动触发冒烟测试每天晚上定时跑全量回归。测试报告会自动归档历史趋势也能看到。我特别喜欢 Robot Framework 报告里那个 关键字耗时统计 的功能能直接看出哪个关键字执行最慢对优化测试效率很有帮助。5.3 测试数据与用例的分离管理汽车电子测试里经常需要根据不同的 ECU 变体跑同一套用例比如高配和低配的 ECU 诊断 DID 不一样或者不同车型的 CAN 信号矩阵不同。这种场景下测试数据和测试逻辑一定要分离。Robot Framework 支持从 CSV、Excel、YAML 文件读取变量我一般把不同变体的参数放在单独的 YAML 文件里运行时通过命令行指定。# variables/ecu_variant_high.yaml ECU_DID_SOFTWARE_VERSION: 0xF189 ECU_DID_HARDWARE_VERSION: 0xF191 CAN_ID_DIAG_REQUEST: 0x7E0 CAN_ID_DIAG_RESPONSE: 0x7E8robot --variablefile variables/ecu_variant_high.yaml testsuites/这样一来同一套测试用例就能适配不同变体不用复制粘贴改参数。项目后期 ECU 变体越来越多的时候这个做法的优势特别明显。6. 常见问题与排查技巧实录6.1 CAN 通信超时与丢帧这是最常见的问题表现是Receive Can Message返回None测试用例失败。可能的原因和排查方法我整理了一个速查表。现象可能原因排查方法解决方案完全收不到报文通道配置错误用 candump 确认总线有数据检查 channel 和 bustype 参数偶发丢帧总线负载过高用 CANoe 看总线负载率降低发送频率加发送间隔收到错误帧波特率不匹配确认 ECU 和测试端波特率统一设为 500k 或 2M收到报文但数据不对字节序问题对比 DBC 定义检查大小端转换逻辑我遇到过最隐蔽的一次是 CAN FD 的 BRS 位配置问题。ECU 开启了比特率切换但测试端没开结果数据场速率不匹配报文能收到但数据全是错的。后来在can.interface.Bus里加上fdTrue和bitrate2000000才解决。6.2 诊断服务无响应UDS 诊断无响应的原因比较多我一般按这个顺序排查。先确认物理层通不通用 CAN 通信关键字发个简单报文看看有没有 ACK。再确认诊断 ID 对不对不同 ECU 的诊断请求 ID 和响应 ID 不一样搞错了就收不到响应。然后看会话模式有些服务需要在扩展会话下才能执行默认会话下会返回0x7F否定响应。最后检查安全访问涉及写入或刷写的服务需要先通过安全算法解锁。注意UDS 的否定响应码0x78表示响应挂起不是错误说明 ECU 还在处理请求需要继续等待。很多新人看到0x78就以为失败了其实再等一会儿就能收到正式响应。6.3 Robot Framework 变量作用域踩坑Robot Framework 的变量作用域分好几种全局变量、测试套件变量、测试用例变量、局部变量。我在项目里踩过的最大的坑是测试套件之间变量不共享。比如在can_communication.robot里设了一个${bus}变量在diagnostic.robot里是访问不到的。解决方法是把公共变量放在resources/variables.py里或者用Set Global Variable关键字显式设为全局。另一个坑是变量类型。Robot Framework 从命令行传进来的变量默认都是字符串如果底层库需要整数得在 Python 里转换。我一般用${int(${var})}这种写法来做类型转换虽然有点丑但能用。6.4 HIL 台架资源冲突HIL 台架通常是共享资源多个测试任务同时跑会冲突。我见过最典型的问题是两个人同时用同一个 CAN 通道报文互相干扰测试结果全是错的。解决办法是在 Jenkins 里给每个台架配置互斥锁或者用 TestRail 之类的测试管理工具做资源预约。另外 Robot Framework 本身并没有资源管理功能这部分需要 CI 层面来解决。7. 我对 Robot Framework 在汽车电子测试中的真实体会用了几年的 Robot Framework 之后我最大的感受是它不是一个万能工具但在合适的场景里能大幅提升测试效率。它适合做功能验证、诊断测试、网络管理测试这类逻辑复杂但实时性要求不高的场景。如果测试对时间精度要求极高比如总线时序测量、精确到微秒级的响应时间验证那 Robot Framework 就不合适还是得用 CAPL 或者直接写 C 代码。另一个体会是前期投入很重要。Robot Framework 的关键字库需要花时间去设计和封装一开始可能觉得比直接写脚本慢但等用例数量上去了优势就体现出来了。我现在的项目里底层关键字库大概有 200 多个关键字上层测试用例 1500 多条新人培训一周就能上手写用例这就是前期投入的回报。还有一个容易被忽略的点是测试报告的价值。Robot Framework 生成的 HTML 报告可以直接作为测试证据存档满足功能安全审核的要求。报告里的每一步操作、每一个请求响应都有时间戳和详细数据出了问题回溯起来非常方便。我们项目做 ISO 26262 审核的时候审核员看到测试报告里的完整日志直接就通过了省了很多解释的功夫。最后分享一个小技巧Robot Framework 的Listener接口非常有用可以在测试执行过程中动态记录额外信息。我写过一个 Listener把每条测试用例执行时的总线负载率、电源电压、环境温度都记录到报告里这样出现问题的时候能快速判断是不是环境因素导致的。这个功能在排查偶发性故障时特别管用常规报告里是看不到这些环境数据的。
返回列表