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
- Vendor SDK — The Python SDK for your robot arm (e.g.,
xarm-python-sdk,piper-sdk) - URDF/xacro — A robot description file (only needed if you want motion planning)
- Connection info — IP address, CAN port, serial device, etc.
Step 1: Create the Adapter
Create a new directory for your arm underdimos/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
_registry.py manifest next to it:
Key implementation notes
-
Unsupported features — Return
Nonefor reads andFalsefor 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
TheAdapterRegistry in dimos/hardware/manipulators/registry.py discovers adapters from _registry.py manifests at import time:
- It iterates over all subpackages under
dimos/hardware/manipulators/ - For each subpackage, it loads
<subpackage>._registryand records eachADAPTER_FACTORIESentry (name →"module:attr"import path) - Your adapter module is imported only when
create("yourarm")is first called
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 underdimos/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
Createdimos/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 ownblueprints.py.
4a. Add your URDF
Prefer an upstream Robot Description Source and create aRobotDescriptionSource 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:
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 yourdimos/robot/yourarm/blueprints.py alongside the coordinator blueprint:
skip
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 portableRobotModel 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
Step 5: Register Blueprints
The blueprint registry indimos/robot/all_blueprints.py is auto-generated by scanning the codebase for blueprint declarations. After adding your blueprints:
- Run the generation test to update the registry:
- 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 usingunittest.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(declaresADAPTER_FACTORIES) -
dimos/robot/yourarm/__init__.py -
dimos/robot/yourarm/blueprints.py(coordinator + planning blueprints)
-
pyproject.toml— Add vendor SDK to optional dependencies (if applicable)
-
adapter_registry.available()includes"yourarm" -
pytest dimos/robot/test_all_blueprints_generation.pypasses (regeneratesall_blueprints.py) -
dimos run coordinator-yourarmstarts successfully
