Skip to main content

Configuration Fields

Complete reference for Teleopit's Hydra configuration fields.

Top-Level Fields

FieldDescriptionDefault
policy_hzPolicy inference frequency50
pd_hzPD control frequency (simulation only)200
viewersViewer set: mocap, retarget, sim2sim, camera, all, none. all opens mocap, retarget, and sim2sim; add camera explicitly.sim2sim
realtimeRate-limit to wall clockfalse
num_stepsNumber of steps; 0 = infinite0
keyboard.enabledEnable realtime keyboard mode control for sim2simfalse
playback.pause_on_endPause at last frame when offline motion endsfalse
playback.keyboard.enabledEnable keyboard control for offline playbackfalse

Robot

FieldDescriptionDefault
robot.typeStable robot type written into recording schemasunitree_g1_29dof
robot.num_actionsJoint action dimension29
robot.xml_pathMuJoCo XML path-
d435i_rgbFixed RGB camera in the G1 MJCF; use viewers=[sim2sim,camera] to display it-
robot.kps / robot.kdsPD gains-
robot.default_anglesDefault standing pose-
robot.torque_limitsJoint torque limits-

Controller

FieldDescriptionDefault
controller.policy_pathRequired. Path to ONNX policy file-
controller.deviceInference device: cpu / auto / cuda:Ncpu
controller.action_scaleAction scaling factor-
controller.clip_rangeAction clipping range-
controller.default_dof_posJoint angle offset base-

Input

Offline BVH

FieldDescription
input.bvh_fileRequired. Path to BVH file
input.bvh_formatlafan1 / hc_mocap
input.human_formatHuman skeleton format

BVH input does not set input.provider — it is inferred from the config group name.

Pico 4

FieldDescriptionDefault
input.providerpico4pico4
input.human_formatRetarget skeleton formatpico_bridge
input.pico4_timeoutWait timeout in seconds60
input.pico4_buffer_sizeFrame buffer size60
input.pause_buttonButton for pause/resumeA
input.pause_debounce_sDebounce time for pause button0.25
input.arms_buttonButton for Pico MOCAP / ARMS toggleB
input.arms_debounce_sDebounce time for arms-mode button0.25
input.bridge_hostTeleopit host receiver bind host0.0.0.0
input.bridge_portTeleopit host receiver TCP/UDP port63901
input.bridge_discoveryEnable pico-bridge discovery advertisingtrue
input.bridge_advertise_ipOptional advertised host IP overridenull
input.bridge_start_timeoutTimeout while starting the bridge10.0
input.bridge_history_sizePico frame history retained by the bridge120
input.video.enabledStream host camera preview back to Pico through pico-bridge 0.2.1false
input.video.sourceVideo source: mujoco, realsense, or test-patternnull
input.video.width / height / fpsVideo capture/render settings1280 / 720 / 30
input.video.deviceOptional RealSense serialnull

Realtime

FieldDescription
retarget_buffer_enabledEnable retarget buffering
retarget_buffer_window_sBuffer window size
retarget_buffer_delay_sBuffer delay
reference_stepsReference window steps
realtime_buffer_warmup_stepsWarmup before playback
reference_velocity_smoothing_alphaVelocity smoothing
reference_anchor_velocity_smoothing_alphaAnchor velocity smoothing

Sim2Real

Fields used by sim2real configs (sim2real.yaml, pico4_sim2real.yaml).

Sim2real defaults to viewers=none. Set viewers=retarget to open an optional MuJoCo window showing the retargeted reference; sim2sim, mocap, camera, and all are simulation-only viewer modes.

Safety

FieldDescriptionDefault
startup_ramp_durationKp ramp duration after entering STANDING; gradually increases PD gains without changing policy targets2.0
joint_vel_limitJoint velocity limit (rad/s); triggers emergency damping if exceeded10.0
mocap_switch.check_framesConsecutive valid frames required before switching to MOCAP10
arm_mocap.controlled_joint_indicesG1 joints driven by live retargeting in Pico ARMS mode[15..28]

Host High-Level Policy (independent sim2real)

high_level_policy_sim2real.yaml is used only by scripts/run/run_high_level_policy_sim2real.py. It starts camera, network client, robot-control, LinkerHand O6, and OpenNeck workers. It does not start PicoBridge, GMR, or a retarget reference worker. The host LeRobot environment remains separate and must track the current client/server message structure and protocol tests. The only shared data file is hand_calibration.json.

FieldDescriptionDefault
camera.sourceOnboard policy camera: realsense or integration-only test-patternrealsense
camera.width / height / fpsExact policy image contract640 / 480 / 30
camera.deviceOptional RealSense serialnull
standing_return_ramp_durationKp-ramp duration when returning from active control to STANDING2.0
high_level_policy.endpointHost policy ZeroMQ TCP endpointtcp://127.0.0.1:5555
high_level_policy.taskNon-empty task prompt sent on reset and every observationdemo
high_level_policy.timeout_sPer-request network deadline; expiry pauses POLICY1.0
high_level_policy.reconnect_backoff_sRetry delay while establishing a new session1.0
high_level_policy.replan_stepsMinimum interval between requests in 30 Hz source frames; must not exceed the horizon reported by the host3
high_level_policy.jpeg_qualityJPEG quality for the 640x480 RGB frame90
high_level_policy.max_observation_age_sMaximum camera/observation age before a request is skipped0.15
high_level_policy.max_result_age_sMaximum local IPC age before a received result is rejected0.1
high_level_policy.entry_timeout_sMaximum time to establish the entry session and receive its first valid chunk, and maximum fresh-chunk wait on resume5.0
high_level_policy.hold_sFinal-reference grace period after the active plan horizon before the action watchdog pauses POLICY3.0
high_level_policy.safety.root_height_min_m / root_height_max_mAccepted absolute root-height range0.55 / 1.05
high_level_policy.safety.max_root_xy_speed_m_sRoot XY speed limit applied to the 50 Hz scheduler output2.5
high_level_policy.safety.max_root_displacement_mSource-frame-equivalent 3D root step used by the 50 Hz output limiter0.1
high_level_policy.safety.max_yaw_rate_rad_sRoot yaw-rate limit applied to the 50 Hz scheduler output2.5
high_level_policy.safety.max_joint_rate_rad_sPer-joint rate limit applied to the 50 Hz scheduler output10.0
high_level_policy.safety.max_joint_projection_radMaximum correction allowed when clipping a G1 joint reference to its position limit0.1
high_level_policy.safety.neck_yaw_min_deg / neck_yaw_max_degOpenNeck yaw clipping range-45 / 45
high_level_policy.safety.neck_pitch_min_deg / neck_pitch_max_degOpenNeck pitch clipping range-40 / 40

The request loop is asynchronous and receding-horizon. The isolated client has at most one ZeroMQ request in flight, selects the latest eligible observation at the configured source-frame stride, and leaves the current action plan running during host inference. A newer response replaces that plan according to its echoed onboard monotonic observation timestamp.

G1 reference joint positions are clipped to real_robot.joint_pos_lower/upper when the required correction does not exceed high_level_policy.safety.max_joint_projection_rad; larger corrections reject the chunk. OpenNeck yaw/pitch values are clipped to their configured ranges and do not reject a chunk solely because of neck overshoot. The initial runtime requires hands.driver=linkerhand_o6, both hand sides, and neck.driver=openneck because all canonical 50D action fields are active. OpenNeck policy values go directly to move_deg(yaw, pitch) after onboard clipping and chunk validation; Pico dead-zone and pitch-gain mapping are not applied.

Real Robot

FieldDescriptionDefault
real_robot.network_interfaceNetwork interface for Unitree DDS communication. For wired PC-to-G1 control, find the cable interface with ifconfig and set that name, for example enp130s0; for onboard robot execution, eth0 is usually correct.eth0
real_robot.kp_realReal-robot proportional gains (per joint)-
real_robot.kd_realReal-robot derivative gains (per joint)-
real_robot.kd_dampingDamping mode kd8.0
real_robot.control_modeAnkle control mode (PR = Pitch-Roll)PR
real_robot.joint_pos_lowerJoint position lower limits (rad)-
real_robot.joint_pos_upperJoint position upper limits (rad)-

Pause/Resume (Pico sim2real)

Realtime Pico resume re-centers heading and ground-plane position before tracking continues. Operators should keep still and stay as close as practical to the paused pose to reduce sudden reference changes.

Dexterous Hand (Pico sim2real)

hands.enabled=true requires input.provider=pico4 plus local editable installs of third_party/linkerhand-python-sdk and third_party/somehand. When enabled, hand control remains active in all sim2real modes. gripper supports linkerhand_l6 and linkerhand_o6. The corresponding controller's side grip trigger is a deadman enable: while it is held, the index trigger interpolates between the configured open and close poses; releasing the side grip trigger commands that hand to open. vr_hand_pose is supported by linkerhand_l6 and linkerhand_o6: missing hand pose holds the last command for that side, the selected hand speed is set to the maximum, and Teleopit converts Pico hand state to 21 landmarks before calling somehand 0.3.0 through somehand.api only.

FieldDescriptionDefault
hands.enabledEnable optional hand workerfalse
hands.driverHand driver plugin: linkerhand_l6 or linkerhand_o6linkerhand_l6
hands.modegripper or vr_hand_posegripper
hands.sidesControlled sides[left, right]
hands.rate_hzMaximum gripper command rate in Hz30.0
hands.frame_timeout_sController or hand-pose staleness threshold0.3
hands.linkerhand_l6.left_can / right_canCAN channels for each handcan0 / can1
hands.linkerhand_l6.speedL6 speed used by gripper; vr_hand_pose overrides this to maximum speedsee config
hands.linkerhand_l6.open_pose / close_poseSix-value L6 open/closed posessee config
hands.linkerhand_o6.left_can / right_canCAN channels for each O6 handcan0 / can1
hands.linkerhand_o6.speedO6 speed used by gripper; vr_hand_pose overrides this to maximum speedsee config
hands.linkerhand_o6.open_pose / close_poseSix-value O6 open/closed posessee config
hands.somehand.l6_config_pathOfficial somehand 0.3.0 bi-hand L6 config used by L6 vr_hand_posesee config
hands.somehand.o6_config_pathOfficial somehand 0.3.0 bi-hand O6 config used by O6 vr_hand_posesee config
hands.somehand.rate_hzLow-latency vr_hand_pose command rate in Hz60.0
hands.somehand.max_iterationssomehand solver iteration cap for vr_hand_pose12
hands.somehand.temporal_filter_alphasomehand input landmark smoothing alpha; 1.0 disables smoothing delay1.0
hands.somehand.output_alphasomehand qpos output smoothing alpha; 1.0 disables smoothing delay1.0

OpenNeck Active Vision (Pico sim2real)

neck.enabled=true requires input.provider=pico4 and the openneck extra. The neck worker reuses Teleopit's existing Pico receiver and does not start a second PicoBridge or RealSense pipeline. OpenNeck runs as a non-critical sim2real worker and does not change the policy observation. Head motion comes from the independent HMD PicoFrame.head.rotation, mapped relative to Body.Spine3 from the same source frame. The neck path never reads the full-body tracker's Body.Head skeleton joint, whose model constraints can under-report extreme head pitch. HMD updates remain independent of duplicate body-frame filtering. The mapper uses the fixed PICO neutral orientation and no neck-side EMA; startup does not capture the operator's first pose as a new zero pose, so the operator does not need to face straight when tracking starts. Teleopit converts the supported PICO convention to OpenNeck's physical convention—positive yaw turns left and positive pitch looks up. After applying neck.dead_zone_deg to the raw relative angles, it multiplies pitch by neck.pitch_gain (default 1.4) while leaving yaw one-to-one. The resulting physical angles are sent through OpenNeck 0.2.0 move_deg(). OpenNeck performs the direct-drive degree-to-step conversion and clips each target to the mechanical step limits in its calibration file.

OpenNeck 0.2.0 calibration files use angle-control fields such as yaw_center_step, yaw_min_step, yaw_max_step, and yaw_step_sign (and the corresponding pitch fields). The previous normalized OpenNeck configuration is unsupported; run openneck calibrate to create a current file. Teleopit's removed neck.yaw_range_deg, neck.pitch_range_deg, and neck.invert_* keys are rejected rather than ignored.

FieldDescriptionDefault
neck.enabledEnable optional OpenNeck workerfalse
neck.driverNeck driver plugin; currently openneckopenneck
neck.config_pathOptional OpenNeck 0.2.0 angle-calibration config pathnull
neck.portOptional serial port override, for example /dev/ttyACM0null
neck.rate_hzMaximum neck command rate in Hz60.0
neck.frame_timeout_sPico HMD/Spine3 pose staleness threshold0.2
neck.active_modesSim2real modes that allow neck motion[standing, mocap, arms, pause]
neck.dead_zone_degYaw/pitch dead zone in degrees0.5
neck.pitch_gainGain applied to relative HMD pitch after the dead zone1.4
neck.center_on_start / center_on_shutdownCenter the gimbal at worker startup/shutdowntrue / false
neck.release_on_shutdownRelease servo torque after shutdown when supportedfalse
neck.dry_runCompute commands without opening OpenNeck hardwarefalse

HDF5 Recording (Pico sim2real)

recording.enabled=true is supported only with input.provider=pico4, input.video.enabled=true, input.video.source=realsense, and an interactive terminal. The recorder is manual: R starts an episode, S saves the active episode, D discards the active episode, and Q shuts down. STANDING, MOCAP, ARMS, and paused mocap can be recorded.

sim2real_record.yaml enables both recording and the required RealSense input.video path. Recording does not open a second camera; it consumes the same frames produced by pico_input.

FieldDescriptionDefault
recording.enabledEnable manual HDF5 recordingfalse
recording.output_dirDataset root directorydata/recordings/sim2real_hdf5
recording.taskEpisode task prompt written to episodes.jsonldemo
recording.fpsRecording/video clock rate30
recording.min_episode_secondsDiscard saved episodes shorter than this duration1.0
recording.record_modesModes that allow recording start and frame writes[standing, mocap, arms, pause]
recording.camera.keyRGB image dataset keyobservation.images.d435i_rgb
recording.camera.width / height / fpsRealSense RGB capture settings640 / 480 / 30
recording.camera.deviceOptional RealSense serialnull
recording.video.codec / quality / pixelformatMP4 sidecar encoder settingslibx264 / 8 / yuv420p

RealSense frame timeouts and disconnects rebuild the capture pipeline in the background and never stop Pico input or G1 control. Recording requires a fresh camera frame before accepting R. An active episode is discarded after one second without a fresh frame, and recording remains idle after the camera recovers until the operator presses R again. If the entire pico_input worker exits, robot_control remains active and holds the latest command; the Unitree remote remains available for returning to STANDING or requesting DAMPING.

The recorder creates an editable source dataset:

recording.output_dir/
├── schema.json
├── episodes.jsonl
├── data/
│ └── episode_000000.h5
└── videos/
└── d435i_rgb/
└── episode_000000.mp4

schema.json contains the FPS, robot_type, hand_type, neck_type, and feature definitions. robot_type comes from robot.type; hand_type is none when hands are disabled, otherwise it is the configured hands.driver. neck_type is none when active-neck control is disabled, otherwise it is the configured neck.driver. These enabled flags directly control whether their state and action fields are recorded; there are no separate recording switches. episodes.jsonl contains one object per saved episode with episode_index, frames, editable task, HDF5 path, and video paths. Task prompts can therefore be relabeled without rewriting HDF5 or MP4 data. Starting another recording run with the same schema resumes at the next episode index and may use a different recording.task.

The format is intentionally not compatible with the earlier attribute-based HDF5 layout. Use an empty recording.output_dir; when an existing schema does not match, the recording worker rejects the dataset and exits without writing episodes. Recording is non-critical, so the main sim2real control runtime continues and reports the worker failure. An episode interrupted before its episodes.jsonl entry is committed is discarded on the next recording-worker startup and does not consume an episode index.

HDF5 datasets:

frame_index                    int64[N]
timestamp float64[N]
observation.state float32[N, 68]
observation.state.hand float32[N, 12] # only when hands are enabled
observation.state.neck float32[N, 2] # only when OpenNeck is enabled
observation.mode int8[N]
action float32[N, 36]
action.hand float32[N, 12] # only when hands are enabled
action.neck float32[N, 2] # only when OpenNeck is enabled

HDF5 files contain only these frame arrays and have no recording metadata root attributes. RGB frames remain in MP4 and are associated through episodes.jsonl; raw RGB HDF5 datasets are not written.

observation.state is ordered as joint_pos(29), joint_vel(29), base_quat_wxyz(4), base_ang_vel(3), and projected_gravity(3). observation.state.hand is the latest LinkerHand hardware readback: left_state(6) + right_state(6), using the SDK's 0-255 joint values. observation.state.neck is the latest OpenNeck servo position returned by read_deg(): [yaw_deg, pitch_deg] in degrees. observation.mode is a numeric categorical: standing=0, mocap=1, arms=2, and pause=3. action is the current reference qpos: root_pos(3) + root_quat_wxyz(4) + reference_joint_pos(29). It is the high-level reference consumed by the motion tracker, not the tracker policy's raw output or the final joint targets sent to G1. action.hand is the latest LinkerHand command from the hand worker: left_pose(6) + right_pose(6), using the SDK's 0-255 pose values. action.neck is the latest mechanically clamped target returned by OpenNeck after a successful command: [yaw_deg, pitch_deg] in degrees. Positive yaw turns left and positive pitch looks up. The reachable range comes from the OpenNeck calibration file and is therefore not fixed in the recording schema.

Critical: default_dof_pos

The RL policy outputs action offsets relative to the default standing pose, not absolute joint angles:

target_dof_pos = clip(action, low, high) * action_scale + default_dof_pos

Therefore:

  • default_dof_pos must align with robot.default_angles
  • Missing this offset causes the robot to fall immediately

TeleopPipeline automatically passes robot.default_angles to the controller's default_dof_pos during initialization. Understanding this chain is important when writing custom entry points or tests.