Skip to main content

Pico 4 VR Teleoperation on Unitree G1

Use this tutorial after Pico Sim2Sim is working. It deploys the same realtime Pico input path to a physical Unitree G1.

Pico headset -> Teleopit host -> retarget -> RL policy -> g1_bridge_sdk -> G1

There are two deployment styles:

DeploymentWhere Teleopit RunsMain Difference
Wired PC-to-G1External workstation or laptopSet real_robot.network_interface to the PC Ethernet interface connected to G1
OnboardG1 onboard computerInstall Teleopit on the onboard computer; eth0 is usually correct

Both styles use Pico4InputProvider and the in-process pico-bridge receiver. There is no separate onboard Pico input mode.

1. Install Runtime Dependencies

Install Pico and sim2real dependencies on the machine that will run Teleopit:

pip install -e '.[pico4]'
git submodule update --init --recursive
bash scripts/setup/setup_g1_bridge.sh

Verify Pico receiver import:

python -c "from pico_bridge import PicoBridge; print('OK')"

2. Choose The Network Interface

real_robot.network_interface is the Linux interface used for Unitree DDS communication.

For wired PC-to-G1 deployment:

  1. Connect the PC to the G1 by Ethernet.
  2. Run ifconfig on the PC.
  3. Use the Ethernet interface connected to the robot, for example enp130s0.
  4. Keep the Pico headset on a network that can reach the PC running Teleopit.

For onboard deployment:

  1. Run Teleopit on the robot onboard computer.
  2. Keep the Pico headset on a network that can reach the onboard computer.
  3. Use real_robot.network_interface=eth0 unless your robot network differs.
  4. Set input.bridge_advertise_ip=<host-ip> if Pico discovery advertises the wrong address.

Onboard RealSense On Arm

The pico-bridge PC receiver supports Arm machines when the required Python dependencies are available. On Arm onboard computers that need RealSense preview, install pyrealsense2 from conda-forge in the active Conda environment instead of relying on the pip package:

pip uninstall pyrealsense2
conda install -c conda-forge pyrealsense2

This only matters when using the optional RealSense preview path (input.video.enabled=true). Pico tracking and robot control do not require RealSense.

3. Run The Controller

Wired PC example:

python scripts/run/run_sim2real.py \
--config-name pico4_sim2real \
controller.policy_path=track.onnx \
real_robot.network_interface=enp130s0

Onboard example:

python scripts/run/run_sim2real.py \
--config-name pico4_sim2real \
controller.policy_path=track.onnx \
real_robot.network_interface=eth0

Operator Flow

Keep the Unitree remote in hand. L1+R1 is the emergency stop path into DAMPING.

ControlAction
Unitree remote StartEnter STANDING
Unitree remote YEnter MOCAP
Pico/controller APause / resume live mocap
Unitree remote XReturn to STANDING
Unitree remote L1+R1Emergency stop (DAMPING)

Enter MOCAP only after Pico tracking is stable. Teleopit validates consecutive mocap frames before switching; if validation fails, the robot stays in STANDING.

Runtime Behavior

Pico sim2real uses the shared realtime reference timeline:

Pico body frames -> retarget -> reference buffer -> observation -> policy -> G1 joints

When entering STANDING, Teleopit releases active Unitree modes, enters debug/low-level control, locks the current joints briefly, resets policy state, and ramps Kp to reduce startup spikes.

When entering MOCAP, Teleopit resets policy/reference state and blends the reference from the current robot state into the live mocap command.

Pause / Resume

Pico pause/resume is a mocap-session control event.

  • ACTIVE: the pause button freezes the current reference pose.
  • PAUSED: pressing it again clears policy/reference state, warms the realtime buffer, re-centers yaw/XY alignment, and resumes from live mocap.
warning

Resume while standing still and close to the paused pose. This reduces sudden reference changes when live tracking resumes.

Optional RealSense Preview

Stream the G1 RealSense color camera back to the Pico headset:

python scripts/run/run_sim2real.py \
--config-name pico4_sim2real \
controller.policy_path=track.onnx \
real_robot.network_interface=enp130s0 \
input.video.enabled=true \
input.video.device=<optional-realsense-serial>

If video fails, control continues unless input.video.fail_on_error=true.

Common Parameters

# Real G1 DDS interface
real_robot.network_interface=enp130s0

# Pico timeout
input.pico4_timeout=30

# Override advertised Pico discovery IP
input.bridge_advertise_ip=192.168.1.20

# Consecutive valid mocap frames required before MOCAP
mocap_switch.check_frames=10

# Smooth transition into mocap reference
transition_duration=2.0

# Realtime frames to collect before resume
pause_resume_warmup_steps=2

# Change Pico pause button
input.pause_button=right_axis_click

# Enable headset video preview
input.video.enabled=true

Troubleshooting

SymptomLikely CauseFix
No LowState receivedWrong interface or G1 network not connectedCheck Ethernet wiring and real_robot.network_interface
TimeoutError: No Pico4 body dataHeadset is not connected or tracking is inactiveCheck headset app, network, and input.pico4_timeout
Cannot enter debug modeUnitree mode release failedStop other robot modes and press Start again
Robot enters STANDING but not MOCAPMocap validation failedKeep tracking active and stable; check mocap_switch.check_frames logs
Pico pause does not return to STANDINGExpected behaviorPico pause freezes mocap; press remote X for STANDING
Video preview is unavailableRealSense or video source failedCheck camera permissions, input.video.source, and logs