4、数据预处理:图像裁剪与归一化、关节角度平滑、数据增强策略、数据集划分
数据预处理这事儿,说实话,我见过太多人栽跟头了。
很多人觉得模型架构才是王道,预处理随便搞搞就行。结果呢?训练出来的模型,在实验室里跑得挺好,一到真实场景就崩了。我早期做机械臂抓取项目时,就吃过这个亏——数据没处理好,模型学了一堆噪声,最后抓个杯子都能把杯子怼飞。
所以这一章,咱们把预处理这块掰开揉碎了讲。你想想看,数据质量决定了模型的上限,这个道理放在具身智能里尤其明显。
4.1 图像裁剪与归一化
图像数据是具身智能最常用的输入之一。但摄像头拍出来的原始图像,直接扔给模型?不行,绝对不行。
4.1.1 图像裁剪
为什么要裁剪?说白了,就是去掉无关区域,让模型聚焦在关键部位。
比如机械臂操作任务,图像里可能有一大半是背景——桌子、墙壁、乱七八糟的工具。这些信息对模型来说就是噪声。我习惯的做法是:
- 中心裁剪:以图像中心为基准,裁剪出固定大小的区域。适合机械臂末端执行器在画面中央的场景。
- ROI裁剪:根据目标检测结果,动态裁剪出感兴趣区域。适合目标位置不固定的情况。
- 随机裁剪:训练时随机裁剪不同区域,相当于一种数据增强。但要注意,别把关键信息裁掉了。
我的经验:做抓取任务时,我一般会先标定相机位置,确保机械臂末端在图像中的大致范围。然后基于这个范围做固定裁剪,省事又有效。
4.1.2 归一化
归一化,就是把像素值从0-255映射到0-1或者-1到1之间。为什么要做?因为神经网络对输入数据的尺度很敏感。
常用的归一化方式有两种:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Min-Max归一化 | (x - min) / (max - min) | 图像像素值范围固定,常用 |
| Z-score归一化 | (x - mean) / std | 数据分布不均匀时效果好 |
我个人习惯用Min-Max归一化,简单直接。但要注意,如果图像中有大量黑色区域(比如背景),归一化后这些区域会变成0,可能会影响模型对暗部细节的感知。
注意:归一化的参数(min, max, mean, std)一定要从训练集计算,然后应用到验证集和测试集。千万别混在一起算,否则会造成数据泄露。
# 图像裁剪与归一化示例
import cv2
import numpy as np
def preprocess_image(image, target_size=(224, 224)):
# 中心裁剪
h, w = image.shape[:2]
crop_size = min(h, w)
start_x = (w - crop_size) // 2
start_y = (h - crop_size) // 2
cropped = image[start_y:start_y+crop_size, start_x:start_x+crop_size]
# 缩放
resized = cv2.resize(cropped, target_size)
# 归一化到[0, 1]
normalized = resized.astype(np.float32) / 255.0
return normalized
4.2 关节角度平滑
关节角度数据,说白了就是机器人每个关节的角度值。这类数据有个特点:噪声大、有毛刺。
为什么会这样?因为编码器读数本身就有误差,再加上机械振动、通信延迟,采集到的角度数据经常是抖动的。如果你直接把这种数据拿去训练,模型学到的动作也会抖。
4.2.1 常用的平滑方法
- 移动平均滤波:取前后N个点的平均值。简单有效,但会引入延迟。
- 中值滤波:取前后N个点的中位数。对脉冲噪声效果特别好。
- 卡尔曼滤波:基于状态估计的平滑方法。效果好,但需要调参。
- Savitzky-Golay滤波:用多项式拟合局部数据。能保留数据的高频特征。
核心要点:平滑不是越平滑越好。过度平滑会丢失动作的细节,比如快速抓取时的瞬间加速度。我建议先做少量平滑,然后观察模型输出效果,再决定是否加强。
4.2.2 避坑指南
我曾经在一个项目中,把关节角度平滑得太过分了。结果模型学到的动作软绵绵的,抓取速度慢得像蜗牛。后来才发现,是平滑窗口设太大了,把快速动作的细节全抹掉了。
所以我的建议是:
- 先试小窗口(比如3-5帧),看看效果
- 如果噪声明显,再逐步增大窗口
- 对于关键动作(如抓取瞬间),可以考虑不进行平滑
# 关节角度平滑示例(移动平均)
def smooth_joint_angles(angles, window_size=5):
smoothed = []
half_window = window_size // 2
for i in range(len(angles)):
start = max(0, i - half_window)
end = min(len(angles), i + half_window + 1)
smoothed.append(np.mean(angles[start:end], axis=0))
return np.array(smoothed)
4.3 数据增强策略
数据增强,说白了就是"无中生有"。你手头只有1000条数据,通过增强可以变成10000条。这在具身智能里特别重要,因为采集真实机器人数据太贵了。
4.3.1 图像数据增强
- 随机旋转:±15度以内,别转太多,否则机械臂姿态会失真
- 随机亮度/对比度调整:模拟不同光照条件
- 随机遮挡:随机遮挡图像的一部分,让模型学会"看不见也能做"
- 色彩抖动:轻微改变颜色,增强泛化能力
4.3.2 关节角度数据增强
这个很多人会忽略。其实关节角度也可以做增强:
- 添加高斯噪声:模拟传感器噪声,让模型更鲁棒
- 时间缩放:改变动作速度,让模型适应不同执行速度
- 关节偏移:整体偏移关节角度,模拟不同的初始姿态
我的习惯:做增强时,我会把图像和关节角度分开处理。图像增强用torchvision的transforms,关节角度增强自己写函数。这样代码清晰,也方便调试。
4.3.3 增强的注意事项
嗯,这里要注意:增强不是越多越好。过度增强会让模型学到"假模式"。比如你把图像旋转了90度,机械臂的抓取姿态就完全不对了。
我曾经犯过这个错——把图像旋转了45度,结果模型学会了"歪着头抓东西"。后来检查数据才发现,增强后的数据里,机械臂末端的位置和图像对不上了。
4.4 数据集划分
数据集划分,听起来简单,但坑很多。我见过有人直接把所有数据随机打乱,然后按比例划分。这在具身智能里是错的。
4.4.1 按时间序列划分
具身智能的数据通常是时间序列——一次演示就是一段连续的动作。如果你随机打乱,同一段演示的数据可能被分到训练集和验证集里。这会造成数据泄露,模型在验证集上的表现会虚高。
正确的做法是:
- 以"演示"为单位划分,而不是以"帧"为单位
- 同一段演示的所有数据,要么全在训练集,要么全在验证集
- 一般按 8:1:1 或 7:2:1 的比例划分训练集、验证集、测试集
4.4.2 按场景划分
如果你的数据来自多个场景(比如不同光照、不同桌面布局),我建议按场景划分。这样能测试模型的泛化能力。
| 划分方式 | 优点 | 缺点 |
|---|---|---|
| 随机划分 | 简单 | 可能数据泄露 |
| 按演示划分 | 避免数据泄露 | 需要记录演示ID |
| 按场景划分 | 测试泛化能力 | 场景间差异可能过大 |
重要提醒:划分完成后,一定要检查一下验证集和测试集的数据分布。如果某个动作类别在验证集里一个都没有,那验证结果就没有意义了。
4.5 本章知识体系
下面这张图,是我梳理的数据预处理整体流程。你可以把它当作一个检查清单,每次做预处理时对照着来。
好了,数据预处理这块就讲到这里。记住一句话:预处理做得好,模型训练少烦恼。别嫌麻烦,每一步都值得认真对待。
公众号:蓝海资料掘金营,微信deep3321