Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# bar_ros2_docs
# Humanoid-Control-Website

Documentation site for [`bar_ros2`](https://github.com/T-K-233/bar_ros2) — the
Documentation site for [`humanoid_control`](https://github.com/Berkeley-Humanoids/humanoid_control) — the
Berkeley Architecture Research (BAR) humanoid low-level control stack.

Built with [Docusaurus 3](https://docusaurus.io/) + `@docusaurus/theme-mermaid`
Expand All @@ -26,7 +26,7 @@ npm run serve # serve the built dist locally
The `build/` directory is a fully static site — drop it on Netlify,
Cloudflare Pages, or GitHub Pages. For GitHub Pages, configure the workflow
to publish `build/`; the bundled `docusaurus.config.ts` already sets
`organizationName` and `projectName` for a `T-K-233/bar_ros2_docs` repo.
`organizationName` and `projectName` for a `Berkeley-Humanoids/Humanoid-Control-Website` repo.

## Layout

Expand Down
58 changes: 29 additions & 29 deletions docs/concepts/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ build against which:

![Module dependency graph](/img/diagrams/concepts__architecture__03_module_deps.svg)

Notice that `bar_controllers` does **not** `find_package(bar_robstride)`.
Notice that `humanoid_controllers` does **not** `find_package(humanoid_control_robstride)`.
The plugin is loaded by `controller_manager` at launch via `pluginlib` — a
runtime dep that doesn't appear in the static graph but is just as binding.
The same applies to every `<plugin>` entry in a controller-manager YAML.
Expand All @@ -36,7 +36,7 @@ MuJoCo — it performs three steps:
| `update()` | read state_interfaces_, write command_interfaces_, lock-free trylock for diag publishers | allocations, blocking, exceptions across the RT boundary |
| `write()` | stage frames into the bus library's outgoing queue | the actual CAN/EtherCAT syscall (that's the I/O thread's job) |

The I/O thread in each hardware plugin (`bar_socketcan::SocketCanBus`,
The I/O thread in each hardware plugin (`humanoid_control_socketcan::SocketCanBus`,
`ethercat_driver_ros2`'s EtherLAB master thread) is **separate** from the
controller-manager thread. RT-safety is preserved by making `read()` /
`write()` allocation-free buffer swaps.
Expand Down Expand Up @@ -80,11 +80,11 @@ Behavior per state:

| State | Plugin | What it writes |
|---|---|---|
| **ZERO_TORQUE** | `bar/ZeroTorqueController` | 0 to all 5 cmd interfaces. Startup default, fault fallback. |
| **DAMPING** | `bar/DampingController` | `K=0`, `D=damping value`, `q_cmd=q_captured` — soft under gravity, resists velocity. |
| **STANDBY** | `bar/StandbyController` | Linear pose interpolation through a YAML sequence; ramps `K_p / K_d` on first segment. Publishes `StandbyState` with `is_finished`. |
| **LOCOMOTION** | `bar/RLPolicyController` | In-process ONNX inference (System 0): packs observations, replays the `.mcap` motion reference, decodes + writes commands — all in the RT `update()`. Runs every learned policy (tracking / piano / locomotion); they differ only by the loaded `.onnx` + `.mcap`. |
| **REMOTE** | `bar/RemotePolicyController` | System 1/2 external-command ingress: subscribes `~/command` (`MITCommand` over DDS) from a *non*-real-time source (gravity-comp today, VLA / manipulation later) with arrival-time stale-command gating. |
| **ZERO_TORQUE** | `humanoid_control/ZeroTorqueController` | 0 to all 5 cmd interfaces. Startup default, fault fallback. |
| **DAMPING** | `humanoid_control/DampingController` | `K=0`, `D=damping value`, `q_cmd=q_captured` — soft under gravity, resists velocity. |
| **STANDBY** | `humanoid_control/StandbyController` | Linear pose interpolation through a YAML sequence; ramps `K_p / K_d` on first segment. Publishes `StandbyState` with `is_finished`. |
| **LOCOMOTION** | `humanoid_control/RLPolicyController` | In-process ONNX inference (System 0): packs observations, replays the `.mcap` motion reference, decodes + writes commands — all in the RT `update()`. Runs every learned policy (tracking / piano / locomotion); they differ only by the loaded `.onnx` + `.mcap`. |
| **REMOTE** | `humanoid_control/RemotePolicyController` | System 1/2 external-command ingress: subscribes `~/command` (`MITCommand` over DDS) from a *non*-real-time source (gravity-comp today, VLA / manipulation later) with arrival-time stale-command gating. |

### Transition mechanics

Expand All @@ -100,8 +100,8 @@ plain `rclcpp::Node` that subscribes:
…and exposes five `std_srvs/Trigger` services so transitions can also be
driven from the command line:

- `/bar/mode/damp`, `/bar/mode/load`, `/bar/mode/start_remote`,
`/bar/mode/start_locomotion`, `/bar/mode/quit`
- `/humanoid_control/mode/damp`, `/humanoid_control/mode/load`, `/humanoid_control/mode/start_remote`,
`/humanoid_control/mode/start_locomotion`, `/humanoid_control/mode/quit`

`/control_mode` is published at 50 Hz. The manager polls
`list_controllers` periodically (every 25 ticks = 500 ms) so controllers
Expand Down Expand Up @@ -141,7 +141,7 @@ rather than the **inference**.
### Launch-time `prepare`, then in-process replay

The heavy, non-real-time work runs **once at launch**, never per tick.
`bar_policy prepare` (and `pianist_policy prepare`) resolves the ONNX
`humanoid_control_policy prepare` (and `pianist_policy prepare`) resolves the ONNX
checkpoint (local file or W&B run) and converts the policy's LeRobot
motion dataset into a single-episode rosbag2 **`.mcap`** bag, then emits
an `rl_policy_controller` parameter overlay. The launch runs `prepare`
Expand Down Expand Up @@ -179,10 +179,10 @@ the hot loop), resolving each term as one of:

:::tip[Why a *generic* topic for the live key state]
Routing live key state through a plain float array — rather than
`pianist_msgs/PianoKeyState` — keeps `bar_controllers` free of any
`pianist_msgs/PianoKeyState` — keeps `humanoid_controllers` free of any
task-package dependency. The core controller package never learns the
piano task exists; it just packs a named extern vector. New tasks add
their own publisher on their own topic without touching `bar_controllers`.
their own publisher on their own topic without touching `humanoid_controllers`.
:::

`reset()` (on activation) and `record_action()` (once per tick, after
Expand All @@ -196,15 +196,15 @@ sources that *should* live out-of-process: it subscribes to `MITCommand`
over DDS and writes the bus, with arrival-time staleness gating. Today
that is the gravity-compensation runner (`Lite-Gravity-Compensation` —
raw CycloneDDS, no `rclpy`); next it is VLA / manipulation. Such a client
does not hand-write the message types: [`bar_msgs_dds`](../reference/packages.md#bar_msgs_dds)
generates wire-compatible `cyclonedds` types from `bar_msgs/msg/*.msg`, and
does not hand-write the message types: [`humanoid_control_msgs_dds`](../reference/packages.md#humanoid_control_msgs_dds)
generates wire-compatible `cyclonedds` types from `humanoid_control_msgs/msg/*.msg`, and
the `lite_sdk2` SDK wraps them in a publisher/subscriber layer — see
[Talk to bar_ros2 from Python](../how_to/talk_to_bar_ros2_from_python.md).
[Talk to humanoid_control from Python](../how_to/talk_to_humanoid_control_from_python.md).
These are System 1/2: slower, deliberative, and tolerant of the latency the
DDS hop adds.

`MITState` is a **code-level** schema (a `bar::MITState` POD in
`bar_common`) — not a published topic. Observations are assembled
`MITState` is a **code-level** schema (a `humanoid_control::MITState` POD in
`humanoid_control_common`) — not a published topic. Observations are assembled
in-process from `/lite/joint_states` (the always-on broadcaster) and
`/imu/data` (the IMU driver). See [Policy runner](../reference/policy_runner.md).

Expand All @@ -214,8 +214,8 @@ A handful of artifacts are **frozen once a trained policy depends on them**:

| Artifact | Frozen because |
|---|---|
| `bar_msgs/MITCommand` | trained policies emit this field-by-field over DDS |
| Joint order in `bar_*_controllers.yaml` | trained policies index into this order |
| `humanoid_control_msgs/MITCommand` | trained policies emit this field-by-field over DDS |
| Joint order in `humanoid_control_*_controllers.yaml` | trained policies index into this order |
| `MITState` struct + Python dataclass | both sides agree on `joint_position`/`joint_velocity`/IMU layout |
| Observation term scale + default vectors | shifts mean retraining |

Expand All @@ -229,8 +229,8 @@ system:

Concrete examples:

- A Robstride bus-off → `bar_robstride` publishes `SafetyStatus{level=FAULT,
source="bar_robstride/can0", flags=BUS_OFF}` → `mode_manager` requests a
- A Robstride bus-off → `humanoid_control_robstride` publishes `SafetyStatus{level=FAULT,
source="humanoid_control_robstride/can0", flags=BUS_OFF}` → `mode_manager` requests a
STRICT switch to DAMPING. If DAMPING fails (e.g. command interfaces
unavailable), `mode_manager` falls back to ZERO_TORQUE.
- A `RemotePolicyController` whose Python publisher stalls for >100 ms
Expand All @@ -240,7 +240,7 @@ Concrete examples:
**arrival time at the subscription callback**, not against
`MITCommand.header.stamp`, so publisher clock skew is irrelevant.
- An RL policy returning NaN in its action vector → `RLPolicyController`
detects via `bar::rt::all_finite(...)` and returns `return_type::ERROR`,
detects via `humanoid_control::rt::all_finite(...)` and returns `return_type::ERROR`,
triggering `fallback_controllers` in the CM YAML.

## Deployment topology
Expand All @@ -249,19 +249,19 @@ The shipping configuration is a **two-machine tethered split**. The
same colcon workspace is installed (and built from the same pixi lock
file) on both machines; each launch boots only the subset of nodes
that belongs on its side. Single-machine sim/dev paths
(`bar_bringup_lite/mujoco.launch.py`, `bar_bringup_lite/view_lite.launch.py`,
`bar_bringup_lite/calibrate.launch.py`) are unaffected — they
(`humanoid_control_bringup_lite/mujoco.launch.py`, `humanoid_control_bringup_lite/view_lite.launch.py`,
`humanoid_control_bringup_lite/calibrate.launch.py`) are unaffected — they
collapse both sides into one process tree.

Launches come from two sibling repos: `bar_ros2` ships every
Launches come from two sibling repos: `humanoid_control` ships every
Lite/Prime control-plane and tracking-policy launch; `pianist_ros2`
ships the piano-task-specific launches.

| Side | Machine | Launch | What lives here |
|---|---|---|---|
| **Robot** | Onboard computer (RT kernel, wired tether) | `bar_bringup_lite/launch/real.launch.py` (bar_ros2) | `ros2_control_node`, `bar_robstride` / `bar_sito` hardware plugins, `joint_state_broadcaster`, the five FSM controllers (`zero_torque` / `damping` / `standby` / `rl_policy` / `remote_policy`), `mode_manager`, `joy_node`, `robot_state_publisher`, IMU driver |
| **Host** | Operator workstation | `bar_bringup_lite/launch/viz.launch.py` (bar_ros2) | `viser_viz` *or* `rerun_viz` (selected by `viewer:=`) |
| **Robot** | Onboard computer | `bar_policy/launch/lite_policy.launch.py` (bar_ros2) / `pianist_policy/launch/piano_policy.launch.py` (pianist_ros2) | Runs `prepare` (resolve ONNX, convert motion → `.mcap` + overlay) then loads `rl_policy_controller` into the local CM. Inference is in-process, so the `.onnx` / `.mcap` artifacts **and** the W&B / HF Hub / `onnxruntime` *prepare-time* deps live here. The RT path itself pulls none of them. |
| **Robot** | Onboard computer (RT kernel, wired tether) | `humanoid_control_bringup_lite/launch/real.launch.py` (humanoid_control) | `ros2_control_node`, `humanoid_control_robstride` / `humanoid_control_sito` hardware plugins, `joint_state_broadcaster`, the five FSM controllers (`zero_torque` / `damping` / `standby` / `rl_policy` / `remote_policy`), `mode_manager`, `joy_node`, `robot_state_publisher`, IMU driver |
| **Host** | Operator workstation | `humanoid_control_bringup_lite/launch/viz.launch.py` (humanoid_control) | `viser_viz` *or* `rerun_viz` (selected by `viewer:=`) |
| **Robot** | Onboard computer | `humanoid_control_policy/launch/lite_policy.launch.py` (humanoid_control) / `pianist_policy/launch/piano_policy.launch.py` (pianist_ros2) | Runs `prepare` (resolve ONNX, convert motion → `.mcap` + overlay) then loads `rl_policy_controller` into the local CM. Inference is in-process, so the `.onnx` / `.mcap` artifacts **and** the W&B / HF Hub / `onnxruntime` *prepare-time* deps live here. The RT path itself pulls none of them. |
| **Robot** | Onboard computer | `pianist_policy/launch/midi_keyboard_driver.launch.py` (pianist_ros2) | USB-MIDI keyboard driver → `/piano/key_state` (`std_msgs/Float32MultiArray`); feeds the on-robot controller's `key_pressed` extern term locally (loopback, does **not** cross the tether). |

:::tip[The deployment trade the in-process move makes]
Expand Down Expand Up @@ -332,7 +332,7 @@ deployed surface.

## Next

- [`mode_manager` source](https://github.com/T-K-233/bar_ros2/blob/main/bar_controllers/src/mode_manager.cpp)
- [`mode_manager` source](https://github.com/Berkeley-Humanoids/humanoid_control/blob/main/humanoid_controllers/src/mode_manager.cpp)
— the FSM is ~150 lines of C++; readable in one sitting.
- [Lite 101](../getting_started/lite_101.md) — see all of this run end-to-end
against mock hardware and MuJoCo.
Expand Down
10 changes: 5 additions & 5 deletions docs/concepts/calibration_math.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Calibration math

# Calibration math

How `bar_robstride` maps between the raw encoder reading and the
How `humanoid_control_robstride` maps between the raw encoder reading and the
URDF joint frame, where the per-joint constants live, and why the
calibration is split across two files.

Expand Down Expand Up @@ -37,7 +37,7 @@ is in the joint frame — controllers never see the raw encoder.
| Parameter | Per | Where it lives | What it captures |
|---|---|---|---|
| `direction` | joint | URDF `<param>` inside the `<ros2_control>` block | Wiring sign — does the motor's positive direction match the URDF's positive joint direction? |
| `homing_offset` | joint | `bar_bringup_lite/config/calibration.yaml` | Per-physical-robot encoder zero offset (motor frame, rad). |
| `homing_offset` | joint | `humanoid_control_bringup_lite/config/calibration.yaml` | Per-physical-robot encoder zero offset (motor frame, rad). |

The split is deliberate:

Expand All @@ -53,7 +53,7 @@ The split is deliberate:

## Deriving the offset

The calibration tool (`bar_bringup_lite/scripts/calibrate_robot.py`)
The calibration tool (`humanoid_control_bringup_lite/scripts/calibrate_robot.py`)
runs the plugin with `calibration_file:=''` — identity calibration —
so `/lite/joint_states` reports `direction · raw_motor_pos` (the
direction-applied-but-not-offset frame). The operator hand-sweeps each
Expand Down Expand Up @@ -141,6 +141,6 @@ URDF. The cost is that hand-edits to `id` / `direction` in
## See also

- [How-to → Calibrate the zero pose](../how_to/calibrate_zero_pose.md) — the recipe.
- [`RobstrideSystem` source](https://github.com/T-K-233/bar_ros2/blob/main/bar_devices/bar_robstride/src/robstride_system.cpp#:~:text=load_calibration)
- [`RobstrideSystem` source](https://github.com/Berkeley-Humanoids/humanoid_control/blob/main/humanoid_control_devices/humanoid_control_robstride/src/robstride_system.cpp#:~:text=load_calibration)
— the loader + apply.
- `bar_bringup_lite/scripts/calibrate_robot.py` — the formula in code.
- `humanoid_control_bringup_lite/scripts/calibrate_robot.py` — the formula in code.
20 changes: 10 additions & 10 deletions docs/concepts/five_mode_fsm.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,18 @@ property.

| Mode | Plugin | What it writes per tick | Use it for |
|---|---|---|---|
| **ZERO_TORQUE** | `bar::ZeroTorqueController` | `0` to all 5 MIT interfaces on every joint | Startup default. Fault fallback when DAMPING can't be applied (e.g. state not yet valid). Robot is alive but inert. |
| **DAMPING** | `bar::DampingController` | `stiffness=0`, `damping=damping_value`, `position=captured`, `velocity=0`, `effort=0` | Compliant fail-safe. Robot stays soft under gravity but resists velocity. The state you pass through between operator-driven transitions. |
| **STANDBY** | `bar::StandbyController` | Interpolated `position` along a YAML pose sequence; `K_p`/`K_d` ramped 0→target during segment 0 | Animate the arms to the piano-ready pose with gain ramp-in. Publishes `~/state.is_finished` so transitions out are gated correctly. |
| **LOCOMOTION** | `bar::RLPolicyController` | In-process ONNX inference (low-latency, C++): packs obs, replays the `.mcap` motion reference, writes commands | **Every learned policy** — tracking, piano, locomotion. They differ only by the loaded `.onnx` + `.mcap`; the ONNX `task_type` selects the term set. This is the System 0 real-time path. |
| **REMOTE** | `bar::RemotePolicyController` | `MITCommand` consumed from `~/command` over DDS | System 1/2 external-command ingress: a *non*-real-time source publishes commands (gravity-comp today via `Lite-Gravity-Compensation`; VLA / manipulation later). Not used by the learned policies. |
| **ZERO_TORQUE** | `humanoid_control::ZeroTorqueController` | `0` to all 5 MIT interfaces on every joint | Startup default. Fault fallback when DAMPING can't be applied (e.g. state not yet valid). Robot is alive but inert. |
| **DAMPING** | `humanoid_control::DampingController` | `stiffness=0`, `damping=damping_value`, `position=captured`, `velocity=0`, `effort=0` | Compliant fail-safe. Robot stays soft under gravity but resists velocity. The state you pass through between operator-driven transitions. |
| **STANDBY** | `humanoid_control::StandbyController` | Interpolated `position` along a YAML pose sequence; `K_p`/`K_d` ramped 0→target during segment 0 | Animate the arms to the piano-ready pose with gain ramp-in. Publishes `~/state.is_finished` so transitions out are gated correctly. |
| **LOCOMOTION** | `humanoid_control::RLPolicyController` | In-process ONNX inference (low-latency, C++): packs obs, replays the `.mcap` motion reference, writes commands | **Every learned policy** — tracking, piano, locomotion. They differ only by the loaded `.onnx` + `.mcap`; the ONNX `task_type` selects the term set. This is the System 0 real-time path. |
| **REMOTE** | `humanoid_control::RemotePolicyController` | `MITCommand` consumed from `~/command` over DDS | System 1/2 external-command ingress: a *non*-real-time source publishes commands (gravity-comp today via `Lite-Gravity-Compensation`; VLA / manipulation later). Not used by the learned policies. |

Full per-controller parameter tables live in
[Reference → Controllers](../reference/controllers.md).

## Transition rules

Encoded in `bar_controllers/src/mode_manager.cpp`. The operator's
Encoded in `humanoid_controllers/src/mode_manager.cpp`. The operator's
gamepad intent goes through `dispatch_intent`, which gates based on the
current mode:

Expand All @@ -58,10 +58,10 @@ QUIT (BACK) : ZERO_TORQUE or DAMPING → rclcpp::shutdown()
```

The same transitions are also exposed as `std_srvs/Trigger` services
under `/bar/mode/{damp,load,start_remote,start_locomotion,quit}`, so a
under `/humanoid_control/mode/{damp,load,start_remote,start_locomotion,quit}`, so a
keyboardless lab box can drive the FSM with `ros2 service call …`.

`mode_manager` publishes `/control_mode` (`bar_msgs/ControlMode`) at
`mode_manager` publishes `/control_mode` (`humanoid_control_msgs/ControlMode`) at
50 Hz. When an intent is rejected, the rejection reason goes into
`status_message` so operators see *why* a button didn't take effect.

Expand All @@ -72,7 +72,7 @@ controllers become visible to `dispatch_intent` immediately.

## The auto-DAMP path (safety)

Every hardware plugin publishes `/safety_status` (`bar_msgs/SafetyStatus`)
Every hardware plugin publishes `/safety_status` (`humanoid_control_msgs/SafetyStatus`)
on a TRANSIENT_LOCAL QoS, with a bitmask of `BUS_OFF`, `RX_TIMEOUT`,
`TX_QUEUE_OVERRUN`, `MOTOR_FAULT`, `TEMPERATURE_LIMIT`, `INVALID_FRAME`.
`mode_manager` subscribes. On any non-`OK` level it requests DAMPING
Expand Down Expand Up @@ -128,4 +128,4 @@ runtime**, not by a launch arg. There's no `policy_mode` parameter on
- [Reference → Controllers](../reference/controllers.md) — per-plugin parameter tables.
- [Concepts → Safety pipeline](./safety_pipeline.md) — what triggers auto-DAMP.
- [How-to → Switch controllers without the FSM](../how_to/switch_controllers_manually.md).
- [`mode_manager` source](https://github.com/T-K-233/bar_ros2/blob/main/bar_controllers/src/mode_manager.cpp).
- [`mode_manager` source](https://github.com/Berkeley-Humanoids/humanoid_control/blob/main/humanoid_controllers/src/mode_manager.cpp).
Loading
Loading