Skip to content

Step 3 - Add Component Subsystems

The gateway subsystem is the lower layer that wraps the provided 2D simulator. Now add the component subsystems that make the robot usable.

component.mobile
component.manipulation
component.gripper
component.perception

Each package in this step is a role: component subsystem. Its public compatibility surface is a component.* contract. In other words, the subsystem is the runtime unit, and the component contract is the API-contract set that other subsystems and robot slots depend on.

Run This Stage

From the installed root.

cd /opt/librux
librux launch run tutorials/subsystems/component.mobile \
  --instance component.mobile \
  --bind actuator_servo=gateway.device_gateway.actuator_servo \
  --detach

librux launch run tutorials/subsystems/component.perception \
  --instance component.perception \
  --bind perception_sensor=gateway.device_gateway.perception_query \
  --detach

librux launch run tutorials/subsystems/component.manipulation \
  --instance component.manipulation \
  --bind actuator_servo=gateway.device_gateway.actuator_servo \
  --bind perception=component.perception.perception \
  --detach

librux launch run tutorials/subsystems/component.gripper \
  --instance component.gripper \
  --bind digital_io=gateway.device_gateway.digital_io \
  --detach

Expected terminal output shape.

Each command returns a detached package launch result.

Open the browser with the same host rule from Step 1. For a local browser and runtime, use the following.

http://127.0.0.1:8091/

For a remote runtime host, use the following.

http://<runtime-host>:8091/

The browser should still show world online. No application is running, so teleop commands are not active yet.

This step uses --bind because each package is launched one subsystem at a time. It does not run librux deploy up yet.

1. Mobile-Base Component Subsystem

Open the manifest.

nano tutorials/subsystems/component.mobile/subsystem.yaml

component.mobile provides the standard mobile-base component contract and requires the standard actuator gateway.

requires:
  actuator_servo:
    contract: api.actuator.servo.v1

provides:
  mobile_base:
    contract: component.mobile_base.v1

Open the code.

nano tutorials/subsystems/component.mobile/mobile_controller.py

The component subsystem registers the mobile-base API surface behind that component contract. velocity_command, set_velocity_limits, get_velocity_limits, stop, get_status, enable_drive, disable_drive, clear_fault, and the mobile_base_state / fault_state events. When velocity_command is called, it does not move simulator state directly. It sends a servo_joint command to the bound gateway through the Control plane, preserving the caller's period and deadline metadata. The gateway exposes servo_joint only as the canonical actuator-servo Control surface.

2. Manipulator Component Subsystem

nano tutorials/subsystems/component.manipulation/manipulation_controller.py

component.manipulation exposes the manipulator controller spec surface.

Endpoint Meaning
joint_velocity low-latency joint-space control surface
stop, pause, resume trajectory control procedures
set_tcp, set_payload tool and payload procedures
move_j, move_l, move_c programmed motion operations
joint_state, tool_pose, motion_state, tool_settings state events

The key pattern is standard-interface composition. The controller exposes manipulator endpoints and sends low-level actuator commands through the gateway.

component.manipulation -> gateway.device_gateway.servo_joint

As with the mobile controller, this tutorial stage calls the bounded servo_joint Control endpoint through the bound actuator-servo interface.

The application and compound subsystem are added later. They should not need to know how the simulator stores arm state.

3. Gripper Component Subsystem

nano tutorials/subsystems/component.gripper/gripper_controller.py

component.gripper exposes the following.

Procedure Meaning
open release held object
close grab reachable trash
get_gripper_state read the gripper output state

This is still a virtual gripper, but it has the same boundary a real gripper component subsystem would need. Application code asks for an action, and the subsystem owns how to realize it through a digital I/O gateway.

4. Perception Component Subsystem

nano tutorials/subsystems/component.perception/perception_controller.py

component.perception wraps gateway scans into application-facing procedures.

Procedure Meaning
scan configurable scan
front front field-of-view scan
nearest nearest target scan

The radar application later depends on this perception component contract. The teleop app does not. That difference is captured in deployment bindings.

5. Why Mixed Endpoint Types Are Used Here

The cleaner component subsystems use a mix of Control and Procedure endpoints.

velocity command
scan once
extend once
grab once
burn once

Periodic or latency-sensitive commands use Control endpoints such as velocity_command, joint_velocity, and the gateway's servo_joint. One-shot bounded actions use Procedure endpoints cover scan, gripper open/close, digital output, stop, and status queries. A contract endpoint must not be registered on both planes.

The simulated world state is read by the browser from the provided simulator API, not from a robot gateway endpoint. The tutorial does not require you to manually run separate transport examples; the cleaner robot demonstrates the communication shape in context.

6. Verify Controller Declarations

Check that each component subsystem requires/provides public component.* contracts.

librux subsystem status component.mobile
librux subsystem status component.manipulation
librux subsystem status component.gripper
librux subsystem status component.perception

You should see active subsystem sessions. The relevant contracts are these.

component.mobile_base.v1
component.manipulator.v1
component.gripper.v1
api.actuator.servo.v1
api.io.digital.output.v1
api.perception.query.v1
component.perception.v1

Cleaner Robot Graph After This Step

flowchart LR Mobile["component.mobile<br/>mobile-base controller"] Arm["component.manipulation<br/>manipulator controller"] Grip["component.gripper<br/>gripper controller"] Perception["component.perception<br/>perception controller"] Gateway["gateway.device_gateway<br/>gateway"] Simulator["provided 2D arena"] Mobile -->|"servo_joint"| Gateway Arm -->|"servo_joint"| Gateway Arm -->|"scan"| Perception Grip -->|"digital I/O"| Gateway Perception -->|"scan"| Gateway Gateway --> Simulator

The robot still has no top-level application. It now has reusable component subsystems that implement standard component contracts and can be combined by different applications.

Continue to Step 4 - Define Platform.