☰
相对动作还是绝对动作?OpenDM动作chunk构建、夹爪处理与归一化统计机制深度剖析
2026/10/8 5:55:52 网站建设 项目流程

相对动作还是绝对动作?OpenDM动作chunk构建、夹爪处理与归一化统计机制深度剖析

【免费下载链接】opendmAn Open-World Foundation Model for General-Purpose Embodied Intelligence.项目地址: https://gitcode.com/gh_mirrors/op/opendm

OpenDM 是一个面向开放世界的具身智能机器人大模型项目,其核心模型 DM0.5 通过"动作 chunk(action chunk)+ 夹爪特殊处理 + 分位数归一化"三件套,把机器人轨迹数据变成模型能学、能复现的动作目标。本文面向新手,用最少代码带你读懂 opendm/data/transforms.py 与 opendm/data/normalize.py 中的关键机制:动作目标从哪来、相对/绝对动作怎么选、夹爪维度为什么特殊、归一化统计值怎么算出来并存进norm_stats.json。

动作流水线全景:从 JSONL 帧到模型输入 🔍

OpenDM 的训练数据是 JSONL 文件——每个.jsonl文件是一个 episode(一段完整操作录像),每一行是一帧,包含相机画面路径、当前机器人状态state、任务指令prompt等字段。示例数据见 assets/demo/episode0.jsonl。

一帧数据进入训练流水线后,与动作相关的处理依次是:

步骤组件作用
① 构建动作 chunkBuildActionChunk从当前帧 + 未来帧读出固定长度的动作序列
② 相对化ActionRelative把绝对目标减掉当前状态,变成"位移增量"
③ 归一化Normalize用分位数统计把数值压到[-1, 1]
④ 离散化ChatTokenization把数值映射成 256 个 bin,变成文本 token 参与训练
⑤ 填充PadAction把动作向量补到统一维度 32

完整训练流水线组装在 opendm/exp/dm05_exp.py。推理时方向相反:反归一化 → 还原绝对动作。

动作chunk构建:固定视野 + 边界重复填充 🎬

核心实现是 BuildActionChunk,它回答三个问题:

1. 目标值从哪来?两种情况:

  • 帧里显式提供了action字段 → 从当前帧开始读chunk_size步的action;
  • 没有action→ 从下一帧开始,把未来的state当作动作目标(机器人下一步实际到的位置,就是最好的"期望动作")。

2. chunk 长度由谁决定?由action_horizon(等于模型配置的chunk_size)决定,模型一次"说"出未来这么多步的动作,而不是一步一播,这正是"动作 chunk"的含义。

3. 读到 episode 末尾怎么办?越界时重复最后一个可用值填充,保证 chunk 长度恒定,训练时不会因边界帧丢弃样本。

构建结果形如data["action"](形状(1, horizon, dim)),并附带全 1 的action_mask。这套规则也在官方数据文档 docs/zh/data.md 的"动作目标与 Episode 边界"一节有权威说明。

相对动作 vs 绝对动作:一行减法,两种哲学 ⚖️

ActionRelative 把绝对目标转成相对增量:relative = action - state。为什么?直觉上,"从当前位置往左移 2cm"比"移到桌面坐标 (12.3, -4.1)"更容易跨场景泛化——这正是 OpenDM"面向开放世界"的设计取向。

模式选择由 BuildAction 统一管理:

  • ActionMode.RELATIVE(默认):内部自动串联BuildActionChunk → ActionRelative;
  • ActionMode.ABSOLUTE:只构建 chunk,不减状态,动作维度和state可以不同。

枚举定义在 opendm/constants/robot.py,训练时用--data-config.action-mode指定。一个硬性约束:相对模式下action与state维度必须逐维对齐,否则相减无意义。

夹爪的特殊待遇:位置相对,开合绝对 🤏

这是 OpenDM 动作编码里最值得玩味的细节。ActionRelative有一个non_delta_ids参数,默认值是(RobotStateDesc.GRIPPER,)——夹爪维度不做差值,直接保留绝对值(见 transforms.py#L399-L402)。

为什么?因为关节角有"当前位置"这个天然的参考系,减掉它有物理意义;但夹爪是二值的开/合状态——"比现在多开 0.3"没有意义,"开到 0.8"才有意义。所以规则是:

位置类维度(joint / eef)→ 相对增量;夹爪维度 → 保持绝对目标。

每个维度是不是夹爪,靠state_desc声明。每种机器人的状态布局在 opendm/constants/robot.py 中逐维标注,例如 Aloha 是6关节+夹爪× 2 臂共 14 维,UR5 则是6维末端位姿(eef)+夹爪共 7 维。

归一化统计机制:分位数 + 在线直方图 📊

连续动作值域混乱(弧度、米、开合度混在一起),必须先归一化。OpenDM 默认用分位数归一化(quantile normalization),实现在 Normalize:

  1. 用统计值q01/q99把异常值裁剪到[q01, q99](clip_to_bounds=True);
  2. 线性映射到[-1, 1]:(x - q01) / (q99 - q01) * 2 - 1;
  3. 若某维度q01 == q99 == 0(如恒为 0 的维度),直接输出 0,避免除零。

相比均值/标准差方案,分位数方案对长尾分布和离群点更稳健——机器人数据里偶尔的急停、碰撞尖峰不会把统计值带偏。

统计值本身怎么来?RunningStats 是一个单趟在线统计器:

  • 用增量公式滚动更新均值和平方均值,避免存下全量数据;
  • 每个维度维护一个 5000 桶的直方图,min/max 变化时自动重分桶,从而不保存原始样本也能算出 q01/q99 分位数;
  • 最终产出 NormStats:mean / std / q01 / q99 / min / max各一份。

驱动它的入口是 compute_norm_stats:带动作变换过一遍数据集,按robot_type分组各自累计(不同机器人尺寸差异巨大,绝不能混算),写入一个 JSON 文件,文件名含数据集与动作变换的哈希摘要(见 dm05_exp.py#L324-L331),换个 chunk 大小会自动重算。

多机器人统计文件 📁

多机型实验产出的norm_stats.json采用"扩展文件"结构:norm_stats_by_robot按机型分桶 +default_robot_type声明默认机型 + 顶层norm_stats保留默认机型副本以兼容旧代码。加载与选择的逻辑在 NormStatsFile.select,还支持按control_mode(如eef末端控制)进一步细分统计值。训练和推理都会按样本/请求里的robot_type自动选对统计值。

推理闭环:ActionAbsolute 把增量还原成动作 🔄

训练用相对动作,推理输出前必须还原。ActionAbsolute 完成逆过程:

  • 位置维度:absolute = state + delta;
  • 夹爪维度:不用加法,直接从模型输出取原值(因为训练时它本就是绝对值);
  • 可选compose_eef_rot:对末端位姿(EEF)的旋转部分用四元数复合代替简单加法,正确处理 SO(3) 上的旋转叠加(配套 ArrangeState 会把 rpy 转成轴角)。

推理流水线组装见 dm05_exp.py#L684-L693:Denormalize →(可选)ActionAbsolute,与训练侧严格对称。

总结:为什么这套机制值得抄作业 🎯

设计点解决的问题
未来state当动作目标无需额外标注,chunk 目标与真实轨迹天然一致
episode 越界重复填充边界帧不浪费,chunk 长度恒定
关节相对 + 夹爪绝对兼顾泛化性与开合语义的物理正确性
q01/q99 分位数归一化抗离群点,统一压到[-1, 1]便于离散化
在线直方图统计单趟、低内存,还能按机型分桶
推理端严格逆变换相对/绝对、归一化/反归一化一一配对

读懂 opendm/data/ 下的 transforms 与 normalize 两个模块,你就掌握了 OpenDM 从原始轨迹到模型 token 的完整桥梁。想动手试试,可以先看 playground/dm05_sft_demo.py 与仓库内置 demo 数据,配合 docs/zh/dm05_finetuning.md 走一遍完整流程。

【免费下载链接】opendmAn Open-World Foundation Model for General-Purpose Embodied Intelligence.项目地址: https://gitcode.com/gh_mirrors/op/opendm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询