Skip to main content
This guide walks through integrating a new robot arm with dimOS, from writing the hardware adapter to creating blueprints for planning and control.

Architecture Overview

dimOS uses a Protocol-based adapter pattern — no base class inheritance required. Your adapter wraps the vendor SDK and exposes a standard interface that the rest of the system consumes:
See also: dimos/hardware/manipulators/README.md for a quick reference.

Prerequisites

  1. Vendor SDK — The Python SDK for your robot arm (e.g., xarm-python-sdk, piper-sdk)
  2. URDF/xacro — A robot description file (only needed if you want motion planning)
  3. Connection info — IP address, CAN port, serial device, etc.

Step 1: Create the Adapter

Create a new directory for your arm under dimos/hardware/manipulators/:

adapter.py — Full Skeleton

Below is a complete annotated adapter. Implement each method by wrapping your vendor SDK calls. All values crossing the adapter boundary must use SI units.
skip
Then declare the adapter in a _registry.py manifest next to it:

Key implementation notes

  • Unsupported features — Return None for reads and False for writes. Never raise exceptions for optional features.
  • Velocity/effort feedback — If your SDK doesn’t provide these, return zeros. The coordinator handles this gracefully.
  • Lazy SDK import — If the vendor SDK is an optional dependency, you can import it inside connect() instead of at module level (see Piper adapter for this pattern):

Step 2: Create Package Files

How discovery works

The AdapterRegistry in dimos/hardware/manipulators/registry.py discovers adapters from _registry.py manifests at import time:
  1. It iterates over all subpackages under dimos/hardware/manipulators/
  2. For each subpackage, it loads <subpackage>._registry and records each ADAPTER_FACTORIES entry (name → "module:attr" import path)
  3. Your adapter module is imported only when create("yourarm") is first called
The manifest must import nothing beyond stdlib — it is loaded even when your vendor SDK is missing, so the name always shows up in available() and a missing SDK fails loudly at create() instead of silently dropping the adapter. A CI test (dimos/hardware/test_adapter_registries.py) fails if an adapter directory has no manifest or a manifest path doesn’t resolve. You can verify discovery works:
skip

Step 3: Create Your Robot Folder and Blueprints

Each robot in dimOS gets its own folder under dimos/robot/. This is where you define all blueprints for your arm — coordinator, planning, perception, etc. This follows the same pattern as Unitree robots (dimos/robot/unitree/).

3a. Create the robot directory

3b. Define your blueprints

Create dimos/robot/yourarm/blueprints.py with your coordinator and (optionally) planning blueprints:
skip

Blueprint field reference

Step 4: Add URDF and Planning Integration (Optional)

If you want motion planning, you need a URDF and a planning blueprint. Add these to your robot’s own blueprints.py.

4a. Add your URDF

Prefer an upstream Robot Description Source and create a RobotDescriptionSource handle beside your robot adapter. Joining paths from the source is lazy: importing the catalog does not clone or update the repo, but the first concrete path access resolves the source into the robot asset cache. Use LfsPath only when the asset is intentionally vendored, locally modified, or has no suitable upstream source. If the planning blueprint selects the RoboPlan TOPP-RA trajectory parametrizer, dimOS currently pins RoboPlan to 0.5.1. Every movable joint in each selected planning group must provide finite, positive velocity limits. Authored extended acceleration limits take precedence; when absent, dimOS temporarily inserts a global 2.0 rad/s² acceleration fallback during RoboPlan model composition:
RoboPlan loads both limits from its scene model. If either is absent, zero, negative, or non-finite, plan materialization fails before preview or execution and identifies the affected joint. dimOS does not substitute RobotModelConfig.max_velocity, velocity_limits, or max_acceleration for this backend. Formal per-joint dimOS overrides will be added separately.
skip

4b. Create a robot model config helper

skip

4c. Create a planning blueprint

Add this to your dimos/robot/yourarm/blueprints.py alongside the coordinator blueprint:
skip
You may omit trajectory_parametrization when the world-based default is appropriate: world_backend="roboplan" selects roboplan_toppra, while world_backend="drake" selects simple_trapezoid. To configure TOPP-RA tuning explicitly, select RoboPlan for the world and parametrizer after adding the URDF limits described above:
skip

Key config fields

Coordinator-facing joint states and trajectories use global joint names derived mechanically as {robot_name}/{local_joint_name} (for example, arm/joint1). Keep hardware-native name translation inside the hardware adapter; manipulation planning config uses local model joint names. Planning-group base_link/tip_link values define kinematic chains and pose target frames. base_link is only the robot-scoped link placed by base_pose; do not use it as a substitute for planning-group chain metadata. See Planning Groups.

4d. Configure Cartesian, EEF-twist, and teleop control IK

Cartesian, EEF-twist, and engagement-relative teleop tasks use the portable RobotModel in RobotModelConfig. Configure package paths and Xacro arguments when creating the model, name the end-effector link, and map coordinator joints to model joints. The task validates the prepared model, frame, and joint mapping at startup. Teleop uses the named frame and does not accept a separate model path or numeric end-effector joint ID. Pass the same model configuration to the common helpers:
skip
Each tick starts from measured joints and applies model position and velocity limits. Twist targets are derived from measured forward kinematics. Teleop targets apply controller deltas to a measured engagement baseline and discard that baseline across disengage, timeout, stop, clear, or E-STOP. Invalid models or mappings fail at startup; invalid runtime output holds the measured position. Validate Cartesian, twist, and teleop behavior in simulation or replay before hardware use.

Step 5: Register Blueprints

The blueprint registry in dimos/robot/all_blueprints.py is auto-generated by scanning the codebase for blueprint declarations. After adding your blueprints:
  1. Run the generation test to update the registry:
  2. Now you can run your arm via CLI:

Step 6: Testing

Verify adapter registration

skip

Unit test with mock

You can test coordinator logic without hardware by using unittest.mock:
skip

Integration test with coordinator

skip

Test the real adapter standalone

skip

Quick Reference Checklist

Files to create:
  • dimos/hardware/manipulators/yourarm/__init__.py
  • dimos/hardware/manipulators/yourarm/adapter.py (implements Protocol)
  • dimos/hardware/manipulators/yourarm/_registry.py (declares ADAPTER_FACTORIES)
  • dimos/robot/yourarm/__init__.py
  • dimos/robot/yourarm/blueprints.py (coordinator + planning blueprints)
Files to modify:
  • pyproject.toml — Add vendor SDK to optional dependencies (if applicable)
Verification:
  • adapter_registry.available() includes "yourarm"
  • pytest dimos/robot/test_all_blueprints_generation.py passes (regenerates all_blueprints.py)
  • dimos run coordinator-yourarm starts successfully