RDA 技术解析:机器人数据集 idle 帧检测的算法实现

前言

本文将深入 RDA 的代码实现,详细解析 idle 检测的算法细节,包括 bimodal gap detection 的具体实现、MAD 回退机制,以及 action discontinuity 的 spike 检测方法。

核心问题

给定一个机器人 episode 的 action 序列,如何区分"idle 帧"和"active 帧"?

输入:action[T, D],T 是帧数,D 是 action 维度

输出:每帧的 motion 值、idle 阈值、idle_ratio

算法一:Bimodal Gap Detection

第一步:计算 motion

复制代码
import numpy as np



# action: shape (T, D)

delta = np.diff(action, axis=0) # 一阶差分,shape (T-1, D)

motion = np.linalg.norm(delta, axis=1) # L2 范数,shape (T-1,)

motion[i] 表示第 i 帧到第 i+1 帧的 action 变化幅度。

第二步:构建 histogram

复制代码
num_bins = 30

max_motion = float(np.max(motion))

hist, bin_edges = np.histogram(motion, bins=num_bins, range=(0.0, max_motion))

将 motion 值分布到 30 个等宽 bin 中。

第三步:寻找双峰谷底

核心逻辑:从左到右扫描 histogram,找到"低值簇"(idle frames)和"高值簇"(active frames)之间的谷底。

复制代码
total = motion.size

low_cluster_count = 0

threshold_candidate = None

in_valley = False

valley_min = float("inf")

min_low_cluster_fraction = 0.1 # 低值簇至少占 10%



for i in range(len(hist)):

frac = hist[i] / total

low_cluster_count += hist[i]

low_frac = low_cluster_count / total

# 条件1:低值簇已经积累足够,且当前 bin 是"谷底"(频率 < 1%)

if low_frac >= min_low_cluster_fraction and frac < 0.01:

in_valley = True

if hist[i] < valley_min:

valley_min = hist[i]

threshold_candidate = float(bin_edges[i + 1])
# 条件2:已经在谷底,且当前 bin 频率回升(> 2%),说明进入高值簇

elif in_valley and frac > 0.02:

if threshold_candidate is not None and threshold_candidate > 0:

return threshold_candidate # 找到阈值

in_valley = False

valley_min = float("inf")



return None # 未找到明显的双峰结构

关键点解释

  1. min_low_cluster_fraction = 0.1:低值簇至少占 10% 的帧。这是为了避免把极少数噪声帧误判为 idle 簇。

  2. frac < 0.01:当前 bin 的频率 < 1%,认为是"谷底"。这个阈值是经验值,适用于大多数 manipulation task。

  3. frac > 0.02:谷底之后,频率回升到 2% 以上,认为进入高值簇。

  4. 返回值 :谷底的右边界 bin_edges[i + 1],即阈值为 motion < threshold 的帧被判定为 idle。

适用场景

  • ✅ 典型的 pick-and-place、insertion task,动静分明
  • ✅ action 分布有明显的双峰结构
  • ❌ 全程缓慢操作,无明显动静分界

算法二:MAD 回退机制

当 bimodal detection 失败时(找不到双峰结构),退回到基于 MAD(Median Absolute Deviation)的保守估计。

复制代码
def _mad(values):

"""计算 MAD"""

median = np.median(values)

return np.median(np.abs(values - median))



mad = _mad(motion)

threshold = 3.0 * mad # 3 倍 MAD 作为阈值

为什么用 MAD 而不是标准差?

  • MAD 是鲁棒统计量,不受极端值影响。如果数据中有少量大幅运动(比如遥操作中的快速移动),标准差会被拉高,导致阈值过大;MAD 则不会。
  • 3 × MAD 假设:如果 motion 分布近似正态,3 × MAD 约等于 2.7σ,覆盖约 99% 的数据。低于这个值的帧被认为是"异常小"的运动。

适用场景

  • ✅ 数据分布单峰,无明显双峰结构
  • ✅ 作为 bimodal detection 的兜底方案
  • ❌ 数据中有大量异常值时,MAD 可能过于保守

算法三:Action Discontinuity 检测

除了 idle 检测,RDA 还检测 action 序列中的突变点(spikes)。这些突变可能表示:

  • 遥操作中的控制模式切换
  • 数据采集中的丢帧或插值问题
  • 控制器的高频振荡

算法:基于 MAD 的 z-score

复制代码
# 计算二阶差分(jerk proxy)

delta2 = np.diff(delta, axis=0) # shape (T-2, D)

delta2_norm = np.linalg.norm(delta2, axis=1) # shape (T-2,)



# 计算 MAD-based z-score

median = np.median(delta2_norm)

mad = _mad(delta2_norm)

z_scores = 0.6745 * (delta2_norm - median) / mad



# 检测 spike:|z| > 5.0

spike_mask = np.abs(z_scores) > 5.0

spike_indices = np.where(spike_mask)[0]

spike_count = len(spike_indices)

关键点

  1. 为什么用二阶差分? 一阶差分(velocity)反映运动速度,二阶差分(acceleration/jerk)反映运动的"突变程度"。spike 通常表现为二阶差分的异常大值。

  2. 为什么用 MAD 而不是标准差? 同上,MAD 对异常值鲁棒。如果数据中有少量真实 spike,标准差会被拉高,导致 z-score 被压缩;MAD 则不会。

  3. 阈值 5.0:z-score > 5 表示该帧的二阶差分偏离中位数超过 5 倍 MAD。这是一个保守阈值,目的是捕捉明显的突变,避免误报。

  4. 系数 0.6745 :这是正态分布下 MAD 与标准差的换算系数。0.6745 * (x - median) / MAD 等价于标准 z-score,但对异常值更鲁棒。

完整流程

复制代码
def compute_idle_ratio(action):

"""

输入: action array, shape (T, D)

输出: dict with idle_ratio, threshold, method

"""

# 1. 计算 motion

delta = np.diff(action, axis=0)

motion = np.linalg.norm(delta, axis=1)

# 2. 尝试 bimodal detection

threshold = _find_bimodal_threshold(motion)

method = "bimodal_gap"

# 3. 如果失败,退回 MAD

if threshold is None:

mad = _mad(motion)

threshold = 3.0 * mad

method = "mad"

# 4. 计算 idle ratio

idle_mask = motion < threshold

idle_ratio = idle_mask.sum() / len(motion)

return {

"idle_ratio": idle_ratio,

"threshold": threshold,

"method": method

}

在 G1 数据集上的实测

我们用 RDA v0.9.12 审计了 G1_WBT_Brainco_Pickup_Pillow 数据集(300 episodes,177,811 帧)。

结果:

  • 所有 300 个 episode 都使用 bimodal gap detection 成功找到阈值,没有触发 MAD 回退
  • idle_ratio 中位数 65.6% ,有效运动仅 34.4%
  • action discontinuity:299/300 episodes 检测到 spike,总计 3340 个突变点

解读

  1. idle_ratio 偏高(65.6%) :这个任务是"拾取枕头并放置到沙发上",包含大量等待、保持姿态的时间,符合 task 特性。

  2. spike 普遍存在:几乎所有 episode 都有 action discontinuity。结合 G1 是遥操作采集的数据,这可能与操作过程中的控制模式切换(比如从连续控制切换到抓取命令)有关。

  3. bimodal detection 全部成功:说明这个数据集的动静分布确实存在双峰结构,idle 检测的阈值选择是合理的。

总结

表格

算法 适用场景 局限性
Bimodal Gap Detection 动静分明的 manipulation task 全程缓慢操作时失效
MAD 回退 单峰分布,作为兜底方案 可能过于保守
MAD-based z-score(spike) 检测 action 突变点 对高频噪声敏感

RDA 的 idle 检测设计是逐 episode 自适应的,阈值基于数据本身的分布计算,不是固定的全局常量。这种设计在大多数 manipulation task 上表现良好,但对于特殊任务(全程缓慢操作),用户需要意识到 idle_ratio 可能偏高,并结合其他指标综合判断。

参考

相关推荐
YMWM_1 个月前
lerobot库中RTC更新逻辑
人工智能·rtc·lerobot
Cx330_FCQ2 个月前
LeRobot SO-101 机械臂从底层控制到 VLA 视觉语言动作模型完整实操复盘
具身智能·vla·lerobot
YMWM_2 个月前
lerobot中use_relative_actions=True需要重新计算meta/stats.json等信息
人工智能·深度学习·lerobot
chen_zn953 个月前
GR00T N1.7源码学习(二):训练数据、Processor与多机器人动作空间解析
深度学习·具身智能·vla·lerobot·gr00t
橘白3165 个月前
GVHMR
人工智能·算法·机器人·机器人数据·视频动捕
时空未宇6 个月前
Hi3403开发板 + openEuler Embedded 运行IB-Robot + 控制 Lerobot 机械臂跳舞
ros2·具身智能·lerobot·hi3403·ib-robot
YMWM_6 个月前
LeRobot 数据集 v2.1 → v3.0 迁移记录
lerobot
YMWM_6 个月前
lerobot下载的pi0.5模型的默认存储位置
huggingface·lerobot
iru10 个月前
在lerobot-dataset-visualizer可视化界面展示的内容
人工智能·lerobot