Browse Source

Initial commit

main
Dennis Da 4 months ago
commit
ffe9f7faea
  1. 23
      .gitattributes
  2. 175
      .gitignore
  3. 38
      LICENSE
  4. 16
      Makefile
  5. 140
      README.md
  6. 160
      docker/.bashrc
  7. 84
      docker/.tmux.conf
  8. 5
      docker/70-manus-hid.rules
  9. 127
      docker/Dockerfile.deploy
  10. 155
      docker/Dockerfile.deploy.base
  11. 55
      docker/build_deploy_base.sh
  12. 5
      docker/entrypoint/bash.sh
  13. 20
      docker/entrypoint/deploy.sh
  14. 23
      docker/entrypoint/install_deps.sh
  15. 1
      docker/image_name.txt
  16. 79
      docker/kill_all_containers.sh
  17. 192
      docker/kill_gr00t_wbc_processors.sh
  18. 3
      docker/publish.sh
  19. 454
      docker/run_docker.sh
  20. 39
      external_dependencies/unitree_sdk2_python/.gitignore
  21. 29
      external_dependencies/unitree_sdk2_python/LICENSE
  22. 121
      external_dependencies/unitree_sdk2_python/README zh.md
  23. 118
      external_dependencies/unitree_sdk2_python/README.md
  24. 51
      external_dependencies/unitree_sdk2_python/example/b2/camera/camera_opencv.py
  25. 51
      external_dependencies/unitree_sdk2_python/example/b2/camera/capture_image.py
  26. 105
      external_dependencies/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py
  27. 175
      external_dependencies/unitree_sdk2_python/example/b2/low_level/b2_stand_example.py
  28. 20
      external_dependencies/unitree_sdk2_python/example/b2/low_level/unitree_legged_const.py
  29. 51
      external_dependencies/unitree_sdk2_python/example/b2w/camera/camera_opencv.py
  30. 51
      external_dependencies/unitree_sdk2_python/example/b2w/camera/capture_image.py
  31. 101
      external_dependencies/unitree_sdk2_python/example/b2w/high_level/b2w_sport_client.py
  32. 196
      external_dependencies/unitree_sdk2_python/example/b2w/low_level/b2w_stand_example.py
  33. 24
      external_dependencies/unitree_sdk2_python/example/b2w/low_level/unitree_legged_const.py
  34. 44
      external_dependencies/unitree_sdk2_python/example/g1/audio/g1_audio_client_example.py
  35. 192
      external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm5_sdk_dds_example.py
  36. 194
      external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm7_sdk_dds_example.py
  37. 117
      external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_loco_client_example.py
  38. 205
      external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_low_level_example.py
  39. 225
      external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_move_hands_example.py
  40. 5
      external_dependencies/unitree_sdk2_python/example/g1/readme.md
  41. 41
      external_dependencies/unitree_sdk2_python/example/go2/front_camera/camera_opencv.py
  42. 30
      external_dependencies/unitree_sdk2_python/example/go2/front_camera/capture_image.py
  43. 170
      external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py
  44. 39
      external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_utlidar_switch.py
  45. 176
      external_dependencies/unitree_sdk2_python/example/go2/low_level/go2_stand_example.py
  46. 20
      external_dependencies/unitree_sdk2_python/example/go2/low_level/unitree_legged_const.py
  47. 99
      external_dependencies/unitree_sdk2_python/example/go2w/high_level/go2w_sport_client.py
  48. 196
      external_dependencies/unitree_sdk2_python/example/go2w/low_level/go2w_stand_example.py
  49. 24
      external_dependencies/unitree_sdk2_python/example/go2w/low_level/unitree_legged_const.py
  50. 96
      external_dependencies/unitree_sdk2_python/example/h1/high_level/h1_loco_client_example.py
  51. 167
      external_dependencies/unitree_sdk2_python/example/h1/low_level/h1_low_level_example.py
  52. 5
      external_dependencies/unitree_sdk2_python/example/h1/low_level/unitree_legged_const.py
  53. 201
      external_dependencies/unitree_sdk2_python/example/h1_2/low_level/h1_2_low_level_example.py
  54. 28
      external_dependencies/unitree_sdk2_python/example/helloworld/publisher.py
  55. 20
      external_dependencies/unitree_sdk2_python/example/helloworld/subscriber.py
  56. 9
      external_dependencies/unitree_sdk2_python/example/helloworld/user_data.py
  57. 36
      external_dependencies/unitree_sdk2_python/example/motionSwitcher/motion_switcher_example.py
  58. 35
      external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py
  59. 94
      external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_switch.py
  60. 77
      external_dependencies/unitree_sdk2_python/example/vui_client/vui_client_example.py
  61. 131
      external_dependencies/unitree_sdk2_python/example/wireless_controller/wireless_controller.py
  62. 3
      external_dependencies/unitree_sdk2_python/pyproject.toml
  63. 21
      external_dependencies/unitree_sdk2_python/setup.py
  64. 10
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/__init__.py
  65. 16
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_api.py
  66. 23
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_client.py
  67. 16
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_api.py
  68. 23
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_client.py
  69. 25
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py
  70. 84
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py
  71. 58
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py
  72. 241
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py
  73. 21
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_api.py
  74. 86
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_client.py
  75. 0
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/__init__.py
  76. 29
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_api.py
  77. 51
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_client.py
  78. 0
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/__init__.py
  79. 290
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel.py
  80. 25
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py
  81. 34
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_name.py
  82. 24
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_api.py
  83. 62
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py
  84. 32
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py
  85. 127
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_client.py
  86. 0
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/__init__.py
  87. 0
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/__init__.py
  88. 19
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_api.py
  89. 60
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py
  90. 0
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/__init__.py
  91. 25
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_api.py
  92. 84
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_client.py
  93. 0
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/__init__.py
  94. 74
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py
  95. 446
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py
  96. 0
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/__init__.py
  97. 16
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_api.py
  98. 23
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_client.py
  99. 0
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/__init__.py
  100. 21
      external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_api.py

23
.gitattributes

@ -0,0 +1,23 @@
# Machine learning models and data files
*.pt filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
# 3D assets and models
*.usd filter=lfs diff=lfs merge=lfs -text
*.usda filter=lfs diff=lfs merge=lfs -text
*.STL filter=lfs diff=lfs merge=lfs -text
*.stl filter=lfs diff=lfs merge=lfs -text
# Shared libraries
*.a filter=lfs diff=lfs merge=lfs -text
*.so* filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.safetensors filter=lfs diff=lfs merge=lfs -text
*.deb filter=lfs diff=lfs merge=lfs -text
# Collect demo in sim
*.hdf5 filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.obj filter=lfs diff=lfs merge=lfs -text
*.dae filter=lfs diff=lfs merge=lfs -text
*.so filter=lfs diff=lfs merge=lfs -text
*.so.* filter=lfs diff=lfs merge=lfs -text

175
.gitignore

@ -0,0 +1,175 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# IDE
.idea/
.vscode/
# log
outputs/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
outputs/
.DS_Store
# Mujoco
MUJOCO_LOG.TXT
# IsaacDeploy
external_dependencies/isaac_teleop_app/isaac-deploy

38
LICENSE

@ -0,0 +1,38 @@
NVIDIA License
1. Definitions
“Licensor” means any person or entity that distributes its Work.
“Work” means (a) the original work of authorship made available under this license, which may include software, documentation, or other files, and (b) any additions to or derivative works thereof that are made available under this license.
The terms “reproduce,” “reproduction,” “derivative works,” and “distribution” have the meaning as provided under U.S. copyright law; provided, however, that for the purposes of this license, derivative works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work.
Works are “made available” under this license by including in or with the Work either (a) a copyright notice referencing the applicability of this license to the Work, or (b) a copy of this license.
2. License Grant
2.1 Copyright Grant. Subject to the terms and conditions of this license, each Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free, copyright license to use, reproduce, prepare derivative works of, publicly display, publicly perform, sublicense and distribute its Work and any resulting derivative works in any form.
3. Limitations
3.1 Redistribution. You may reproduce or distribute the Work only if (a) you do so under this license, (b) you include a complete copy of this license with your distribution, and (c) you retain without modification any copyright, patent, trademark, or attribution notices that are present in the Work.
3.2 Derivative Works. You may specify that additional or different terms apply to the use, reproduction, and distribution of your derivative works of the Work (“Your Terms”) only if (a) Your Terms provide that the use limitation in Section 3.3 applies to your derivative works; (b) you comply with Other Licenses, and (c) you identify the specific derivative works that are subject to Your Terms and Other Licenses, as applicable. Notwithstanding Your Terms, this license (including the redistribution requirements in Section 3.1) will continue to apply to the Work itself.
3.3 Use Limitation. The Work and any derivative works thereof only may be used or intended for use non-commercially. As used herein, “non-commercially” means for non-commercial research purposes only, and excludes any military, surveillance, service of nuclear technology or biometric processing purposes.
3.4 Patent Claims. If you bring or threaten to bring a patent claim against any Licensor (including any claim, cross-claim or counterclaim in a lawsuit) to enforce any patents that you allege are infringed by any Work, then your rights under this license from such Licensor (including the grant in Section 2.1) will terminate immediately.
3.5 Trademarks. This license does not grant any rights to use any Licensor’s or its affiliates’ names, logos, or trademarks, except as necessary to reproduce the notices described in this license.
3.6 Termination. If you violate any term of this license, then your rights under this license (including the grant in Section 2.1) will terminate immediately.
3.7 Components Under Other Licenses. The Work may include or be distributed with components provided with separate legal notices or terms that accompany the components, such as open source software licenses and other license terms, including but not limited to the Meta OPT-IML 175B License Agreement (“Other Licenses”). The components are subject to the applicable Other Licenses, including any proprietary notices, disclaimers, requirements and extended use rights; except that this Agreement will prevail regarding the use of third-party software, unless a third-party software license requires it license terms to prevail.
4. Disclaimer of Warranty.
THE WORK IS PROVIDED “AS IS” WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER THIS LICENSE.
5. Limitation of Liability.
EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK (INCLUDING BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER DAMAGES OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

16
Makefile

@ -0,0 +1,16 @@
.PHONY : run-checks
run-checks :
isort --check .
black --check .
ruff check .
# mypy .
.PHONY : format
format :
isort .
black .
.PHONY : build
build :
rm -rf *.egg-info/
python -m build

140
README.md

@ -0,0 +1,140 @@
# gr00t_wbc
Software stack for loco-manipulation experiments across multiple humanoid platforms, with primary support for the Unitree G1. This repository provides whole-body control policies, a teleoperation stack, and a data exporter.
---
## System Installation
### Prerequisites
- Ubuntu 22.04
- NVIDIA GPU with a recent driver
- Docker and NVIDIA Container Toolkit (required for GPU access inside the container)
### Repository Setup
Install Git and Git LFS:
```bash
sudo apt update
sudo apt install git git-lfs
git lfs install
```
Clone the repository:
```bash
mkdir -p ~/Projects
cd ~/Projects
git clone https://github.com/NVlabs/gr00t_wbc.git
cd gr00t_wbc
```
### Docker Environment
We provide a Docker image with all dependencies pre-installed.
Install a fresh image and start a container:
```bash
./docker/run_docker.sh --install --root
```
This pulls the latest `gr00t_wbc` image from `docker.io/nvgear`.
Start or re-enter a container:
```bash
./docker/run_docker.sh --root
```
Use `--root` to run as the `root` user. To run as a normal user, build the image locally:
```bash
./docker/run_docker.sh --build
```
---
## Running the Control Stack
Once inside the container, the control policies can be launched directly.
- Simulation:
```bash
python gr00t_wbc/control/main/teleop/run_g1_control_loop.py
```
- Real robot: Ensure the host machine network is configured per the [G1 SDK Development Guide](https://support.unitree.com/home/en/G1_developer) and set a static IP at `192.168.123.222`, subnet mask `255.255.255.0`:
```bash
python gr00t_wbc/control/main/teleop/run_g1_control_loop.py --interface real
```
Keyboard shortcuts (terminal window):
- `]`: Activate policy
- `o`: Deactivate policy
- `9`: Release / Hold the robot
- `w` / `s`: Move forward / backward
- `a` / `d`: Strafe left / right
- `q` / `e`: Rotate left / right
- `z`: Zero navigation commands
- `1` / `2`: Raise / lower the base height
- `backspace` (viewer): Reset the robot in the visualizer
---
## Running the Teleoperation Stack
The teleoperation policy primarily uses Pico controllers for coordinated hand and body control. It also supports other teleoperation devices, including LeapMotion and HTC Vive with Nintendo Switch Joy-Con controllers.
Keep `run_g1_control_loop.py` running, and in another terminal run:
```bash
python gr00t_wbc/control/main/teleop/run_teleop_policy_loop.py --hand_control_device=pico --body_control_device=pico
```
### Pico Setup and Controls
Configure the teleop app on your Pico headset by following the [XR Robotics guidelines](https://github.com/XR-Robotics).
The necessary PC software is pre-installed in the Docker container. Only the [XRoboToolkit-PC-Service](https://github.com/XR-Robotics/XRoboToolkit-PC-Service) component is needed.
Prerequisites: Connect the Pico to the same network as the host computer.
Controller bindings:
- `menu + left trigger`: Toggle lower-body policy
- `menu + right trigger`: Toggle upper-body policy
- `Left stick`: X/Y translation
- `Right stick`: Yaw rotation
- `L/R triggers`: Control hand grippers
Pico unit test:
```bash
python gr00t_wbc/control/teleop/streamers/pico_streamer.py
```
---
## Running the Data Collection Stack
Run the full stack (control loop, teleop policy, and camera forwarder) via the deployment helper:
```bash
python scripts/deploy_g1.py \
--interface sim \
--camera_host localhost \
--sim_in_single_process \
--simulator robocasa \
--image-publish \
--enable-offscreen \
--env_name PnPBottle \
--hand_control_device=pico \
--body_control_device=pico
```
The `tmux` session `g1_deployment` is created with panes for:
- `control_data_teleop`: Main control loop, data collection, and teleoperation policy
- `camera`: Camera forwarder
- `camera_viewer`: Optional live camera feed
Operations in the `controller` window (`control_data_teleop` pane, left):
- `]`: Activate policy
- `o`: Deactivate policy
- `k`: Reset the simulation and policies
- `` ` ``: Terminate the tmux session
- `ctrl + d`: Exit the shell in the pane
Operations in the `data exporter` window (`control_data_teleop` pane, right top):
- Enter the task prompt
Operations on Pico controllers:
- `A`: Start/Stop recording
- `B`: Discard trajectory

160
docker/.bashrc

@ -0,0 +1,160 @@
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
# append to the history file, don't overwrite it
shopt -s histappend
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar
# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
xterm-color|*-256color) color_prompt=yes;;
esac
# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
force_color_prompt=yes
if [ -n "$force_color_prompt" ]; then
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# We have color support; assume it's compliant with Ecma-48
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
# a case would tend to support setf rather than setaf.)
color_prompt=yes
else
color_prompt=
fi
fi
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
;;
*)
;;
esac
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
alias ls='ls --color=auto'
#alias dir='dir --color=auto'
#alias vdir='vdir --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
fi
# colored GCC warnings and errors
export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
# Set terminal type for color support
export TERM=xterm-256color
# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
# Add an "alert" alias for long running commands. Use like so:
# sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
if [ -f /usr/share/bash-completion/bash_completion ]; then
. /usr/share/bash-completion/bash_completion
elif [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
fi
# useful commands
bind '"\e[A": history-search-backward'
bind '"\e[B": history-search-forward'
# Store the last 10 directories in a history file
CD_HISTFILE=~/.cd_history
CD_HISTSIZE=10
cd() {
local histfile="${CD_HISTFILE:-$HOME/.cd_history}"
local max="${CD_HISTSIZE:-10}"
case "$1" in
--) [ -f "$histfile" ] && tac "$histfile" | nl -w2 -s' ' || echo "No directory history yet."; return ;;
-[0-9]*)
local idx=${1#-}
local dir=$(tac "$histfile" 2>/dev/null | sed -n "${idx}p")
[ -n "$dir" ] && builtin cd "$dir" || echo "Invalid selection: $1"
return ;;
esac
builtin cd "$@" || return
[[ $(tail -n1 "$histfile" 2>/dev/null) != "$PWD" ]] && echo "$PWD" >> "$histfile"
tail -n "$max" "$histfile" > "${histfile}.tmp" && mv "${histfile}.tmp" "$histfile"
}
# Manus to LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$GR00T_WBC_DIR/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib:$LD_LIBRARY_PATH
# CUDA support
export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:/usr/lib:/lib/x86_64-linux-gnu:/lib64:/lib:$LD_LIBRARY_PATH
# gr00t_wbc aliases
alias dg="python $GR00T_WBC_DIR/scripts/deploy_g1.py"
alias rsl="python $GR00T_WBC_DIR/gr00t_wbc/control/main/teleop/run_sim_loop.py"
alias rgcl="python $GR00T_WBC_DIR/gr00t_wbc/control/main/teleop/run_g1_control_loop.py"
alias rtpl="python $GR00T_WBC_DIR/gr00t_wbc/control/main/teleop/run_teleop_policy_loop.py"
alias tgcl="pytest $GR00T_WBC_DIR/tests/control/main/teleop/test_g1_control_loop.py -s"

84
docker/.tmux.conf

@ -0,0 +1,84 @@
# Enable mouse mode
set -g mouse on
# Start window numbering at 0 (default)
set -g base-index 0
setw -g pane-base-index 0
# Increase scrollback buffer size
set -g history-limit 50000
# Use Alt-arrow keys without prefix key to switch panes
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D
# Use Alt-1,2,3... to switch windows
bind -n M-1 select-window -t 1
bind -n M-2 select-window -t 2
bind -n M-3 select-window -t 3
bind -n M-4 select-window -t 4
bind -n M-5 select-window -t 5
bind -n M-6 select-window -t 6
bind -n M-7 select-window -t 7
bind -n M-8 select-window -t 8
bind -n M-9 select-window -t 9
# Split panes using Alt-| and Alt--
bind -n M-| split-window -h
bind -n M-- split-window -v
# Easy config reload
bind -n M-r source-file ~/.tmux.conf \; display-message "Config reloaded!"
# Status bar customization
set -g status-style bg=colour240,fg=colour255
set -g status-left "#[fg=colour255,bg=colour240] #S #[fg=colour240,bg=colour238]"
set -g status-right "#[fg=colour255,bg=colour240] %H:%M #[fg=colour240,bg=colour238]"
# Window status format
setw -g window-status-format "#[fg=colour255,bg=colour238] #I:#W "
setw -g window-status-current-format "#[fg=colour238,bg=colour255]#[fg=colour238,bg=colour255] #I:#W #[fg=colour255,bg=colour238]"
# Pane border colors
set -g pane-border-style fg=colour240
set -g pane-active-border-style fg=colour255
# Message text
set -g message-style bg=colour238,fg=colour255
# Clock mode
setw -g clock-mode-colour colour255
# Enable focus events
set -g focus-events on
# Increase escape time
set -sg escape-time 0
# Enable true color support
set -ga terminal-overrides ",*256col*:Tc"
# Set default terminal mode to 256 colors
set -g default-terminal "screen-256color"
# Display a message when a window is created
set -g display-time 4000
# Automatically set window title
setw -g automatic-rename on
set -g set-titles on
set -g set-titles-string "#T"
# Enable clipboard integration
set -g @plugin 'tmux-plugins/tmux-yank'
# List of plugins
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
run '~/.tmux/plugins/tpm/tpm'

5
docker/70-manus-hid.rules

@ -0,0 +1,5 @@
# HIDAPI/libusb
SUBSYSTEMS=="usb", ATTRS{idVendor}=="3325", MODE:="0666"
# HIDAPI/hidraw
KERNEL=="hidraw*", ATTRS{idVendor}=="3325", MODE:="0666"

127
docker/Dockerfile.deploy

@ -0,0 +1,127 @@
FROM nvgear/ros-2:latest
# Accept build argument for username
ARG USERNAME
ARG USERID
ARG HOME_DIR
ARG WORKTREE_NAME
# Create user with the same name as host
RUN if [ "$USERID" != "0" ]; then \
useradd -m -u ${USERID} -s /bin/bash ${USERNAME} && \
echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \
# Add user to video and render groups for GPU access
usermod -a -G video,render ${USERNAME} || true; \
fi
# Copy .bashrc with color settings before switching user
COPY --chown=${USERNAME}:${USERNAME} docker/.bashrc ${HOME_DIR}/.bashrc
# Install Manus udev rules
COPY --chown=${USERNAME}:${USERNAME} docker/70-manus-hid.rules /etc/udev/rules.d/70-manus-hid.rules
# Copy tmux configuration
COPY --chown=${USERNAME}:${USERNAME} docker/.tmux.conf ${HOME_DIR}/.tmux.conf
# Switch to user
USER ${USERNAME}
# Install tmux plugin manager and uv in parallel
RUN git clone https://github.com/tmux-plugins/tpm ${HOME_DIR}/.tmux/plugins/tpm & \
curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=${HOME_DIR}/.cargo/bin sh & \
wait
# Install tmux plugins automatically
RUN ${HOME_DIR}/.tmux/plugins/tpm/bin/install_plugins || true
# Add uv to PATH
ENV PATH="${HOME_DIR}/.cargo/bin:$PATH"
ENV UV_PYTHON=${HOME_DIR}/venv/bin/python
# Create venv
RUN uv venv --python 3.10 ${HOME_DIR}/venv
# Install hardware-specific packages (x86 only - not available on ARM64/Orin)
USER root
COPY --chown=${USERNAME}:${USERNAME} gr00t_wbc/control/teleop/device/pico/XRoboToolkit_PC_Service_1.0.0_ubuntu_22.04_amd64.deb ${HOME_DIR}/XRoboToolkit_PC_Service_1.0.0_ubuntu_22.04_amd64.deb
COPY --chown=${USERNAME}:${USERNAME} gr00t_wbc/control/teleop/device/pico/roboticsservice_1.0.0.0_arm64.deb ${HOME_DIR}/roboticsservice_1.0.0.0_arm64.deb
RUN if [ "$(dpkg --print-architecture)" = "amd64" ]; then \
# Ultra Leap setup
wget -qO - https://repo.ultraleap.com/keys/apt/gpg | gpg --dearmor | tee /etc/apt/trusted.gpg.d/ultraleap.gpg && \
echo 'deb [arch=amd64] https://repo.ultraleap.com/apt stable main' | tee /etc/apt/sources.list.d/ultraleap.list && \
apt-get update && \
echo "yes" | DEBIAN_FRONTEND=noninteractive apt-get install -y ultraleap-hand-tracking libhidapi-dev && \
# Space Mouse udev rules
echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"' > /etc/udev/rules.d/99-hidraw-permissions.rules && \
usermod -aG plugdev ${USERNAME}; \
# Pico setup
apt-get install -y xdg-utils && \
dpkg -i ${HOME_DIR}/XRoboToolkit_PC_Service_1.0.0_ubuntu_22.04_amd64.deb; \
else \
echo "Skipping x86-only hardware packages on $(dpkg --print-architecture)"; \
fi
USER ${USERNAME}
# Install hardware Python packages (x86 only) with caching
RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \
if [ "$(dpkg --print-architecture)" = "amd64" ]; then \
# Ultra Leap Python bindings
git clone https://github.com/ultraleap/leapc-python-bindings ${HOME_DIR}/leapc-python-bindings && \
cd ${HOME_DIR}/leapc-python-bindings && \
UV_CONCURRENT_DOWNLOADS=8 uv pip install -r requirements.txt && \
MAKEFLAGS="-j$(nproc)" ${HOME_DIR}/venv/bin/python -m build leapc-cffi && \
uv pip install leapc-cffi/dist/leapc_cffi-0.0.1.tar.gz && \
uv pip install -e leapc-python-api && \
# Space Mouse Python package
uv pip install pyspacemouse && \
# Pico Python bindings
git clone https://github.com/XR-Robotics/XRoboToolkit-PC-Service-Pybind.git ${HOME_DIR}/XRoboToolkit-PC-Service-Pybind && \
cd ${HOME_DIR}/XRoboToolkit-PC-Service-Pybind && \
uv pip install setuptools pybind11 && \
sed -i "s|pip install|uv pip install|g" setup_ubuntu.sh && \
sed -i "s|pip uninstall|uv pip uninstall|g" setup_ubuntu.sh && \
sed -i "s|python setup.py install|${HOME_DIR}/venv/bin/python setup.py install|g" setup_ubuntu.sh && \
bash setup_ubuntu.sh; \
fi
# Install Python dependencies using uv with caching
RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \
UV_CONCURRENT_DOWNLOADS=8 uv pip install --upgrade pip ipython jupyter notebook debugpy
# Copy entire project to the workspace directory where it will be mounted at runtime
# NOTE: The build context must be the project root for this to work
# Use dynamic worktree name to match runtime mount path
COPY --chown=${USERNAME}:${USERNAME} . ${HOME_DIR}/Projects/${WORKTREE_NAME}
# Install Python dependencies inside the venv with caching - split into separate commands
RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \
UV_CONCURRENT_DOWNLOADS=8 uv pip install \
-e ${HOME_DIR}/Projects/${WORKTREE_NAME}/external_dependencies/unitree_sdk2_python
# Unlike pip, uv downloads LFS files by default. There's a bug in uv that causes LFS files
# to fail to download (https://github.com/astral-sh/uv/issues/3312). So we need to set
# UV_GIT_LFS=1 to prevent uv from downloading LFS files.
# Install project dependencies (VLA extras only on x86) with caching
RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \
GIT_LFS_SKIP_SMUDGE=1 UV_CONCURRENT_DOWNLOADS=8 uv pip install -e "${HOME_DIR}/Projects/${WORKTREE_NAME}[dev]"
# Clone and install robosuite with specific branch
RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \
git clone https://github.com/xieleo5/robosuite.git ${HOME_DIR}/robosuite && \
cd ${HOME_DIR}/robosuite && \
git checkout leo/support_g1_locomanip && \
UV_CONCURRENT_DOWNLOADS=8 uv pip install -e .
# Install gr00trobocasa
RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \
UV_CONCURRENT_DOWNLOADS=8 uv pip install -e ${HOME_DIR}/Projects/${WORKTREE_NAME}/gr00t_wbc/dexmg/gr00trobocasa
# Configure bash environment with virtual environment and ROS2 setup
RUN echo "source ${HOME_DIR}/venv/bin/activate" >> ${HOME_DIR}/.bashrc && \
echo "source /opt/ros/humble/setup.bash" >> ${HOME_DIR}/.bashrc && \
echo "export ROS_LOCALHOST_ONLY=1" >> ${HOME_DIR}/.bashrc
# Default command (can be overridden at runtime)
CMD ["/bin/bash"]

155
docker/Dockerfile.deploy.base

@ -0,0 +1,155 @@
# Multi-architecture Dockerfile for NVIDIA CUDA
# Supports linux/amd64 and linux/arm64
FROM nvidia/cuda:12.4.0-runtime-ubuntu22.04
# Build info - ARGs need to be redeclared after FROM to use in RUN commands
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "Building for $TARGETPLATFORM on $BUILDPLATFORM"
# Avoid prompts from apt
ENV DEBIAN_FRONTEND=noninteractive
# Install minimal system dependencies
RUN apt-get update && \
apt-get install -y \
# Basic tools
build-essential \
curl \
gdb \
git \
git-lfs \
net-tools \
sudo \
wget \
iputils-ping \
vim \
unzip \
# System services
udev \
# Graphics and X11
libgl1-mesa-dri \
libgl1-mesa-glx \
libglu1-mesa \
mesa-utils \
libxcb-cursor0 \
x11-apps \
xauth \
# EGL and GPU access
libegl1 \
libegl1-mesa \
libegl1-mesa-dev \
libgl1-mesa-dev \
libgles2-mesa-dev \
libglvnd-dev \
mesa-common-dev \
# XCB and Qt platform dependencies
libxcb-icccm4 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-randr0 \
libxcb-render-util0 \
libxcb-shape0 \
libxcb-xfixes0 \
libxcb-xinerama0 \
libxcb-xinput0 \
libxcb-xkb1 \
libxkbcommon-x11-0 \
# D-Bus and system dependencies
libdbus-1-3 \
# Other dependencies
libncurses5-dev \
libudev-dev \
libusb-1.0-0-dev \
# Python 3.10 and pip
python3.10 \
python3.10-venv \
python3.10-distutils \
python3-pip \
expect \
# ffmpeg and related libraries
ffmpeg \
libavcodec-dev \
libavformat-dev \
libavdevice-dev \
libavfilter-dev \
libavutil-dev \
libswresample-dev \
libswscale-dev \
# opencv
libgtk2.0-dev \
# Clean up
&& rm -rf /var/lib/apt/lists/*
# --- Install ROS 2 Humble (following official instructions) ---
# Enable required repositories
RUN apt-get update && apt-get install -y software-properties-common \
&& add-apt-repository universe \
# Add ROS 2 GPG key and repository
&& apt-get install -y curl gnupg lsb-release \
&& curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" > /etc/apt/sources.list.d/ros2.list \
# Upgrade system to avoid removal of critical packages (see ROS 2 docs)
&& apt-get update \
&& apt-get upgrade -y \
# Install ROS 2 Humble desktop
&& apt-get install -y ros-humble-desktop \
# (Optional) Install development tools
&& apt-get install -y ros-dev-tools \
# Install Eclipse Cyclone DDS RMW implementation
&& apt-get install -y ros-humble-rmw-cyclonedds-cpp \
# Clean up
&& rm -rf /var/lib/apt/lists/*
# Source ROS 2 setup in bashrc for all users
RUN echo 'source /opt/ros/humble/setup.bash' >> /etc/bash.bashrc
# Clone, build, and install CycloneDDS 0.10.x
RUN git clone --branch releases/0.10.x https://github.com/eclipse-cyclonedds/cyclonedds /opt/cyclonedds && \
mkdir -p /opt/cyclonedds/build /opt/cyclonedds/install && \
cd /opt/cyclonedds/build && \
cmake .. -DCMAKE_INSTALL_PREFIX=../install && \
cmake --build . --target install && \
# Clean up build files to reduce image size
rm -rf /opt/cyclonedds/build
# Set CYCLONEDDS_HOME for all users
ENV CYCLONEDDS_HOME=/opt/cyclonedds/install
# Install uv
RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/opt/uv sh
# Add uv to PATH
ENV PATH="/opt/uv:$PATH"
ENV UV_PYTHON=/opt/venv/bin/python
# Fix dpkg state and install tmux
RUN dpkg --configure -a && apt-get update && apt-get install -y tmux && rm -rf /var/lib/apt/lists/*
# Create NVIDIA ICD config for EGL if it doesn't exist
# Note: This might need platform-specific handling for ARM64
RUN if [ ! -f /usr/share/glvnd/egl_vendor.d/10_nvidia.json ]; then \
mkdir -p /usr/share/glvnd/egl_vendor.d && \
printf '{\n "file_format_version" : "1.0.0",\n "ICD" : {\n "library_path" : "libEGL_nvidia.so.0"\n }\n}' | tee /usr/share/glvnd/egl_vendor.d/10_nvidia.json > /dev/null; \
fi
# Platform-specific configurations
RUN case "$TARGETPLATFORM" in \
"linux/arm64") \
echo "Configuring for ARM64 platform" && \
# Add any ARM64-specific configurations here
echo "export GPU_FORCE_64BIT_PTR=1" >> /etc/environment \
;; \
"linux/amd64") \
echo "Configuring for AMD64 platform" && \
# Add any AMD64-specific configurations here
echo "AMD64 platform configured" \
;; \
*) \
echo "Unknown platform: $TARGETPLATFORM" \
;; \
esac
# Add labels for better image management
LABEL org.opencontainers.image.title="Multi-Arch CUDA Runtime with ROS 2 Humble"
LABEL org.opencontainers.image.description="Multi-architecture Docker image with CUDA runtime and ROS 2 Humble"

55
docker/build_deploy_base.sh

@ -0,0 +1,55 @@
#!/bin/bash
# Multi-architecture Docker build script
# Supports linux/amd64
set -e
# Configuration
IMAGE_NAME="nvgear/ros-2"
TAG="${1:-latest}"
DOCKERFILE="docker/Dockerfile.deploy.base"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}Building multi-architecture Docker image: ${IMAGE_NAME}:${TAG}${NC}"
# Ensure we're using the multiarch builder
echo -e "${YELLOW}Setting up multiarch builder...${NC}"
sudo docker buildx use multiarch-builder 2>/dev/null || {
echo -e "${YELLOW}Creating multiarch builder...${NC}"
sudo docker buildx create --name multiarch-builder --use --bootstrap
}
# Show supported platforms
echo -e "${YELLOW}Supported platforms:${NC}"
sudo docker buildx inspect --bootstrap | grep Platforms
# Build for multiple architectures
echo -e "${GREEN}Starting multi-arch build...${NC}"
sudo docker buildx build \
--platform linux/amd64 \
--file "${DOCKERFILE}" \
--tag "${IMAGE_NAME}:${TAG}" \
--push \
.
# Alternative: Build and load locally (only works for single platform)
# docker buildx build \
# --platform linux/amd64 \
# --file "${DOCKERFILE}" \
# --tag "${IMAGE_NAME}:${TAG}" \
# --load \
# .
echo -e "${GREEN}Multi-arch build completed successfully!${NC}"
echo -e "${GREEN}Image: ${IMAGE_NAME}:${TAG}${NC}"
echo -e "${GREEN}Platforms: linux/amd64${NC}"
# Verify the manifest
echo -e "${YELLOW}Verifying multi-arch manifest...${NC}"
sudo docker buildx imagetools inspect "${IMAGE_NAME}:${TAG}"

5
docker/entrypoint/bash.sh

@ -0,0 +1,5 @@
#!/bin/bash
set -e # Exit on error
echo "Dependencies installed successfully. Starting interactive bash shell..."
exec /bin/bash

20
docker/entrypoint/deploy.sh

@ -0,0 +1,20 @@
#!/bin/bash
set -e # Exit on error
# Run the deployment script
# Check for script existence before running
DEPLOY_SCRIPT="scripts/deploy_g1.py"
if [ -f "$DEPLOY_SCRIPT" ]; then
echo "Running deployment script at $DEPLOY_SCRIPT"
echo "Using python from $(which python)"
echo "Deploy args: $@"
exec python "$DEPLOY_SCRIPT" "$@"
else
echo "ERROR: Deployment script not found at $DEPLOY_SCRIPT"
echo "Current directory structure:"
find . -type f -name "*.py" | grep -i deploy
echo "Available script options:"
find . -type f -name "*.py" | sort
echo "Starting a bash shell for troubleshooting..."
exec /bin/bash
fi

23
docker/entrypoint/install_deps.sh

@ -0,0 +1,23 @@
#!/bin/bash
set -e
# Source virtual environment and ROS2
source ${HOME}/venv/bin/activate
source /opt/ros/humble/setup.bash
export ROS_LOCALHOST_ONLY=1
# Install external dependencies
echo "Current directory: $(pwd)"
echo "Installing dependencies..."
# Install Unitree SDK and LeRobot
if [ -d "external_dependencies/unitree_sdk2_python" ]; then
cd external_dependencies/unitree_sdk2_python/
uv pip install -e . --no-deps
cd ../..
fi
# Install project
if [ -f "pyproject.toml" ]; then
UV_GIT_LFS=1 uv pip install -e ".[dev]"
fi

1
docker/image_name.txt

@ -0,0 +1 @@
nvcr.io/nvidian/gr00t_wbc:base

79
docker/kill_all_containers.sh

@ -0,0 +1,79 @@
#!/bin/bash
# Script to kill all running Docker containers
# Usage: ./kill_all_containers.sh [--force]
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Function to print colored output
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if Docker is running
if ! sudo docker info >/dev/null 2>&1; then
print_error "Docker is not running or not accessible. Please start Docker first."
exit 1
fi
# Get list of running containers
RUNNING_CONTAINERS=$(sudo docker ps -q)
if [ -z "$RUNNING_CONTAINERS" ]; then
print_info "No running containers found."
exit 0
fi
# Count running containers
CONTAINER_COUNT=$(echo "$RUNNING_CONTAINERS" | wc -l | tr -d ' ')
print_info "Found $CONTAINER_COUNT running container(s):"
sudo docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Status}}"
# Check for --force flag
FORCE_KILL=false
if [ "$1" = "--force" ]; then
FORCE_KILL=true
print_warning "Force mode enabled. Containers will be killed without confirmation."
fi
# Ask for confirmation unless --force is used
if [ "$FORCE_KILL" = false ]; then
echo
read -p "Are you sure you want to kill all running containers? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "Operation cancelled."
exit 0
fi
fi
# Kill all running containers
print_info "Killing all running containers..."
if sudo docker kill $RUNNING_CONTAINERS; then
print_info "Successfully killed all running containers."
else
print_error "Failed to kill some containers. You may need to run with sudo or check Docker permissions."
exit 1
fi
# Optional: Remove stopped containers (commented out by default)
# Uncomment the following lines if you also want to remove the stopped containers
# print_info "Removing stopped containers..."
# sudo docker container prune -f
print_info "Done!"

192
docker/kill_gr00t_wbc_processors.sh

@ -0,0 +1,192 @@
#!/bin/bash
# kill_gr00t_wbc_processors.sh
# Kill gr00t_wbc processes in current container to prevent message passing conflicts
# Note: Don't use 'set -e' as tmux/pgrep commands may return non-zero exit codes
# Configuration
DRY_RUN=false
FORCE=false
QUIET=false
declare -A FOUND_PROCESSES
# Default to verbose mode if no arguments
[[ $# -eq 0 ]] && { QUIET=false; DRY_RUN=false; }
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run) DRY_RUN=true ;;
--force) FORCE=true ;;
--verbose|-v) VERBOSE=true ;;
--help|-h)
echo "Usage: $0 [--dry-run] [--force] [--verbose] [--help]"
echo "Kill gr00t_wbc processes to prevent message passing conflicts"
exit 0 ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
shift
done
# Colors (only if not quiet)
if [[ "$QUIET" != true ]]; then
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
else
RED=''; GREEN=''; YELLOW=''; BLUE=''; NC=''
fi
# Show processes by pattern (for preview)
show_processes_by_pattern() {
local pattern="$1" desc="$2"
local pids=$(pgrep -f "$pattern" 2>/dev/null || true)
[[ -z "$pids" ]] && return 0
echo -e "${YELLOW}$desc processes:${NC}"
for pid in $pids; do
local cmd=$(ps -p $pid -o cmd= 2>/dev/null || echo "Process not found")
echo " PID $pid: $cmd"
done
}
# Kill processes by pattern (silent killing)
kill_by_pattern() {
local pattern="$1" desc="$2" signal="${3:-TERM}"
local pids=$(pgrep -f "$pattern" 2>/dev/null || true)
[[ -z "$pids" ]] && return 0
for pid in $pids; do
# Kill if not dry run
[[ "$DRY_RUN" != true ]] && kill -$signal $pid 2>/dev/null
done
}
# Show tmux sessions (for preview)
show_tmux() {
local pattern="$1"
local sessions=$(tmux list-sessions 2>/dev/null | grep "$pattern" | cut -d: -f1 || true)
[[ -z "$sessions" ]] && return 0
echo -e "${YELLOW}Tmux sessions:${NC}"
for session in $sessions; do
echo " Session: $session"
done
}
# Kill tmux sessions (silent killing)
kill_tmux() {
local pattern="$1"
local sessions=$(tmux list-sessions 2>/dev/null | grep "$pattern" | cut -d: -f1 || true)
[[ -z "$sessions" ]] && return 0
for session in $sessions; do
[[ "$DRY_RUN" != true ]] && tmux kill-session -t "$session" 2>/dev/null
done
}
# Show processes by port (for preview)
show_processes_by_port() {
local port="$1" desc="$2"
local pids=$(lsof -ti:$port 2>/dev/null || true)
[[ -z "$pids" ]] && return 0
echo -e "${YELLOW}$desc (port $port):${NC}"
for pid in $pids; do
local cmd=$(ps -p $pid -o cmd= 2>/dev/null || echo "Process not found")
echo " PID $pid: $cmd"
done
}
# Kill processes by port (silent killing)
kill_by_port() {
local port="$1" desc="$2"
local pids=$(lsof -ti:$port 2>/dev/null || true)
[[ -z "$pids" ]] && return 0
for pid in $pids; do
[[ "$DRY_RUN" != true ]] && kill -TERM $pid 2>/dev/null
done
}
# Check if any processes exist
has_processes() {
# Check for processes
local has_tmux=$(tmux list-sessions 2>/dev/null | grep "g1_deployment" || true)
local has_control=$(pgrep -f "run_g1_control_loop.py" 2>/dev/null || true)
local has_teleop=$(pgrep -f "run_teleop_policy_loop.py" 2>/dev/null || true)
local has_camera=$(pgrep -f "camera_forwarder.py" 2>/dev/null || true)
local has_rqt=$(pgrep -f "rqt.*image_view" 2>/dev/null || true)
local has_port=$(lsof -ti:5555 2>/dev/null || true)
[[ -n "$has_tmux" || -n "$has_control" || -n "$has_teleop" || -n "$has_camera" || -n "$has_rqt" || -n "$has_port" ]]
}
# Main execution
main() {
# Check if any processes exist first
if ! has_processes; then
# No processes to kill, exit silently
exit 0
fi
# Show header and processes to be killed
if [[ "$QUIET" != true ]]; then
echo -e "${BLUE}=== gr00t_wbc Process Killer ===${NC}"
[[ "$DRY_RUN" == true ]] && echo -e "${BLUE}=== DRY RUN MODE ===${NC}"
# Show what will be killed
show_tmux "g1_deployment"
show_processes_by_pattern "run_g1_control_loop.py" "G1 control loop"
show_processes_by_pattern "run_teleop_policy_loop.py" "Teleop policy"
show_processes_by_pattern "camera_forwarder.py" "Camera forwarder"
show_processes_by_pattern "rqt.*image_view" "RQT viewer"
show_processes_by_port "5555" "Inference server"
# Ask for confirmation
if [[ "$FORCE" != true && "$DRY_RUN" != true ]]; then
echo
echo -e "${RED}WARNING: This will terminate the above gr00t_wbc processes!${NC}"
read -p "Continue? [Y/n]: " -n 1 -r
echo
# Default to Y - only abort if user explicitly types 'n' or 'N'
[[ $REPLY =~ ^[Nn]$ ]] && { echo "Aborted."; exit 0; }
fi
echo
fi
# Kill processes (silently)
kill_tmux "g1_deployment"
kill_by_pattern "run_g1_control_loop.py" "G1 control loop"
kill_by_pattern "run_teleop_policy_loop.py" "Teleop policy"
kill_by_pattern "camera_forwarder.py" "Camera forwarder"
kill_by_pattern "rqt.*image_view" "RQT viewer"
kill_by_port "5555" "Inference server"
# Force kill remaining (SIGKILL)
[[ "$DRY_RUN" != true ]] && {
sleep 1
kill_by_pattern "run_g1_control_loop.py" "G1 control loop" "KILL"
kill_by_pattern "run_teleop_policy_loop.py" "Teleop policy" "KILL"
kill_by_pattern "camera_forwarder.py" "Camera forwarder" "KILL"
}
# Summary (unless quiet)
[[ "$QUIET" != true ]] && {
if [[ "$DRY_RUN" == true ]]; then
echo -e "${BLUE}=== DRY RUN COMPLETE ===${NC}"
else
echo -e "${GREEN}All gr00t_wbc processes terminated${NC}"
fi
}
}
main "$@"

3
docker/publish.sh

@ -0,0 +1,3 @@
#!/bin/bash
image_name=$(cat image_name.txt)
docker push "$@" $image_name

454
docker/run_docker.sh

@ -0,0 +1,454 @@
#!/bin/bash
# Docker run script for gr00t_wbc with branch-based container isolation
#
# Usage:
# ./docker/run_docker.sh [OPTIONS]
#
# Options:
# --build Build Docker image
# --clean Clean containers
# --deploy Run in deploy mode
# --install Pull prebuilt Docker image
# --push Push built image to Docker Hub
# --branch Use branch-specific container names
#
# Branch-based Container Isolation (when --branch flag is used):
# - Each git branch gets its own isolated containers
# - Container names include branch identifier (e.g., gr00t_wbc-deploy-user-main)
# - Works with git worktrees, separate clones, or nested repositories
# - Clean and build operations only affect the current branch
# Exit on error
set -e
# Default values
BUILD=false
CLEAN=false
DEPLOY=false
INSTALL=false
# Flag to push the built Docker image to Docker Hub
# This should be used when someone updates the Docker image dependencies
# because this image is used for CI/CD pipelines
# When true, the image will be tagged and pushed to docker.io/nvgear/gr00t_wbc:latest (lowercased in practice)
DOCKER_HUB_PUSH=false
# Flag to build the docker with root user
# This could cause some of your local files to be owned by root
# If you get error like "PermissionError: [Errno 13] Permission denied:"
# You can run `sudo chown -R $USER:$USER .` in local machine to fix it
ROOT=false
BRANCH_MODE=false
EXTRA_ARGS=()
PROJECT_NAME="gr00t_wbc"
PROJECT_SLUG=$(echo "$PROJECT_NAME" | tr '[:upper:]' '[:lower:]')
REMOTE_IMAGE="nvgear/${PROJECT_SLUG}:latest"
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--build)
BUILD=true
shift
;;
--clean)
CLEAN=true
shift
;;
--deploy)
DEPLOY=true
shift
;;
--install)
INSTALL=true
shift
;;
--push)
DOCKER_HUB_PUSH=true
shift
;;
--root)
ROOT=true
shift
;;
--branch)
BRANCH_MODE=true
shift
;;
*)
# Collect all unknown arguments as extra args for the deployment script
EXTRA_ARGS+=("$1")
shift
;;
esac
done
if [ "$INSTALL" = true ] && [ "$BUILD" = true ]; then
echo "Cannot use --install and --build together. Choose one."
exit 1
fi
# Function to get branch name for container naming
function get_branch_id {
# Check if we're in a git repository
if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
# Get current branch name (returns "HEAD" in detached state)
local branch_name=$(git rev-parse --abbrev-ref HEAD)
# Replace forward slashes with dashes for valid container names
echo "${branch_name//\//-}"
else
# Default: no branch identifier (not in git repo)
echo ""
fi
}
# Architecture detection helpers
is_arm64() { [ "$(dpkg --print-architecture)" = "arm64" ]; }
is_amd64() { [ "$(dpkg --print-architecture)" = "amd64" ]; }
# Get current user's username and UID
if [ "$ROOT" = true ]; then
USERNAME=root
USERID=0
DOCKER_HOME_DIR=/root
CACHE_FROM=${PROJECT_SLUG}-deploy-cache-root
else
USERNAME=$(whoami)
USERID=$(id -u)
DOCKER_HOME_DIR=/home/${USERNAME}
CACHE_FROM=${PROJECT_SLUG}-deploy-cache
fi
# Get input group ID for device access
INPUT_GID=$(getent group input | cut -d: -f3)
# Get script directory for path calculations
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Function to get the actual project directory (worktree-aware)
function get_project_dir {
# For worktrees, use the actual worktree root path
if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
git rev-parse --show-toplevel
else
# Fallback to script-based detection
dirname "$SCRIPT_DIR"
fi
}
# Get branch identifier
BRANCH_ID=$(get_branch_id)
# Set project directory (needs to be after branch detection)
PROJECT_DIR="$(get_project_dir)"
# Function to generate container name with optional branch support
function get_container_name {
local container_type="$1"
if [[ -n "$BRANCH_ID" ]] && [[ "$BRANCH_MODE" = true ]]; then
echo "${PROJECT_SLUG}-${container_type}-${USERNAME}-${BRANCH_ID}"
else
echo "${PROJECT_SLUG}-${container_type}-${USERNAME}"
fi
}
# Set common variables used throughout the script
DEPLOY_CONTAINER=$(get_container_name "deploy")
BASH_CONTAINER=$(get_container_name "bash")
WORKTREE_NAME=$(basename "$PROJECT_DIR")
# Debug output for branch detection
if [[ -n "$BRANCH_ID" ]] && [[ "$BRANCH_MODE" = true ]]; then
echo "Branch mode enabled - using branch: $BRANCH_ID"
echo "Project directory: $PROJECT_DIR"
elif [[ -n "$BRANCH_ID" ]]; then
echo "Branch mode disabled - using default containers"
echo "Project directory: $PROJECT_DIR"
else
echo "Running outside git repository"
echo "Project directory: $PROJECT_DIR"
fi
# Get host's hostname and append -docker
HOSTNAME=$(hostname)-docker
function clean_container {
echo "Cleaning up Docker containers..."
# Stop containers
sudo docker stop $DEPLOY_CONTAINER 2>/dev/null || true
sudo docker stop $BASH_CONTAINER 2>/dev/null || true
# Remove containers
echo "Removing containers..."
sudo docker rm $DEPLOY_CONTAINER 2>/dev/null || true
sudo docker rm $BASH_CONTAINER 2>/dev/null || true
echo "Containers cleaned!"
}
# Function to install Docker Buildx if needed
function install_docker_buildx {
# Check if Docker Buildx is already installed
if sudo docker buildx version &> /dev/null; then
echo "Docker Buildx is already installed."
return 0
fi
echo "Installing Docker Buildx..."
# Create directories and detect architecture
mkdir -p ~/.docker/cli-plugins/ && sudo mkdir -p /root/.docker/cli-plugins/
ARCH=$(dpkg --print-architecture)
[[ "$ARCH" == "arm64" ]] && BUILDX_ARCH="linux-arm64" || BUILDX_ARCH="linux-amd64"
# Get version (with fallback)
BUILDX_VERSION=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | grep tag_name | cut -d '"' -f 4)
BUILDX_VERSION=${BUILDX_VERSION:-v0.13.1}
# Download and install for both user and root
curl -L "https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.${BUILDX_ARCH}" -o ~/.docker/cli-plugins/docker-buildx
sudo cp ~/.docker/cli-plugins/docker-buildx /root/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx && sudo chmod +x /root/.docker/cli-plugins/docker-buildx
# Create builder
sudo docker buildx create --use --name mybuilder || true
sudo docker buildx inspect --bootstrap
echo "Docker Buildx installation complete!"
}
# Function to install NVIDIA Container Toolkit if needed
function install_nvidia_toolkit {
# Check if NVIDIA Container Toolkit is already installed
if command -v nvidia-container-toolkit &> /dev/null; then
echo "NVIDIA Container Toolkit is already installed."
return 0
fi
echo "Installing NVIDIA Container Toolkit..."
# Add the package repositories
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
# Check if GPG key exists and remove it if it does
if [ -f "/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg" ]; then
echo "Removing existing NVIDIA GPG key..."
sudo rm /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
fi
# Add new GPG key
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
# Add repository
curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list
# Install nvidia-container-toolkit and docker if needed
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
# Install docker if not already installed
if ! command -v docker &> /dev/null; then
sudo apt-get install -y docker.io
fi
# Configure Docker to use the NVIDIA runtime
sudo nvidia-ctk runtime configure --runtime=docker
# Restart the Docker daemon
sudo systemctl restart docker
echo "NVIDIA Container Toolkit installation complete!"
}
# Function to build Docker image for current branch
function build_docker_image {
echo "Building Docker image: $DEPLOY_CONTAINER"
sudo docker buildx build \
--build-arg USERNAME=$USERNAME \
--build-arg USERID=$USERID \
--build-arg HOME_DIR=$DOCKER_HOME_DIR \
--build-arg WORKTREE_NAME=$WORKTREE_NAME \
--cache-from $CACHE_FROM \
-t $DEPLOY_CONTAINER \
-f docker/Dockerfile.deploy \
--load \
.
# Tag for persistent cache
# sudo docker tag $DEPLOY_CONTAINER $CACHE_FROM
echo "Docker image build complete!"
}
# Build function
function build_with_cleanup {
echo "Building Docker image..."
echo "Removing existing containers and images..."
clean_container
# Tag for persistent cache before deleting the image
sudo docker tag $DEPLOY_CONTAINER $CACHE_FROM 2>/dev/null || true
sudo docker rmi $DEPLOY_CONTAINER 2>/dev/null || true
echo "Images cleaned!"
install_docker_buildx
install_nvidia_toolkit
build_docker_image
}
function install_remote_image {
echo "Installing Docker image from remote registry: $REMOTE_IMAGE"
echo "Removing existing containers to ensure a clean install..."
clean_container
sudo docker pull "$REMOTE_IMAGE"
sudo docker tag "$REMOTE_IMAGE" "$DEPLOY_CONTAINER"
sudo docker tag "$REMOTE_IMAGE" "$CACHE_FROM" 2>/dev/null || true
echo "Docker image install complete!"
}
# Clean up if requested
if [ "$CLEAN" = true ]; then
clean_container
exit 0
fi
# Build if requested
if [ "$BUILD" = true ]; then
build_with_cleanup
fi
if [ "$INSTALL" = true ]; then
install_remote_image
fi
if [ "$DOCKER_HUB_PUSH" = true ]; then
echo "Pushing Docker image to Docker Hub: docker.io/nvgear/${PROJECT_SLUG}:latest"
sudo docker tag $DEPLOY_CONTAINER docker.io/nvgear/${PROJECT_SLUG}:latest
sudo docker push docker.io/nvgear/${PROJECT_SLUG}:latest
echo "Docker image pushed to Docker Hub!"
exit 0
fi
# Setup X11 display forwarding
setup_x11() {
# Set display if missing and X server available
if [ -z "$DISPLAY" ] && command -v xset >/dev/null 2>&1 && xset q >/dev/null 2>&1; then
export DISPLAY=:1
echo "No DISPLAY set, using :1"
fi
# Enable X11 forwarding if possible
if [ -n "$DISPLAY" ] && command -v xhost >/dev/null 2>&1 && xhost +local:docker 2>/dev/null; then
echo "X11 forwarding enabled"
return 0
else
echo "Headless environment - X11 disabled"
export DISPLAY=""
return 1
fi
}
X11_ENABLED=false
setup_x11 && X11_ENABLED=true
# Mount entire /dev directory for dynamic device access (including hidraw for joycon)
# This allows JoyCon controllers to be detected even when connected after container launch
sudo chmod g+r+w /dev/input/*
# Detect GPU setup and set appropriate environment variables
echo "Detecting GPU setup..."
GPU_ENV_VARS=""
# Check if we have both integrated and discrete GPUs (hybrid/Optimus setup)
HAS_AMD_GPU=$(lspci | grep -i "vga\|3d\|display" | grep -i amd | wc -l)
HAS_INTEL_GPU=$(lspci | grep -i "vga\|3d\|display" | grep -i intel | wc -l)
HAS_NVIDIA_GPU=$(lspci | grep -i "vga\|3d\|display" | grep -i nvidia | wc -l)
if [[ "$HAS_INTEL_GPU" -gt 0 ]] || [[ "$HAS_AMD_GPU" -gt 0 ]] && [[ "$HAS_NVIDIA_GPU" -gt 0 ]]; then
echo "Detected hybrid GPU setup (Intel/AMD integrated + NVIDIA discrete)"
echo "Setting NVIDIA Optimus environment variables for proper rendering offload..."
GPU_ENV_VARS="-e __NV_PRIME_RENDER_OFFLOAD=1 \
-e __VK_LAYER_NV_optimus=NVIDIA_only"
else
GPU_ENV_VARS=""
fi
# Set GPU runtime based on architecture
if is_arm64; then
echo "Detected ARM64 architecture (Jetson Orin), using device access instead of nvidia runtime..."
GPU_RUNTIME_ARGS="--device /dev/nvidia0 --device /dev/nvidiactl --device /dev/nvidia-modeset --device /dev/nvidia-uvm --device /dev/nvidia-uvm-tools"
else
GPU_RUNTIME_ARGS="--gpus all --runtime=nvidia"
fi
# Common Docker run parameters
DOCKER_RUN_ARGS="--hostname $HOSTNAME \
--user $USERNAME \
--group-add $INPUT_GID \
$GPU_RUNTIME_ARGS \
--ipc=host \
--network=host \
--privileged \
--device=/dev \
$GPU_ENV_VARS \
-p 5678:5678 \
-e DISPLAY=$DISPLAY \
-e NVIDIA_VISIBLE_DEVICES=all \
-e NVIDIA_DRIVER_CAPABILITIES=graphics,compute,utility \
-e __GLX_VENDOR_LIBRARY_NAME=nvidia \
-e USERNAME=$USERNAME \
-e GR00T_WBC_DIR="$DOCKER_HOME_DIR/Projects/$WORKTREE_NAME" \
-v /dev/bus/usb:/dev/bus/usb \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v $HOME/.ssh:$DOCKER_HOME_DIR/.ssh \
-v $HOME/.gear:$DOCKER_HOME_DIR/.gear \
-v $HOME/.Xauthority:$DOCKER_HOME_DIR/.Xauthority \
-v $PROJECT_DIR:$DOCKER_HOME_DIR/Projects/$(basename "$PROJECT_DIR")
--device /dev/snd \
--group-add audio \
-e PULSE_SERVER=unix:/run/user/$(id -u)/pulse/native \
-v /run/user/$(id -u)/pulse/native:/run/user/$(id -u)/pulse/native \
-v $HOME/.config/pulse/cookie:/home/$USERNAME/.config/pulse/cookie"
# Check if RL mode first, then handle container logic
if [ "$DEPLOY" = true ]; then
# Deploy mode - use gr00t_wbc-deploy-${USERNAME} container
# Always clean up old processes and create a new container
# Kill all gr00t_wbc processes across containers to prevent message passing conflicts
"$SCRIPT_DIR/kill_gr00t_wbc_processors.sh"
echo "Creating new deploy container..."
# Clean up old processes and create a fresh deploy container
# Remove existing deploy container if it exists
if sudo docker ps -a --format '{{.Names}}' | grep -q "^$DEPLOY_CONTAINER$"; then
echo "Removing existing deploy container..."
sudo docker rm -f $DEPLOY_CONTAINER
fi
sudo docker run -it --rm $DOCKER_RUN_ARGS \
-w $DOCKER_HOME_DIR/Projects/$WORKTREE_NAME \
--name $DEPLOY_CONTAINER \
$DEPLOY_CONTAINER \
/bin/bash -ic 'exec "$0" "$@"' \
"${DOCKER_HOME_DIR}/Projects/${WORKTREE_NAME}/docker/entrypoint/deploy.sh" \
"${EXTRA_ARGS[@]}"
else
# Bash mode - use gr00t_wbc-bash-${USERNAME} container
if sudo docker ps -a --format '{{.Names}}' | grep -q "^$BASH_CONTAINER$"; then
echo "Bash container exists, starting it..."
sudo docker start $BASH_CONTAINER > /dev/null
sudo docker exec -it $BASH_CONTAINER /bin/bash
else
echo "Creating new bash container with auto-install gr00t_wbc..."
sudo docker run -it $DOCKER_RUN_ARGS \
-w $DOCKER_HOME_DIR/Projects/$WORKTREE_NAME \
--name $BASH_CONTAINER \
$DEPLOY_CONTAINER \
/bin/bash -ic 'exec "$0"' \
"${DOCKER_HOME_DIR}/Projects/${WORKTREE_NAME}/docker/entrypoint/bash.sh"
fi
fi
# Cleanup X11 permissions
$X11_ENABLED && xhost -local:docker 2>/dev/null

39
external_dependencies/unitree_sdk2_python/.gitignore

@ -0,0 +1,39 @@
# Generated by MacOS
.DS_Store
# Generated by Windows
Thumbs.db
# Applications
*.app
*.exe
*.war
# Large media files
*.mp4
*.tiff
*.avi
*.flv
*.mov
*.wmv
*.jpg
*.png
# VS Code
.vscode
# other
*.egg-info
__pycache__
# IDEs
.idea
# cache
.pytest_cache
# JetBrains IDE
.idea/
# python
dist/

29
external_dependencies/unitree_sdk2_python/LICENSE

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2016-2024 HangZhou YuShu TECHNOLOGY CO.,LTD. ("Unitree Robotics")
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

121
external_dependencies/unitree_sdk2_python/README zh.md

@ -0,0 +1,121 @@
# unitree_sdk2_python
unitree_sdk2 python 接口
# 安装
## 依赖
- python>=3.8
- cyclonedds==0.10.2
- numpy
- opencv-python
## 安装 unitree_sdk2_python
在终端中执行:
```bash
cd ~
sudo apt install python3-pip
git clone https://github.com/unitreerobotics/unitree_sdk2_python.git
cd unitree_sdk2_python
pip3 install -e .
```
## FAQ
##### 1. `pip3 install -e .` 遇到报错
```bash
Could not locate cyclonedds. Try to set CYCLONEDDS_HOME or CMAKE_PREFIX_PATH
```
该错误提示找不到 cyclonedds 路径。首先编译安装cyclonedds:
```bash
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 install
```
进入 unitree_sdk2_python 目录,设置 `CYCLONEDDS_HOME` 为刚刚编译好的 cyclonedds 所在路径,再安装 unitree_sdk2_python
```bash
cd ~/unitree_sdk2_python
export CYCLONEDDS_HOME="~/cyclonedds/install"
pip3 install -e .
```
详细见:
https://pypi.org/project/cyclonedds/#installing-with-pre-built-binaries
# 使用
python sdk2 接口与 unitree_skd2的接口保持一致,通过请求响应或订阅发布topic实现机器人的状态获取和控制。相应的例程位于`/example`目录下。在运行例程前,需要根据文档 https://support.unitree.com/home/zh/developer/Quick_start 配置好机器人的网络连接。
## DDS通讯
在终端中执行:
```bash
python3 ./example/helloworld/publisher.py
```
打开新的终端,执行:
```bash
python3 ./example/helloworld/subscriber.py
```
可以看到终端输出的数据信息。`publisher.py` 和 `subscriber.py` 传输的数据定义在 `user_data.py` 中,用户可以根据需要自行定义需要传输的数据结构。
## 高层状态和控制
高层接口的数据结构和控制方式与unitree_sdk2一致。具体可见:https://support.unitree.com/home/zh/developer/sports_services
### 高层状态
终端中执行:
```bash
python3 ./example/high_level/read_highstate.py enp2s0
```
其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。
### 高层控制
终端中执行:
```bash
python3 ./example/high_level/sportmode_test.py enp2s0
```
其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。
该例程提供了几种测试方法,可根据测试需要选择:
```python
test.StandUpDown() # 站立趴下
# test.VelocityMove() # 速度控制
# test.BalanceAttitude() # 姿态控制
# test.TrajectoryFollow() # 轨迹跟踪
# test.SpecialMotions() # 特殊动作
```
## 底层状态和控制
底层接口的数据结构和控制方式与unitree_sdk2一致。具体可见:https://support.unitree.com/home/zh/developer/Basic_services
### 底层状态
终端中执行:
```bash
python3 ./example/low_level/lowlevel_control.py enp2s0
```
其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。程序会输出右前腿hip关节的状态、IMU和电池电压信息。
### 底层电机控制
首先使用 app 关闭高层运动服务(sport_mode),否则会导致指令冲突。
终端中执行:
```bash
python3 ./example/low_level/lowlevel_control.py enp2s0
```
其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。左后腿 hip 关节会保持在0角度 (安全起见,这里设置 kp=10, kd=1),左后腿 calf 关节将持续输出 1Nm 的转矩。
## 遥控器状态获取
终端中执行:
```bash
python3 ./example/wireless_controller/wireless_controller.py enp2s0
```
其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。
终端将输出每一个按键的状态。对于遥控器按键的定义和数据结构可见: https://support.unitree.com/home/zh/developer/Get_remote_control_status
## 前置摄像头
使用opencv获取前置摄像头(确保在有图形界面的系统下运行, 按 ESC 退出程序):
```bash
python3 ./example/front_camera/camera_opencv.py enp2s0
```
其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。
## 避障开关
```bash
python3 ./example/obstacles_avoid_switch/obstacles_avoid_switch.py enp2s0
```
其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。机器人将循环开启和关闭避障功能。关于避障服务,详细见 https://support.unitree.com/home/zh/developer/ObstaclesAvoidClient
## 灯光音量控制
```bash
python3 ./example/vui_client/vui_client_example.py enp2s0
```
其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。机器人将循环调节音量和灯光亮度。该接口详细见 https://support.unitree.com/home/zh/developer/VuiClient

118
external_dependencies/unitree_sdk2_python/README.md

@ -0,0 +1,118 @@
# unitree_sdk2_python
Python interface for unitree sdk2
# Installation
## Dependencies
- Python >= 3.8
- cyclonedds == 0.10.2
- numpy
- opencv-python
## Install unitree_sdk2_python
```bash
pip install unitree_sdk2py
```
### Installing from source
Execute the following commands in the terminal:
```bash
cd ~
sudo apt install python3-pip
git clone https://github.com/unitreerobotics/unitree_sdk2_python.git
cd unitree_sdk2_python
pip3 install -e .
```
## FAQ
##### 1. Error when `pip3 install -e .`:
```bash
Could not locate cyclonedds. Try to set CYCLONEDDS_HOME or CMAKE_PREFIX_PATH
```
This error mentions that the cyclonedds path could not be found. First compile and install cyclonedds:
```bash
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 install
```
Enter the unitree_sdk2_python directory, set `CYCLONEDDS_HOME` to the path of the cyclonedds you just compiled, and then install unitree_sdk2_python.
```bash
cd ~/unitree_sdk2_python
export CYCLONEDDS_HOME="~/cyclonedds/install"
pip3 install -e .
```
For details, see: https://pypi.org/project/cyclonedds/#installing-with-pre-built-binaries
# Usage
The Python sdk2 interface maintains consistency with the unitree_sdk2 interface, achieving robot status acquisition and control through request-response or topic subscription/publishing. Example programs are located in the `/example` directory. Before running the examples, configure the robot's network connection as per the instructions in the document at https://support.unitree.com/home/en/developer/Quick_start.
## DDS Communication
In the terminal, execute:
```bash
python3 ./example/helloworld/publisher.py
```
Open a new terminal and execute:
```bash
python3 ./example/helloworld/subscriber.py
```
You will see the data output in the terminal. The data structure transmitted between `publisher.py` and `subscriber.py` is defined in `user_data.py`, and users can define the required data structure as needed.
## High-Level Status and Control
The high-level interface maintains consistency with unitree_sdk2 in terms of data structure and control methods. For detailed information, refer to https://support.unitree.com/home/en/developer/sports_services.
### High-Level Status
Execute the following command in the terminal:
```bash
python3 ./example/high_level/read_highstate.py enp2s0
```
Replace `enp2s0` with the name of the network interface to which the robot is connected,.
### High-Level Control
Execute the following command in the terminal:
```bash
python3 ./example/high_level/sportmode_test.py enp2s0
```
Replace `enp2s0` with the name of the network interface to which the robot is connected. This example program provides several test methods, and you can choose the required tests as follows:
```python
test.StandUpDown() # Stand up and lie down
# test.VelocityMove() # Velocity control
# test.BalanceAttitude() # Attitude control
# test.TrajectoryFollow() # Trajectory tracking
# test.SpecialMotions() # Special motions
```
## Low-Level Status and Control
The low-level interface maintains consistency with unitree_sdk2 in terms of data structure and control methods. For detailed information, refer to https://support.unitree.com/home/en/developer/Basic_services.
### Low-Level Status
Execute the following command in the terminal:
```bash
python3 ./example/low_level/lowlevel_control.py enp2s0
```
Replace `enp2s0` with the name of the network interface to which the robot is connected. The program will output the state of the right front leg hip joint, IMU, and battery voltage.
### Low-Level Motor Control
First, use the app to turn off the high-level motion service (sport_mode) to prevent conflicting instructions.
Execute the following command in the terminal:
```bash
python3 ./example/low_level/lowlevel_control.py enp2s0
```
Replace `enp2s0` with the name of the network interface to which the robot is connected. The left hind leg hip joint will maintain a 0-degree position (for safety, set kp=10, kd=1), and the left hind leg calf joint will continuously output 1Nm of torque.
## Wireless Controller Status
Execute the following command in the terminal:
```bash
python3 ./example/wireless_controller/wireless_controller.py enp2s0
```
Replace `enp2s0` with the name of the network interface to which the robot is connected. The terminal will output the status of each key. For the definition and data structure of the remote control keys, refer to https://support.unitree.com/home/en/developer/Get_remote_control_status.
## Front Camera
Use OpenCV to obtain the front camera (ensure to run on a system with a graphical interface, and press ESC to exit the program):
```bash
python3 ./example/front_camera/camera_opencv.py enp2s0
```
Replace `enp2s0` with the name of the network interface to which the robot is connected.
## Obstacle Avoidance Switch
```bash
python3 ./example/obstacles_avoid_switch/obstacles_avoid_switch.py enp2s0
```
Replace `enp2s0` with the name of the network interface to which the robot is connected. The robot will cycle obstacle avoidance on and off. For details on the obstacle avoidance service, see https://support.unitree.com/home/en/developer/ObstaclesAvoidClient
## Light and volume control
```bash
python3 ./example/vui_client/vui_client_example.py enp2s0
```
Replace `enp2s0` with the name of the network interface to which the robot is connected.T he robot will cycle the volume and light brightness. The interface is detailed at https://support.unitree.com/home/en/developer/VuiClient

51
external_dependencies/unitree_sdk2_python/example/b2/camera/camera_opencv.py

@ -0,0 +1,51 @@
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.b2.front_video.front_video_client import FrontVideoClient
from unitree_sdk2py.b2.back_video.back_video_client import BackVideoClient
import cv2
import numpy as np
import sys
def display_image(window_name, data):
# If data is a list, we need to convert it to a bytes object
if isinstance(data, list):
data = bytes(data)
# Now convert to numpy image
image_data = np.frombuffer(data, dtype=np.uint8)
image = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
if image is not None:
cv2.imshow(window_name, image)
if __name__ == "__main__":
if len(sys.argv) > 1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
frontCameraClient = FrontVideoClient() # Create a front camera video client
frontCameraClient.SetTimeout(3.0)
frontCameraClient.Init()
backCameraClient = BackVideoClient() # Create a back camera video client
backCameraClient.SetTimeout(3.0)
backCameraClient.Init()
# Loop to continuously fetch images
while True:
# Get front camera image
front_code, front_data = frontCameraClient.GetImageSample()
if front_code == 0:
display_image("Front Camera", front_data)
# Get back camera image
back_code, back_data = backCameraClient.GetImageSample()
if back_code == 0:
display_image("Back Camera", back_data)
# Press ESC to stop
if cv2.waitKey(20) == 27:
break
# Clean up windows
cv2.destroyAllWindows()

51
external_dependencies/unitree_sdk2_python/example/b2/camera/capture_image.py

@ -0,0 +1,51 @@
import time
import os
import sys
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.b2.front_video.front_video_client import FrontVideoClient
from unitree_sdk2py.b2.back_video.back_video_client import BackVideoClient
if __name__ == "__main__":
if len(sys.argv) > 1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
# 创建前置相机客户端
front_client = FrontVideoClient()
front_client.SetTimeout(3.0)
front_client.Init()
# 创建后置相机客户端
back_client = BackVideoClient()
back_client.SetTimeout(3.0)
back_client.Init()
print("##################Get Front Camera Image###################")
# 获取前置相机图像
front_code, front_data = front_client.GetImageSample()
if front_code != 0:
print("Get front camera image error. Code:", front_code)
else:
front_image_name = "./front_img.jpg"
print("Front Image Saved as:", front_image_name)
with open(front_image_name, "+wb") as f:
f.write(bytes(front_data))
print("##################Get Back Camera Image###################")
# 获取后置相机图像
back_code, back_data = back_client.GetImageSample()
if back_code != 0:
print("Get back camera image error. Code:", back_code)
else:
back_image_name = "./back_img.jpg"
print("Back Image Saved as:", back_image_name)
with open(back_image_name, "+wb") as f:
f.write(bytes(back_data))
time.sleep(1)

105
external_dependencies/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py

@ -0,0 +1,105 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.b2.sport.sport_client import SportClient
import math
from dataclasses import dataclass
@dataclass
class TestOption:
name: str
id: int
option_list = [
TestOption(name="damp", id=0),
TestOption(name="stand_up", id=1),
TestOption(name="stand_down", id=2),
TestOption(name="move forward", id=3),
TestOption(name="move lateral", id=4),
TestOption(name="move rotate", id=5),
TestOption(name="stop_move", id=6),
TestOption(name="switch_gait", id=7),
TestOption(name="switch_gait", id=8),
TestOption(name="recovery", id=9),
TestOption(name="balanced stand", id=10)
]
class UserInterface:
def __init__(self):
self.test_option_ = None
def convert_to_int(self, input_str):
try:
return int(input_str)
except ValueError:
return None
def terminal_handle(self):
input_str = input("Enter id or name: \n")
if input_str == "list":
self.test_option_.name = None
self.test_option_.id = None
for option in option_list:
print(f"{option.name}, id: {option.id}")
return
for option in option_list:
if input_str == option.name or self.convert_to_int(input_str) == option.id:
self.test_option_.name = option.name
self.test_option_.id = option.id
print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}")
return
print("No matching test option found.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: python3 {sys.argv[0]} networkInterface")
sys.exit(-1)
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
ChannelFactoryInitialize(0, sys.argv[1])
test_option = TestOption(name=None, id=None)
user_interface = UserInterface()
user_interface.test_option_ = test_option
sport_client = SportClient()
sport_client.SetTimeout(10.0)
sport_client.Init()
print("Input \"list\" to list all test option ...")
while True:
user_interface.terminal_handle()
print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}")
if test_option.id == 0:
sport_client.Damp()
elif test_option.id == 1:
sport_client.StandUp()
elif test_option.id == 2:
sport_client.StandDown()
elif test_option.id == 3:
sport_client.Move(0.3,0,0)
elif test_option.id == 4:
sport_client.Move(0,0.3,0)
elif test_option.id == 5:
sport_client.Move(0,0,0.5)
elif test_option.id == 6:
sport_client.StopMove()
elif test_option.id == 7:
sport_client.SwitchGait(0)
elif test_option.id == 8:
sport_client.SwitchGait(1)
elif test_option.id == 9:
sport_client.RecoveryStand()
elif test_option.id == 10:
sport_client.BalanceStand()
time.sleep(1)

175
external_dependencies/unitree_sdk2_python/example/b2/low_level/b2_stand_example.py

@ -0,0 +1,175 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_
from unitree_sdk2py.utils.thread import RecurrentThread
import unitree_legged_const as b2
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
from unitree_sdk2py.b2.sport.sport_client import SportClient
from unitree_sdk2py.utils.crc import CRC
class Custom:
def __init__(self):
self.Kp = 1000.0
self.Kd = 10.0
self.time_consume = 0
self.rate_count = 0
self.sin_count = 0
self.motiontime = 0
self.dt = 0.002
self.low_cmd = unitree_go_msg_dds__LowCmd_()
self.low_state = None
self.targetPos_1 = [0.0, 1.36, -2.65, 0.0, 1.36, -2.65,
-0.2, 1.36, -2.65, 0.2, 1.36, -2.65]
self.targetPos_2 = [0.0, 0.67, -1.3, 0.0, 0.67, -1.3,
0.0, 0.67, -1.3, 0.0, 0.67, -1.3]
self.targetPos_3 = [-0.5, 1.36, -2.65, 0.5, 1.36, -2.65,
-0.5, 1.36, -2.65, 0.5, 1.36, -2.65]
self.startPos = [0.0] * 12
self.duration_1 = 500
self.duration_2 = 900
self.duration_3 = 1000
self.duration_4 = 900
self.percent_1 = 0
self.percent_2 = 0
self.percent_3 = 0
self.percent_4 = 0
self.firstRun = True
self.done = False
# thread handling
self.lowCmdWriteThreadPtr = None
self.crc = CRC()
def Init(self):
self.InitLowCmd()
# create publisher #
self.lowcmd_publisher = ChannelPublisher("rt/lowcmd", LowCmd_)
self.lowcmd_publisher.Init()
# create subscriber #
self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateMessageHandler, 10)
self.sc = SportClient()
self.sc.SetTimeout(5.0)
self.sc.Init()
self.msc = MotionSwitcherClient()
self.msc.SetTimeout(5.0)
self.msc.Init()
status, result = self.msc.CheckMode()
while result['name']:
self.sc.StandDown()
self.msc.ReleaseMode()
status, result = self.msc.CheckMode()
time.sleep(1)
def Start(self):
self.lowCmdWriteThreadPtr = RecurrentThread(
interval=0.002, target=self.LowCmdWrite, name="writebasiccmd"
)
self.lowCmdWriteThreadPtr.Start()
def InitLowCmd(self):
self.low_cmd.head[0] = 0xFE
self.low_cmd.head[1] = 0xEF
self.low_cmd.level_flag = 0xFF
self.low_cmd.gpio = 0
for i in range(20):
self.low_cmd.motor_cmd[i].mode = 0x01
self.low_cmd.motor_cmd[i].q= b2.PosStopF
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = b2.VelStopF
self.low_cmd.motor_cmd[i].kd = 0
self.low_cmd.motor_cmd[i].tau = 0
def LowStateMessageHandler(self, msg: LowState_):
self.low_state = msg
def LowCmdWrite(self):
if self.firstRun:
for i in range(12):
self.startPos[i] = self.low_state.motor_state[i].q
self.firstRun = False
self.percent_1 += 1.0 / self.duration_1
self.percent_1 = min(self.percent_1, 1)
if self.percent_1 < 1:
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_1) * self.startPos[i] + self.percent_1 * self.targetPos_1[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 <= 1):
self.percent_2 += 1.0 / self.duration_2
self.percent_2 = min(self.percent_2, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_2) * self.targetPos_1[i] + self.percent_2 * self.targetPos_2[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 < 1):
self.percent_3 += 1.0 / self.duration_3
self.percent_3 = min(self.percent_3, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = self.targetPos_2[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 == 1) and (self.percent_4 <= 1):
self.percent_4 += 1.0 / self.duration_4
self.percent_4 = min(self.percent_4, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_4) * self.targetPos_2[i] + self.percent_4 * self.targetPos_3[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
self.low_cmd.crc = self.crc.Crc(self.low_cmd)
self.lowcmd_publisher.Write(self.low_cmd)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
custom.Start()
while True:
if custom.percent_4 == 1.0:
time.sleep(1)
print("Done!")
sys.exit(-1)
time.sleep(1)

20
external_dependencies/unitree_sdk2_python/example/b2/low_level/unitree_legged_const.py

@ -0,0 +1,20 @@
LegID = {
"FR_0": 0, # Front right hip
"FR_1": 1, # Front right thigh
"FR_2": 2, # Front right calf
"FL_0": 3,
"FL_1": 4,
"FL_2": 5,
"RR_0": 6,
"RR_1": 7,
"RR_2": 8,
"RL_0": 9,
"RL_1": 10,
"RL_2": 11,
}
HIGHLEVEL = 0xEE
LOWLEVEL = 0xFF
TRIGERLEVEL = 0xF0
PosStopF = 2.146e9
VelStopF = 16000.0

51
external_dependencies/unitree_sdk2_python/example/b2w/camera/camera_opencv.py

@ -0,0 +1,51 @@
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.b2.front_video.front_video_client import FrontVideoClient
from unitree_sdk2py.b2.back_video.back_video_client import BackVideoClient
import cv2
import numpy as np
import sys
def display_image(window_name, data):
# If data is a list, we need to convert it to a bytes object
if isinstance(data, list):
data = bytes(data)
# Now convert to numpy image
image_data = np.frombuffer(data, dtype=np.uint8)
image = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
if image is not None:
cv2.imshow(window_name, image)
if __name__ == "__main__":
if len(sys.argv) > 1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
frontCameraClient = FrontVideoClient() # Create a front camera video client
frontCameraClient.SetTimeout(3.0)
frontCameraClient.Init()
backCameraClient = BackVideoClient() # Create a back camera video client
backCameraClient.SetTimeout(3.0)
backCameraClient.Init()
# Loop to continuously fetch images
while True:
# Get front camera image
front_code, front_data = frontCameraClient.GetImageSample()
if front_code == 0:
display_image("Front Camera", front_data)
# Get back camera image
back_code, back_data = backCameraClient.GetImageSample()
if back_code == 0:
display_image("Back Camera", back_data)
# Press ESC to stop
if cv2.waitKey(20) == 27:
break
# Clean up windows
cv2.destroyAllWindows()

51
external_dependencies/unitree_sdk2_python/example/b2w/camera/capture_image.py

@ -0,0 +1,51 @@
import time
import os
import sys
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.b2.front_video.front_video_client import FrontVideoClient
from unitree_sdk2py.b2.back_video.back_video_client import BackVideoClient
if __name__ == "__main__":
if len(sys.argv) > 1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
# 创建前置相机客户端
front_client = FrontVideoClient()
front_client.SetTimeout(3.0)
front_client.Init()
# 创建后置相机客户端
back_client = BackVideoClient()
back_client.SetTimeout(3.0)
back_client.Init()
print("##################Get Front Camera Image###################")
# 获取前置相机图像
front_code, front_data = front_client.GetImageSample()
if front_code != 0:
print("Get front camera image error. Code:", front_code)
else:
front_image_name = "./front_img.jpg"
print("Front Image Saved as:", front_image_name)
with open(front_image_name, "+wb") as f:
f.write(bytes(front_data))
print("##################Get Back Camera Image###################")
# 获取后置相机图像
back_code, back_data = back_client.GetImageSample()
if back_code != 0:
print("Get back camera image error. Code:", back_code)
else:
back_image_name = "./back_img.jpg"
print("Back Image Saved as:", back_image_name)
with open(back_image_name, "+wb") as f:
f.write(bytes(back_data))
time.sleep(1)

101
external_dependencies/unitree_sdk2_python/example/b2w/high_level/b2w_sport_client.py

@ -0,0 +1,101 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.b2.sport.sport_client import SportClient
import math
from dataclasses import dataclass
@dataclass
class TestOption:
name: str
id: int
option_list = [
TestOption(name="damp", id=0),
TestOption(name="stand_up", id=1),
TestOption(name="stand_down", id=2),
TestOption(name="move forward", id=3),
TestOption(name="move lateral", id=4),
TestOption(name="move rotate", id=5),
TestOption(name="stop_move", id=6),
TestOption(name="switch_gait", id=7),
TestOption(name="switch_gait", id=8),
TestOption(name="recovery", id=9)
]
class UserInterface:
def __init__(self):
self.test_option_ = None
def convert_to_int(self, input_str):
try:
return int(input_str)
except ValueError:
return None
def terminal_handle(self):
input_str = input("Enter id or name: \n")
if input_str == "list":
self.test_option_.name = None
self.test_option_.id = None
for option in option_list:
print(f"name: {option.name}, id: {option.id}")
return
for option in option_list:
if input_str == option.name or self.convert_to_int(input_str) == option.id:
self.test_option_.name = option.name
self.test_option_.id = option.id
print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}")
return
print("No matching test option found.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: python3 {sys.argv[0]} networkInterface")
sys.exit(-1)
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
ChannelFactoryInitialize(0, sys.argv[1])
test_option = TestOption(name=None, id=None)
user_interface = UserInterface()
user_interface.test_option_ = test_option
sport_client = SportClient()
sport_client.SetTimeout(10.0)
sport_client.Init()
print("Input \"list\" to list all test option ...")
while True:
user_interface.terminal_handle()
print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}\n")
if test_option.id == 0:
sport_client.Damp()
elif test_option.id == 1:
sport_client.StandUp()
elif test_option.id == 2:
sport_client.StandDown()
elif test_option.id == 3:
sport_client.Move(0.3,0,0)
elif test_option.id == 4:
sport_client.Move(0,0.3,0)
elif test_option.id == 5:
sport_client.Move(0,0,0.5)
elif test_option.id == 6:
sport_client.StopMove()
elif test_option.id == 7:
sport_client.SwitchGait(0)
elif test_option.id == 8:
sport_client.SwitchGait(1)
elif test_option.id == 9:
sport_client.RecoveryStand()
time.sleep(1)

196
external_dependencies/unitree_sdk2_python/example/b2w/low_level/b2w_stand_example.py

@ -0,0 +1,196 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_
from unitree_sdk2py.utils.crc import CRC
from unitree_sdk2py.utils.thread import RecurrentThread
import unitree_legged_const as b2w
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
from unitree_sdk2py.b2.sport.sport_client import SportClient
class Custom:
def __init__(self):
self.Kp = 1000.0
self.Kd = 10.0
self.time_consume = 0
self.rate_count = 0
self.sin_count = 0
self.motiontime = 0
self.dt = 0.002
self.low_cmd = unitree_go_msg_dds__LowCmd_()
self.low_state = None
self.targetPos_1 = [0.0, 1.36, -2.65, 0.0, 1.36, -2.65,
-0.2, 1.36, -2.65, 0.2, 1.36, -2.65]
self.targetPos_2 = [0.0, 0.67, -1.3, 0.0, 0.67, -1.3,
0.0, 0.67, -1.3, 0.0, 0.67, -1.3]
self.targetPos_3 = [-0.65, 1.36, -2.65, 0.65, 1.36, -2.65,
-0.65, 1.36, -2.65, 0.65, 1.36, -2.65]
self.startPos = [0.0] * 12
self.duration_1 = 800
self.duration_2 = 800
self.duration_3 = 2000
self.duration_4 = 1500
self.percent_1 = 0
self.percent_2 = 0
self.percent_3 = 0
self.percent_4 = 0
self.firstRun = True
self.done = False
# thread handling
self.lowCmdWriteThreadPtr = None
self.crc = CRC()
def Init(self):
self.InitLowCmd()
# create publisher #
self.lowcmd_publisher = ChannelPublisher("rt/lowcmd", LowCmd_)
self.lowcmd_publisher.Init()
# create subscriber #
self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateMessageHandler, 10)
self.sc = SportClient()
self.sc.SetTimeout(5.0)
self.sc.Init()
self.msc = MotionSwitcherClient()
self.msc.SetTimeout(5.0)
self.msc.Init()
status, result = self.msc.CheckMode()
while result['name']:
self.sc.StandDown()
self.msc.ReleaseMode()
status, result = self.msc.CheckMode()
time.sleep(1)
def Start(self):
self.lowCmdWriteThreadPtr = RecurrentThread(
name="writebasiccmd", interval=self.dt, target=self.LowCmdWrite,
)
self.lowCmdWriteThreadPtr.Start()
def InitLowCmd(self):
self.low_cmd.head[0] = 0xFE
self.low_cmd.head[1] = 0xEF
self.low_cmd.level_flag = 0xFF
self.low_cmd.gpio = 0
for i in range(20):
self.low_cmd.motor_cmd[i].mode = 0x01 # (PMSM) mode
self.low_cmd.motor_cmd[i].q = b2w.PosStopF
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = b2w.VelStopF
self.low_cmd.motor_cmd[i].kd = 0
self.low_cmd.motor_cmd[i].tau = 0
def LowStateMessageHandler(self, msg: LowState_):
self.low_state = msg
def LowCmdWrite(self):
if self.firstRun:
for i in range(12):
self.startPos[i] = self.low_state.motor_state[i].q
self.firstRun = False
self.percent_1 += 1.0 / self.duration_1
self.percent_1 = min(self.percent_1, 1)
if self.percent_1 < 1:
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_1) * self.startPos[i] + self.percent_1 * self.targetPos_1[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 <= 1):
self.percent_2 += 1.0 / self.duration_2
self.percent_2 = min(self.percent_2, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_2) * self.targetPos_1[i] + self.percent_2 * self.targetPos_2[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 < 1):
self.percent_3 += 1.0 / self.duration_3
self.percent_3 = min(self.percent_3, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = self.targetPos_2[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if self.percent_3 < 0.4:
for i in range(12, 16):
self.low_cmd.motor_cmd[i].q = 0
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = 3
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if 0.4 <= self.percent_3 < 0.8:
for i in range(12, 16):
self.low_cmd.motor_cmd[i].q = 0
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = -3
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if self.percent_3 >= 0.8:
for i in range(12, 16):
self.low_cmd.motor_cmd[i].q = 0
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 == 1) and (self.percent_4 <= 1):
self.percent_4 += 1.0 / self.duration_4
self.percent_4 = min(self.percent_4, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_4) * self.targetPos_2[i] + self.percent_4 * self.targetPos_3[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
self.low_cmd.crc = self.crc.Crc(self.low_cmd)
self.lowcmd_publisher.Write(self.low_cmd)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
custom.Start()
while True:
if custom.percent_4 == 1.0:
time.sleep(1)
print("Done!")
sys.exit(-1)
time.sleep(1)

24
external_dependencies/unitree_sdk2_python/example/b2w/low_level/unitree_legged_const.py

@ -0,0 +1,24 @@
LegID = {
"FR_0": 0, # Front right hip
"FR_1": 1, # Front right thigh
"FR_2": 2, # Front right calf
"FL_0": 3,
"FL_1": 4,
"FL_2": 5,
"RR_0": 6,
"RR_1": 7,
"RR_2": 8,
"RL_0": 9,
"RL_1": 10,
"RL_2": 11,
"FR_w": 12, # Front right wheel
"FL_w": 13, # Front left wheel
"RR_w": 14, # Rear right wheel
"RL_w": 15, # Rear left wheel
}
HIGHLEVEL = 0xEE
LOWLEVEL = 0xFF
TRIGERLEVEL = 0xF0
PosStopF = 2.146e9
VelStopF = 16000.0

44
external_dependencies/unitree_sdk2_python/example/g1/audio/g1_audio_client_example.py

@ -0,0 +1,44 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.g1.audio.g1_audio_client import AudioClient
from unitree_sdk2py.g1.loco.g1_loco_client import LocoClient
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: python3 {sys.argv[0]} networkInterface")
sys.exit(-1)
ChannelFactoryInitialize(0, sys.argv[1])
audio_client = AudioClient()
audio_client.SetTimeout(10.0)
audio_client.Init()
sport_client = LocoClient()
sport_client.SetTimeout(10.0)
sport_client.Init()
ret = audio_client.GetVolume()
print("debug GetVolume: ",ret)
audio_client.SetVolume(85)
ret = audio_client.GetVolume()
print("debug GetVolume: ",ret)
sport_client.WaveHand()
audio_client.TtsMaker("大家好!我是宇树科技人形机器人。语音开发测试例程运行成功! 很高兴认识你!",0)
time.sleep(8)
audio_client.TtsMaker("接下来测试灯带开发例程!",0)
time.sleep(1)
audio_client.LedControl(255,0,0)
time.sleep(1)
audio_client.LedControl(0,255,0)
time.sleep(1)
audio_client.LedControl(0,0,255)
time.sleep(3)
audio_client.TtsMaker("测试完毕,谢谢大家!",0)

192
external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm5_sdk_dds_example.py

@ -0,0 +1,192 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_
from unitree_sdk2py.utils.crc import CRC
from unitree_sdk2py.utils.thread import RecurrentThread
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
import numpy as np
kPi = 3.141592654
kPi_2 = 1.57079632
class G1JointIndex:
# Left leg
LeftHipPitch = 0
LeftHipRoll = 1
LeftHipYaw = 2
LeftKnee = 3
LeftAnklePitch = 4
LeftAnkleB = 4
LeftAnkleRoll = 5
LeftAnkleA = 5
# Right leg
RightHipPitch = 6
RightHipRoll = 7
RightHipYaw = 8
RightKnee = 9
RightAnklePitch = 10
RightAnkleB = 10
RightAnkleRoll = 11
RightAnkleA = 11
WaistYaw = 12
WaistRoll = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked
WaistA = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked
WaistPitch = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked
WaistB = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked
# Left arm
LeftShoulderPitch = 15
LeftShoulderRoll = 16
LeftShoulderYaw = 17
LeftElbow = 18
LeftWristRoll = 19
LeftWristPitch = 20 # NOTE: INVALID for g1 23dof
LeftWristYaw = 21 # NOTE: INVALID for g1 23dof
# Right arm
RightShoulderPitch = 22
RightShoulderRoll = 23
RightShoulderYaw = 24
RightElbow = 25
RightWristRoll = 26
RightWristPitch = 27 # NOTE: INVALID for g1 23dof
RightWristYaw = 28 # NOTE: INVALID for g1 23dof
kNotUsedJoint = 29 # NOTE: Weight
class Custom:
def __init__(self):
self.time_ = 0.0
self.control_dt_ = 0.02
self.duration_ = 3.0
self.counter_ = 0
self.weight = 0.
self.weight_rate = 0.2
self.kp = 60.
self.kd = 1.5
self.dq = 0.
self.tau_ff = 0.
self.mode_machine_ = 0
self.low_cmd = unitree_hg_msg_dds__LowCmd_()
self.low_state = None
self.first_update_low_state = False
self.crc = CRC()
self.done = False
self.target_pos = [
0.0, kPi_2, 0.0, kPi_2, 0.0,
0.0, -kPi_2, 0.0, kPi_2, 0.0,
0.0, 0.0, 0.0
]
self.arm_joints = [
G1JointIndex.LeftShoulderPitch, G1JointIndex.LeftShoulderRoll,
G1JointIndex.LeftShoulderYaw, G1JointIndex.LeftElbow,
G1JointIndex.LeftWristRoll,
G1JointIndex.RightShoulderPitch, G1JointIndex.RightShoulderRoll,
G1JointIndex.RightShoulderYaw, G1JointIndex.RightElbow,
G1JointIndex.RightWristRoll,
G1JointIndex.WaistYaw,
G1JointIndex.WaistRoll,
G1JointIndex.WaistPitch
]
def Init(self):
# create publisher #
self.arm_sdk_publisher = ChannelPublisher("rt/arm_sdk", LowCmd_)
self.arm_sdk_publisher.Init()
# create subscriber #
self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateHandler, 10)
def Start(self):
self.lowCmdWriteThreadPtr = RecurrentThread(
interval=self.control_dt_, target=self.LowCmdWrite, name="control"
)
while self.first_update_low_state == False:
time.sleep(1)
if self.first_update_low_state == True:
self.lowCmdWriteThreadPtr.Start()
def LowStateHandler(self, msg: LowState_):
self.low_state = msg
if self.first_update_low_state == False:
self.first_update_low_state = True
def LowCmdWrite(self):
self.time_ += self.control_dt_
if self.time_ < self.duration_ :
# [Stage 1]: set robot to zero posture
self.low_cmd.motor_cmd[G1JointIndex.kNotUsedJoint].q = 1 # 1:Enable arm_sdk, 0:Disable arm_sdk
for i,joint in enumerate(self.arm_joints):
ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0)
self.low_cmd.motor_cmd[joint].tau = 0.
self.low_cmd.motor_cmd[joint].q = (1.0 - ratio) * self.low_state.motor_state[joint].q
self.low_cmd.motor_cmd[joint].dq = 0.
self.low_cmd.motor_cmd[joint].kp = self.kp
self.low_cmd.motor_cmd[joint].kd = self.kd
elif self.time_ < self.duration_ * 3 :
# [Stage 2]: lift arms up
for i,joint in enumerate(self.arm_joints):
ratio = np.clip((self.time_ - self.duration_) / (self.duration_ * 2), 0.0, 1.0)
self.low_cmd.motor_cmd[joint].tau = 0.
self.low_cmd.motor_cmd[joint].q = ratio * self.target_pos[i] + (1.0 - ratio) * self.low_state.motor_state[joint].q
self.low_cmd.motor_cmd[joint].dq = 0.
self.low_cmd.motor_cmd[joint].kp = self.kp
self.low_cmd.motor_cmd[joint].kd = self.kd
elif self.time_ < self.duration_ * 6 :
# [Stage 3]: set robot back to zero posture
for i,joint in enumerate(self.arm_joints):
ratio = np.clip((self.time_ - self.duration_*3) / (self.duration_ * 3), 0.0, 1.0)
self.low_cmd.motor_cmd[joint].tau = 0.
self.low_cmd.motor_cmd[joint].q = (1.0 - ratio) * self.low_state.motor_state[joint].q
self.low_cmd.motor_cmd[joint].dq = 0.
self.low_cmd.motor_cmd[joint].kp = self.kp
self.low_cmd.motor_cmd[joint].kd = self.kd
elif self.time_ < self.duration_ * 7 :
# [Stage 4]: release arm_sdk
for i,joint in enumerate(self.arm_joints):
ratio = np.clip((self.time_ - self.duration_*6) / (self.duration_), 0.0, 1.0)
self.low_cmd.motor_cmd[G1JointIndex.kNotUsedJoint].q = (1 - ratio) # 1:Enable arm_sdk, 0:Disable arm_sdk
else:
self.done = True
self.low_cmd.crc = self.crc.Crc(self.low_cmd)
self.arm_sdk_publisher.Write(self.low_cmd)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
custom.Start()
while True:
time.sleep(1)
if custom.done:
print("Done!")
sys.exit(-1)

194
external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm7_sdk_dds_example.py

@ -0,0 +1,194 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_
from unitree_sdk2py.utils.crc import CRC
from unitree_sdk2py.utils.thread import RecurrentThread
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
import numpy as np
kPi = 3.141592654
kPi_2 = 1.57079632
class G1JointIndex:
# Left leg
LeftHipPitch = 0
LeftHipRoll = 1
LeftHipYaw = 2
LeftKnee = 3
LeftAnklePitch = 4
LeftAnkleB = 4
LeftAnkleRoll = 5
LeftAnkleA = 5
# Right leg
RightHipPitch = 6
RightHipRoll = 7
RightHipYaw = 8
RightKnee = 9
RightAnklePitch = 10
RightAnkleB = 10
RightAnkleRoll = 11
RightAnkleA = 11
WaistYaw = 12
WaistRoll = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked
WaistA = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked
WaistPitch = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked
WaistB = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked
# Left arm
LeftShoulderPitch = 15
LeftShoulderRoll = 16
LeftShoulderYaw = 17
LeftElbow = 18
LeftWristRoll = 19
LeftWristPitch = 20 # NOTE: INVALID for g1 23dof
LeftWristYaw = 21 # NOTE: INVALID for g1 23dof
# Right arm
RightShoulderPitch = 22
RightShoulderRoll = 23
RightShoulderYaw = 24
RightElbow = 25
RightWristRoll = 26
RightWristPitch = 27 # NOTE: INVALID for g1 23dof
RightWristYaw = 28 # NOTE: INVALID for g1 23dof
kNotUsedJoint = 29 # NOTE: Weight
class Custom:
def __init__(self):
self.time_ = 0.0
self.control_dt_ = 0.02
self.duration_ = 3.0
self.counter_ = 0
self.weight = 0.
self.weight_rate = 0.2
self.kp = 60.
self.kd = 1.5
self.dq = 0.
self.tau_ff = 0.
self.mode_machine_ = 0
self.low_cmd = unitree_hg_msg_dds__LowCmd_()
self.low_state = None
self.first_update_low_state = False
self.crc = CRC()
self.done = False
self.target_pos = [
0., kPi_2, 0., kPi_2, 0., 0., 0.,
0., -kPi_2, 0., kPi_2, 0., 0., 0.,
0, 0, 0
]
self.arm_joints = [
G1JointIndex.LeftShoulderPitch, G1JointIndex.LeftShoulderRoll,
G1JointIndex.LeftShoulderYaw, G1JointIndex.LeftElbow,
G1JointIndex.LeftWristRoll, G1JointIndex.LeftWristPitch,
G1JointIndex.LeftWristYaw,
G1JointIndex.RightShoulderPitch, G1JointIndex.RightShoulderRoll,
G1JointIndex.RightShoulderYaw, G1JointIndex.RightElbow,
G1JointIndex.RightWristRoll, G1JointIndex.RightWristPitch,
G1JointIndex.RightWristYaw,
G1JointIndex.WaistYaw,
G1JointIndex.WaistRoll,
G1JointIndex.WaistPitch
]
def Init(self):
# create publisher #
self.arm_sdk_publisher = ChannelPublisher("rt/arm_sdk", LowCmd_)
self.arm_sdk_publisher.Init()
# create subscriber #
self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateHandler, 10)
def Start(self):
self.lowCmdWriteThreadPtr = RecurrentThread(
interval=self.control_dt_, target=self.LowCmdWrite, name="control"
)
while self.first_update_low_state == False:
time.sleep(1)
if self.first_update_low_state == True:
self.lowCmdWriteThreadPtr.Start()
def LowStateHandler(self, msg: LowState_):
self.low_state = msg
if self.first_update_low_state == False:
self.first_update_low_state = True
def LowCmdWrite(self):
self.time_ += self.control_dt_
if self.time_ < self.duration_ :
# [Stage 1]: set robot to zero posture
self.low_cmd.motor_cmd[G1JointIndex.kNotUsedJoint].q = 1 # 1:Enable arm_sdk, 0:Disable arm_sdk
for i,joint in enumerate(self.arm_joints):
ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0)
self.low_cmd.motor_cmd[joint].tau = 0.
self.low_cmd.motor_cmd[joint].q = (1.0 - ratio) * self.low_state.motor_state[joint].q
self.low_cmd.motor_cmd[joint].dq = 0.
self.low_cmd.motor_cmd[joint].kp = self.kp
self.low_cmd.motor_cmd[joint].kd = self.kd
elif self.time_ < self.duration_ * 3 :
# [Stage 2]: lift arms up
for i,joint in enumerate(self.arm_joints):
ratio = np.clip((self.time_ - self.duration_) / (self.duration_ * 2), 0.0, 1.0)
self.low_cmd.motor_cmd[joint].tau = 0.
self.low_cmd.motor_cmd[joint].q = ratio * self.target_pos[i] + (1.0 - ratio) * self.low_state.motor_state[joint].q
self.low_cmd.motor_cmd[joint].dq = 0.
self.low_cmd.motor_cmd[joint].kp = self.kp
self.low_cmd.motor_cmd[joint].kd = self.kd
elif self.time_ < self.duration_ * 6 :
# [Stage 3]: set robot back to zero posture
for i,joint in enumerate(self.arm_joints):
ratio = np.clip((self.time_ - self.duration_*3) / (self.duration_ * 3), 0.0, 1.0)
self.low_cmd.motor_cmd[joint].tau = 0.
self.low_cmd.motor_cmd[joint].q = (1.0 - ratio) * self.low_state.motor_state[joint].q
self.low_cmd.motor_cmd[joint].dq = 0.
self.low_cmd.motor_cmd[joint].kp = self.kp
self.low_cmd.motor_cmd[joint].kd = self.kd
elif self.time_ < self.duration_ * 7 :
# [Stage 4]: release arm_sdk
for i,joint in enumerate(self.arm_joints):
ratio = np.clip((self.time_ - self.duration_*6) / (self.duration_), 0.0, 1.0)
self.low_cmd.motor_cmd[G1JointIndex.kNotUsedJoint].q = (1 - ratio) # 1:Enable arm_sdk, 0:Disable arm_sdk
else:
self.done = True
self.low_cmd.crc = self.crc.Crc(self.low_cmd)
self.arm_sdk_publisher.Write(self.low_cmd)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
custom.Start()
while True:
time.sleep(1)
if custom.done:
print("Done!")
sys.exit(-1)

117
external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_loco_client_example.py

@ -0,0 +1,117 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__SportModeState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_
from unitree_sdk2py.g1.loco.g1_loco_client import LocoClient
import math
from dataclasses import dataclass
@dataclass
class TestOption:
name: str
id: int
option_list = [
TestOption(name="damp", id=0),
TestOption(name="Squat2StandUp", id=1),
TestOption(name="StandUp2Squat", id=2),
TestOption(name="move forward", id=3),
TestOption(name="move lateral", id=4),
TestOption(name="move rotate", id=5),
TestOption(name="low stand", id=6),
TestOption(name="high stand", id=7),
TestOption(name="zero torque", id=8),
TestOption(name="wave hand1", id=9), # wave hand without turning around
TestOption(name="wave hand2", id=10), # wave hand and trun around
TestOption(name="shake hand", id=11),
TestOption(name="Lie2StandUp", id=12),
]
class UserInterface:
def __init__(self):
self.test_option_ = None
def convert_to_int(self, input_str):
try:
return int(input_str)
except ValueError:
return None
def terminal_handle(self):
input_str = input("Enter id or name: \n")
if input_str == "list":
self.test_option_.name = None
self.test_option_.id = None
for option in option_list:
print(f"{option.name}, id: {option.id}")
return
for option in option_list:
if input_str == option.name or self.convert_to_int(input_str) == option.id:
self.test_option_.name = option.name
self.test_option_.id = option.id
print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}")
return
print("No matching test option found.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: python3 {sys.argv[0]} networkInterface")
sys.exit(-1)
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
ChannelFactoryInitialize(0, sys.argv[1])
test_option = TestOption(name=None, id=None)
user_interface = UserInterface()
user_interface.test_option_ = test_option
sport_client = LocoClient()
sport_client.SetTimeout(10.0)
sport_client.Init()
print("Input \"list\" to list all test option ...")
while True:
user_interface.terminal_handle()
print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}")
if test_option.id == 0:
sport_client.Damp()
elif test_option.id == 1:
sport_client.Damp()
time.sleep(0.5)
sport_client.Squat2StandUp()
elif test_option.id == 2:
sport_client.StandUp2Squat()
elif test_option.id == 3:
sport_client.Move(0.3,0,0)
elif test_option.id == 4:
sport_client.Move(0,0.3,0)
elif test_option.id == 5:
sport_client.Move(0,0,0.3)
elif test_option.id == 6:
sport_client.LowStand()
elif test_option.id == 7:
sport_client.HighStand()
elif test_option.id == 8:
sport_client.ZeroTorque()
elif test_option.id == 9:
sport_client.WaveHand()
elif test_option.id == 10:
sport_client.WaveHand(True)
elif test_option.id == 11:
sport_client.ShakeHand()
time.sleep(3)
sport_client.ShakeHand()
elif test_option.id == 12:
sport_client.Damp()
time.sleep(0.5)
sport_client.Lie2StandUp() # When using the Lie2StandUp function, ensure that the robot faces up and the ground is hard, flat and rough.
time.sleep(1)

205
external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_low_level_example.py

@ -0,0 +1,205 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_
from unitree_sdk2py.utils.crc import CRC
from unitree_sdk2py.utils.thread import RecurrentThread
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
import numpy as np
G1_NUM_MOTOR = 29
Kp = [
60, 60, 60, 100, 40, 40, # legs
60, 60, 60, 100, 40, 40, # legs
60, 40, 40, # waist
40, 40, 40, 40, 40, 40, 40, # arms
40, 40, 40, 40, 40, 40, 40 # arms
]
Kd = [
1, 1, 1, 2, 1, 1, # legs
1, 1, 1, 2, 1, 1, # legs
1, 1, 1, # waist
1, 1, 1, 1, 1, 1, 1, # arms
1, 1, 1, 1, 1, 1, 1 # arms
]
class G1JointIndex:
LeftHipPitch = 0
LeftHipRoll = 1
LeftHipYaw = 2
LeftKnee = 3
LeftAnklePitch = 4
LeftAnkleB = 4
LeftAnkleRoll = 5
LeftAnkleA = 5
RightHipPitch = 6
RightHipRoll = 7
RightHipYaw = 8
RightKnee = 9
RightAnklePitch = 10
RightAnkleB = 10
RightAnkleRoll = 11
RightAnkleA = 11
WaistYaw = 12
WaistRoll = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked
WaistA = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked
WaistPitch = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked
WaistB = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked
LeftShoulderPitch = 15
LeftShoulderRoll = 16
LeftShoulderYaw = 17
LeftElbow = 18
LeftWristRoll = 19
LeftWristPitch = 20 # NOTE: INVALID for g1 23dof
LeftWristYaw = 21 # NOTE: INVALID for g1 23dof
RightShoulderPitch = 22
RightShoulderRoll = 23
RightShoulderYaw = 24
RightElbow = 25
RightWristRoll = 26
RightWristPitch = 27 # NOTE: INVALID for g1 23dof
RightWristYaw = 28 # NOTE: INVALID for g1 23dof
class Mode:
PR = 0 # Series Control for Pitch/Roll Joints
AB = 1 # Parallel Control for A/B Joints
class Custom:
def __init__(self):
self.time_ = 0.0
self.control_dt_ = 0.002 # [2ms]
self.duration_ = 3.0 # [3 s]
self.counter_ = 0
self.mode_pr_ = Mode.PR
self.mode_machine_ = 0
self.low_cmd = unitree_hg_msg_dds__LowCmd_()
self.low_state = None
self.update_mode_machine_ = False
self.crc = CRC()
def Init(self):
self.msc = MotionSwitcherClient()
self.msc.SetTimeout(5.0)
self.msc.Init()
status, result = self.msc.CheckMode()
while result['name']:
self.msc.ReleaseMode()
status, result = self.msc.CheckMode()
time.sleep(1)
# create publisher #
self.lowcmd_publisher_ = ChannelPublisher("rt/lowcmd", LowCmd_)
self.lowcmd_publisher_.Init()
# create subscriber #
self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateHandler, 10)
def Start(self):
self.lowCmdWriteThreadPtr = RecurrentThread(
interval=self.control_dt_, target=self.LowCmdWrite, name="control"
)
while self.update_mode_machine_ == False:
time.sleep(1)
if self.update_mode_machine_ == True:
self.lowCmdWriteThreadPtr.Start()
def LowStateHandler(self, msg: LowState_):
self.low_state = msg
if self.update_mode_machine_ == False:
self.mode_machine_ = self.low_state.mode_machine
self.update_mode_machine_ = True
self.counter_ +=1
if (self.counter_ % 500 == 0) :
self.counter_ = 0
print(self.low_state.imu_state.rpy)
def LowCmdWrite(self):
self.time_ += self.control_dt_
if self.time_ < self.duration_ :
# [Stage 1]: set robot to zero posture
for i in range(G1_NUM_MOTOR):
ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0)
self.low_cmd.mode_pr = Mode.PR
self.low_cmd.mode_machine = self.mode_machine_
self.low_cmd.motor_cmd[i].mode = 1 # 1:Enable, 0:Disable
self.low_cmd.motor_cmd[i].tau = 0.
self.low_cmd.motor_cmd[i].q = (1.0 - ratio) * self.low_state.motor_state[i].q
self.low_cmd.motor_cmd[i].dq = 0.
self.low_cmd.motor_cmd[i].kp = Kp[i]
self.low_cmd.motor_cmd[i].kd = Kd[i]
elif self.time_ < self.duration_ * 2 :
# [Stage 2]: swing ankle using PR mode
max_P = np.pi * 30.0 / 180.0
max_R = np.pi * 10.0 / 180.0
t = self.time_ - self.duration_
L_P_des = max_P * np.sin(2.0 * np.pi * t)
L_R_des = max_R * np.sin(2.0 * np.pi * t)
R_P_des = max_P * np.sin(2.0 * np.pi * t)
R_R_des = -max_R * np.sin(2.0 * np.pi * t)
self.low_cmd.mode_pr = Mode.PR
self.low_cmd.mode_machine = self.mode_machine_
self.low_cmd.motor_cmd[G1JointIndex.LeftAnklePitch].q = L_P_des
self.low_cmd.motor_cmd[G1JointIndex.LeftAnkleRoll].q = L_R_des
self.low_cmd.motor_cmd[G1JointIndex.RightAnklePitch].q = R_P_des
self.low_cmd.motor_cmd[G1JointIndex.RightAnkleRoll].q = R_R_des
else :
# [Stage 3]: swing ankle using AB mode
max_A = np.pi * 30.0 / 180.0
max_B = np.pi * 10.0 / 180.0
t = self.time_ - self.duration_ * 2
L_A_des = max_A * np.sin(2.0 * np.pi * t)
L_B_des = max_B * np.sin(2.0 * np.pi * t + np.pi)
R_A_des = -max_A * np.sin(2.0 * np.pi * t)
R_B_des = -max_B * np.sin(2.0 * np.pi * t + np.pi)
self.low_cmd.mode_pr = Mode.AB
self.low_cmd.mode_machine = self.mode_machine_
self.low_cmd.motor_cmd[G1JointIndex.LeftAnkleA].q = L_A_des
self.low_cmd.motor_cmd[G1JointIndex.LeftAnkleB].q = L_B_des
self.low_cmd.motor_cmd[G1JointIndex.RightAnkleA].q = R_A_des
self.low_cmd.motor_cmd[G1JointIndex.RightAnkleB].q = R_B_des
max_WristYaw = np.pi * 30.0 / 180.0
L_WristYaw_des = max_WristYaw * np.sin(2.0 * np.pi * t)
R_WristYaw_des = max_WristYaw * np.sin(2.0 * np.pi * t)
self.low_cmd.motor_cmd[G1JointIndex.LeftWristRoll].q = L_WristYaw_des
self.low_cmd.motor_cmd[G1JointIndex.RightWristRoll].q = R_WristYaw_des
self.low_cmd.crc = self.crc.Crc(self.low_cmd)
self.lowcmd_publisher_.Write(self.low_cmd)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
custom.Start()
while True:
time.sleep(1)

225
external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_move_hands_example.py

@ -0,0 +1,225 @@
import time
import sys
import numpy as np
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import HandCmd_, HandState_
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__HandCmd_
from unitree_sdk2py.utils.crc import CRC
from unitree_sdk2py.utils.thread import RecurrentThread
MOTOR_NUM_HAND = 7
Kp = [0.5] * MOTOR_NUM_HAND
Kd = [0.1] * MOTOR_NUM_HAND
Kp[0] = 2.0
maxTorqueLimits_left = [1.05, 1.05, 1.75, 0.0, 0.0, 0.0, 0.0]
minTorqueLimits_left = [-1.05, -0.72, 0.0, -1.57, -1.75, -1.57, -1.75]
maxTorqueLimits_right = [1.05, 0.74, 0.0, 1.57, 1.75, 1.57, 1.75]
minTorqueLimits_right = [-1.05, -1.05, -1.75, 0.0, 0.0, 0.0, 0.0]
def make_hand_mode(motor_index):
status = 0x01
timeout = 0x01
mode = (motor_index & 0x0F)
mode |= (status << 4) # bits [4..6]
mode |= (timeout << 7) # bit 7
return mode
class HandControl:
def __init__(self, network_interface="enp36s0f1"):
ChannelFactoryInitialize(0, network_interface)
self.left_cmd_pub = ChannelPublisher("rt/dex3/left/cmd", HandCmd_)
self.right_cmd_pub = ChannelPublisher("rt/dex3/right/cmd", HandCmd_)
self.left_state_sub = ChannelSubscriber("rt/dex3/left/state", HandState_)
self.right_state_sub = ChannelSubscriber("rt/dex3/right/state", HandState_)
self.left_cmd_pub.Init()
self.right_cmd_pub.Init()
self.left_state_sub.Init(self.left_state_handler, 10)
self.right_state_sub.Init(self.right_state_handler, 10)
self.left_state = None
self.right_state = None
self.crc = CRC()
# Control loop timing
self.control_dt = 0.01 # 10 ms
self.time_ = 0.0
self.stage_time = 3.0 # 3s per stage
# To let the code run once we start:
self.run_flag = False
self.counter = 0
def left_state_handler(self, msg: HandState_):
self.left_state = msg
# self.counter +=1
# if (self.counter % 1000 == 0) :
# self.counter = 0
# print('Left hand state:')
# for i in range(MOTOR_NUM_HAND):
# print(180/np.pi*self.left_state.motor_state[i].q)
def right_state_handler(self, msg: HandState_):
self.right_state = msg
self.counter += 1
if (self.counter % 1000 == 0):
self.counter = 0
print('Right hand state:')
for i in range(MOTOR_NUM_HAND):
print(180 / np.pi * self.right_state.motor_state[i].q)
def start(self):
"""
Kick off the main control loop thread.
"""
self.run_flag = True
self.control_thread = RecurrentThread(interval=self.control_dt,
target=self.hand_control_loop,
name="HandControlLoop")
self.control_thread.Start()
def hand_control_loop(self):
"""
This gets called at a fixed rate (every self.control_dt seconds).
We'll demonstrate 3 stages of motion:
1) Move from current position to 'zero' (or some nominal) in 3s
2) Sinusoidal motion in stage 2 (3s)
3) Another motion in stage 3 (final)
"""
if not self.run_flag:
return
self.time_ += self.control_dt
t = self.time_
cmd_left = unitree_hg_msg_dds__HandCmd_()
cmd_right = unitree_hg_msg_dds__HandCmd_()
# cmd_left.motor_cmd.resize(MOTOR_NUM_HAND)
# cmd_right.motor_cmd.resize(MOTOR_NUM_HAND)
# Prepare stage times
stage1_end = self.stage_time
stage2_end = self.stage_time * 2.0
# We'll fetch current positions for left and right if available
# so we can blend from actual state to zero.
# If we haven't gotten a state yet, default to 0.
left_q_now = [0.0] * MOTOR_NUM_HAND
right_q_now = [0.0] * MOTOR_NUM_HAND
if self.left_state is not None:
for i in range(MOTOR_NUM_HAND):
left_q_now[i] = self.left_state.motor_state[i].q
if self.right_state is not None:
for i in range(MOTOR_NUM_HAND):
right_q_now[i] = self.right_state.motor_state[i].q
left_q_desired = np.deg2rad([50.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
right_q_desired = np.deg2rad([50.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
# Decide the desired position for each stage:
if t < stage1_end:
# Stage 1: Move from the current joint positions to zero in [0..3s]
ratio = np.clip(t / stage1_end, 0.0, 1.0)
# Simple linear blend: final = (1-ratio)*initial + ratio*0
left_q_des = np.zeros(MOTOR_NUM_HAND)
right_q_des = np.zeros(MOTOR_NUM_HAND)
for i in range(MOTOR_NUM_HAND):
left_q_des[i] = (1.0 - ratio) * (left_q_now[i] - left_q_desired[i]) + left_q_desired[i]
right_q_des[i] = (1.0 - ratio) * (right_q_now[i] - right_q_desired[i]) + right_q_desired[i]
else:
# Stage 2: Some sinusoidal wave
# We'll wave only the first 2 or 3 motors, just as a demo
dt2 = t - stage1_end
freq = 1.0 # 1 Hz
amp = 0.3 # ~ 0.3 rad amplitude
left_q_des = left_q_desired
right_q_des = right_q_desired
# E.g. wave motor 0 and 1:
left_q_des[0] += amp * np.sin(2 * np.pi * freq * dt2)
left_q_des[1] += amp * (1 - np.cos(2 * np.pi * freq * dt2))
left_q_des[2] += amp * (1 - np.cos(2 * np.pi * freq * dt2))
right_q_des[0] += amp * np.sin(2 * np.pi * freq * dt2)
right_q_des[1] += amp * (np.cos(2 * np.pi * freq * dt2) - 1)
right_q_des[2] += amp * (np.cos(2 * np.pi * freq * dt2) - 1)
freqA = 2.0
freqB = 2.0
ampA = 0.2
ampB = 0.4
left_q_des[3] += ampA * (np.cos(2 * np.pi * freqA * dt2) - 1)
left_q_des[4] += ampB * (np.cos(2 * np.pi * freqB * dt2) - 1)
left_q_des[5] += ampA * (np.cos(2 * np.pi * freqA * dt2) - 1)
left_q_des[6] += ampB * (np.cos(2 * np.pi * freqB * dt2) - 1)
right_q_des[3] += ampA * (1 - np.cos(2 * np.pi * freqA * dt2))
right_q_des[4] += ampB * (1 - np.cos(2 * np.pi * freqB * dt2))
right_q_des[5] += ampA * (1 - np.cos(2 * np.pi * freqA * dt2))
right_q_des[6] += ampB * (1 - np.cos(2 * np.pi * freqB * dt2))
# Fill in the commands
for i in range(MOTOR_NUM_HAND):
# Build the bitfield mode (see your C++ example)
mode_val = make_hand_mode(i)
# Left
cmd_left.motor_cmd[i].mode = mode_val
cmd_left.motor_cmd[i].q = left_q_des[i]
cmd_left.motor_cmd[i].dq = 0.0
cmd_left.motor_cmd[i].tau = 0.0
cmd_left.motor_cmd[i].kp = Kp[i]
cmd_left.motor_cmd[i].kd = Kd[i]
# Right
cmd_right.motor_cmd[i].mode = mode_val
cmd_right.motor_cmd[i].q = right_q_des[i]
cmd_right.motor_cmd[i].dq = 0.0
cmd_right.motor_cmd[i].tau = 0.0
cmd_right.motor_cmd[i].kp = Kp[i]
cmd_right.motor_cmd[i].kd = Kd[i]
# Compute CRC if your firmware requires it
# cmd_left.crc = self.crc.Crc(cmd_left)
# cmd_right.crc = self.crc.Crc(cmd_right)
# Publish
self.left_cmd_pub.Write(cmd_left)
self.right_cmd_pub.Write(cmd_right)
if __name__ == "__main__":
print("WARNING: Make sure your robot’s hands can move freely before running.")
input("Press Enter to continue...")
# Optionally pass a specific interface name, e.g. "enp37s0f0" or "eth0"
if len(sys.argv) > 1:
net_if = sys.argv[1]
else:
net_if = "enp36s0f1"
# Create and start
hand_control = HandControl(network_interface=net_if)
hand_control.start()
# Just wait
while True:
time.sleep(1)

5
external_dependencies/unitree_sdk2_python/example/g1/readme.md

@ -0,0 +1,5 @@
This example is a test of Unitree G1/H1-2 robot.
**Note:**
idl/unitree_go is used for Unitree Go2/B2/H1/B2w/Go2w robots
idl/unitree_hg is used for Unitree G1/H1-2 robots

41
external_dependencies/unitree_sdk2_python/example/go2/front_camera/camera_opencv.py

@ -0,0 +1,41 @@
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.go2.video.video_client import VideoClient
import cv2
import numpy as np
import sys
if __name__ == "__main__":
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
client = VideoClient() # Create a video client
client.SetTimeout(3.0)
client.Init()
code, data = client.GetImageSample()
# Request normal when code==0
while code == 0:
# Get Image data from Go2 robot
code, data = client.GetImageSample()
# Convert to numpy image
image_data = np.frombuffer(bytes(data), dtype=np.uint8)
image = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
# Display image
cv2.imshow("front_camera", image)
# Press ESC to stop
if cv2.waitKey(20) == 27:
break
if code != 0:
print("Get image sample error. code:", code)
else:
# Capture an image
cv2.imwrite("front_image.jpg", image)
cv2.destroyWindow("front_camera")

30
external_dependencies/unitree_sdk2_python/example/go2/front_camera/capture_image.py

@ -0,0 +1,30 @@
import time
import os
import sys
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.go2.video.video_client import VideoClient
if __name__ == "__main__":
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
client = VideoClient()
client.SetTimeout(3.0)
client.Init()
print("##################GetImageSample###################")
code, data = client.GetImageSample()
if code != 0:
print("get image sample error. code:", code)
else:
imageName = "./img.jpg"
print("ImageName:", imageName)
with open(imageName, "+wb") as f:
f.write(bytes(data))
time.sleep(1)

170
external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py

@ -0,0 +1,170 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__SportModeState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_
from unitree_sdk2py.go2.sport.sport_client import (
SportClient,
PathPoint,
SPORT_PATH_POINT_SIZE,
)
import math
from dataclasses import dataclass
@dataclass
class TestOption:
name: str
id: int
option_list = [
TestOption(name="damp", id=0),
TestOption(name="stand_up", id=1),
TestOption(name="stand_down", id=2),
TestOption(name="move forward", id=3),
TestOption(name="move lateral", id=4),
TestOption(name="move rotate", id=5),
TestOption(name="stop_move", id=6),
TestOption(name="switch_gait", id=7),
TestOption(name="switch_gait", id=8),
TestOption(name="balanced stand", id=9),
TestOption(name="recovery", id=10),
TestOption(name="recovery", id=10),
TestOption(name="left flip", id=11),
TestOption(name="back flip", id=12),
TestOption(name="free walk", id=13),
TestOption(name="free bound", id=14),
TestOption(name="free avoid", id=15),
TestOption(name="walk stair", id=16),
TestOption(name="walk upright", id=17),
TestOption(name="cross step", id=18),
TestOption(name="free jump", id=19)
]
class UserInterface:
def __init__(self):
self.test_option_ = None
def convert_to_int(self, input_str):
try:
return int(input_str)
except ValueError:
return None
def terminal_handle(self):
input_str = input("Enter id or name: \n")
if input_str == "list":
self.test_option_.name = None
self.test_option_.id = None
for option in option_list:
print(f"{option.name}, id: {option.id}")
return
for option in option_list:
if input_str == option.name or self.convert_to_int(input_str) == option.id:
self.test_option_.name = option.name
self.test_option_.id = option.id
print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}")
return
print("No matching test option found.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: python3 {sys.argv[0]} networkInterface")
sys.exit(-1)
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
ChannelFactoryInitialize(0, sys.argv[1])
test_option = TestOption(name=None, id=None)
user_interface = UserInterface()
user_interface.test_option_ = test_option
sport_client = SportClient()
sport_client.SetTimeout(10.0)
sport_client.Init()
while True:
user_interface.terminal_handle()
print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}\n")
if test_option.id == 0:
sport_client.Damp()
elif test_option.id == 1:
sport_client.StandUp()
elif test_option.id == 2:
sport_client.StandDown()
elif test_option.id == 3:
sport_client.Move(0.3,0,0)
elif test_option.id == 4:
sport_client.Move(0,0.3,0)
elif test_option.id == 5:
sport_client.Move(0,0,0.5)
elif test_option.id == 6:
sport_client.StopMove()
elif test_option.id == 7:
sport_client.SwitchGait(0)
elif test_option.id == 8:
sport_client.SwitchGait(1)
elif test_option.id == 9:
sport_client.BalanceStand()
elif test_option.id == 10:
sport_client.RecoveryStand()
elif test_option.id == 11:
ret = sport_client.LeftFlip()
print("ret: ",ret)
elif test_option.id == 12:
ret = sport_client.BackFlip()
print("ret: ",ret)
elif test_option.id == 13:
ret = sport_client.FreeWalk(True)
print("ret: ",ret)
elif test_option.id == 14:
ret = sport_client.FreeBound(True)
print("ret: ",ret)
time.sleep(2)
ret = sport_client.FreeBound(False)
print("ret: ",ret)
elif test_option.id == 14:
ret = sport_client.FreeBound(True)
print("ret: ",ret)
time.sleep(2)
ret = sport_client.FreeBound(False)
print("ret: ",ret)
elif test_option.id == 15:
ret = sport_client.FreeAvoid(True)
print("ret: ",ret)
time.sleep(2)
ret = sport_client.FreeAvoid(False)
print("ret: ",ret)
elif test_option.id == 16:
ret = sport_client.WalkStair(True)
print("ret: ",ret)
time.sleep(10)
ret = sport_client.WalkStair(False)
print("ret: ",ret)
elif test_option.id == 17:
ret = sport_client.WalkUpright(True)
print("ret: ",ret)
time.sleep(4)
ret = sport_client.WalkUpright(False)
print("ret: ",ret)
elif test_option.id == 18:
ret = sport_client.CrossStep(True)
print("ret: ",ret)
time.sleep(4)
ret = sport_client.CrossStep(False)
print("ret: ",ret)
elif test_option.id == 19:
ret = sport_client.FreeJump(True)
print("ret: ",ret)
time.sleep(4)
ret = sport_client.FreeJump(False)
print("ret: ",ret)
time.sleep(1)

39
external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_utlidar_switch.py

@ -0,0 +1,39 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.std_msgs.msg.dds_ import String_
from unitree_sdk2py.idl.default import std_msgs_msg_dds__String_
class Custom:
def __init__(self):
# create publisher #
self.publisher = ChannelPublisher("rt/utlidar/switch", String_)
self.publisher.Init()
self.low_cmd = std_msgs_msg_dds__String_()
def go2_utlidar_switch(self,status):
if status == "OFF":
self.low_cmd.data = "OFF"
elif status == "ON":
self.low_cmd.data = "ON"
self.publisher.Write(self.low_cmd)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.go2_utlidar_switch("OFF")

176
external_dependencies/unitree_sdk2_python/example/go2/low_level/go2_stand_example.py

@ -0,0 +1,176 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_
from unitree_sdk2py.utils.crc import CRC
from unitree_sdk2py.utils.thread import RecurrentThread
import unitree_legged_const as go2
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
from unitree_sdk2py.go2.sport.sport_client import SportClient
class Custom:
def __init__(self):
self.Kp = 60.0
self.Kd = 5.0
self.time_consume = 0
self.rate_count = 0
self.sin_count = 0
self.motiontime = 0
self.dt = 0.002 # 0.001~0.01
self.low_cmd = unitree_go_msg_dds__LowCmd_()
self.low_state = None
self._targetPos_1 = [0.0, 1.36, -2.65, 0.0, 1.36, -2.65,
-0.2, 1.36, -2.65, 0.2, 1.36, -2.65]
self._targetPos_2 = [0.0, 0.67, -1.3, 0.0, 0.67, -1.3,
0.0, 0.67, -1.3, 0.0, 0.67, -1.3]
self._targetPos_3 = [-0.35, 1.36, -2.65, 0.35, 1.36, -2.65,
-0.5, 1.36, -2.65, 0.5, 1.36, -2.65]
self.startPos = [0.0] * 12
self.duration_1 = 500
self.duration_2 = 500
self.duration_3 = 1000
self.duration_4 = 900
self.percent_1 = 0
self.percent_2 = 0
self.percent_3 = 0
self.percent_4 = 0
self.firstRun = True
self.done = False
# thread handling
self.lowCmdWriteThreadPtr = None
self.crc = CRC()
# Public methods
def Init(self):
self.InitLowCmd()
# create publisher #
self.lowcmd_publisher = ChannelPublisher("rt/lowcmd", LowCmd_)
self.lowcmd_publisher.Init()
# create subscriber #
self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateMessageHandler, 10)
self.sc = SportClient()
self.sc.SetTimeout(5.0)
self.sc.Init()
self.msc = MotionSwitcherClient()
self.msc.SetTimeout(5.0)
self.msc.Init()
status, result = self.msc.CheckMode()
while result['name']:
self.sc.StandDown()
self.msc.ReleaseMode()
status, result = self.msc.CheckMode()
time.sleep(1)
def Start(self):
self.lowCmdWriteThreadPtr = RecurrentThread(
interval=0.002, target=self.LowCmdWrite, name="writebasiccmd"
)
self.lowCmdWriteThreadPtr.Start()
# Private methods
def InitLowCmd(self):
self.low_cmd.head[0]=0xFE
self.low_cmd.head[1]=0xEF
self.low_cmd.level_flag = 0xFF
self.low_cmd.gpio = 0
for i in range(20):
self.low_cmd.motor_cmd[i].mode = 0x01 # (PMSM) mode
self.low_cmd.motor_cmd[i].q= go2.PosStopF
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = go2.VelStopF
self.low_cmd.motor_cmd[i].kd = 0
self.low_cmd.motor_cmd[i].tau = 0
def LowStateMessageHandler(self, msg: LowState_):
self.low_state = msg
# print("FR_0 motor state: ", msg.motor_state[go2.LegID["FR_0"]])
# print("IMU state: ", msg.imu_state)
# print("Battery state: voltage: ", msg.power_v, "current: ", msg.power_a)
def LowCmdWrite(self):
if self.firstRun:
for i in range(12):
self.startPos[i] = self.low_state.motor_state[i].q
self.firstRun = False
self.percent_1 += 1.0 / self.duration_1
self.percent_1 = min(self.percent_1, 1)
if self.percent_1 < 1:
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_1) * self.startPos[i] + self.percent_1 * self._targetPos_1[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 <= 1):
self.percent_2 += 1.0 / self.duration_2
self.percent_2 = min(self.percent_2, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_2) * self._targetPos_1[i] + self.percent_2 * self._targetPos_2[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 < 1):
self.percent_3 += 1.0 / self.duration_3
self.percent_3 = min(self.percent_3, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = self._targetPos_2[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 == 1) and (self.percent_4 <= 1):
self.percent_4 += 1.0 / self.duration_4
self.percent_4 = min(self.percent_4, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_4) * self._targetPos_2[i] + self.percent_4 * self._targetPos_3[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
self.low_cmd.crc = self.crc.Crc(self.low_cmd)
self.lowcmd_publisher.Write(self.low_cmd)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
custom.Start()
while True:
if custom.percent_4 == 1.0:
time.sleep(1)
print("Done!")
sys.exit(-1)
time.sleep(1)

20
external_dependencies/unitree_sdk2_python/example/go2/low_level/unitree_legged_const.py

@ -0,0 +1,20 @@
LegID = {
"FR_0": 0, # Front right hip
"FR_1": 1, # Front right thigh
"FR_2": 2, # Front right calf
"FL_0": 3,
"FL_1": 4,
"FL_2": 5,
"RR_0": 6,
"RR_1": 7,
"RR_2": 8,
"RL_0": 9,
"RL_1": 10,
"RL_2": 11,
}
HIGHLEVEL = 0xEE
LOWLEVEL = 0xFF
TRIGERLEVEL = 0xF0
PosStopF = 2.146e9
VelStopF = 16000.0

99
external_dependencies/unitree_sdk2_python/example/go2w/high_level/go2w_sport_client.py

@ -0,0 +1,99 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__SportModeState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_
from unitree_sdk2py.go2.sport.sport_client import SportClient
import math
from dataclasses import dataclass
@dataclass
class TestOption:
name: str
id: int
option_list = [
TestOption(name="damp", id=0),
TestOption(name="stand_up", id=1),
TestOption(name="stand_down", id=2),
TestOption(name="move", id=3),
TestOption(name="stop_move", id=4),
TestOption(name="speed_level", id=5),
TestOption(name="switch_gait", id=6),
TestOption(name="get_state", id=7),
TestOption(name="recovery", id=8),
TestOption(name="balance", id=9)
]
class UserInterface:
def __init__(self):
self.test_option_ = None
def convert_to_int(self, input_str):
try:
return int(input_str)
except ValueError:
return None
def terminal_handle(self):
input_str = input("Enter id or name: \n")
if input_str == "list":
self.test_option_.name = None
self.test_option_.id = None
for option in option_list:
print(f"{option.name}, id: {option.id}")
return
for option in option_list:
if input_str == option.name or self.convert_to_int(input_str) == option.id:
self.test_option_.name = option.name
self.test_option_.id = option.id
print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}")
return
print("No matching test option found.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: python3 {sys.argv[0]} networkInterface")
sys.exit(-1)
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
ChannelFactoryInitialize(0, sys.argv[1])
test_option = TestOption(name=None, id=None)
user_interface = UserInterface()
user_interface.test_option_ = test_option
sport_client = SportClient()
sport_client.SetTimeout(10.0)
sport_client.Init()
while True:
user_interface.terminal_handle()
print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}\n")
if test_option.id == 0:
sport_client.Damp()
elif test_option.id == 1:
sport_client.StandUp()
elif test_option.id == 2:
sport_client.StandDown()
elif test_option.id == 3:
sport_client.Move(0.5,0,0)
elif test_option.id == 4:
sport_client.StopMove()
elif test_option.id == 5:
sport_client.SpeedLevel(1)
elif test_option.id == 6:
sport_client.SwitchGait(1)
elif test_option.id == 8:
sport_client.RecoveryStand()
elif test_option.id == 9:
sport_client.BalanceStand()
time.sleep(1)

196
external_dependencies/unitree_sdk2_python/example/go2w/low_level/go2w_stand_example.py

@ -0,0 +1,196 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_
from unitree_sdk2py.utils.crc import CRC
from unitree_sdk2py.utils.thread import RecurrentThread
import unitree_legged_const as go2w
from unitree_sdk2py.go2.robot_state.robot_state_client import RobotStateClient
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
from unitree_sdk2py.go2.sport.sport_client import SportClient
class Custom:
def __init__(self):
self.Kp = 70.0
self.Kd = 5.0
self.time_consume = 0
self.rate_count = 0
self.sin_count = 0
self.motiontime = 0
self.dt = 0.002
self.low_cmd = unitree_go_msg_dds__LowCmd_()
self.low_state = None
self.targetPos_1 = [0.0, 1.36, -2.65, 0.0, 1.36, -2.65,
-0.2, 1.36, -2.65, 0.2, 1.36, -2.65]
self.targetPos_2 = [0.0, 0.67, -1.3, 0.0, 0.67, -1.3,
0.0, 0.67, -1.3, 0.0, 0.67, -1.3]
self.targetPos_3 = [-0.35, 1.36, -2.65, 0.35, 1.36, -2.65,
-0.5, 1.36, -2.65, 0.5, 1.36, -2.65]
self.startPos = [0.0] * 12
self.duration_1 = 500
self.duration_2 = 500
self.duration_3 = 2000
self.duration_4 = 900
self.percent_1 = 0
self.percent_2 = 0
self.percent_3 = 0
self.percent_4 = 0
self.firstRun = True
self.done = False
# thread handling
self.lowCmdWriteThreadPtr = None
self.crc = CRC()
# Public methods
def Init(self):
self.InitLowCmd()
# create publisher #
self.lowcmd_publisher = ChannelPublisher("rt/lowcmd", LowCmd_)
self.lowcmd_publisher.Init()
# create subscriber #
self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateMessageHandler, 10)
self.sc = SportClient()
self.sc.SetTimeout(5.0)
self.sc.Init()
self.msc = MotionSwitcherClient()
self.msc.SetTimeout(5.0)
self.msc.Init()
status, result = self.msc.CheckMode()
while result['name']:
self.sc.StandUp()
self.sc.StandDown()
self.msc.ReleaseMode()
status, result = self.msc.CheckMode()
time.sleep(1)
def Start(self):
self.lowCmdWriteThreadPtr = RecurrentThread(
name="writebasiccmd", interval=0.002, target=self.LowCmdWrite,
)
self.lowCmdWriteThreadPtr.Start()
def InitLowCmd(self):
self.low_cmd.head[0] = 0xFE
self.low_cmd.head[1] = 0xEF
self.low_cmd.level_flag = 0xFF
self.low_cmd.gpio = 0
for i in range(20):
self.low_cmd.motor_cmd[i].mode = 0x01
self.low_cmd.motor_cmd[i].q= go2w.PosStopF
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = go2w.VelStopF
self.low_cmd.motor_cmd[i].kd = 0
self.low_cmd.motor_cmd[i].tau = 0
def LowStateMessageHandler(self, msg: LowState_):
self.low_state = msg
def LowCmdWrite(self):
if self.firstRun:
for i in range(12):
self.startPos[i] = self.low_state.motor_state[i].q
self.firstRun = False
self.percent_1 += 1.0 / self.duration_1
self.percent_1 = min(self.percent_1, 1)
if self.percent_1 < 1:
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_1) * self.startPos[i] + self.percent_1 * self.targetPos_1[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 <= 1):
self.percent_2 += 1.0 / self.duration_2
self.percent_2 = min(self.percent_2, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_2) * self.targetPos_1[i] + self.percent_2 * self.targetPos_2[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 < 1):
self.percent_3 += 1.0 / self.duration_3
self.percent_3 = min(self.percent_3, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = self.targetPos_2[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if self.percent_3 < 0.4:
for i in range(12, 16):
self.low_cmd.motor_cmd[i].q = 0
self.low_cmd.motor_cmd[i].kp = 0.0
self.low_cmd.motor_cmd[i].dq = 3
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if 0.4 <= self.percent_3 < 0.8:
for i in range(12, 16):
self.low_cmd.motor_cmd[i].q = 0
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = -3
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if self.percent_3 >= 0.8:
for i in range(12, 16):
self.low_cmd.motor_cmd[i].q = 0
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 == 1) and (self.percent_4 <= 1):
self.percent_4 += 1.0 / self.duration_4
self.percent_4 = min(self.percent_4, 1)
for i in range(12):
self.low_cmd.motor_cmd[i].q = (1 - self.percent_4) * self.targetPos_2[i] + self.percent_4 * self.targetPos_3[i]
self.low_cmd.motor_cmd[i].dq = 0
self.low_cmd.motor_cmd[i].kp = self.Kp
self.low_cmd.motor_cmd[i].kd = self.Kd
self.low_cmd.motor_cmd[i].tau = 0
self.low_cmd.crc = self.crc.Crc(self.low_cmd)
self.lowcmd_publisher.Write(self.low_cmd)
if __name__ == '__main__':
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
custom.Start()
while True:
if custom.percent_4 == 1.0:
time.sleep(1)
print("Done!")
sys.exit(-1)
time.sleep(1)

24
external_dependencies/unitree_sdk2_python/example/go2w/low_level/unitree_legged_const.py

@ -0,0 +1,24 @@
LegID = {
"FR_0": 0, # Front right hip
"FR_1": 1, # Front right thigh
"FR_2": 2, # Front right calf
"FL_0": 3,
"FL_1": 4,
"FL_2": 5,
"RR_0": 6,
"RR_1": 7,
"RR_2": 8,
"RL_0": 9,
"RL_1": 10,
"RL_2": 11,
"FR_w": 12, # Front right wheel
"FL_w": 13, # Front left wheel
"RR_w": 14, # Rear right wheel
"RL_w": 15, # Rear left wheel
}
HIGHLEVEL = 0xEE
LOWLEVEL = 0xFF
TRIGERLEVEL = 0xF0
PosStopF = 2.146e9
VelStopF = 16000.0

96
external_dependencies/unitree_sdk2_python/example/h1/high_level/h1_loco_client_example.py

@ -0,0 +1,96 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__SportModeState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_
from unitree_sdk2py.h1.loco.h1_loco_client import LocoClient
import math
from dataclasses import dataclass
@dataclass
class TestOption:
name: str
id: int
option_list = [
TestOption(name="damp", id=0),
TestOption(name="stand_up", id=1),
TestOption(name="move forward", id=3),
TestOption(name="move lateral", id=4),
TestOption(name="move rotate", id=5),
TestOption(name="low stand", id=6),
TestOption(name="high stand", id=7),
TestOption(name="zero torque", id=8)
]
class UserInterface:
def __init__(self):
self.test_option_ = None
def convert_to_int(self, input_str):
try:
return int(input_str)
except ValueError:
return None
def terminal_handle(self):
input_str = input("Enter id or name: \n")
if input_str == "list":
self.test_option_.name = None
self.test_option_.id = None
for option in option_list:
print(f"{option.name}, id: {option.id}")
return
for option in option_list:
if input_str == option.name or self.convert_to_int(input_str) == option.id:
self.test_option_.name = option.name
self.test_option_.id = option.id
print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}")
return
print("No matching test option found.")
if __name__ == "__main__":
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
test_option = TestOption(name=None, id=None)
user_interface = UserInterface()
user_interface.test_option_ = test_option
sport_client = LocoClient()
sport_client.SetTimeout(10.0)
sport_client.Init()
while True:
user_interface.terminal_handle()
print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}\n")
if test_option.id == 0:
sport_client.Damp()
elif test_option.id == 1:
sport_client.StandUp()
elif test_option.id == 3:
sport_client.Move(0.3,0,0)
elif test_option.id == 4:
sport_client.Move(0,0.3,0)
elif test_option.id == 5:
sport_client.Move(0,0,0.3)
elif test_option.id == 6:
sport_client.LowStand()
elif test_option.id == 7:
sport_client.HighStand()
elif test_option.id == 8:
sport_client.ZeroTorque()
time.sleep(1)

167
external_dependencies/unitree_sdk2_python/example/h1/low_level/h1_low_level_example.py

@ -0,0 +1,167 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_
from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_
from unitree_sdk2py.utils.crc import CRC
from unitree_sdk2py.utils.thread import RecurrentThread
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
import unitree_legged_const as h1
import numpy as np
H1_NUM_MOTOR = 20
class H1JointIndex:
# Right leg
kRightHipYaw = 8
kRightHipRoll = 0
kRightHipPitch = 1
kRightKnee = 2
kRightAnkle = 11
# Left leg
kLeftHipYaw = 7
kLeftHipRoll = 3
kLeftHipPitch = 4
kLeftKnee = 5
kLeftAnkle = 10
kWaistYaw = 6
kNotUsedJoint = 9
# Right arm
kRightShoulderPitch = 12
kRightShoulderRoll = 13
kRightShoulderYaw = 14
kRightElbow = 15
# Left arm
kLeftShoulderPitch = 16
kLeftShoulderRoll = 17
kLeftShoulderYaw = 18
kLeftElbow = 19
class Custom:
def __init__(self):
self.time_ = 0.0
self.control_dt_ = 0.01
self.duration_ = 10.0
self.counter_ = 0
self.kp_low_ = 60.0
self.kp_high_ = 200.0
self.kd_low_ = 1.5
self.kd_high_ = 5.0
self.low_cmd = unitree_go_msg_dds__LowCmd_()
self.InitLowCmd()
self.low_state = None
self.crc = CRC()
def Init(self):
# # create publisher #
self.lowcmd_publisher_ = ChannelPublisher("rt/lowcmd", LowCmd_)
self.lowcmd_publisher_.Init()
# # create subscriber #
self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateHandler, 10)
self.msc = MotionSwitcherClient()
self.msc.SetTimeout(5.0)
self.msc.Init()
status, result = self.msc.CheckMode()
while result['name']:
self.msc.ReleaseMode()
status, result = self.msc.CheckMode()
time.sleep(1)
self.report_rpy_ptr_ = RecurrentThread(
interval=0.1, target=self.ReportRPY, name="report_rpy"
)
self.report_rpy_ptr_.Start()
def is_weak_motor(self,motor_index):
return motor_index in {
H1JointIndex.kLeftAnkle,
H1JointIndex.kRightAnkle,
H1JointIndex.kRightShoulderPitch,
H1JointIndex.kRightShoulderRoll,
H1JointIndex.kRightShoulderYaw,
H1JointIndex.kRightElbow,
H1JointIndex.kLeftShoulderPitch,
H1JointIndex.kLeftShoulderRoll,
H1JointIndex.kLeftShoulderYaw,
H1JointIndex.kLeftElbow,
}
def InitLowCmd(self):
self.low_cmd.head[0] = 0xFE
self.low_cmd.head[1] = 0xEF
self.low_cmd.level_flag = 0xFF
self.low_cmd.gpio = 0
for i in range(H1_NUM_MOTOR):
if self.is_weak_motor(i):
self.low_cmd.motor_cmd[i].mode = 0x01
else:
self.low_cmd.motor_cmd[i].mode = 0x0A
self.low_cmd.motor_cmd[i].q= h1.PosStopF
self.low_cmd.motor_cmd[i].kp = 0
self.low_cmd.motor_cmd[i].dq = h1.VelStopF
self.low_cmd.motor_cmd[i].kd = 0
self.low_cmd.motor_cmd[i].tau = 0
def Start(self):
self.lowCmdWriteThreadPtr = RecurrentThread(
interval=self.control_dt_, target=self.LowCmdWrite, name="control"
)
self.lowCmdWriteThreadPtr.Start()
def LowStateHandler(self, msg: LowState_):
self.low_state = msg
def ReportRPY(self):
print("rpy: [",self.low_state.imu_state.rpy[0],", "
,self.low_state.imu_state.rpy[1],", "
,self.low_state.imu_state.rpy[2],"]"
)
def LowCmdWrite(self):
self.time_ += self.control_dt_
self.time_ = np.clip(self.time_ , 0.0, self.duration_)
# set robot to zero posture
for i in range(H1_NUM_MOTOR):
ratio = self.time_ / self.duration_
self.low_cmd.motor_cmd[i].tau = 0.
self.low_cmd.motor_cmd[i].q = (1.0 - ratio) * self.low_state.motor_state[i].q
self.low_cmd.motor_cmd[i].dq = 0.
self.low_cmd.motor_cmd[i].kp = self.kp_low_ if self.is_weak_motor(i) else self.kp_high_
self.low_cmd.motor_cmd[i].kd = self.kd_low_ if self.is_weak_motor(i) else self.kd_high_
self.low_cmd.crc = self.crc.Crc(self.low_cmd)
self.lowcmd_publisher_.Write(self.low_cmd)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
custom.Start()
while True:
if custom.time_ == custom.duration_:
time.sleep(1)
print("Done!")
sys.exit(-1)
time.sleep(1)

5
external_dependencies/unitree_sdk2_python/example/h1/low_level/unitree_legged_const.py

@ -0,0 +1,5 @@
HIGHLEVEL = 0xEE
LOWLEVEL = 0xFF
TRIGERLEVEL = 0xF0
PosStopF = 2.146e9
VelStopF = 16000.0

201
external_dependencies/unitree_sdk2_python/example/h1_2/low_level/h1_2_low_level_example.py

@ -0,0 +1,201 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_
from unitree_sdk2py.utils.crc import CRC
from unitree_sdk2py.utils.thread import RecurrentThread
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
import numpy as np
H1_2_NUM_MOTOR = 27
class H1_2_JointIndex:
# legs
LeftHipYaw = 0
LeftHipPitch = 1
LeftHipRoll = 2
LeftKnee = 3
LeftAnklePitch = 4
LeftAnkleB = 4
LeftAnkleRoll = 5
LeftAnkleA = 5
RightHipYaw = 6
RightHipPitch = 7
RightHipRoll = 8
RightKnee = 9
RightAnklePitch = 10
RightAnkleB = 10
RightAnkleRoll = 11
RightAnkleA = 11
# torso
WaistYaw = 12
# arms
LeftShoulderPitch = 13
LeftShoulderRoll = 14
LeftShoulderYaw = 15
LeftElbow = 16
LeftWristRoll = 17
LeftWristPitch = 18
LeftWristYaw = 19
RightShoulderPitch = 20
RightShoulderRoll = 21
RightShoulderYaw = 22
RightElbow = 23
RightWristRoll = 24
RightWristPitch = 25
RightWristYaw = 26
class Mode:
PR = 0 # Series Control for Pitch/Roll Joints
AB = 1 # Parallel Control for A/B Joints
class Custom:
def __init__(self):
self.time_ = 0.0
self.control_dt_ = 0.002 # [2ms]
self.duration_ = 3.0 # [3 s]
self.counter_ = 0
self.mode_pr_ = Mode.PR
self.mode_machine_ = 0
self.low_cmd = unitree_hg_msg_dds__LowCmd_()
self.low_state = None
self.update_mode_machine_ = False
self.crc = CRC()
def Init(self):
self.msc = MotionSwitcherClient()
self.msc.SetTimeout(5.0)
self.msc.Init()
status, result = self.msc.CheckMode()
while result['name']:
self.msc.ReleaseMode()
status, result = self.msc.CheckMode()
time.sleep(1)
# create publisher #
self.lowcmd_publisher_ = ChannelPublisher("rt/lowcmd", LowCmd_)
self.lowcmd_publisher_.Init()
# create subscriber #
self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateHandler, 10)
def Start(self):
self.lowCmdWriteThreadPtr = RecurrentThread(
interval=self.control_dt_, target=self.LowCmdWrite, name="control"
)
while self.update_mode_machine_ == False:
time.sleep(1)
if self.update_mode_machine_ == True:
self.lowCmdWriteThreadPtr.Start()
def LowStateHandler(self, msg: LowState_):
self.low_state = msg
if self.update_mode_machine_ == False:
self.mode_machine_ = self.low_state.mode_machine
self.update_mode_machine_ = True
self.counter_ +=1
if (self.counter_ % 500 == 0) :
self.counter_ = 0
print(self.low_state.imu_state.rpy)
def LowCmdWrite(self):
self.time_ += self.control_dt_
self.low_cmd.mode_pr = Mode.PR
self.low_cmd.mode_machine = self.mode_machine_
for i in range(H1_2_NUM_MOTOR):
ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0)
self.low_cmd.motor_cmd[i].mode = 1 # 1:Enable, 0:Disable
self.low_cmd.motor_cmd[i].tau = 0.0
self.low_cmd.motor_cmd[i].q = 0.0
self.low_cmd.motor_cmd[i].dq = 0.0
self.low_cmd.motor_cmd[i].kp = 100.0 if i < 13 else 50.0
self.low_cmd.motor_cmd[i].kd = 1.0
if self.time_ < self.duration_ :
# [Stage 1]: set robot to zero posture
for i in range(H1_2_NUM_MOTOR):
ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0)
self.low_cmd.mode_pr = Mode.PR
self.low_cmd.mode_machine = self.mode_machine_
self.low_cmd.motor_cmd[i].mode = 1 # 1:Enable, 0:Disable
self.low_cmd.motor_cmd[i].tau = 0.
self.low_cmd.motor_cmd[i].q = (1.0 - ratio) * self.low_state.motor_state[i].q
self.low_cmd.motor_cmd[i].dq = 0.
self.low_cmd.motor_cmd[i].kp = 100.0 if i < 13 else 50.0
self.low_cmd.motor_cmd[i].kd = 1.0
else :
# [Stage 2]: swing ankle using PR mode
max_P = 0.25
max_R = 0.25
t = self.time_ - self.duration_
L_P_des = max_P * np.cos(2.0 * np.pi * t)
L_R_des = max_R * np.sin(2.0 * np.pi * t)
R_P_des = max_P * np.cos(2.0 * np.pi * t)
R_R_des = -max_R * np.sin(2.0 * np.pi * t)
Kp_Pitch = 80
Kd_Pitch = 1
Kp_Roll = 80
Kd_Roll = 1
self.low_cmd.mode_pr = Mode.PR
self.low_cmd.mode_machine = self.mode_machine_
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnklePitch].q = L_P_des
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnklePitch].dq = 0
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnklePitch].kp = Kp_Pitch
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnklePitch].kd = Kd_Pitch
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnkleRoll].q = L_R_des
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnkleRoll].dq = 0
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnkleRoll].kp = Kp_Roll
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnkleRoll].kd = Kd_Roll
self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnklePitch].q = R_P_des
self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnklePitch].dq = 0
self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnklePitch].kp = Kp_Pitch
self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnklePitch].kd = Kd_Pitch
self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnkleRoll].q = R_R_des
self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnkleRoll].dq = 0
self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnkleRoll].kp = Kp_Roll
self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnkleRoll].kd = Kd_Roll
max_wrist_roll_angle = 0.5; # [rad]
WristRoll_des = max_wrist_roll_angle * np.sin(2.0 * np.pi * t)
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftWristRoll].q = WristRoll_des
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftWristRoll].dq = 0
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftWristRoll].kp = 50
self.low_cmd.motor_cmd[H1_2_JointIndex.LeftWristRoll].kd = 1
self.low_cmd.motor_cmd[H1_2_JointIndex.RightWristRoll].q = WristRoll_des
self.low_cmd.motor_cmd[H1_2_JointIndex.RightWristRoll].dq = 0
self.low_cmd.motor_cmd[H1_2_JointIndex.RightWristRoll].kp = 50
self.low_cmd.motor_cmd[H1_2_JointIndex.RightWristRoll].kd = 1
self.low_cmd.crc = self.crc.Crc(self.low_cmd)
self.lowcmd_publisher_.Write(self.low_cmd)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
custom.Start()
while True:
time.sleep(1)

28
external_dependencies/unitree_sdk2_python/example/helloworld/publisher.py

@ -0,0 +1,28 @@
import time
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from user_data import *
if __name__ == "__main__":
ChannelFactoryInitialize()
# Create a publisher to publish the data defined in UserData class
pub = ChannelPublisher("topic", UserData)
pub.Init()
for i in range(30):
# Create a Userdata message
msg = UserData(" ", 0)
msg.string_data = "Hello world"
msg.float_data = time.time()
# Publish message
if pub.Write(msg, 0.5):
print("Publish success. msg:", msg)
else:
print("Waitting for subscriber.")
time.sleep(1)
pub.Close()

20
external_dependencies/unitree_sdk2_python/example/helloworld/subscriber.py

@ -0,0 +1,20 @@
import time
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from user_data import *
if __name__ == "__main__":
ChannelFactoryInitialize()
# Create a subscriber to subscribe the data defined in UserData class
sub = ChannelSubscriber("topic", UserData)
sub.Init()
while True:
msg = sub.Read()
if msg is not None:
print("Subscribe success. msg:", msg)
else:
print("No data subscribed.")
break
sub.Close()

9
external_dependencies/unitree_sdk2_python/example/helloworld/user_data.py

@ -0,0 +1,9 @@
from dataclasses import dataclass
from cyclonedds.idl import IdlStruct
# This class defines user data consisting of a float data and a string data
@dataclass
class UserData(IdlStruct, typename="UserData"):
string_data: str
float_data: float

36
external_dependencies/unitree_sdk2_python/example/motionSwitcher/motion_switcher_example.py

@ -0,0 +1,36 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient
class Custom:
def __init__(self):
self.msc = MotionSwitcherClient()
self.msc.SetTimeout(5.0)
self.msc.Init()
def selectMode(self,name):
ret = self.msc.SelectMode(name)
return ret
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
selectMode = "ai"
# selectMode = "normal"
# selectMode = "advanced"
# selectMode = "ai-w" # for wheeled robot
ret = custom.selectMode(selectMode)
print("ret: ",ret)

35
external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py

@ -0,0 +1,35 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.go2.obstacles_avoid.obstacles_avoid_client import ObstaclesAvoidClient
if __name__ == "__main__":
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
try:
client = ObstaclesAvoidClient()
client.SetTimeout(3.0)
client.Init()
while not client.SwitchGet()[1]:
client.SwitchSet(True)
time.sleep(0.1)
print("obstacles avoid switch on")
client.UseRemoteCommandFromApi(True)
time.sleep(0.5)
client.Move(0.5, 0.0, 0.0)
time.sleep(1.0) # move 1s
client.Move(0.0, 0.0, 0.0)
client.UseRemoteCommandFromApi(False)
except KeyboardInterrupt:
client.Move(0.0, 0.0, 0.0)
client.UseRemoteCommandFromApi(False)
print("exit!!")

94
external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_switch.py

@ -0,0 +1,94 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.go2.obstacles_avoid.obstacles_avoid_client import ObstaclesAvoidClient
if __name__ == "__main__":
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
client = ObstaclesAvoidClient()
client.SetTimeout(3.0)
client.Init()
while True:
print("##################GetServerApiVersion###################")
code, serverAPiVersion = client.GetServerApiVersion()
if code != 0:
print("get server api error. code:", code)
else:
print("get server api version:", serverAPiVersion)
if serverAPiVersion != client.GetApiVersion():
print("api version not equal.")
time.sleep(3)
print("##################SwitchGet###################")
code, enable = client.SwitchGet()
if code != 0:
print("switch get error. code:", code)
else:
print("switch get success. enable:", enable)
time.sleep(3)
print("##################SwitchSet (on)###################")
code = client.SwitchSet(True)
if code != 0:
print("switch set error. code:", code)
else:
print("switch set success.")
time.sleep(3)
print("##################SwitchGet###################")
code, enable1 = client.SwitchGet()
if code != 0:
print("switch get error. code:", code)
else:
print("switch get success. enable:", enable1)
time.sleep(3)
print("##################SwitchSet (off)###################")
code = client.SwitchSet(False)
if code != 0:
print("switch set error. code:", code)
else:
print("switch set success.")
time.sleep(3)
print("##################SwitchGet###################")
code, enable1 = client.SwitchGet()
if code != 0:
print("switch get error. code:", code)
else:
print("switch get success. enable:", enable1)
time.sleep(3)
print("##################SwitchSet (enable)###################")
code = client.SwitchSet(enable)
if code != 0:
print("switch set error. code:", code)
else:
print("switch set success. enable:", enable)
time.sleep(3)
print("##################SwitchGet###################")
code, enable = client.SwitchGet()
if code != 0:
print("switch get error. code:", code)
else:
print("switch get success. enable:", enable)
time.sleep(3)

77
external_dependencies/unitree_sdk2_python/example/vui_client/vui_client_example.py

@ -0,0 +1,77 @@
import time
import sys
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.go2.vui.vui_client import VuiClient
if __name__ == "__main__":
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
client = VuiClient()
client.SetTimeout(3.0)
client.Init()
for i in range(1, 11):
print("#################GetBrightness####################")
code, level = client.GetBrightness()
if code != 0:
print("get brightness error. code:", code)
else:
print("get brightness success. level:", level)
time.sleep(1)
print("#################SetBrightness####################")
code = client.SetBrightness(i)
if code != 0:
print("set brightness error. code:", code)
else:
print("set brightness success. level:", i)
time.sleep(1)
print("#################SetBrightness 0####################")
code = client.SetBrightness(0)
if code != 0:
print("set brightness error. code:", code)
else:
print("set brightness 0 success.")
for i in range(1, 11):
print("#################GetVolume####################")
code, level = client.GetVolume()
if code != 0:
print("get volume error. code:", code)
else:
print("get volume success. level:", level)
time.sleep(1)
print("#################SetVolume####################")
code = client.SetVolume(i)
if code != 0:
print("set volume error. code:", code)
else:
print("set volume success. level:", i)
time.sleep(1)
print("#################SetVolume 0####################")
code = client.SetVolume(0)
if code != 0:
print("set volume error. code:", code)
else:
print("set volume 0 success.")

131
external_dependencies/unitree_sdk2_python/example/wireless_controller/wireless_controller.py

@ -0,0 +1,131 @@
import time
import sys
import struct
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
# Uncomment the following two lines when using Go2、Go2-W、B2、B2-W、H1 robot
# from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_
# from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_
# Uncomment the following two lines when using G1、H1-2 robot
from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_
class unitreeRemoteController:
def __init__(self):
# key
self.Lx = 0
self.Rx = 0
self.Ry = 0
self.Ly = 0
# button
self.L1 = 0
self.L2 = 0
self.R1 = 0
self.R2 = 0
self.A = 0
self.B = 0
self.X = 0
self.Y = 0
self.Up = 0
self.Down = 0
self.Left = 0
self.Right = 0
self.Select = 0
self.F1 = 0
self.F3 = 0
self.Start = 0
def parse_botton(self,data1,data2):
self.R1 = (data1 >> 0) & 1
self.L1 = (data1 >> 1) & 1
self.Start = (data1 >> 2) & 1
self.Select = (data1 >> 3) & 1
self.R2 = (data1 >> 4) & 1
self.L2 = (data1 >> 5) & 1
self.F1 = (data1 >> 6) & 1
self.F3 = (data1 >> 7) & 1
self.A = (data2 >> 0) & 1
self.B = (data2 >> 1) & 1
self.X = (data2 >> 2) & 1
self.Y = (data2 >> 3) & 1
self.Up = (data2 >> 4) & 1
self.Right = (data2 >> 5) & 1
self.Down = (data2 >> 6) & 1
self.Left = (data2 >> 7) & 1
def parse_key(self,data):
lx_offset = 4
self.Lx = struct.unpack('<f', data[lx_offset:lx_offset + 4])[0]
rx_offset = 8
self.Rx = struct.unpack('<f', data[rx_offset:rx_offset + 4])[0]
ry_offset = 12
self.Ry = struct.unpack('<f', data[ry_offset:ry_offset + 4])[0]
L2_offset = 16
L2 = struct.unpack('<f', data[L2_offset:L2_offset + 4])[0] # Placeholder,unused
ly_offset = 20
self.Ly = struct.unpack('<f', data[ly_offset:ly_offset + 4])[0]
def parse(self,remoteData):
self.parse_key(remoteData)
self.parse_botton(remoteData[2],remoteData[3])
print("debug unitreeRemoteController: ")
print("Lx:", self.Lx)
print("Rx:", self.Rx)
print("Ry:", self.Ry)
print("Ly:", self.Ly)
print("L1:", self.L1)
print("L2:", self.L2)
print("R1:", self.R1)
print("R2:", self.R2)
print("A:", self.A)
print("B:", self.B)
print("X:", self.X)
print("Y:", self.Y)
print("Up:", self.Up)
print("Down:", self.Down)
print("Left:", self.Left)
print("Right:", self.Right)
print("Select:", self.Select)
print("F1:", self.F1)
print("F3:", self.F3)
print("Start:", self.Start)
print("\n")
class Custom:
def __init__(self):
self.low_state = None
self.remoteController = unitreeRemoteController()
def Init(self):
self.lowstate_subscriber = ChannelSubscriber("rt/lf/lowstate", LowState_)
self.lowstate_subscriber.Init(self.LowStateMessageHandler, 10)
def LowStateMessageHandler(self, msg: LowState_):
self.low_state = msg
wireless_remote_data = self.low_state.wireless_remote
self.remoteController.parse(wireless_remote_data)
if __name__ == '__main__':
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
if len(sys.argv)>1:
ChannelFactoryInitialize(0, sys.argv[1])
else:
ChannelFactoryInitialize(0)
custom = Custom()
custom.Init()
while True:
time.sleep(1)

3
external_dependencies/unitree_sdk2_python/pyproject.toml

@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

21
external_dependencies/unitree_sdk2_python/setup.py

@ -0,0 +1,21 @@
from setuptools import setup, find_packages
setup(name='unitree_sdk2py',
version='1.0.1',
author='UnitreeRobotics',
author_email='unitree@unitree.com',
long_description=open('README.md').read(),
long_description_content_type="text/markdown",
license="BSD-3-Clause",
packages=find_packages(include=['unitree_sdk2py','unitree_sdk2py.*']),
description='Unitree robot sdk version 2 for python',
project_urls={
"Source Code": "https://github.com/unitreerobotics/unitree_sdk2_python",
},
python_requires='>=3.8',
install_requires=[
"cyclonedds==0.10.2",
"numpy",
"opencv-python",
],
)

10
external_dependencies/unitree_sdk2_python/unitree_sdk2py/__init__.py

@ -0,0 +1,10 @@
from . import idl, utils, core, rpc, go2, b2
__all__ = [
"idl"
"utils"
"core",
"rpc",
"go2",
"b2",
]

16
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_api.py

@ -0,0 +1,16 @@
"""
" service name
"""
ROBOT_BACK_VIDEO_SERVICE_NAME = "back_videohub"
"""
" service api version
"""
ROBOT_BACK_VIDEO_API_VERSION = "1.0.0.0"
"""
" api id
"""
ROBOT_BACK_VIDEO_API_ID_GETIMAGESAMPLE = 1001

23
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_client.py

@ -0,0 +1,23 @@
import json
from ...rpc.client import Client
from .back_video_api import *
"""
" class FrontVideoClient
"""
class BackVideoClient(Client):
def __init__(self):
super().__init__(ROBOT_BACK_VIDEO_SERVICE_NAME, False)
def Init(self):
# set api version
self._SetApiVerson(ROBOT_BACK_VIDEO_API_VERSION)
# regist api
self._RegistApi(ROBOT_BACK_VIDEO_API_ID_GETIMAGESAMPLE, 0)
# 1001
def GetImageSample(self):
return self._CallBinary(ROBOT_BACK_VIDEO_API_ID_GETIMAGESAMPLE, [])

16
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_api.py

@ -0,0 +1,16 @@
"""
" service name
"""
ROBOT_FRONT_VIDEO_SERVICE_NAME = "front_videohub"
"""
" service api version
"""
ROBOT_FRONT_VIDEO_API_VERSION = "1.0.0.0"
"""
" api id
"""
ROBOT_FRONT_VIDEO_API_ID_GETIMAGESAMPLE = 1001

23
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_client.py

@ -0,0 +1,23 @@
import json
from ...rpc.client import Client
from .front_video_api import *
"""
" class FrontVideoClient
"""
class FrontVideoClient(Client):
def __init__(self):
super().__init__(ROBOT_FRONT_VIDEO_SERVICE_NAME, False)
def Init(self):
# set api version
self._SetApiVerson(ROBOT_FRONT_VIDEO_API_VERSION)
# regist api
self._RegistApi(ROBOT_FRONT_VIDEO_API_ID_GETIMAGESAMPLE, 0)
# 1001
def GetImageSample(self):
return self._CallBinary(ROBOT_FRONT_VIDEO_API_ID_GETIMAGESAMPLE, [])

25
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py

@ -0,0 +1,25 @@
"""
" service name
"""
ROBOT_STATE_SERVICE_NAME = "robot_state"
"""
" service api version
"""
ROBOT_STATE_API_VERSION = "1.0.0.1"
"""
" api id
"""
ROBOT_STATE_API_ID_SERVICE_SWITCH = 1001
ROBOT_STATE_API_ID_REPORT_FREQ = 1002
ROBOT_STATE_API_ID_SERVICE_LIST = 1003
"""
" error code
"""
ROBOT_STATE_ERR_SERVICE_SWITCH = 5201
ROBOT_STATE_ERR_SERVICE_PROTECTED = 5202

84
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py

@ -0,0 +1,84 @@
import json
from ...rpc.client import Client
from ...rpc.client_internal import *
from .robot_state_api import *
"""
" class ServiceState
"""
class ServiceState:
def __init__(self, name: str = None, status: int = None, protect: bool = None):
self.name = name
self.status = status
self.protect = protect
"""
" class RobotStateClient
"""
class RobotStateClient(Client):
def __init__(self):
super().__init__(ROBOT_STATE_SERVICE_NAME, False)
def Init(self):
# set api version
self._SetApiVerson(ROBOT_STATE_API_VERSION)
# regist api
self._RegistApi(ROBOT_STATE_API_ID_SERVICE_SWITCH, 0)
self._RegistApi(ROBOT_STATE_API_ID_REPORT_FREQ, 0)
self._RegistApi(ROBOT_STATE_API_ID_SERVICE_LIST, 0)
def ServiceList(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(ROBOT_STATE_API_ID_SERVICE_LIST, parameter)
if code != 0:
return code, None
lst = []
d = json.loads(data)
for t in d:
s = ServiceState()
s.name = t["name"]
s.status = t["status"]
s.protect = t["protect"]
lst.append(s)
return code, lst
def ServiceSwitch(self, name: str, switch: bool):
p = {}
p["name"] = name
p["switch"] = int(switch)
parameter = json.dumps(p)
code, data = self._Call(ROBOT_STATE_API_ID_SERVICE_SWITCH, parameter)
if code != 0:
return code
d = json.loads(data)
status = d["status"]
if status == 5:
return ROBOT_STATE_ERR_SERVICE_PROTECTED
if status != 0 and status != 1:
return ROBOT_STATE_ERR_SERVICE_SWITCH
return code
def SetReportFreq(self, interval: int, duration: int):
p = {}
p["interval"] = interval
p["duration"] = duration
parameter = json.dumps(p)
code, data = self._Call(ROBOT_STATE_API_ID_REPORT_FREQ, p)
return code

58
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py

@ -0,0 +1,58 @@
"""
" service name
"""
SPORT_SERVICE_NAME = "sport"
"""
" service api version
"""
SPORT_API_VERSION = "1.0.0.1"
"""
" api id
"""
SPORT_API_ID_DAMP = 1001;
SPORT_API_ID_BALANCESTAND = 1002;
SPORT_API_ID_STOPMOVE = 1003;
SPORT_API_ID_STANDUP = 1004;
SPORT_API_ID_STANDDOWN = 1005;
SPORT_API_ID_RECOVERYSTAND = 1006;
SPORT_API_ID_EULER = 1007;
SPORT_API_ID_MOVE = 1008;
SPORT_API_ID_SIT = 1009;
SPORT_API_ID_RISESIT = 1010;
SPORT_API_ID_SWITCHGAIT = 1011;
SPORT_API_ID_TRIGGER = 1012;
SPORT_API_ID_BODYHEIGHT = 1013;
SPORT_API_ID_FOOTRAISEHEIGHT = 1014;
SPORT_API_ID_SPEEDLEVEL = 1015;
SPORT_API_ID_HELLO = 1016;
SPORT_API_ID_STRETCH = 1017;
SPORT_API_ID_TRAJECTORYFOLLOW = 1018;
SPORT_API_ID_CONTINUOUSGAIT = 1019;
SPORT_API_ID_CONTENT = 1020;
SPORT_API_ID_WALLOW = 1021;
SPORT_API_ID_DANCE1 = 1022;
SPORT_API_ID_DANCE2 = 1023;
SPORT_API_ID_GETBODYHEIGHT = 1024;
SPORT_API_ID_GETFOOTRAISEHEIGHT = 1025;
SPORT_API_ID_GETSPEEDLEVEL = 1026;
SPORT_API_ID_SWITCHJOYSTICK = 1027;
SPORT_API_ID_POSE = 1028;
SPORT_API_ID_FRONTJUMP = 1031;
SPORT_API_ID_ECONOMICGAIT = 1035;
SPORT_API_ID_MOVETOPOS = 1036;
SPORT_API_ID_SWITCHEULERMODE = 1037;
SPORT_API_ID_SWITCHMOVEMODE = 1038;
"""
" error code
"""
# client side
SPORT_ERR_CLIENT_POINT_PATH = 4101
# server side
SPORT_ERR_SERVER_OVERTIME = 4201
SPORT_ERR_SERVER_NOT_INIT = 4202

241
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py

@ -0,0 +1,241 @@
import json
from ...rpc.client import Client
from .sport_api import *
"""
" SPORT_PATH_POINT_SIZE
"""
SPORT_PATH_POINT_SIZE = 30
"""
" class PathPoint
"""
class PathPoint:
def __init__(self, timeFromStart: float, x: float, y: float, yaw: float, vx: float, vy: float, vyaw: float):
self.timeFromStart = timeFromStart
self.x = x
self.y = y
self.yaw = yaw
self.vx = vx
self.vy = vy
self.vyaw = vyaw
"""
" class SportClient
"""
class SportClient(Client):
def __init__(self, enableLease: bool = False):
super().__init__(SPORT_SERVICE_NAME, enableLease)
def Init(self):
# set api version
self._SetApiVerson(SPORT_API_VERSION)
# regist api
self._RegistApi(SPORT_API_ID_DAMP, 0)
self._RegistApi(SPORT_API_ID_BALANCESTAND, 0)
self._RegistApi(SPORT_API_ID_STOPMOVE, 0)
self._RegistApi(SPORT_API_ID_STANDUP, 0)
self._RegistApi(SPORT_API_ID_STANDDOWN, 0)
self._RegistApi(SPORT_API_ID_RECOVERYSTAND, 0)
self._RegistApi(SPORT_API_ID_EULER, 0)
self._RegistApi(SPORT_API_ID_MOVE, 0)
self._RegistApi(SPORT_API_ID_SIT, 0)
self._RegistApi(SPORT_API_ID_SWITCHGAIT, 0)
self._RegistApi(SPORT_API_ID_BODYHEIGHT, 0)
self._RegistApi(SPORT_API_ID_FOOTRAISEHEIGHT, 0)
self._RegistApi(SPORT_API_ID_SPEEDLEVEL, 0)
self._RegistApi(SPORT_API_ID_TRAJECTORYFOLLOW, 0)
self._RegistApi(SPORT_API_ID_CONTINUOUSGAIT, 0)
self._RegistApi(SPORT_API_ID_MOVETOPOS, 0)
self._RegistApi(SPORT_API_ID_FRONTJUMP, 0)
self._RegistApi(SPORT_API_ID_ECONOMICGAIT, 0)
self._RegistApi(SPORT_API_ID_POSE, 0)
self._RegistApi(SPORT_API_ID_SWITCHEULERMODE, 0)
self._RegistApi(SPORT_API_ID_SWITCHMOVEMODE, 0)
# 1001
def Damp(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_DAMP, parameter)
return code
# 1002
def BalanceStand(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_BALANCESTAND, parameter)
return code
# 1003
def StopMove(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_STOPMOVE, parameter)
return code
# 1004
def StandUp(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_STANDUP, parameter)
return code
# 1005
def StandDown(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_STANDDOWN, parameter)
return code
# 1006
def RecoveryStand(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_RECOVERYSTAND, parameter)
return code
# 1007
def Euler(self, roll: float, pitch: float, yaw: float):
p = {}
p["x"] = roll
p["y"] = pitch
p["z"] = yaw
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_EULER, parameter)
return code
# 1008
def Move(self, vx: float, vy: float, vyaw: float):
p = {}
p["x"] = vx
p["y"] = vy
p["z"] = vyaw
parameter = json.dumps(p)
code = self._CallNoReply(SPORT_API_ID_MOVE, parameter)
return code
# 1009
def Sit(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SIT, parameter)
return code
# 1011
def SwitchGait(self, t: int):
p = {}
p["data"] = t
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SWITCHGAIT, parameter)
return code
# 1013
def BodyHeight(self, height: float):
p = {}
p["data"] = height
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_BODYHEIGHT, parameter)
return code
# 1014
def FootRaiseHeight(self, height: float):
p = {}
p["data"] = height
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_FOOTRAISEHEIGHT, parameter)
return code
# 1015
def SpeedLevel(self, level: int):
p = {}
p["data"] = level
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SPEEDLEVEL, parameter)
return code
# 1018
def TrajectoryFollow(self, path: list):
l = len(path)
if l != SPORT_PATH_POINT_SIZE:
return SPORT_ERR_CLIENT_POINT_PATH
path_p = []
for i in range(l):
point = path[i]
p = {}
p["t_from_start"] = point.timeFromStart
p["x"] = point.x
p["y"] = point.y
p["yaw"] = point.yaw
p["vx"] = point.vx
p["vy"] = point.vy
p["vyaw"] = point.vyaw
path_p.append(p)
parameter = json.dumps(path_p)
code = self._CallNoReply(SPORT_API_ID_TRAJECTORYFOLLOW, parameter)
return code
# 1019
def ContinuousGait(self, flag: int):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_CONTINUOUSGAIT, parameter)
return code
# 1036
def MoveToPos(self, x: float, y: float, yaw: float):
p = {}
p["x"] = x
p["y"] = y
p["yaw"] = yaw
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_MOVETOPOS, parameter)
return code
# 1031
def FrontJump(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_FRONTJUMP, parameter)
return code
# 1035
def EconomicGait(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_ECONOMICGAIT, parameter)
return code
# 1028
def Pose(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_POSE, parameter)
return code
# 1037
def SwitchEulerMode(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SWITCHEULERMODE, parameter)
return code
# 1038
def SwitchMoveMode(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SWITCHMOVEMODE, parameter)
return code

21
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_api.py

@ -0,0 +1,21 @@
"""
" service name
"""
VUI_SERVICE_NAME = "vui"
"""
" service api version
"""
VUI_API_VERSION = "1.0.0.1"
"""
" api id
"""
VUI_API_ID_SETSWITCH = 1001
VUI_API_ID_GETSWITCH = 1002
VUI_API_ID_SETVOLUME = 1003
VUI_API_ID_GETVOLUME = 1004
VUI_API_ID_SETBRIGHTNESS = 1005
VUI_API_ID_GETBRIGHTNESS = 1006

86
external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_client.py

@ -0,0 +1,86 @@
import json
from ...rpc.client import Client
from .vui_api import *
"""
" class VideoClient
"""
class VuiClient(Client):
def __init__(self):
super().__init__(VUI_SERVICE_NAME, False)
def Init(self):
# set api version
self._SetApiVerson(VUI_API_VERSION)
# regist api
self._RegistApi(VUI_API_ID_SETSWITCH, 0)
self._RegistApi(VUI_API_ID_GETSWITCH, 0)
self._RegistApi(VUI_API_ID_SETVOLUME, 0)
self._RegistApi(VUI_API_ID_GETVOLUME, 0)
self._RegistApi(VUI_API_ID_SETBRIGHTNESS, 0)
self._RegistApi(VUI_API_ID_GETBRIGHTNESS, 0)
# 1001
def SetSwitch(self, enable: int):
p = {}
p["enable"] = enable
parameter = json.dumps(p)
code, data = self._Call(VUI_API_ID_SETSWITCH, parameter)
return code
# 1002
def GetSwitch(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(VUI_API_ID_GETSWITCH, parameter)
if code == 0:
d = json.loads(data)
return code, d["enable"]
else:
return code, None
# 1003
def SetVolume(self, level: int):
p = {}
p["volume"] = level
parameter = json.dumps(p)
code, data = self._Call(VUI_API_ID_SETVOLUME, parameter)
return code
# 1006
def GetVolume(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(VUI_API_ID_GETVOLUME, parameter)
if code == 0:
d = json.loads(data)
return code, d["volume"]
else:
return code, None
# 1005
def SetBrightness(self, level: int):
p = {}
p["brightness"] = level
parameter = json.dumps(p)
code, data = self._Call(VUI_API_ID_SETBRIGHTNESS, parameter)
return code
# 1006
def GetBrightness(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(VUI_API_ID_GETBRIGHTNESS, parameter)
if code == 0:
d = json.loads(data)
return code, d["brightness"]
else:
return code, None

0
external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/__init__.py

29
external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_api.py

@ -0,0 +1,29 @@
"""
" service name
"""
MOTION_SWITCHER_SERVICE_NAME = "motion_switcher"
"""
" service api version
"""
MOTION_SWITCHER_API_VERSION = "1.0.0.1"
"""
" api id
"""
MOTION_SWITCHER_API_ID_CHECK_MODE = 1001
MOTION_SWITCHER_API_ID_SELECT_MODE = 1002
MOTION_SWITCHER_API_ID_RELEASE_MODE = 1003
MOTION_SWITCHER_API_ID_SET_SILENT = 1004
MOTION_SWITCHER_API_ID_GET_SILENT = 1005
# """
# " error code
# """
# # client side
# SPORT_ERR_CLIENT_POINT_PATH = 4101
# # server side
# SPORT_ERR_SERVER_OVERTIME = 4201
# SPORT_ERR_SERVER_NOT_INIT = 4202

51
external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_client.py

@ -0,0 +1,51 @@
import json
from ...rpc.client import Client
from .motion_switcher_api import *
"""
" class MotionSwitcherClient
"""
class MotionSwitcherClient(Client):
def __init__(self):
super().__init__(MOTION_SWITCHER_SERVICE_NAME, False)
def Init(self):
# set api version
self._SetApiVerson(MOTION_SWITCHER_API_VERSION)
# regist api
self._RegistApi(MOTION_SWITCHER_API_ID_CHECK_MODE, 0)
self._RegistApi(MOTION_SWITCHER_API_ID_SELECT_MODE, 0)
self._RegistApi(MOTION_SWITCHER_API_ID_RELEASE_MODE, 0)
self._RegistApi(MOTION_SWITCHER_API_ID_SET_SILENT, 0)
self._RegistApi(MOTION_SWITCHER_API_ID_GET_SILENT, 0)
# 1001
def CheckMode(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(MOTION_SWITCHER_API_ID_CHECK_MODE, parameter)
if code == 0:
return code, json.loads(data)
else:
return code, None
# 1002
def SelectMode(self, nameOrAlias):
p = {}
p["name"] = nameOrAlias
parameter = json.dumps(p)
code, data = self._Call(MOTION_SWITCHER_API_ID_SELECT_MODE, parameter)
return code, None
# 1003
def ReleaseMode(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(MOTION_SWITCHER_API_ID_RELEASE_MODE, parameter)
return code, None

0
external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/__init__.py

290
external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel.py

@ -0,0 +1,290 @@
import time
from typing import Any, Callable
from threading import Thread, Event
from cyclonedds.domain import Domain, DomainParticipant
from cyclonedds.internal import dds_c_t
from cyclonedds.pub import DataWriter
from cyclonedds.sub import DataReader
from cyclonedds.topic import Topic
from cyclonedds.qos import Qos
from cyclonedds.core import DDSException, Listener
from cyclonedds.util import duration
from cyclonedds.internal import dds_c_t, InvalidSample
# for channel config
from .channel_config import ChannelConfigAutoDetermine, ChannelConfigHasInterface
# for singleton
from ..utils.singleton import Singleton
from ..utils.bqueue import BQueue
"""
" class ChannelReader
"""
"""
" class Channel
"""
class Channel:
"""
" internal class __Reader
"""
class __Reader:
def __init__(self):
self.__reader = None
self.__handler = None
self.__queue = None
self.__queueEnable = False
self.__threadEvent = None
self.__threadReader = None
def Init(self, participant: DomainParticipant, topic: Topic, qos: Qos = None, handler: Callable = None, queueLen: int = 0):
if handler is None:
self.__reader = DataReader(participant, topic, qos)
else:
self.__handler = handler
if queueLen > 0:
self.__queueEnable = True
self.__queue = BQueue(queueLen)
self.__threadEvent = Event()
self.__threadReader = Thread(target=self.__ChannelReaderThreadFunc, name="ch_reader", daemon=True)
self.__threadReader.start()
self.__reader = DataReader(participant, topic, qos, Listener(on_data_available=self.__OnDataAvailable))
def Read(self, timeout: float = None):
sample = None
try:
if timeout is None:
sample = self.__reader.take_one()
else:
sample = self.__reader.take_one(timeout=duration(seconds=timeout))
except DDSException as e:
print("[Reader] catch DDSException msg:", e.msg)
except TimeoutError as e:
print("[Reader] take sample timeout")
except:
print("[Reader] take sample error")
return sample
def Close(self):
if self.__reader is not None:
del self.__reader
if self.__queueEnable:
self.__threadEvent.set()
self.__queue.Interrupt()
self.__queue.Clear()
self.__threadReader.join()
def __OnDataAvailable(self, reader: DataReader):
samples = []
try:
samples = reader.take(1)
except DDSException as e:
print("[Reader] catch DDSException error. msg:", e.msg)
return
except TimeoutError as e:
print("[Reader] take sample timeout")
return
except:
print("[Reader] take sample error")
return
if samples is None:
return
# check invalid sample
sample = samples[0]
if isinstance(sample, InvalidSample):
return
# do sample
if self.__queueEnable:
self.__queue.Put(sample)
else:
self.__handler(sample)
def __ChannelReaderThreadFunc(self):
while not self.__threadEvent.is_set():
sample = self.__queue.Get()
if sample is not None:
self.__handler(sample)
"""
" internal class __Writer
"""
class __Writer:
def __init__(self):
self.__writer = None
self.__publication_matched_count = 0
def Init(self, participant: DomainParticipant, topic: Topic, qos: Qos = None):
self.__writer = DataWriter(participant, topic, qos, Listener(on_publication_matched=self.__OnPublicationMatched))
time.sleep(0.2)
def Write(self, sample: Any, timeout: float = None):
waitsec = 0.0 if timeout is None else timeout
# check publication_matched_count
while waitsec > 0.0 and self.__publication_matched_count == 0:
time.sleep(0.1)
waitsec = waitsec - 0.1
# print(time.time())
# check waitsec
if timeout is not None and waitsec <= 0.0:
return False
try:
self.__writer.write(sample)
except DDSException as e:
print("[Writer] catch DDSException error. msg:", e.msg)
return False
except Exception as e:
print("[Writer] write sample error. msg:", e.args())
return False
return True
def Close(self):
if self.__writer is not None:
del self.__writer
def __OnPublicationMatched(self, writer: DataWriter, status: dds_c_t.publication_matched_status):
self.__publication_matched_count = status.current_count
# channel __init__
def __init__(self, participant: DomainParticipant, name: str, type: Any, qos: Qos = None):
self.__reader = self.__Reader()
self.__writer = self.__Writer()
self.__participant = participant
self.__topic = Topic(self.__participant, name, type, qos)
def SetWriter(self, qos: Qos = None):
self.__writer.Init(self.__participant, self.__topic, qos)
def SetReader(self, qos: Qos = None, handler: Callable = None, queueLen: int = 0):
self.__reader.Init(self.__participant, self.__topic, qos, handler, queueLen)
def Write(self, sample: Any, timeout: float = None):
return self.__writer.Write(sample, timeout)
def Read(self, timeout: float = None):
return self.__reader.Read(timeout)
def CloseReader(self):
self.__reader.Close()
def CloseWriter(self):
self.__writer.Close()
"""
" class ChannelFactory
"""
class ChannelFactory(Singleton):
__domain = None
__participant = None
__qos = None
def __init__(self):
super().__init__()
def Init(self, id: int, networkInterface: str = None, qos: Qos = None):
config = None
# choose config
if networkInterface is None:
config = ChannelConfigAutoDetermine
else:
config = ChannelConfigHasInterface.replace('$__IF_NAME__$', networkInterface)
try:
self.__domain = Domain(id, config)
except DDSException as e:
print("[ChannelFactory] create domain error. msg:", e.msg)
return False
except:
print("[ChannelFactory] create domain error.")
return False
try:
self.__participant = DomainParticipant(id)
except DDSException as e:
print("[ChannelFactory] create domain participant error. msg:", e.msg)
return False
except:
print("[ChannelFactory] create domain participant error")
return False
self.__qos = qos
return True
def CreateChannel(self, name: str, type: Any):
return Channel(self.__participant, name, type, self.__qos)
def CreateSendChannel(self, name: str, type: Any):
channel = self.CreateChannel(name, type)
channel.SetWriter(None)
return channel
def CreateRecvChannel(self, name: str, type: Any, handler: Callable = None, queueLen: int = 0):
channel = self.CreateChannel(name, type)
channel.SetReader(None, handler, queueLen)
return channel
"""
" class ChannelPublisher
"""
class ChannelPublisher:
def __init__(self, name: str, type: Any):
factory = ChannelFactory()
self.__channel = factory.CreateChannel(name, type)
self.__inited = False
def Init(self):
if not self.__inited:
self.__channel.SetWriter(None)
self.__inited = True
def Close(self):
self.__channel.CloseWriter()
self.__inited = False
def Write(self, sample: Any, timeout: float = None):
return self.__channel.Write(sample, timeout)
"""
" class ChannelSubscriber
"""
class ChannelSubscriber:
def __init__(self, name: str, type: Any):
factory = ChannelFactory()
self.__channel = factory.CreateChannel(name, type)
self.__inited = False
def Init(self, handler: Callable = None, queueLen: int = 0):
if not self.__inited:
self.__channel.SetReader(None, handler, queueLen)
self.__inited = True
def Close(self):
self.__channel.CloseReader()
self.__inited = False
def Read(self, timeout: float = None):
return self.__channel.Read(timeout)
"""
" function ChannelFactoryInitialize. used to intialize channel everenment.
"""
def ChannelFactoryInitialize(id: int = 0, networkInterface: str = None):
factory = ChannelFactory()
if not factory.Init(id, networkInterface):
raise Exception("channel factory init error.")

25
external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py

@ -0,0 +1,25 @@
ChannelConfigHasInterface = '''<?xml version="1.0" encoding="UTF-8" ?>
<CycloneDDS>
<Domain Id="any">
<General>
<Interfaces>
<NetworkInterface name="$__IF_NAME__$" priority="default" multicast="default"/>
</Interfaces>
</General>
<Tracing>
<Verbosity>config</Verbosity>
<OutputFile>/tmp/cdds.LOG</OutputFile>
</Tracing>
</Domain>
</CycloneDDS>'''
ChannelConfigAutoDetermine = '''<?xml version="1.0" encoding="UTF-8" ?>
<CycloneDDS>
<Domain Id="any">
<General>
<Interfaces>
<NetworkInterface autodetermine=\"true\" priority=\"default\" multicast=\"default\" />
</Interfaces>
</General>
</Domain>
</CycloneDDS>'''

34
external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_name.py

@ -0,0 +1,34 @@
from enum import Enum
"""
" Enum ChannelType
"""
class ChannelType(Enum):
SEND = 0
RECV = 1
"""
" function GetClientChannelName
"""
def GetClientChannelName(serviceName: str, channelType: ChannelType):
name = "rt/api/" + serviceName
if channelType == ChannelType.SEND:
name += "/request"
else:
name += "/response"
return name
"""
" function GetClientChannelName
"""
def GetServerChannelName(serviceName: str, channelType: ChannelType):
name = "rt/api/" + serviceName
if channelType == ChannelType.SEND:
name += "/response"
else:
name += "/request"
return name

24
external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_api.py

@ -0,0 +1,24 @@
"""
" service name
"""
AUDIO_SERVICE_NAME = "voice"
"""
" service api version
"""
AUDIO_API_VERSION = "1.0.0.0"
"""
" api id
"""
ROBOT_API_ID_AUDIO_TTS = 1001
ROBOT_API_ID_AUDIO_ASR = 1002
ROBOT_API_ID_AUDIO_START_PLAY = 1003
ROBOT_API_ID_AUDIO_STOP_PLAY = 1004
ROBOT_API_ID_AUDIO_GET_VOLUME = 1005
ROBOT_API_ID_AUDIO_SET_VOLUME = 1006
ROBOT_API_ID_AUDIO_SET_RGB_LED = 1010
"""
" error code
"""

62
external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py

@ -0,0 +1,62 @@
import json
from ...rpc.client import Client
from .g1_audio_api import *
"""
" class SportClient
"""
class AudioClient(Client):
def __init__(self):
super().__init__(AUDIO_SERVICE_NAME, False)
self.tts_index = 0
def Init(self):
# set api version
self._SetApiVerson(AUDIO_API_VERSION)
# regist api
self._RegistApi(ROBOT_API_ID_AUDIO_TTS, 0)
self._RegistApi(ROBOT_API_ID_AUDIO_ASR, 0)
self._RegistApi(ROBOT_API_ID_AUDIO_START_PLAY, 0)
self._RegistApi(ROBOT_API_ID_AUDIO_STOP_PLAY, 0)
self._RegistApi(ROBOT_API_ID_AUDIO_GET_VOLUME, 0)
self._RegistApi(ROBOT_API_ID_AUDIO_SET_VOLUME, 0)
self._RegistApi(ROBOT_API_ID_AUDIO_SET_RGB_LED, 0)
## API Call ##
def TtsMaker(self, text: str, speaker_id: int):
self.tts_index += self.tts_index
p = {}
p["index"] = self.tts_index
p["text"] = text
p["speaker_id"] = speaker_id
parameter = json.dumps(p)
code, data = self._Call(ROBOT_API_ID_AUDIO_TTS, parameter)
return code
def GetVolume(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(ROBOT_API_ID_AUDIO_GET_VOLUME, parameter)
if code == 0:
return code, json.loads(data)
else:
return code, None
def SetVolume(self, volume: int):
p = {}
p["volume"] = volume
# p["name"] = 'volume'
parameter = json.dumps(p)
code, data = self._Call(ROBOT_API_ID_AUDIO_SET_VOLUME, parameter)
return code
def LedControl(self, R: int, G: int, B: int):
p = {}
p["R"] = R
p["G"] = G
p["B"] = B
parameter = json.dumps(p)
code, data = self._Call(ROBOT_API_ID_AUDIO_SET_RGB_LED, parameter)
return code

32
external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py

@ -0,0 +1,32 @@
"""
" service name
"""
LOCO_SERVICE_NAME = "loco"
"""
" service api version
"""
LOCO_API_VERSION = "1.0.0.0"
"""
" api id
"""
ROBOT_API_ID_LOCO_GET_FSM_ID = 7001
ROBOT_API_ID_LOCO_GET_FSM_MODE = 7002
ROBOT_API_ID_LOCO_GET_BALANCE_MODE = 7003
ROBOT_API_ID_LOCO_GET_SWING_HEIGHT = 7004
ROBOT_API_ID_LOCO_GET_STAND_HEIGHT = 7005
ROBOT_API_ID_LOCO_GET_PHASE = 7006 # deprecated
ROBOT_API_ID_LOCO_SET_FSM_ID = 7101
ROBOT_API_ID_LOCO_SET_BALANCE_MODE = 7102
ROBOT_API_ID_LOCO_SET_SWING_HEIGHT = 7103
ROBOT_API_ID_LOCO_SET_STAND_HEIGHT = 7104
ROBOT_API_ID_LOCO_SET_VELOCITY = 7105
ROBOT_API_ID_LOCO_SET_ARM_TASK = 7106
"""
" error code
"""

127
external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_client.py

@ -0,0 +1,127 @@
import json
from ...rpc.client import Client
from .g1_loco_api import *
"""
" class SportClient
"""
class LocoClient(Client):
def __init__(self):
super().__init__(LOCO_SERVICE_NAME, False)
self.first_shake_hand_stage_ = -1
def Init(self):
# set api version
self._SetApiVerson(LOCO_API_VERSION)
# regist api
self._RegistApi(ROBOT_API_ID_LOCO_GET_FSM_ID, 0)
self._RegistApi(ROBOT_API_ID_LOCO_GET_FSM_MODE, 0)
self._RegistApi(ROBOT_API_ID_LOCO_GET_BALANCE_MODE, 0)
self._RegistApi(ROBOT_API_ID_LOCO_GET_SWING_HEIGHT, 0)
self._RegistApi(ROBOT_API_ID_LOCO_GET_STAND_HEIGHT, 0)
self._RegistApi(ROBOT_API_ID_LOCO_GET_PHASE, 0) # deprecated
self._RegistApi(ROBOT_API_ID_LOCO_SET_FSM_ID, 0)
self._RegistApi(ROBOT_API_ID_LOCO_SET_BALANCE_MODE, 0)
self._RegistApi(ROBOT_API_ID_LOCO_SET_SWING_HEIGHT, 0)
self._RegistApi(ROBOT_API_ID_LOCO_SET_STAND_HEIGHT, 0)
self._RegistApi(ROBOT_API_ID_LOCO_SET_VELOCITY, 0)
self._RegistApi(ROBOT_API_ID_LOCO_SET_ARM_TASK, 0)
# 7101
def SetFsmId(self, fsm_id: int):
p = {}
p["data"] = fsm_id
parameter = json.dumps(p)
code, data = self._Call(ROBOT_API_ID_LOCO_SET_FSM_ID, parameter)
return code
# 7102
def SetBalanceMode(self, balance_mode: int):
p = {}
p["data"] = balance_mode
parameter = json.dumps(p)
code, data = self._Call(ROBOT_API_ID_LOCO_SET_BALANCE_MODE, parameter)
return code
# 7104
def SetStandHeight(self, stand_height: float):
p = {}
p["data"] = stand_height
parameter = json.dumps(p)
code, data = self._Call(ROBOT_API_ID_LOCO_SET_STAND_HEIGHT, parameter)
return code
# 7105
def SetVelocity(self, vx: float, vy: float, omega: float, duration: float = 1.0):
p = {}
velocity = [vx,vy,omega]
p["velocity"] = velocity
p["duration"] = duration
parameter = json.dumps(p)
code, data = self._Call(ROBOT_API_ID_LOCO_SET_VELOCITY, parameter)
return code
# 7106
def SetTaskId(self, task_id: float):
p = {}
p["data"] = task_id
parameter = json.dumps(p)
code, data = self._Call(ROBOT_API_ID_LOCO_SET_ARM_TASK, parameter)
return code
def Damp(self):
self.SetFsmId(1)
def Start(self):
self.SetFsmId(200)
def Squat2StandUp(self):
self.SetFsmId(706)
def Lie2StandUp(self):
self.SetFsmId(702)
def Sit(self):
self.SetFsmId(3)
def StandUp2Squat(self):
self.SetFsmId(706)
def ZeroTorque(self):
self.SetFsmId(0)
def StopMove(self):
self.SetVelocity(0., 0., 0.)
def HighStand(self):
UINT32_MAX = (1 << 32) - 1
self.SetStandHeight(UINT32_MAX)
def LowStand(self):
UINT32_MIN = 0
self.SetStandHeight(UINT32_MIN)
def Move(self, vx: float, vy: float, vyaw: float, continous_move: bool = False):
duration = 864000.0 if continous_move else 1
self.SetVelocity(vx, vy, vyaw, duration)
def BalanceStand(self, balance_mode: int):
self.SetBalanceMode(balance_mode)
def WaveHand(self, turn_flag: bool = False):
self.SetTaskId(1 if turn_flag else 0)
def ShakeHand(self, stage: int = -1):
if stage == 0:
self.first_shake_hand_stage_ = False
self.SetTaskId(2)
elif stage == 1:
self.first_shake_hand_stage_ = True
self.SetTaskId(3)
else:
self.first_shake_hand_stage_ = not self.first_shake_hand_stage_
return self.SetTaskId(3 if self.first_shake_hand_stage_ else 2)

0
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/__init__.py

0
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/__init__.py

19
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_api.py

@ -0,0 +1,19 @@
"""
" service name
"""
OBSTACLES_AVOID_SERVICE_NAME = "obstacles_avoid"
"""
" service api version
"""
OBSTACLES_AVOID_API_VERSION = "1.0.0.2"
"""
" api id
"""
OBSTACLES_AVOID_API_ID_SWITCH_SET = 1001
OBSTACLES_AVOID_API_ID_SWITCH_GET = 1002
OBSTACLES_AVOID_API_ID_MOVE = 1003
OBSTACLES_AVOID_API_ID_USE_REMOTE_COMMAND_FROM_API = 1004

60
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py

@ -0,0 +1,60 @@
import json
from ...rpc.client import Client
from .obstacles_avoid_api import *
"""
" class ObstaclesAvoidClient
"""
class ObstaclesAvoidClient(Client):
def __init__(self):
super().__init__(OBSTACLES_AVOID_SERVICE_NAME, False)
def Init(self):
# set api version
self._SetApiVerson(OBSTACLES_AVOID_API_VERSION)
# regist api
self._RegistApi(OBSTACLES_AVOID_API_ID_SWITCH_SET, 0)
self._RegistApi(OBSTACLES_AVOID_API_ID_SWITCH_GET, 0)
self._RegistApi(OBSTACLES_AVOID_API_ID_MOVE, 0)
self._RegistApi(OBSTACLES_AVOID_API_ID_USE_REMOTE_COMMAND_FROM_API, 0)
# 1001
def SwitchSet(self, on: bool):
p = {}
p["enable"] = on
parameter = json.dumps(p)
code, data = self._Call(OBSTACLES_AVOID_API_ID_SWITCH_SET, parameter)
return code
# 1002
def SwitchGet(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(OBSTACLES_AVOID_API_ID_SWITCH_GET, parameter)
if code == 0:
d = json.loads(data)
return code, d["enable"]
else:
return code, None
# 1003
def Move(self, vx: float, vy: float, vyaw: float):
p = {}
p["x"] = vx
p["y"] = vy
p["yaw"] = vyaw
p["mode"] = 0
parameter = json.dumps(p)
code = self._CallNoReply(OBSTACLES_AVOID_API_ID_MOVE, parameter)
return code
def UseRemoteCommandFromApi(self, isRemoteCommandsFromApi: bool):
p = {}
p["is_remote_commands_from_api"] = isRemoteCommandsFromApi
parameter = json.dumps(p)
code, data = self._Call(OBSTACLES_AVOID_API_ID_USE_REMOTE_COMMAND_FROM_API, parameter)
return code

0
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/__init__.py

25
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_api.py

@ -0,0 +1,25 @@
"""
" service name
"""
ROBOT_STATE_SERVICE_NAME = "robot_state"
"""
" service api version
"""
ROBOT_STATE_API_VERSION = "1.0.0.1"
"""
" api id
"""
ROBOT_STATE_API_ID_SERVICE_SWITCH = 1001
ROBOT_STATE_API_ID_REPORT_FREQ = 1002
ROBOT_STATE_API_ID_SERVICE_LIST = 1003
"""
" error code
"""
ROBOT_STATE_ERR_SERVICE_SWITCH = 5201
ROBOT_STATE_ERR_SERVICE_PROTECTED = 5202

84
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_client.py

@ -0,0 +1,84 @@
import json
from ...rpc.client import Client
from ...rpc.internal import *
from .robot_state_api import *
"""
" class ServiceState
"""
class ServiceState:
def __init__(self, name: str = None, status: int = None, protect: bool = None):
self.name = name
self.status = status
self.protect = protect
"""
" class RobotStateClient
"""
class RobotStateClient(Client):
def __init__(self):
super().__init__(ROBOT_STATE_SERVICE_NAME, False)
def Init(self):
# set api version
self._SetApiVerson(ROBOT_STATE_API_VERSION)
# regist api
self._RegistApi(ROBOT_STATE_API_ID_SERVICE_SWITCH, 0)
self._RegistApi(ROBOT_STATE_API_ID_REPORT_FREQ, 0)
self._RegistApi(ROBOT_STATE_API_ID_SERVICE_LIST, 0)
def ServiceList(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(ROBOT_STATE_API_ID_SERVICE_LIST, parameter)
if code != 0:
return code, None
lst = []
d = json.loads(data)
for t in d:
s = ServiceState()
s.name = t["name"]
s.status = t["status"]
s.protect = t["protect"]
lst.append(s)
return code, lst
def ServiceSwitch(self, name: str, switch: bool):
p = {}
p["name"] = name
p["switch"] = int(switch)
parameter = json.dumps(p)
code, data = self._Call(ROBOT_STATE_API_ID_SERVICE_SWITCH, parameter)
if code != 0:
return code
d = json.loads(data)
status = d["status"]
if status == 5:
return ROBOT_STATE_ERR_SERVICE_PROTECTED
if status != 0 and status != 1:
return ROBOT_STATE_ERR_SERVICE_SWITCH
return code
def SetReportFreq(self, interval: int, duration: int):
p = {}
p["interval"] = interval
p["duration"] = duration
parameter = json.dumps(p)
code, data = self._Call(ROBOT_STATE_API_ID_REPORT_FREQ, p)
return code

0
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/__init__.py

74
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py

@ -0,0 +1,74 @@
"""
" service name
"""
SPORT_SERVICE_NAME = "sport"
"""
" service api version
"""
SPORT_API_VERSION = "1.0.0.1"
"""
" api id
"""
SPORT_API_ID_DAMP = 1001
SPORT_API_ID_BALANCESTAND = 1002
SPORT_API_ID_STOPMOVE = 1003
SPORT_API_ID_STANDUP = 1004
SPORT_API_ID_STANDDOWN = 1005
SPORT_API_ID_RECOVERYSTAND = 1006
SPORT_API_ID_EULER = 1007
SPORT_API_ID_MOVE = 1008
SPORT_API_ID_SIT = 1009
SPORT_API_ID_RISESIT = 1010
SPORT_API_ID_SWITCHGAIT = 1011
SPORT_API_ID_TRIGGER = 1012
SPORT_API_ID_BODYHEIGHT = 1013
SPORT_API_ID_FOOTRAISEHEIGHT = 1014
SPORT_API_ID_SPEEDLEVEL = 1015
SPORT_API_ID_HELLO = 1016
SPORT_API_ID_STRETCH = 1017
SPORT_API_ID_TRAJECTORYFOLLOW = 1018
SPORT_API_ID_CONTINUOUSGAIT = 1019
SPORT_API_ID_CONTENT = 1020
SPORT_API_ID_WALLOW = 1021
SPORT_API_ID_DANCE1 = 1022
SPORT_API_ID_DANCE2 = 1023
SPORT_API_ID_GETBODYHEIGHT = 1024
SPORT_API_ID_GETFOOTRAISEHEIGHT = 1025
SPORT_API_ID_GETSPEEDLEVEL = 1026
SPORT_API_ID_SWITCHJOYSTICK = 1027
SPORT_API_ID_POSE = 1028
SPORT_API_ID_SCRAPE = 1029
SPORT_API_ID_FRONTFLIP = 1030
SPORT_API_ID_FRONTJUMP = 1031
SPORT_API_ID_FRONTPOUNCE = 1032
SPORT_API_ID_WIGGLEHIPS = 1033
SPORT_API_ID_GETSTATE = 1034
SPORT_API_ID_ECONOMICGAIT = 1035
SPORT_API_ID_HEART = 1036
ROBOT_SPORT_API_ID_DANCE3 = 1037
ROBOT_SPORT_API_ID_DANCE4 = 1038
ROBOT_SPORT_API_ID_HOPSPINLEFT = 1039
ROBOT_SPORT_API_ID_HOPSPINRIGHT = 1040
ROBOT_SPORT_API_ID_LEFTFLIP = 1042
ROBOT_SPORT_API_ID_BACKFLIP = 1044
ROBOT_SPORT_API_ID_FREEWALK = 1045
ROBOT_SPORT_API_ID_FREEBOUND = 1046
ROBOT_SPORT_API_ID_FREEJUMP = 1047
ROBOT_SPORT_API_ID_FREEAVOID = 1048
ROBOT_SPORT_API_ID_WALKSTAIR = 1049
ROBOT_SPORT_API_ID_WALKUPRIGHT = 1050
ROBOT_SPORT_API_ID_CROSSSTEP = 1051
"""
" error code
"""
# client side
SPORT_ERR_CLIENT_POINT_PATH = 4101
# server side
SPORT_ERR_SERVER_OVERTIME = 4201
SPORT_ERR_SERVER_NOT_INIT = 4202

446
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py

@ -0,0 +1,446 @@
import json
from ...rpc.client import Client
from .sport_api import *
"""
" SPORT_PATH_POINT_SIZE
"""
SPORT_PATH_POINT_SIZE = 30
"""
" class PathPoint
"""
class PathPoint:
def __init__(self, timeFromStart: float, x: float, y: float, yaw: float, vx: float, vy: float, vyaw: float):
self.timeFromStart = timeFromStart
self.x = x
self.y = y
self.yaw = yaw
self.vx = vx
self.vy = vy
self.vyaw = vyaw
"""
" class SportClient
"""
class SportClient(Client):
def __init__(self, enableLease: bool = False):
super().__init__(SPORT_SERVICE_NAME, enableLease)
def Init(self):
# set api version
self._SetApiVerson(SPORT_API_VERSION)
# regist api
self._RegistApi(SPORT_API_ID_DAMP, 0)
self._RegistApi(SPORT_API_ID_BALANCESTAND, 0)
self._RegistApi(SPORT_API_ID_STOPMOVE, 0)
self._RegistApi(SPORT_API_ID_STANDUP, 0)
self._RegistApi(SPORT_API_ID_STANDDOWN, 0)
self._RegistApi(SPORT_API_ID_RECOVERYSTAND, 0)
self._RegistApi(SPORT_API_ID_EULER, 0)
self._RegistApi(SPORT_API_ID_MOVE, 0)
self._RegistApi(SPORT_API_ID_SIT, 0)
self._RegistApi(SPORT_API_ID_RISESIT, 0)
self._RegistApi(SPORT_API_ID_SWITCHGAIT, 0)
self._RegistApi(SPORT_API_ID_TRIGGER, 0)
self._RegistApi(SPORT_API_ID_BODYHEIGHT, 0)
self._RegistApi(SPORT_API_ID_FOOTRAISEHEIGHT, 0)
self._RegistApi(SPORT_API_ID_SPEEDLEVEL, 0)
self._RegistApi(SPORT_API_ID_HELLO, 0)
self._RegistApi(SPORT_API_ID_STRETCH, 0)
self._RegistApi(SPORT_API_ID_TRAJECTORYFOLLOW, 0)
self._RegistApi(SPORT_API_ID_CONTINUOUSGAIT, 0)
# self._RegistApi(SPORT_API_ID_CONTENT, 0)
self._RegistApi(SPORT_API_ID_WALLOW, 0)
self._RegistApi(SPORT_API_ID_DANCE1, 0)
self._RegistApi(SPORT_API_ID_DANCE2, 0)
# self._RegistApi(SPORT_API_ID_GETBODYHEIGHT, 0)
# self._RegistApi(SPORT_API_ID_GETFOOTRAISEHEIGHT, 0)
# self._RegistApi(SPORT_API_ID_GETSPEEDLEVEL, 0)
self._RegistApi(SPORT_API_ID_SWITCHJOYSTICK, 0)
self._RegistApi(SPORT_API_ID_POSE, 0)
self._RegistApi(SPORT_API_ID_SCRAPE, 0)
self._RegistApi(SPORT_API_ID_FRONTFLIP, 0)
self._RegistApi(SPORT_API_ID_FRONTJUMP, 0)
self._RegistApi(SPORT_API_ID_FRONTPOUNCE, 0)
self._RegistApi(SPORT_API_ID_WIGGLEHIPS, 0)
self._RegistApi(SPORT_API_ID_GETSTATE, 0)
self._RegistApi(SPORT_API_ID_ECONOMICGAIT, 0)
self._RegistApi(SPORT_API_ID_HEART, 0)
self._RegistApi(ROBOT_SPORT_API_ID_LEFTFLIP, 0)
self._RegistApi(ROBOT_SPORT_API_ID_BACKFLIP, 0)
self._RegistApi(ROBOT_SPORT_API_ID_FREEWALK, 0)
self._RegistApi(ROBOT_SPORT_API_ID_FREEBOUND, 0)
self._RegistApi(ROBOT_SPORT_API_ID_FREEJUMP, 0)
self._RegistApi(ROBOT_SPORT_API_ID_FREEAVOID, 0)
self._RegistApi(ROBOT_SPORT_API_ID_WALKSTAIR, 0)
self._RegistApi(ROBOT_SPORT_API_ID_WALKUPRIGHT, 0)
self._RegistApi(ROBOT_SPORT_API_ID_CROSSSTEP, 0)
# 1001
def Damp(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_DAMP, parameter)
return code
# 1002
def BalanceStand(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_BALANCESTAND, parameter)
return code
# 1003
def StopMove(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_STOPMOVE, parameter)
return code
# 1004
def StandUp(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_STANDUP, parameter)
return code
# 1005
def StandDown(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_STANDDOWN, parameter)
return code
# 1006
def RecoveryStand(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_RECOVERYSTAND, parameter)
return code
# 1007
def Euler(self, roll: float, pitch: float, yaw: float):
p = {}
p["x"] = roll
p["y"] = pitch
p["z"] = yaw
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_EULER, parameter)
return code
# 1008
def Move(self, vx: float, vy: float, vyaw: float):
p = {}
p["x"] = vx
p["y"] = vy
p["z"] = vyaw
parameter = json.dumps(p)
code = self._CallNoReply(SPORT_API_ID_MOVE, parameter)
return code
# 1009
def Sit(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SIT, parameter)
return code
#1010
def RiseSit(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_RISESIT, parameter)
return code
# 1011
def SwitchGait(self, t: int):
p = {}
p["data"] = t
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SWITCHGAIT, parameter)
return code
# 1012
def Trigger(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_TRIGGER, parameter)
return code
# 1013
def BodyHeight(self, height: float):
p = {}
p["data"] = height
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_BODYHEIGHT, parameter)
return code
# 1014
def FootRaiseHeight(self, height: float):
p = {}
p["data"] = height
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_FOOTRAISEHEIGHT, parameter)
return code
# 1015
def SpeedLevel(self, level: int):
p = {}
p["data"] = level
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SPEEDLEVEL, parameter)
return code
# 1016
def Hello(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_HELLO, parameter)
return code
# 1017
def Stretch(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_STRETCH, parameter)
return code
# 1018
def TrajectoryFollow(self, path: list):
l = len(path)
if l != SPORT_PATH_POINT_SIZE:
return SPORT_ERR_CLIENT_POINT_PATH
path_p = []
for i in range(l):
point = path[i]
p = {}
p["t_from_start"] = point.timeFromStart
p["x"] = point.x
p["y"] = point.y
p["yaw"] = point.yaw
p["vx"] = point.vx
p["vy"] = point.vy
p["vyaw"] = point.vyaw
path_p.append(p)
parameter = json.dumps(path_p)
code = self._CallNoReply(SPORT_API_ID_TRAJECTORYFOLLOW, parameter)
return code
# 1019
def ContinuousGait(self, flag: int):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_CONTINUOUSGAIT, parameter)
return code
# # 1020
# def Content(self):
# p = {}
# parameter = json.dumps(p)
# code, data = self._Call(SPORT_API_ID_CONTENT, parameter)
# return code
# 1021
def Wallow(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_WALLOW, parameter)
return code
# 1022
def Dance1(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_DANCE1, parameter)
return code
# 1023
def Dance2(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_DANCE2, parameter)
return code
# 1025
def GetFootRaiseHeight(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_GETFOOTRAISEHEIGHT, parameter)
if code == 0:
d = json.loads(data)
return code, d["data"]
else:
return code, None
# 1026
def GetSpeedLevel(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_GETSPEEDLEVEL, parameter)
if code == 0:
d = json.loads(data)
return code, d["data"]
else:
return code, None
# 1027
def SwitchJoystick(self, on: bool):
p = {}
p["data"] = on
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SWITCHJOYSTICK, parameter)
return code
# 1028
def Pose(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_POSE, parameter)
return code
# 1029
def Scrape(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_SCRAPE, parameter)
return code
# 1030
def FrontFlip(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_FRONTFLIP, parameter)
return code
# 1031
def FrontJump(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_FRONTJUMP, parameter)
return code
# 1032
def FrontPounce(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_FRONTPOUNCE, parameter)
return code
# 1033
def WiggleHips(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_WIGGLEHIPS, parameter)
return code
# 1034
def GetState(self, keys: list):
parameter = json.dumps(keys)
code, data = self._Call(SPORT_API_ID_GETSTATE, parameter)
if code == 0:
return code, json.loads(data)
else:
return code, None
# 1035
def EconomicGait(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_ECONOMICGAIT, parameter)
return code
# 1036
def Heart(self):
p = {}
parameter = json.dumps(p)
code, data = self._Call(SPORT_API_ID_HEART, parameter)
return code
# 1042
def LeftFlip(self):
p = {}
p["data"] = True
parameter = json.dumps(p)
code, data = self._Call(ROBOT_SPORT_API_ID_LEFTFLIP, parameter)
return code
# 1044
def BackFlip(self):
p = {}
p["data"] = True
parameter = json.dumps(p)
code, data = self._Call(ROBOT_SPORT_API_ID_BACKFLIP, parameter)
return code
# 1045
def FreeWalk(self, flag: bool):
p = {}
p["data"] = True
parameter = json.dumps(p)
code, data = self._Call(ROBOT_SPORT_API_ID_FREEWALK, parameter)
return code
# 1046
def FreeBound(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(ROBOT_SPORT_API_ID_FREEBOUND, parameter)
return code
# 1047
def FreeJump(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(ROBOT_SPORT_API_ID_FREEJUMP, parameter)
return code
# 1048
def FreeAvoid(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(ROBOT_SPORT_API_ID_FREEAVOID, parameter)
return code
# 1049
def WalkStair(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(ROBOT_SPORT_API_ID_WALKSTAIR, parameter)
return code
# 1050
def WalkUpright(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(ROBOT_SPORT_API_ID_WALKUPRIGHT, parameter)
return code
# 1051
def CrossStep(self, flag: bool):
p = {}
p["data"] = flag
parameter = json.dumps(p)
code, data = self._Call(ROBOT_SPORT_API_ID_CROSSSTEP, parameter)
return code

0
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/__init__.py

16
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_api.py

@ -0,0 +1,16 @@
"""
" service name
"""
VIDEO_SERVICE_NAME = "videohub"
"""
" service api version
"""
VIDEO_API_VERSION = "1.0.0.1"
"""
" api id
"""
VIDEO_API_ID_GETIMAGESAMPLE = 1001

23
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_client.py

@ -0,0 +1,23 @@
import json
from ...rpc.client import Client
from .video_api import *
"""
" class VideoClient
"""
class VideoClient(Client):
def __init__(self):
super().__init__(VIDEO_SERVICE_NAME, False)
def Init(self):
# set api version
self._SetApiVerson(VIDEO_API_VERSION)
# regist api
self._RegistApi(VIDEO_API_ID_GETIMAGESAMPLE, 0)
# 1001
def GetImageSample(self):
return self._CallBinary(VIDEO_API_ID_GETIMAGESAMPLE, [])

0
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/__init__.py

21
external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_api.py

@ -0,0 +1,21 @@
"""
" service name
"""
VUI_SERVICE_NAME = "vui"
"""
" service api version
"""
VUI_API_VERSION = "1.0.0.1"
"""
" api id
"""
VUI_API_ID_SETSWITCH = 1001
VUI_API_ID_GETSWITCH = 1002
VUI_API_ID_SETVOLUME = 1003
VUI_API_ID_GETVOLUME = 1004
VUI_API_ID_SETBRIGHTNESS = 1005
VUI_API_ID_GETBRIGHTNESS = 1006

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save