G1 suspended-limb dynamics calibration tools.
This repository is for the real robot part of the "suspended robot draws circles in air" experiment. The experimental constraint is explicit:
Real robot and MuJoCo simulation must receive the same precomputed open-loop joint command sequence. RL closed-loop accuracy is a separate later step.
- Load a precomputed
.npzaction sequence, usually generated by the MuJoCo IK script. - Replay its
q_cmdsequence on the real G1 through Unitree SDK2 lowcmd. - Record Unitree lowstate
q / dq / ddq / tau_estand optional Nokov end-effector positions. - Replay the same
q_cmdin MuJoCo with pelvis frozen and gravity disabled. - Compare the real run against a simulation run with the same
q_cmd. - Plot joint curves, end-effector 3D trajectories, error time series, and statistics.
- uv environment
- real robot calibration guide
- right leg circle experiment 2026-07-09
- left leg circle experiment 2026-07-09
- torso-fixed lower-body circle experiment 2026-07-10
These checked-in videos verify the per-limb MuJoCo replay and MP4 recording pipeline. They were recorded with the repository smoke-test action, not the final IK circle action.
| Limb | Preview |
|---|---|
left_palm |
|
right_palm |
|
left_foot |
|
right_foot |
The action .npz should contain:
tortime_s: shape(N,)q_cmdorq_reforq_target: shape(N, 29)
Optional:
dq_cmd,ddq_cmdee_desired_left_palm,ee_desired_right_palm,ee_desired_left_foot,ee_desired_right_footjoint_namesmeta_json
The real recorder saves the exact loaded q_cmd into the real .npz. The comparison script checks q_cmd_real against q_cmd_sim before reporting dynamics errors.
cd /data/user_data/users/metabot-workspace/pingpong/calig1
uv sync --extra simRun commands through uv:
uv run calig1-sim --help
uv run calig1-real --help
uv run calig1-compare --helpFor the real robot machine, Unitree SDK2 Python and Nokov SDK must also be
installed or available on PYTHONPATH. They are site-local robot dependencies
and are documented in the uv environment guide.
Prepare the real robot uv environment:
scripts/setup_real_env.shThis records the simulation response under the same loaded q_cmd.
uv run calig1-sim \
--config configs/sim_circle.yaml \
--action logs/circle_action_sequence.npz \
--out logs/suspended_circle_sim_data.npzTo match the real collection mode, run each limb separately. The non-active joints are held at the first pose of the action file, and the same limb mask is used by the real and MuJoCo runners.
uv run calig1-sim \
--config configs/sim_circle.yaml \
--action logs/circle_action_sequence.npz \
--limb left_palm \
--out logs/sim_left_palm.npz \
--video logs/sim_left_palm.mp4Available limbs:
left_palmright_palmleft_footright_foot
Batch run all four simulation limbs with video:
PYTHON="uv run python" scripts/run_all_sim_limbs.shDirect right-palm smoke test:
scripts/run_right_palm_test.shThis creates logs/right_palm_smoke_action.npz if needed and records
logs/right_palm_test/sim_right_palm.mp4. It is a conservative joint-space
smoke test, not the official IK circle action. In real modes, the wrapper
regenerates the smoke action from the current Unitree lowstate so repeat tests
do not jump back to a hard-coded zero pose. The default smoke action raises the
right hand before replay so the hand is not hanging straight down.
Default simulation settings:
- XML:
../PingPongFSM/assets/g1_description/g1_robot_movable_base.xml - gravity disabled
- pelvis freejoint frozen at its initial pose
- PD torque tracking of the same open-loop
q_cmd - end-effector site recording:
left_palm,right_palm,left_foot,right_foot
The runner refuses to publish lowcmd unless --confirm-suspended is passed.
uv run calig1-real \
--config configs/real_circle.yaml \
--action logs/circle_action_sequence.npz \
--out logs/preflight_real.npz \
--preflight-only \
--confirm-suspendeduv run calig1-real \
--config configs/real_circle.yaml \
--action logs/circle_action_sequence.npz \
--out logs/circle_real_001.npz \
--confirm-suspendedFor the real robot, collect one limb at a time using the same --limb names.
Preflight one limb:
uv run calig1-real \
--config configs/real_circle.yaml \
--action logs/circle_action_sequence.npz \
--limb left_palm \
--out logs/preflight_left_palm.npz \
--preflight-only \
--confirm-suspendedRun one limb:
uv run calig1-real \
--config configs/real_circle.yaml \
--action logs/circle_action_sequence.npz \
--limb left_palm \
--out logs/real_left_palm.npz \
--confirm-suspendedRight-palm real preflight through the wrapper:
MODE=preflight CONFIRM_SUSPENDED=1 ACTION=logs/circle_action_sequence.npz \
scripts/run_right_palm_test.shIf the robot is not already at the first frame of the sim action, add a slow ramp before the official replay segment:
MODE=preflight CONFIRM_SUSPENDED=1 REGENERATE_ACTION=0 RAMP_TO_START_S=5.0 \
ACTION=logs/sim_right_palm.npz scripts/run_right_palm_test.shRight-palm real replay through the wrapper:
MODE=real CONFIRM_SUSPENDED=1 ACTION=logs/circle_action_sequence.npz \
scripts/run_right_palm_test.shReplay the right-palm action already recorded by simulation:
MODE=real CONFIRM_SUSPENDED=1 REGENERATE_ACTION=0 RAMP_TO_START_S=5.0 \
ACTION=logs/sim_right_palm.npz scripts/run_right_palm_test.shSafety defaults:
- Requires suspended confirmation.
- Aborts on stale Unitree lowstate.
- Aborts on large command jumps, large tracking error, or excessive measured joint velocity.
- Sends damping command on exit.
Default ramp_to_start_s is 0.0 so the official replay segment uses the action file unchanged. If a ramp is needed for safety, set it in the config and report it separately.
uv run calig1-compare \
--real logs/real_left_palm.npz \
--sim logs/sim_left_palm.npz \
--out logs/report_left_palmOutputs:
summary.jsonreal_to_sim_report.mdjoint_q_error_stats.csv,joint_dq_error_stats.csv,joint_ddq_error_stats.csvee_3d_trajectories.pngee_error_timeseries.pngjoint_q_tracking_overview.pngjoint_q_rmse.png
uv run python scripts/make_dummy_pair.py --out logs/dummy
uv run calig1-compare \
--real logs/dummy/real_dummy.npz \
--sim logs/dummy/sim_dummy.npz \
--out logs/dummy/reportThis only tests data loading and plotting.