第2章:仿真数据基础:数据格式解析与预处理

大家好,我是你们的老朋友。今天咱们聊聊仿真数据的那些事儿。

做运动控制仿真,说白了就是跟数据打交道。你仿真跑得再漂亮,数据读不出来、格式不对、有脏数据,那都是白搭。我个人习惯,拿到任何仿真结果,第一件事就是搞清楚它的「户口本」——数据格式。

2.1 三种主流数据格式:CSV、MAT、HDF5

咱们做运动控制仿真,最常见的就这三种格式。我一个个说。

2.1.1 CSV:最通用的「表格」

CSV,全称逗号分隔值。说白了就是一个纯文本表格。每行是一条记录,每列用逗号隔开。

优点: 任何软件都能打开,Excel、记事本、Python、MATLAB,通吃。

缺点: 不支持复杂数据结构,存不了多维数组。你想想看,一个6轴机器人的关节角度数据,存成CSV就得6列,还行。但要是存个3D点云,那就麻烦了。

实战经验: 我在项目中遇到过,客户给的CSV文件里,时间戳那一列居然有中文字符「秒」。嗯,这种坑我踩过不止一次。

# 一个典型的运动控制仿真CSV文件
time, joint1_angle, joint2_angle, joint3_angle, torque1, torque2, torque3
0.000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
0.001, 0.015, 0.008, -0.003, 1.23, 0.89, -0.45
0.002, 0.031, 0.017, -0.007, 2.45, 1.78, -0.91

2.1.2 MAT:MATLAB的「亲儿子」

MAT格式是MATLAB的专用二进制格式。它支持各种数据类型:结构体、细胞数组、多维矩阵,都能存。

优点: 保存变量名、数据类型、维度信息,读取速度快。

缺点: 非MATLAB环境读取麻烦。不过Python有scipy.io库可以读。

小技巧: 我建议你在MATLAB里保存数据时,用结构体打包。比如:

sim_data.time = t;
sim_data.joint_angles = q;
sim_data.torques = tau;
save('sim_result.mat', 'sim_data');

这样在Python里读出来也是一个结构体,清晰明了。

2.1.3 HDF5:大数据时代的「集装箱」

HDF5,全称层次化数据格式。它就像一个文件系统,里面可以放很多「数据集」和「组」。每个数据集可以是一个多维数组,组可以嵌套。

优点: 支持超大文件(TB级别),支持并行I/O,支持数据压缩。

缺点: 学习曲线陡峭,小数据用起来杀鸡用牛刀。

注意: 我曾经在读取一个10GB的HDF5文件时,直接用了h5py.File('data.h5', 'r')['dataset'][:],结果内存爆了。正确的做法是用切片读取:dataset[0:1000, :]

2.2 数据导入与导出:Python实战

好了,理论说完了,咱们上代码。我习惯用Python做数据处理,因为它生态好、库多。

2.2.1 CSV的读写

import pandas as pd
import numpy as np

# 读取CSV
df = pd.read_csv('sim_result.csv')
print(df.head())  # 看看前5行

# 写入CSV
data = {
    'time': np.linspace(0, 1, 100),
    'position': np.sin(np.linspace(0, 2*np.pi, 100))
}
df_out = pd.DataFrame(data)
df_out.to_csv('output.csv', index=False)

2.2.2 MAT文件的读写

import scipy.io as sio

# 读取MAT
mat_data = sio.loadmat('sim_result.mat')
print(mat_data.keys())  # 看看有哪些变量

# 写入MAT
sio.savemat('output.mat', {'time': t, 'position': pos})

2.2.3 HDF5的读写

import h5py

# 写入HDF5
with h5py.File('sim_data.h5', 'w') as f:
    f.create_dataset('time', data=t)
    f.create_dataset('joint_angles', data=q)
    f.create_dataset('torques', data=tau)
    
    # 可以加属性
    f.attrs['description'] = '6-axis robot simulation data'
    f.attrs['date'] = '2024-01-15'

# 读取HDF5
with h5py.File('sim_data.h5', 'r') as f:
    time = f['time'][:]
    angles = f['joint_angles'][:]
    print(f.attrs['description'])

2.3 数据清洗与预处理:别让脏数据毁了你的分析

数据清洗,说白了就是「去伪存真」。我见过太多人,仿真数据直接拿来用,结果分析出来的结论全是错的。

2.3.1 常见的数据问题

问题类型 表现 原因
缺失值 NaN、空值 传感器丢包、仿真步长不匹配
异常值 数值突然跳变 数值积分发散、碰撞检测错误
重复值 相同时间戳出现多次 数据记录逻辑错误
时间戳不均匀 采样间隔不一致 变步长仿真、CPU负载波动

2.3.2 清洗实战:一个完整的例子

假设我们有一段机器人关节角度数据,里面有缺失值和异常值。咱们一步步处理。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 模拟脏数据
np.random.seed(42)
t = np.linspace(0, 10, 1000)
q = np.sin(t) + 0.1 * np.random.randn(1000)

# 人为制造问题
q[100:105] = np.nan  # 缺失值
q[500] = 100.0       # 异常值
q[501] = -100.0      # 异常值

# 创建DataFrame
df = pd.DataFrame({'time': t, 'joint_angle': q})

# 1. 检查缺失值
print(f"缺失值数量: {df['joint_angle'].isna().sum()}")

# 2. 填充缺失值(线性插值)
df['joint_angle'] = df['joint_angle'].interpolate(method='linear')

# 3. 检测异常值(3-sigma法则)
mean = df['joint_angle'].mean()
std = df['joint_angle'].std()
df['is_outlier'] = np.abs(df['joint_angle'] - mean) > 3 * std
print(f"异常值数量: {df['is_outlier'].sum()}")

# 4. 处理异常值(用前后均值替换)
df.loc[df['is_outlier'], 'joint_angle'] = np.nan
df['joint_angle'] = df['joint_angle'].interpolate(method='linear')

# 5. 检查时间戳是否均匀
time_diff = np.diff(df['time'])
print(f"时间间隔均值: {time_diff.mean():.6f}, 标准差: {time_diff.std():.6f}")

# 6. 如果需要,重采样到固定频率
df_resampled = df.set_index('time').resample('0.01S').interpolate()

避坑指南: 我曾经在处理一个机器人轨迹数据时,直接用均值填充了缺失值。结果轨迹出现了明显的「台阶」,机器人运动不连续。后来改用样条插值,效果就好多了。记住:运动控制数据,连续性很重要。

2.4 知识体系总览

下面这张图,是我自己总结的数据处理流程。你照着这个思路走,基本不会出错。

仿真数据处理流程 数据获取 格式识别 CSV MAT HDF5 数据导入 数据清洗 缺失值处理 异常值检测 重复值去重 时间戳对齐 数据预处理

这张图把整个流程串起来了。从数据获取开始,到格式识别,再到导入、清洗、预处理。每一步都有对应的技术方案。

我的建议: 刚开始做数据清洗时,别想着一步到位。先写个简单的脚本,把缺失值和异常值标出来看看。确认没问题了,再批量处理。我见过太多人,一上来就写了个复杂的清洗流程,结果把好数据也洗坏了。

好了,这一章的内容就到这儿。数据格式和清洗是基本功,但也是最容易出问题的地方。你把这些搞定了,后面的分析才能站得住脚。


专注资料整理