File size: 4,224 Bytes
d8290eb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# H2 ROS 2 LowCmd bridge

This C++17 package connects Unitree `unitree_hg` ROS 2 messages to the Python controller through H2BR shared memory. It receives ROS `/lowstate` (native DDS `rt/lowstate`), mirrors `/wbc_racket_command`, and publishes ROS `/lowcmd` (native DDS `rt/lowcmd`) at 1000 Hz.

Read [setup](../docs/SETUP.md) and complete site-specific hardware commissioning before a live launch. The bridge executable is a hardware control component; compilation or a passing contract check does not authorize robot operation.

## Build and inspect on Linux

From the repository root, with ROS 2 Humble, the installed `unitree_hg` workspace, and the selected network interface configured:

```bash
export H2_IF=enp4s0f0
export UNITREE_ROS2_ROOT="$HOME/unitree_ros2"
source scripts/source_unitree_env.sh "$H2_IF"
colcon build --packages-select cpp_lowcmd_bridge \
  --cmake-args -DCMAKE_BUILD_TYPE=Release
source install/setup.bash
ros2 run cpp_lowcmd_bridge h2_lowcmd_shm_bridge --print-contract
ros2 run cpp_lowcmd_bridge h2_lowcmd_shm_bridge \
  --validate-network-only --network-interface "$H2_IF"
```

The expected contract is:

```text
H2BR magic=0x48324252 version=7 bytes=1544 state_dof=31 lowcmd_slots=35 mode_pr=0 joint_map=unitree_sdk2@e02dc701-h2-cpp cmd_q=raw_learned_desired ros_lowstate=/lowstate ros_lowcmd=/lowcmd publish_hz=1000
```

Network validation checks the local RMW/interface/CycloneDDS configuration only. The native LowState inspector and physical identity check are separate commissioning gates. Build outputs belong in `build/`, `install/`, and `log/` and are not release source.

## Admission and shared memory

Live startup requires `--network-interface`. The interface must be UP and match `CYCLONEDDS_URI` generated by the environment script. The bridge publishes no command until it has valid H2 LowState in PR mode, no discovered competing LowCmd publisher, and a finite Python H2BR command carrying the validated MotionSwitcher attestation bit. It never changes AB mode into PR mode.

The 1544-byte H2BR v7 interface uses 31 state/command slots and the audited C++ motor map. Waist IDs 12/13/14 are yaw/roll/pitch; wrist IDs are roll/pitch/yaw. Policy ordering is different and is mapped by name in the Python H2 contract. Shared-memory locks protect coherent snapshots and allow only one Python command writer.

The production shared-memory name is `h2_pingpong_bridge`. The simulator uses a separate name and ownership contract; it must not attach to production memory. Before a runner starts, the [no-runner validation script](../scripts/validate_bridge_no_runner.sh) checks healthy/disarmed state, LowState update rate, exclusive ROS endpoint ownership, and absence of actual LowCmd samples.

## Command and stop semantics

`cmd_q` contains the raw learned desired joint target. Immediately before each 1000 Hz publication, the bridge projects that target into the PD-effort interval computed from the latest valid joint position/velocity and actuator effort limits. This allows an out-of-range desired target to request saturated effort without first rewriting it to a mechanical angle boundary. Measured state and captured stop-hold poses remain subject to mechanical limits.

Python supplies zero desired velocity and zero feed-forward torque. It also sets both raw head targets to zero; the effort-projected target can differ from zero when required by the actuator limit. Do not independently change action scaling, motor ordering, gains, or projection semantics.

L1 latches the policy-start request. R1 latches stop and captures the last legal measured pose. The 100 ms watchdog, motor faults, invalid commands, competing publishers, and non-PR/invalid state also latch faults. Stale or invalid LowState inhibits LowCmd output; the bridge does not compute a hold from stale state. A recovered input does not clear a latch. Exit and restart through the full admission procedure.

The bridge must stop last among the control processes during normal shutdown. Stale production shared memory is handled only with the guarded `--replace-stale-shm` path after verifying that no bridge process owns it. Software stop is not a substitute for an independent hardware emergency stop.