https://support.unitree.com/home/en/G1_developer https://github.com/unitreerobotics/avp_teleoperate

C++ SDK https://github.com/unitreerobotics/unitree_sdk2

Python SDK https://github.com/unitreerobotics/unitree_sdk2_python

Basic Command

Run

Run Robot
uv run src/run.py unitree_g1_humanoid

Installation on Mac

brew install uv portaudio cmake

Install cycloneDDS via https://cyclonedds.io/docs/cyclonedds/latest/installation/installation.html.

git clone https://github.com/eclipse-cyclonedds/cyclonedds -b releases/0.10.x
cd cyclonedds
mkdir build
cd build
cmake -DBUILD_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX=$HOME/Documents/GitHub/cyclonedds/install ..
cmake --build .
cmake --build . --target install

At this point, if you are connected to a robot and you run ./ddsperf sanity (located in $HOME/Documents/GitHub/cyclonedds/install/bin/ or whatever you chose) you should start to see data:

[8268] 2.005  rss:4.7MB vcsw:0 ivcsw:472 tev:0%+1% recv:0%+1%
[8268] 3.005  rss:4.8MB vcsw:0 ivcsw:275 tev:0%+1% others:0%+1%

List CycloneDDS Topics on Mac

export CYCLONEDDS_HOME=$HOME/Documents/GitHub/cyclonedds/install
export CMAKE_PREFIX_PATH=$HOME/Documents/GitHub/cyclonedds/install

cd $HOME/Documents/GitHub/cyclonedds/install/share/CycloneDDS/examples/listtopics
cmake .
cmake --build .

Then, set your wired Ethernet adapter to 192.168.123.99 and 255.255.255.0, double check via ifconfig, and set the correct Ethernet adapter name in NetworkInterface:

export CYCLONEDDS_URI='
<CycloneDDS><Domain>
  <General>
  <Interfaces><NetworkInterface name="en0" priority="default" multicast="default" /></Interfaces>
  </General>
  <Discovery><EnableTopicDiscoveryEndpoints>true</EnableTopicDiscoveryEndpoints></Discovery>
  </Domain>
</CycloneDDS>'

Now, when you run ./listtopics, you should see an extensive data dump:

alive: ea9d27e0:9769a902:84fcfb59:2b6083bc rt/lf/lowstate unitree_go::msg::dds_::LowState_
alive: 3b07fe34:41cac3a8:9f12e6e6:f719133e rt/api/motion_switcher/response unitree_api::msg::dds_::Response_
alive: eec56342:f93120c6:bab05432:a1b4a0f8 rt/api/motion_switcher/request unitree_api::msg::dds_::Request_
alive: 8e9380cf:5e6f9214:9d5e768f:dfd6d595 rt/utlidar/voxel_map sensor_msgs::msg::dds_::PointCloud2_
alive: fee410f9:eaab3e20:b1c6a349:ac7b8b8c rt/utlidar/voxel_map_compressed unitree_go::msg::dds_::VoxelMapCompressed_
alive: d0709b33:bfcd6551:4d85a309:74df1cbb rt/utlidar/height_map sensor_msgs::msg::dds_::PointCloud2_
alive: 9e531810:a229c451:3315fd9c:415edc24 rt/utlidar/range_map sensor_msgs::msg::dds_::PointCloud2_
alive: 0a6d5257:787977ea:5ac0156c:1b39ba46 rt/utlidar/range_info geometry_msgs::msg::dds_::PointStamped_
alive: 83ebb9e5:6d69cbf8:4b458542:dae1be14 rt/utlidar/height_map_array unitree_go::msg::dds_::HeightMap_
alive: 1bf83b4b:562d97ef:9df5443a:84971a8a rt/utlidar/map_state unitree_go::msg::dds_::VoxelHeightMapState_
alive: a71b9ea9:984d116d:a2400d39:0839ce01 rt/utlidar/grid_map sensor_msgs::msg::dds_::PointCloud2_
alive: 3db4a0f1:1e75176b:ed20434a:fe87a63f rt/utlidar/robot_odom nav_msgs::msg::dds_::Odometry_
alive: 036f7221:ad729459:9435a047:09a9934e rt/utlidar/cloud_deskewed sensor_msgs::msg::dds_::PointCloud2_
alive: aa29f45f:8ce692b7:22cfb924:75a5bc71 rt/utlidar/mapping_cmd std_msgs::msg::dds_::String_
alive: 33264264:c316c9f9:525f6b90:cb1eb2ec rt/wirelesscontroller unitree_go::msg::dds_::WirelessController_
alive: 9613c077:46dfce42:88f8e974:9c7c8717 rt/api/sport/request unitree_api::msg::dds_::Request_
alive: d994fe76:2755380f:1d1835bb:ddba9c93 rt/api/obstacles_avoid/request unitree_api::msg::dds_::Request_
...

The Ethernet adapter you set above (such as en0) is the value you should provide to /config/unitree_g1_humanoid.json.

Then add the optional Python CycloneDDS module to OM1:

uv pip install -r pyproject.toml --extra dds

Installation on Linux

You will need:

  • OM1
  • uv
  • ffmpeg (for audio, otherwise the audio out will not work due to missing ffprobe)
  • v4l-utils (for video)
  • CycloneDDS (for DDS comms to the G1 motion client)
sudo apt-get update
sudo apt-get install ffmpeg v4l-utils

v4l-utils is also useful to debug video problems. WARNING: The camera system, if not correctly configured, has a tendency to bring down the entire USB bus. FIX: reboot the humanoid.

Set the correct CYCLONEDDS_HOME env var. This is where the actual CycloneDDS is installed on your computer:

export CYCLONEDDS_HOME="$HOME/unitree_ros2/cyclonedds_ws/install/cyclonedds"

If you do not do this correctly, installation of the Python CycloneDDS, a later step, will fail since it cannot find the correct libraries. Note: make absolutely sure CYCLONEDDS_HOME actually points to the CycloneDDS install /lib. This can be confusing, since if you install indirectly via unitree_ros2, then the location of the CycloneDDS libraries will be in slightly different location than if you install directly, via git clone https://github.com/eclipse-cyclonedds/cyclonedds.

Then add the optional Python CycloneDDS module to OM1:

uv pip install -r pyproject.toml --extra dds

Note: on first invocation, the system sometimes cannot find the Unitree libraries. This should resolve by itself quickly.

ORIN/G1: System Description

Your development computer will (should) be at 192.168.123.99

The LIDAR is 192.168.123.120

The internal control computer (RockChip, aka the operation and control computing unit) is at 192.168.123.161

The internal development computer (Orin 16GB, aka the development computing unit) is at 192.168.123.164

Useful commands:

sudo nmcli radio wifi on # Turn the wifi on/off
sudo nmcli device wifi connect XXXXX password XXXXX # Join WiFi network
sudo timedatectl set-ntp yes # Set time via NTP

ORIN: Set default input and output Audio devices

pactl list sources short                      # List input (microphone) devices
pactl list sinks short                        # List output (speaker) devices
pactl set-default-sink [SINK_NAME || SINK_ID] # Set default output device
pactl set-default-source [SOURCE_NAME || SOURCE_ID] # Set default input device
pactl set-sink-volume @DEFAULT_SINK@ 70%      # Set default output volume

Control via Unitree Hand Controller

Hang G1 on gantry
Turn on (short press, long press)
Wait for boot to complete
When the G1 boots, it is in damp state
Use the hand controller to command “L1+A” and “L1+UP”\

The system is then ready to move using the ai_sport client. The system will respond to manual controller and SDK commands.

  • Press “L1+A” -> EMERGENCY DAMP / SINK TO FLOOR
  • Press “L1+UP” -> Stand firmly (aka “lock stand”). The arms will move slightly. The system is now in the “ready” state.
  • Lower the G1 to the ground (but do not unclip her yet). Stability not yet running - she will fall over if let go.
  • Press “R2+X” -> Start motion control. The arms will jump outwards and she will actively control her stability.
  • Press “Start” to switch back and forth between stand and step in place.

Other actions:

  • “SELECT + Y” -> Wave Hand. Alternates sides.
  • “SELECT + A” -> Handshake. Hold for movement to complete. Wait 3s and press again to relax arm to initial state.
  • “SELECT + X” -> Turn around and wave hand.s

Use the joysticks to move forwards and backwards, and to rotate/turn

Boot from Chair

This is similar to boot from gantry, except, when you press “L1+UP”, you have to help the humanoid stand up, while it straightens itself. For the sit-down procedure, back up the chair behind the robot, select “L1+LEFT”, and help the humanoid settle back into the chair.

Special DEBUG state

Avoid this mode since it disables all high level motion since it turns off the ai_sport client.

  • Press L2+R2 -> Enter DEBUG STATE
  • Press L2+A -> Diagnostic Posture (Arms bent)
  • Press L2+B -> Relax arms, damping state

To exit this mode, reboot the G1.

Using the Internal Orin

SSH to Orin via

ssh unitree@192.168.123.164

The default password is 123 but you should obviously change this. Result:

Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.10.104-tegra aarch64)
Last login: Thu Jan  1 08:30:46 1970
ros:foxy(1) noetic(2) ?

Select foxy(1).

Fixing the broken CycloneDDS installation on the Nvidia Orin

The default installation of CycloneDDS on the G1 Orin is broken, since it does not support the newer unitree_hg IDL data format for the G1. Solution: remove the default CycloneDDS installation and reinstall following the Unitree ROS2 installation instructions. The unitree_hg bug was fixed in early Dec. 2024 in this commit: unitreerobotics/unitree_ros2@b34fdf7.

You will need to export suitable env variables and correct the setting in .bashrc and in $HOME/unitree_ros2/setup.sh. Add this to the .bashrc:

export CYCLONEDDS_HOME="$HOME/unitree_ros2/cyclonedds_ws/install/cyclonedds"

Set $HOME/unitree_ros2/setup.sh to

#!/bin/bash
echo "Setup Unitree ROS2 Environment"
source /opt/ros/foxy/setup.bash
source $HOME/unitree_ros2/cyclonedds_ws/install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI='<CycloneDDS><Domain><General><Interfaces>
<NetworkInterface name="eth0" priority="default" multicast="default" />
</Interfaces></General></Domain></CycloneDDS>'

Source the setup.sh via source ~/unitree_ros2/setup.sh. Finally, you should start to see data.

ros2 topic echo lowstate
version:
- 0
- 0
mode_pr: 0
mode_machine: 4
tick: 1120016
imu_state:
  quaternion:
  - 0.9940093159675598
  - 0.0074933432042598724
  - -0.0023974007926881313
  - -0.10901317745447159
  gyroscope:
  - -0.00523598724976182
  - -0.00523598724976182
  - -0.0017453291220590472
  accelerometer:
  - 0.009999999776482582
  - -0.05000000074505806
  - 9.829999923706055
  rpy:
  - 0.015420285053551197
  - -0.003132336074486375
  - -0.21849146485328674
  temperature: 78
motor_state:
- mode: 1
  q: -0.008177042007446289
  dq: 0.0
  ddq: 0.0
  tau_est: -0.11186079680919647
  temperature:
  - 29
  - 28
  vol: 49.0
  sensor:
  - 0
  - 0
  motorstate: 0
  reserve:
  - 0
  - 1142
  - 4
  - 0
ros2 topic echo lowcmd
mode_pr: 0
mode_machine: 4
motor_cmd:
- mode: 1
  q: 0.0
  dq: 0.0
  tau: 0.0
  kp: 0.0
  kd: 0.0
  reserve: 0
ros2 topic echo odommodestate
stamp:
  sec: 0
  nanosec: 0
error_code: 0
imu_state:
  quaternion:
  - 0.9981062412261963
  - 0.007613412104547024
  - -0.0016289741033688188
  - -0.06102222576737404
  gyroscope:
  - 0.0
  - 0.0
  - 0.0
  accelerometer:
  - 0.0
  - 0.0
  - 0.0
  rpy:
  - 0.01539743971079588
  - -0.002322605811059475
  - -0.12214189022779465
  temperature: 0
mode: 0
progress: 0.0
gait_type: 0
foot_raise_height: 0.0
position:
- 0.0013194791972637177
- -0.018103253096342087
- 0.7247929573059082
body_height: 0.0
velocity:
- 6.116795248090057e-07
- -1.2412459682309418e-06
- -4.238392648403533e-05
yaw_speed: 0.0017453291220590472
range_obstacle:
- 0.0
...
foot_force:
- 0
- 0
- 0
- 0
foot_position_body:
- 0.0
...
foot_speed_body:
- 0.0
...
ros2 topic list
/EstimatorData
/SymState
/SymState_back
/api/bashrunner/request
/api/bashrunner/response
/api/config/request
/api/config/response
/api/loco/request
/api/loco/response
/api/motion_switcher/request
/api/motion_switcher/response
/api/robot_state/request
/api/robot_state/response
/arm_sdk
/audiosender
/config_change_status
/dex3/left/cmd
/dex3/left/state
/dex3/right/cmd
/dex3/right/state
/frontvideostream
/gnss
/lf/bmsstate
/lf/dex3/left/state
/lf/dex3/right/state
/lf/lowstate
/lf/lowstate_doubleimu
/lf/mainboardstate
/lf/odommodestate
/loco_sdk
/lowcmd
/lowstate
/lowstate_doubleimu
/multiplestate
/odommodestate
/parameter_events
/public_network_status
/rosout
/rtc/state
/rtc_status
/selftest
/servicestate
/servicestateactivate
/videohub/inner
/webrtcreq
/webrtcres
/wirelesscontroller

Here is what will be visible on an external development machine at .99, for example, using ./bin/listtopics:

rt/dex3/right/state unitree_hg::msg::dds_::HandState_
rt/lf/dex3/right/state unitree_hg::msg::dds_::HandState_
rt/dex3/right/cmd unitree_hg::msg::dds_::HandCmd_
rt/EstimatorData unitree_go::msg::dds_::EstimatorData_
rt/SymState_back unitree_go::msg::dds_::SymState_
rt/odommodestate unitree_go::msg::dds_::SportModeState_
rt/lf/odommodestate unitree_go::msg::dds_::SportModeState_
rt/lowstate unitree_hg::msg::dds_::LowState_
rt/SymState unitree_go::msg::dds_::SymState_
rt/api/bashrunner/response unitree_api::msg::dds_::Response_
rt/selftest std_msgs::msg::dds_::String_
rt/api/bashrunner/request unitree_api::msg::dds_::Request_
rt/lf/lowstate unitree_hg::msg::dds_::LowState_
rt/api/motion_switcher/response unitree_api::msg::dds_::Response_
rt/api/motion_switcher/request unitree_api::msg::dds_::Request_
rt/config_change_status unitree_go::msg::dds_::ConfigChangeStatus_
rt/api/config/response unitree_api::msg::dds_::Response_
rt/api/config/request unitree_api::msg::dds_::Request_
rt/api/robot_state/response unitree_api::msg::dds_::Response_
rt/api/robot_state/request unitree_api::msg::dds_::Request_
rt/servicestate std_msgs::msg::dds_::String_
rt/multiplestate std_msgs::msg::dds_::String_
rt/public_network_status std_msgs::msg::dds_::String_
rt/gnss std_msgs::msg::dds_::String_
rt/lf/bmsstate unitree_hg::msg::dds_::BmsState_
rt/lf/mainboardstate unitree_hg::msg::dds_::MainBoardState_
rt/webrtcreq std_msgs::msg::dds_::String_
rt/webrtcres std_msgs::msg::dds_::String_
rt/lowcmd unitree_hg::msg::dds_::LowCmd_
rt/lowstate_doubleimu unitree_hg_doubleimu::msg::dds_::doubleIMUState_
rt/lf/lowstate_doubleimu unitree_hg_doubleimu::msg::dds_::doubleIMUState_
rt/wirelesscontroller unitree_go::msg::dds_::WirelessController_
rt/frontvideostream unitree_go::msg::dds_::Go2FrontVideoData_
rt/audiosender unitree_go::msg::dds_::AudioData_
rt/servicestateactivate std_msgs::msg::dds_::String_
rt/rtc_status std_msgs::msg::dds_::String_
rt/videohub/inner std_msgs::msg::dds_::String_
rt/rtc/state std_msgs::msg::dds_::String_
rt/api/bashrunner/request unitree_api::msg::dds_::Request_
rt/lf/dex3/left/state unitree_hg::msg::dds_::HandState_
rt/dex3/left/state unitree_hg::msg::dds_::HandState_
rt/dex3/left/cmd unitree_hg::msg::dds_::HandCmd_

Terminal based setup of Bluetooth audio devices

This is only needed on the headless Orin, otherwise (e.g. on the Mac) just use the system settings.

bluetoothctl

list    # show all paired devices
scan on # search for nearby devices
# once you have found the right device, you can then pair it
# many devices also require 'trusting' them

trust <MAC>
pair <MAC>
connect <MAC>