unitree_sdk2_python: talking to Unitree quadrupeds over DDS from Python
Python interface for unitree sdk2
At a glance
- What is it?
- unitree_sdk2_python is a Python binding for Unitree's sdk2 that exposes robot status and control through request-response calls and topic publish/subscribe, with a high-level and a low-level interface matching the C++ sdk. The catch is installation: the cyclonedds dependency has to be built from source and pointed at with CYCLONEDDS_HOME.
- Who is it for?
- unitree_sdk2_python suits engineers who already have a Unitree quadruped on the local network and want to script it in Python rather than work through the C++ sdk, and who are prepared to compile cyclonedds and export CYCLONEDDS_HOME before pip can install the package.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 33 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A Python surface over unitree sdk2, not a reimplementation
unitree_sdk2_python describes itself as a Python interface for unitree sdk2, and the documentation is explicit that the Python interface maintains consistency with the unitree_sdk2 interface. That consistency is stated twice, once for the general SDK and once for each of the high-level and low-level layers, and it covers both data structure and control methods.
So the division of labour is clear. Message definitions, topic names and service semantics come from the DDS layer that sdk2 already defines, and this repository is the Python binding on top of it. If you already know the C++ sdk, the curve here is short. If you have never touched sdk2, this is still an entry point, but you will be reading Unitree's own documentation for the semantics of what you are calling.
Communication follows two shapes, and the documentation names both: request-response for acquiring status and issuing control, and topic subscription or publishing for streaming data. The package directory is unitree_sdk2py, setup.py declares the distribution name as unitree_sdk2py, and the version recorded there is 1.0.1. The repository also holds an example directory organised by robot model, with entries for a2, as2, b2, b2w, g1, go2, go2w, h1, h1_2, h2, r1, helloworld, motionSwitcher, obstacles_avoid, vui_client and wireless_controller.
Installing from source, and the cyclonedds build that gates it
Installation is a source install, and the documentation gives the exact terminal sequence:
cd ~
sudo apt install python3-pip
git clone https://github.com/unitreerobotics/unitree_sdk2_python.git
cd unitree_sdk2_python
pip3 install -e .The dependency list is Python >= 3.8, cyclonedds == 0.10.2, numpy and opencv-python, and setup.py encodes the same set in install_requires with the same exact pin on cyclonedds. That pin is where the friction sits. The documentation has a dedicated FAQ entry for the failure that follows:
Could not locate cyclonedds. Try to set CYCLONEDDS_HOME or CMAKE_PREFIX_PATHThe documented fix is to compile and install cyclonedds yourself from the 0.10.x release branch, then point the environment at the result:
cd ~
git clone https://github.com/eclipse-cyclonedds/cyclonedds -b releases/0.10.x
cd cyclonedds && mkdir build install && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=../install
cmake --build . --target installThen the install is retried with the environment variable set:
cd ~/unitree_sdk2_python
export CYCLONEDDS_HOME=~/cyclonedds/install
pip3 install -e .The export is not persisted anywhere in the repository, so it has to be repeated in each new shell. The documentation also points at the cyclonedds page on PyPI for pre-built binaries as an alternative to compiling.
First check: a publisher and subscriber exchanging a user-defined structure
The DDS communication example is the right first run, because it exercises the transport without moving anything. The documentation asks you to configure the robot's network connection first, following the Quick start document hosted on support.unitree.com.
With the network in place, the publisher runs in one terminal:
python3 ./example/helloworld/publisher.pyand the subscriber in a second:
python3 ./example/helloworld/subscriber.pyData output appears in the terminal. The payload that travels between the two scripts is defined in user_data.py, and the documentation says users can define the required data structure as needed. That detail is the most useful line in the section for anyone planning to do more than run the demo: the example is a template for a custom DDS topic, not a fixed message.
A note on the naming: the example directory in the repository is listed as helloworld, and the commands match it. Everything else in the example tree is grouped by model, which suggests the intended way to find a starting point is to look for the folder matching the robot on your desk.
The high-level interface: read_highstate and sport mode
The high-level layer is the one that maps to what most users think of as the robot's own behaviour. The documentation describes it as consistent with unitree_sdk2 in terms of data structure and control methods, and points to the sports services page on support.unitree.com for details.
Reading the full state is a single command with the network interface as its argument:
python3 ./example/high_level/read_highstate.py enp2s0Every command in the documentation takes that interface name, and the instruction is to replace enp2s0 with the name of the network interface the robot is connected to. It is a positional argument, not an environment variable, and it is repeated in each command rather than configured once.
Control is exercised through the sport mode test program:
python3 ./example/high_level/sportmode_test.py enp2s0The program exposes several test methods, and the documentation shows the list with the ones you want commented out:
test.StandUpDown() # Stand up and lie down
# test.VelocityMove() # Velocity control
# test.BalanceAttitude() # Attitude control
# test.TrajectoryFollow() # Trajectory tracking
# test.SpecialMotions() # Special motionsThe commenting pattern is the ergonomic point. You enable one behaviour at a time by uncommenting the line, which keeps a test run to a single movement type. This is also where the physical risk sits: these calls command a real machine, and StandUpDown means the robot stands up.
The low-level interface: joint state and motor control
The low-level layer addresses the legs and the IMU directly, and it maps to the basic services documentation rather than the sports services one. The low-level control program prints the state of the right front leg hip joint, the IMU and the battery voltage:
python3 ./example/low_level/lowlevel_control.py enp2s0Motor control runs through the same program, and the documentation is careful about the order of operations. Before issuing low-level commands, you are told to use the app to turn off the high-level motion service, sport mode, so that the two layers do not send conflicting instructions. That is a safety step stated in prose rather than enforced in code, and it depends on a phone or desktop application rather than on anything in this repository.
The second low-level command, again with the interface name:
python3 ./example/low_level/lowlevel_control.py enp2s0The documented behaviour is specific: the left hind leg hip joint holds a 0-degree position, with kp=10 and kd=1 for safety, and the left hind leg calf joint continuously outputs 1Nm of torque. A continuous torque on a real joint deserves attention before you run it on hardware you care about.
One discrepancy is worth flagging. The documentation section for motor control and the section for low-level status print the identical command line, so the documentation does not show a distinct invocation for the motor control path.
Peripheral services: wireless controller, camera, obstacle avoidance, vui
Beyond locomotion, the example tree covers the accessories, and each one is again a single command with the interface argument.
The wireless controller example reads the state of each key on the handheld remote:
python3 ./example/wireless_controller/wireless_controller.py enp2s0The front camera example uses OpenCV to grab the camera feed. The documentation adds a condition here that the other sections do not carry: run it on a system with a graphical interface, and press ESC to exit the program.
python3 ./example/front_camera/camera_opencv.py enp2s0That constraint explains why opencv-python, rather than a headless build, appears in the dependency list.
The obstacle avoidance switch example cycles the avoidance behaviour on and off:
python3 ./example/obstacles_avoid_switch/obstacles_avoid_switch.py enp2s0The vui client example cycles volume and light brightness:
python3 ./example/vui_client/vui_client_example.py enp2s0Taken together, these are thin wrappers over services the robot already exposes, and the documentation defers the semantics of each to a support.unitree.com page. The value of the examples is the working Python call pattern and the argument convention, not new functionality.
Where this SDK does not fit, and what to compare it against
Three limits are visible in the documentation itself.
The first is platform. The install sequence uses apt and the camera example requires a graphical interface, so the documented path is Linux with a display on the machine running the camera code. The repository does not describe macOS or Windows support.
The second is the cyclonedds build. If pip cannot find cyclonedds, the answer is to clone and compile the 0.10.x branch, which means a C toolchain, CMake and a manual environment variable. The PyPI page for cyclonedds offers pre-built binaries as a way around this, but the SDK's own pinned version still has to match. A team without a C build environment is blocked at step one.
The third is versioning discipline. The repository publishes no GitHub releases, and setup.py records version 1.0.1 against the current source. There is no tag history to pin against, so a deployment has to track master and accept that pip install -e . installs whatever the branch held at that moment.
The obvious alternative is the C++ sdk2 itself, and the documentation positions this package as consistent with it in data structure and control methods, so the difference is language and distribution rather than capability. If your team already writes C++ and builds against sdk2 natively, this binding adds an install dependency rather than removing one. A third option is to skip the SDK and talk DDS directly to the robot's topics, which avoids the Python packaging entirely but means reimplementing the message structures the SDK already defines. Choose this repository when Python is what you want to write the control logic in, and the interface names and high-level and low-level split are the parts that make that pleasant.
Editorial conclusion
unitree_sdk2_python suits engineers who already have a Unitree quadruped on the local network and want to script it in Python rather than work through the C++ sdk, and who are prepared to compile cyclonedds and export CYCLONEDDS_HOME before pip can install the package. It is the wrong choice for a workstation that has no robot on its subnet, for a macOS user, or for anyone who needs a released, pinned version of the library, because the repository publishes no GitHub releases. Before you start, confirm that the robot's network interface is up, that you can reach the support.unitree.com Quick start page for the network configuration, and that the cyclonedds 0.10.x branch you build matches the cyclonedds==0.10.2 pin in setup.py. The last push on the repository was on 2026-09-21.
Frequently asked questions
How do I install unitree_sdk2_python?
Clone the repository and run pip3 install -e . from the unitree_sdk2_python directory, as the documentation's install section shows. The package needs Python >= 3.8 and depends on cyclonedds==0.10.2, numpy and opencv-python.
What does the error "Could not locate cyclonedds. Try to set CYCLONEDDS_HOME or CMAKE_PREFIX_PATH" mean?
It means the pip build could not find the cyclonedds library on the system. The documented answer is to compile and install cyclonedds from the releases/0.10.x branch, then export CYCLONEDDS_HOME to that install path before running pip3 install -e . again.
How do I read the robot's state and send high-level commands from Python?
Run python3 ./example/high_level/read_highstate.py with the robot's network interface name as the argument to read the state, and python3 ./example/high_level/sportmode_test.py with the same argument to exercise high-level control such as StandUpDown, VelocityMove or BalanceAttitude.
What licence is unitree_sdk2_python released under?
The repository is under BSD-3-Clause, and setup.py declares the same licence for the unitree_sdk2py distribution, with author UnitreeRobotics and version 1.0.1.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/unitreerobotics-unitree-sdk2-python)Was this useful?