FFW SG2 · wave-left · proprioception-only Diffusion Policy

Diffusion Policy (LeRobot 0.6.1) trained on the wave-left task of omkarpatil/wave-traj (11 teleop episodes, ROBOTIS AI Worker ffw_sg2_rev1, instruction "wave using the left hand"). The policy conditions on joint state only — no cameras.

Inputs observation.state (22): arm_l ×7, gripper_l, arm_r ×7, gripper_r, head ×2, lift, cmd_vel linear_x / linear_y / angular_z
Outputs action (22), same layout, published to /leader/*/joint_trajectory and /cmd_vel
Chunking n_obs_steps=1, horizon=32, n_action_steps=16 at 15 Hz (= 2.1 s predicted, 1.07 s executed per chunk)
Inference DDPM, num_inference_steps=10 (≈ 70 ms on an A5000; 100 steps gives the same accuracy at ~650 ms)
Training 30 k steps, batch 64, lr 1e-4 cosine, seed 1000; final loss 1e-3; offline 16-step open-loop MAE 0.002 rad

Initial state for inference

The demonstrations all start from the pose below (mean over 11 episodes; std is the spread across demos). Put the robot at this pose before issuing START. Head and lift barely varied during collection, so use those values. Base velocity dims must read ~0.

joint mean [rad] std range over demos
arm_l_joint1 -0.127 0.075 [-0.237, +0.040]
arm_l_joint2 +0.124 0.018 [+0.095, +0.161]
arm_l_joint3 -0.003 0.056 [-0.094, +0.079]
arm_l_joint4 -1.591 0.086 [-1.792, -1.479]
arm_l_joint5 +0.133 0.058 [+0.057, +0.276]
arm_l_joint6 +0.065 0.059 [-0.023, +0.159]
arm_l_joint7 -0.071 0.045 [-0.170, -0.009]
gripper_l_joint1 +0.160 0.044 [+0.113, +0.247]
arm_r_joint1 -0.202 0.060 [-0.278, -0.058]
arm_r_joint2 -0.038 0.016 [-0.063, -0.003]
arm_r_joint3 +0.063 0.034 [+0.008, +0.109]
arm_r_joint4 -1.400 0.092 [-1.642, -1.290]
arm_r_joint5 -0.041 0.040 [-0.111, +0.023]
arm_r_joint6 +0.058 0.059 [-0.017, +0.199]
arm_r_joint7 -0.081 0.036 [-0.158, -0.028]
gripper_r_joint1 +0.111 0.007 [+0.098, +0.117]
head_joint1 -0.018 0.058 [-0.201, -0.000]
head_joint2 -0.031 0.093 [-0.324, -0.002]
lift_joint -0.001 0.001 [-0.004, +0.000]
linear_x +0.000 0.000 [-0.000, +0.000]
linear_y +0.000 0.000 [-0.000, +0.000]
angular_z +0.000 0.000 [-0.000, +0.001]

The same numbers are in initial_state.json (initial_state_mean is the vector to command, in joint_names order; final_state_mean is where the demos end).

Running it with cyclo_intelligence

  • Set the task's inference_hz = 15 (default) and control_hz = 100 in the UI; ActionChunkProcessor spaces the chunk's 16 steps at 1/inference_hz, so 15 Hz must match the dataset fps this policy was trained at.
  • Stock LeRobot refuses to build a Diffusion Policy without an image/environment-state input, and the container engine sends a single (B, D) state per request. Both are handled by cyclo_brain/policy/lerobot/lerobot_engine/diffusion_compat.py (loaded automatically by the engine's loading.py / prediction.py) — the policy container needs that version of the bind-mounted lerobot_engine/. Loading in plain LeRobot: call diffusion_compat.allow_state_only_diffusion() first, then DiffusionPolicy.from_pretrained(...), and add the time axis with expand_obs_time_dim(batch, 1) before predict_action_chunk.

MuJoCo rollouts — read before deploying

In the lerobot-mujoco-tutorial FFW SG2 model, 10 rollouts from sampled initial states gave 3 full waves, 5 partial, 2 stalled (left-arm joint range 62 % of the demonstrations' on average). The failure mode is hesitation: the policy sometimes holds the start pose for several seconds before waving, or waves with reduced amplitude. Expect the same on hardware; starting closer to the mean pose and allowing a longer episode helps, and more demonstrations would fix it properly.

Downloads last month
9
Safetensors
Model size
0.2B params
Tensor type
F32
·
Video Preview
loading

Dataset used to train omkarpatil/ffw_sg2_wave-left_diffusion_state