Skip to content

G1 + Brainco Revo2 five-finger hands: setup guide and fixes #46

Description

@brownwa

Summary

This issue documents the end-to-end process for running the Unitree G1 EDU (23-DOF variant) with Brainco Revo2 five-finger dextrous hands under the Decision-Making Mode inference pipeline. The G1 23-DOF robot has no wrist pitch/yaw joints and uses forearm roll only. The Brainco Revo2 hands connect via /dev/ttyUSB1 (left) and /dev/ttyUSB2 (right), driven by a ROS2 stark_node.


Why a new robot type is needed

The existing g1_dex1 config assumes Dex_1 parallel grippers controlled via DDS. Brainco hands use Modbus RTU over RS-485, exposed through a ROS2 node (stark_node) in a Python 3.8 / ROS2 Foxy environment. Since unitree_deploy requires Python 3.10, direct import is impossible — a TCP bridge bridges the two environments.


New g1_brainco robot type

Action space: 14 arm DOFs (4 phantom wrist slots retained for SDK compatibility) + 12 hand DOFs = 26 total

  • Hand DOF range: 0.0 = open, 1.0 = closed
  • Finger order (both hands): thumb, thumb_aux, index, middle, ring, pinky

New files:

  • unitree_deploy/scripts/brainco_bridge.py — TCP bridge: subscribes to motor_status{,_r} (MotorStatus, uint8[6]), publishes to joint_commands_{left,right} (JointState), serves {"cmd":"get/set"} JSON on 127.0.0.1:9877
  • unitree_deploy/unitree_deploy/robot_devices/endeffector/brainco_hand.pyBrainco_DualHand_Controller: 12-DOF, connects to bridge via TCP

Modified files:

  • robot_devices/endeffector/configs.py — added BrancoDualHandConfig
  • robot_devices/endeffector/utils.py — added brainco_dual branch
  • robot/robot_configs.py — added G1_Brainco_RobotConfig (g1_brainco), brainco_dual_arm_default_factory, brainco_default_factory
  • robot/robot_utils.py — added g1_brainco to make_robot_config()
  • scripts/robot_client.py — added g1_brainco to INIT_POSE, ZERO_ACTION, CAM_KEY; inference server URL now reads from INFERENCE_SERVER_HOST / INFERENCE_SERVER_PORT env vars
  • scripts/evaluation/real_eval_server.py — server now binds to 0.0.0.0 by default (was 127.0.0.1); added --host / --port args

Startup sequence (three terminals on robot)

Terminal 1 — stark_node:

conda activate g1brainco
source ~/unitree_ros2/setup.sh
source ~/unitree-g1-brainco-hand/ros2_stark_ws/install/setup.bash
ros2 launch stark_bringup brainco_launch.py

Terminal 2 — TCP bridge:

conda activate g1brainco
source ~/unitree_ros2/setup.sh
source ~/unitree-g1-brainco-hand/ros2_stark_ws/install/setup.bash
python ~/unifolm-world-model-action/unitree_deploy/scripts/brainco_bridge.py

Terminal 3 — robot client:

conda activate unitree_deploy
cd ~/unifolm-world-model-action/unitree_deploy
export LD_PRELOAD=/home/unitree/miniconda3/envs/unitree_deploy/lib/libgomp.so.1
export INFERENCE_SERVER_HOST=<inference-server-ip>
python scripts/robot_client.py --robot_type g1_brainco --language_instruction "your task"

Fixes applied

Issue Fix
requires-python == 3.10.18 but robot has 3.10.12 Change to >=3.10 in pyproject.toml on robot
pinocchio cannot allocate memory in static TLS block (aarch64) export LD_PRELOAD=.../libgomp.so.1
Robot type 'g1_brainco' is not available Add g1_brainco branch to make_robot_config()
Server binds only to 127.0.0.1, robot cannot reach it Default to 0.0.0.0 in real_eval_server.py
Robot client hardcodes HOST=127.0.0.1 Read INFERENCE_SERVER_HOST env var
Arms jerk on startup (INIT_POSE was zeros) Set INIT_POSE['g1_brainco'] to Ready Mode positions
Arms whirring (two competing init_pose sources) Align brainco_dual_arm_default_factory init_pose with INIT_POSE
Arms whirring (control loop resets to zeros) Init G1_29_ArmController.q_target = np.array(self.init_pose) instead of np.zeros(14)

Known remaining issues

  • Tensor size mismatch (26 vs 12): The inference server's normalizer raises RuntimeError: The size of tensor a (26) must match the size of tensor b (12) when using a model checkpoint trained on a 12-DOF dataset. A g1_brainco 26-DOF trained checkpoint is needed.
  • Minor forearm roll correction: The static init_pose uses 0.0 for forearm roll, but the actual resting value may be slightly different (~0.035 left / ~-0.115 right), causing small ongoing corrections. A future fix would read the actual post-go_start() position and use that as q_target.

Reference implementation

Full implementation is available in the fork branch:
brownwa/unifolm-world-model-actiondocs/brainco-setup-guide

Documentation is in unitree_deploy/README.md under section 2.1 Run G1 with Brainco Revo2 Five-Finger Hands.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions