3. 测试数据接入与统一数据模型
测试现场的数据格式往往比想象中更杂:台架采集软件导出 CSV,人工记录整理成 Excel,MATLAB 仿真或后处理结果保存为 MAT,NI 采集设备则常以 TDMS 落盘。如果每个分析脚本都针对一种格式单独写读取逻辑,后续做批量分析、自动报告和跨试验对比时就会反复重写。本节要解决的核心问题,是建立一个“多格式读取 → 统一表结构”的接入层,让上层分析代码只面对一种数据形态。
统一数据模型:先定“表”,再谈“读”
统一表结构不是把所有数据塞进一张大表,而是约定一套稳定的列语义。对测试数据而言,最常用的是长表(long table)结构,每行代表一个采样点或一条记录,至少包含以下列:
- time:时间戳或相对时间,单位统一为秒(s),作为横轴基准;
- signal:信号名称,字符串,用于区分同一时刻的不同通道;
- value:该信号在该时刻的数值,统一为 double;
- unit:物理单位,如 V、℃、rpm,便于报告标注;
- source:数据来源文件或设备标识,用于追溯;
- meta:可选,存放采样率、量程、试验编号等元信息。
这种结构的好处是:无论原始数据是宽表(每列一个信号)还是多通道二进制,转换后都能用同一套筛选、重采样、绘图和统计代码处理。若某些信号采样率不同,长表允许各信号拥有独立时间轴,后续按需对齐,而不是在读取阶段强行插值。
四类格式的读取要点
CSV 是最简单也最容易出问题的格式。常见坑包括:分隔符不统一(逗号、分号、制表符)、小数点用逗号、首行有中文注释、时间列是“2024/06/01 10:23:45.123”这类字符串。读取时建议显式指定参数,而不是依赖自动探测:
opts = detectImportOptions('run01.csv', ...
'Delimiter', ',', ...
'DecimalSeparator', '.', ...
'VariableNamingRule', 'preserve');
opts = setvartype(opts, 'Time', 'datetime');
T = readtable('run01.csv', opts);
读入后先检查 height(T)、summary(T) 和缺失值比例,再决定是否剔除无效行。时间列若为字符串,用 datetime 解析并转换为相对秒:t = seconds(T.Time - T.Time(1))。
Excel 的复杂性在于多工作表、合并单元格和公式。读取时用 readtable 指定 Sheet 和 Range,避免把表头说明行当成数据。若一个文件里每个 sheet 是一个工况,可用循环批量读取并追加 source 列。注意 Excel 的日期序列号与 MATLAB datetime 的转换,建议统一用 readcell 先看原始内容再决定解析方式。
MAT 文件通常已经是 MATLAB 变量,读取用 load 或 matfile。关键不是“读”,而是识别变量结构:可能是 data.time 与 data.signals 的嵌套结构,也可能是 Simulink 的 logsout 对象。接入层需要写一个适配函数,把已知字段映射到统一列。对于大文件,用 matfile 按需读取变量,避免一次性载入内存。
TDMS 是 NI 的层次化二进制格式,包含文件、组、通道三级结构。MATLAB 没有内置读取函数,通常借助 tdmsreader 或第三方包。读取后得到的是各通道的属性与数据数组,需要把通道名、单位、采样率提取出来,再展开为长表。TDMS 的时间信息常以“起始时间 + 采样间隔”给出,转换时按 t = t0 + (0:N-1)/fs 生成时间列。
转换实现:一个可复用的接入函数
把上述逻辑封装成一个入口函数 loadTestData(filePath),根据扩展名分派到具体读取器,再统一调用 toLongTable 完成列映射。核心转换代码大致如下:
function T = toLongTable(time, signals, units, source)
% time: N×1 double (s)
% signals: N×M double
% units: 1×M cellstr
n = numel(time);
m = numel(units);
T = table();
for k = 1:m
Tk = table(repmat(time,1,1), ...
repmat(string(units{k}), n, 1), ...
signals(:,k), ...
repmat(string(units{k}), n, 1), ...
repmat(string(source), n, 1), ...
'VariableNames', {'time','signal','value','unit','source'});
T = [T; Tk];
end
end
实际工程中还要处理三个细节:一是时间列若为 datetime,先转 posixtime 再减去首值;二是不同信号采样率不同时,保留各自时间列,不强行对齐;三是缺失值用 NaN 标记,而不是删除行,以便后续统计缺失率。
接入质量检查
转换完成后,建议做一次快速体检,判断数据是否可用:
| 检查项 | 判断依据 | 异常处理 |
|---|---|---|
| 行数是否合理 | 行数 ≈ 信号数 × 采样点数 | 检查是否漏读通道或重复追加 |
| 时间单调性 | all(diff(time) > 0) | 排序或剔除重复时间戳 |
| 缺失值比例 | 各信号 NaN 占比 < 5% | 标记并记录,报告中说明 |
| 单位一致性 | 同信号 unit 唯一 | 统一换算,如 mV → V |
| 采样间隔稳定性 | std(diff(time)) 远小于均值 | 检查丢包或时间戳异常 |
ingest_log 表,随分析结果一起保存。这样当报告数据异常时,可以快速定位是原始数据问题还是转换逻辑问题。