4. HIL测试数据分类与元数据建模

在dSPACE HIL测试中,一次完整的测试运行往往同时产生多种类型的数据:高速采样的测量信号、标定参数快照、诊断故障码、事件日志、以及摄像头或雷达回放视频流。如果这些数据没有统一的分类体系和元数据描述,后续的检索、复现、报告生成和长期归档都会变得极其困难。本节的核心任务是建立一套可落地的数据分类方法,并设计与之匹配的元数据Schema。

一、按数据产生机制与用途分类

HIL测试数据不应简单按文件扩展名分类,而应按“产生机制+用途+生命周期”三个维度综合划分。以下是工程中最常用的五类:

  • 测量数据(Measurement Data):由dSPACE实时系统以固定采样率采集的物理量或总线信号,如电机转速、电池电压、CAN报文周期。典型格式为MDF4(.mf4)或DAT,特点是数据量大、时间连续、带时间戳。
  • 标定数据(Calibration Data):描述ECU或被控对象模型参数的数据集,如PID增益、MAP图、查表值。通常以DCM、A2L、PAR或JSON形式存在,特点是版本敏感、与测量数据存在引用关系。
  • 诊断数据(Diagnostic Data):UDS/OBD诊断请求与响应记录,包括DTC故障码、快照数据、扩展数据。格式多为BLF、ASC或自定义日志,特点是事件驱动、非周期。
  • 事件日志(Event Log):测试脚本执行记录、模型状态切换、断言触发、错误注入等离散事件。通常为CSV或JSON行记录,特点是带时间戳和严重等级。
  • 视频流(Video Stream):摄像头、雷达或激光雷达的原始或编码视频,用于感知算法HIL测试。格式为H.264/H.265、RAW或ROS bag,特点是码率高、需与测量数据时间对齐。

分类的关键在于:同一测试用例产生的五类数据必须通过一个共同的测试运行标识(TestRunID)关联起来。这个标识是元数据模型的主键,也是后续数据治理的锚点。

二、元数据Schema设计原则

元数据Schema不是简单的字段堆砌,而应满足四个原则:可检索(支持按时间、DUT、测试用例、结果状态查询)、可追溯(记录数据来源、版本、生成工具)、可扩展(新增数据类型不破坏已有结构)、可校验(字段类型、必填项、枚举值有明确约束)。

推荐采用分层Schema结构:核心层描述所有数据共有的属性,类型层描述各类数据的专有属性,关系层描述数据之间的引用与派生关系。以下是一个基于JSON Schema的示例片段,用于描述测量数据:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "HIL_Measurement_Metadata",
  "type": "object",
  "required": ["testRunId", "dataType", "filePath", "startTime", "sampleRate"],
  "properties": {
    "testRunId": { "type": "string", "pattern": "^TR-[0-9]{8}-[0-9]{4}$" },
    "dataType": { "enum": ["measurement", "calibration", "diagnostic", "eventLog", "video"] },
    "filePath": { "type": "string" },
    "startTime": { "type": "string", "format": "date-time" },
    "sampleRate": { "type": "number", "minimum": 1 },
    "channelList": { "type": "array", "items": { "type": "string" } },
    "unit": { "type": "string" },
    "sourceDevice": { "type": "string" },
    "calibrationRef": { "type": "string" }
  }
}

对于标定数据,核心字段应包含calibrationId、version、parameterSet、applicableDUT;诊断数据需增加dtcList、sessionType、ecuAddress;视频流则需codec、resolution、frameRate、syncOffsetMs。所有类型共享testRunId、startTime、endTime、operator、benchId等核心字段。

三、分类与元数据的关系图解

下图展示了五类数据如何通过TestRunID汇聚,并映射到统一元数据Schema的核心字段与类型专有字段。图中同时标出了数据流方向与引用关系,便于理解Schema设计时的字段归属。

HIL测试数据分类与元数据Schema映射关系 核心元数据层(共享字段) testRunId · startTime · endTime · operator · benchId 测量数据 MDF4 / DAT sampleRate channelList unit · sourceDevice 标定数据 DCM / A2L / PAR calibrationId version · parameterSet applicableDUT 诊断数据 BLF / ASC / UDS Log dtcList sessionType ecuAddress 事件日志 CSV / JSON Lines eventType severity assertionResult 视频流 H.264 / RAW / bag codec · resolution frameRate syncOffsetMs 关系层(引用与派生) measurement.calibrationRef → calibration.calibrationId diagnostic.testRunId → eventLog.testRunId → video.testRunId 所有类型通过 testRunId 实现跨类型关联与时间对齐 数据治理输出 检索 · 复现 · 报告生成 · 长期归档 · 审计追溯 图例 核心共享字段 类型专有字段 引用/派生关系

四、Schema落地时的关键约束

设计完Schema后,必须通过校验工具(如ajv或jsonschema)在数据入库前强制执行。以下约束在HIL场景中尤为重要:

  • testRunId格式约束:建议采用TR-YYYYMMDD-NNNN,其中NNNN为当日运行序号,确保全局唯一且可排序。
  • 时间戳统一为UTC:所有startTime、endTime、syncOffsetMs均以UTC为基准,避免跨时区测试台架产生歧义。
  • 枚举值封闭:dataType、severity、sessionType等字段必须使用封闭枚举,禁止自由文本,否则检索会失效。
  • 文件路径相对化:filePath应相对于测试运行根目录,便于整体迁移和归档。
  • 视频同步偏移:syncOffsetMs记录视频首帧相对于测量数据起始时间的偏移,正值表示视频晚于测量开始,负值表示视频早于测量开始。
在实际项目中,建议将元数据Schema版本化,例如schemaVersion: "1.2.0",并在数据入库时记录所用Schema版本。当Schema升级时,旧数据仍可按旧版本解析,新数据按新版本写入,避免因字段增删导致历史数据不可读。

五、一个完整的元数据记录示例

以下是一条测量数据的完整元数据记录,展示了核心字段与类型专有字段如何组合。该记录可直接作为数据治理平台的入库单元:

{
  "schemaVersion": "1.2.0",
  "testRunId": "TR-20250612-0007",
  "dataType": "measurement",
  "filePath": "./runs/TR-20250612-0007/meas/motor_speed.mf4",
  "startTime": "2025-06-12T08:32:15.000Z",
  "endTime": "2025-06-12T08:47:42.000Z",
  "operator": "zhang.wei",
  "benchId": "HIL-BENCH-03",
  "sampleRate": 1000,
  "channelList": ["MotorSpeed_rpm", "MotorTorque_Nm", "DCBusVoltage_V"],
  "unit": "rpm/Nm/V",
  "sourceDevice": "DS1006-ProcessorBoard",
  "calibrationRef": "CAL-20250610-0012"
}

当这条记录与同一次运行的标定数据、诊断数据、事件日志和视频流元数据一起入库后,数据治理平台即可通过testRunId实现一键复现:加载对应标定参数、回放测量曲线、叠加诊断事件标记、同步视频画面。这正是元数据建模的最终价值——让数据从“文件堆”变成“可检索、可关联、可复现的测试资产”。

常见错误:将标定数据直接嵌入测量数据文件而不单独建模。这会导致标定版本无法独立追溯,当同一测量数据需要与不同标定版本对比时,无法建立清晰的引用关系。正确做法是标定数据独立存储、独立元数据记录,测量数据仅通过calibrationRef引用。