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