We are teaching the Unitree G1 humanoid to pick up a box with both hands, carry it a few metres and put it down. It is our first in-house project and a test bed for clients' logistics tasks. The task looks simple, but keeping the box stable and the robot balanced while walking needs careful work on grasping and gait, which makes it a good starting point for more complex tasks.
- 1. Approachstop in front of box
- 2. Grasptwo hands, side squeeze
- 3. Liftarm force control
- 4. Walkarms held fixed
- 5. Placestop, release
How we approach it
- We split the task into consecutive stages instead of one neural network covering everything, so each stage can be checked and improved on its own.
- The robot stops before grasping, so walking and manipulation never happen at the same time.
- While walking the arms hold a fixed pose and the gait controller treats the box as extra payload.
- A known limit from the manufacturer data: G1 arm payload is about 2 kg (EDU about 3 kg, pose-dependent), so we start with light, rigid boxes.
Results
We do not publish results: no success rate, cycle time or videos.
Code
Illustrative code Illustrative code, not yet run on a robot. Two short sketches by our team showing how we plan to structure the task. They are deliberately independent of the Unitree SDK and ROS 2, so they do not stand for an integration we have not built yet.
01_carry_state_machine.py · not yet run on a robot.# ILLUSTRATIVE / PLANNED - not from our repo, not run on a robot.
# G1 two-handed box carry: the 5 stages as a fail-closed state machine.
# Licence: (c) NeoHumanoidX sp. z o.o., shown for illustration only.
from enum import Enum, auto
class Stage(Enum):
APPROACH = auto(); GRASP = auto(); LIFT = auto()
WALK = auto(); PLACE = auto(); DONE = auto(); STOPPED = auto()
NEXT = {Stage.APPROACH: Stage.GRASP, Stage.GRASP: Stage.LIFT,
Stage.LIFT: Stage.WALK, Stage.WALK: Stage.PLACE,
Stage.PLACE: Stage.DONE}
class CarryTask:
def __init__(self, skills, safety):
self.skills, self.safety = skills, safety # injected; sim first
self.stage = Stage.APPROACH
def step(self):
if self.safety.stop_requested(): # e-stop / voice stop
self.stage = Stage.STOPPED # latched until reset
return self.stage
if self.stage in (Stage.DONE, Stage.STOPPED):
return self.stage
ok = self.skills[self.stage].run() # bounded, with timeout
self.stage = NEXT[self.stage] if ok else Stage.STOPPED
return self.stage02_arm_hold_while_walking.py · not yet run on a robot.# ILLUSTRATIVE / PLANNED - not from our repo, not run on a robot.
# Keep both hands clamped on the box while the legs walk: hold a fixed
# arm pose with gentle stiffness and watch grip force for a slipping box.
# Licence: (c) NeoHumanoidX sp. z o.o., shown for illustration only.
from dataclasses import dataclass
@dataclass
class ArmHold:
pose: list # 14 arm joint targets [rad], left + right
kp: float = 40.0 # soft enough to absorb walking sway
kd: float = 1.5
min_grip_n: float = 8.0
def hold_step(hold, read_state, send_arm_targets, on_slip):
"""One control tick (e.g. 100 Hz) during the WALK stage."""
state = read_state() # joints + hand forces
if min(state.grip_forces) < hold.min_grip_n:
on_slip() # stop walking, set box down
return False
send_arm_targets(hold.pose, kp=hold.kp, kd=hold.kd) # via the robot's SDK
return True