Skip to content

Commit f32c38b

Browse files
authored
Merge pull request #831 from dimensionalOS/generalized_manipulator_driver
Generalized manipulator driver Former-commit-id: b6d3878
2 parents 1ecee66 + 9abe83b commit f32c38b

99 files changed

Lines changed: 16081 additions & 572 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ __pycache__/
1212
*venv*/
1313
.venv*/
1414
.ssh/
15+
.direnv/
1516

1617
# Ignore python tooling dirs
1718
*.egg-info/

dimos/agents/temp/webcam_agent.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
from dimos.agents.cli.human import HumanInput
2929
from dimos.agents.spec import Model, Provider
3030
from dimos.core import LCMTransport, Module, rpc, start
31-
from dimos.hardware.camera import zed
32-
from dimos.hardware.camera.module import CameraModule
33-
from dimos.hardware.camera.webcam import Webcam
31+
from dimos.hardware.sensors.camera import zed
32+
from dimos.hardware.sensors.camera.module import CameraModule
33+
from dimos.hardware.sensors.camera.webcam import Webcam
3434
from dimos.msgs.geometry_msgs import Quaternion, Transform, Vector3
3535
from dimos.msgs.sensor_msgs import CameraInfo, Image
3636
from dimos.protocol.skill.test_coordinator import SkillContainerTest
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2025 Dimensional Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from .end_effector import EndEffector
16+
17+
__all__ = ["EndEffector"]
File renamed without changes.
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Manipulator Drivers
2+
3+
Component-based framework for integrating robotic manipulators into DIMOS.
4+
5+
## Quick Start: Adding a New Manipulator
6+
7+
Adding support for a new robot arm requires **two files**:
8+
1. **SDK Wrapper** (~200-500 lines) - Translates vendor SDK to standard interface
9+
2. **Driver** (~30-50 lines) - Assembles components and configuration
10+
11+
## Directory Structure
12+
13+
```
14+
manipulators/
15+
├── base/ # Framework (don't modify)
16+
│ ├── sdk_interface.py # BaseManipulatorSDK abstract class
17+
│ ├── driver.py # BaseManipulatorDriver base class
18+
│ ├── spec.py # ManipulatorCapabilities dataclass
19+
│ └── components/ # Reusable standard components
20+
├── xarm/ # XArm implementation (reference)
21+
└── piper/ # Piper implementation (reference)
22+
```
23+
24+
## Hardware Requirements
25+
26+
Your manipulator **must** support:
27+
28+
| Requirement | Description |
29+
|-------------|-------------|
30+
| Joint Position Feedback | Read current joint angles |
31+
| Joint Position Control | Command target joint positions |
32+
| Servo Enable/Disable | Enable and disable motor power |
33+
| Error Reporting | Report error codes/states |
34+
| Emergency Stop | Hardware or software e-stop |
35+
36+
**Optional:** velocity control, torque control, cartesian control, F/T sensor, gripper
37+
38+
## Step 1: Implement SDK Wrapper
39+
40+
Create `your_arm/your_arm_wrapper.py` implementing `BaseManipulatorSDK`:
41+
42+
```python
43+
from dimos.hardware.manipulators.base.sdk_interface import BaseManipulatorSDK, ManipulatorInfo
44+
45+
class YourArmSDKWrapper(BaseManipulatorSDK):
46+
def __init__(self):
47+
self._sdk = None
48+
49+
def connect(self, config: dict) -> bool:
50+
self._sdk = YourNativeSDK(config['ip'])
51+
return self._sdk.connect()
52+
53+
def get_joint_positions(self) -> list[float]:
54+
"""Return positions in RADIANS."""
55+
degrees = self._sdk.get_angles()
56+
return [math.radians(d) for d in degrees]
57+
58+
def set_joint_positions(self, positions: list[float],
59+
velocity: float, acceleration: float) -> bool:
60+
return self._sdk.move_joints(positions, velocity)
61+
62+
def enable_servos(self) -> bool:
63+
return self._sdk.motor_on()
64+
65+
# ... implement remaining required methods (see sdk_interface.py)
66+
```
67+
68+
### Unit Conventions
69+
70+
**All SDK wrappers must use these standard units:**
71+
72+
| Quantity | Unit |
73+
|----------|------|
74+
| Joint positions | radians |
75+
| Joint velocities | rad/s |
76+
| Joint accelerations | rad/s^2 |
77+
| Joint torques | Nm |
78+
| Cartesian positions | meters |
79+
| Forces | N |
80+
81+
## Step 2: Create Driver Assembly
82+
83+
Create `your_arm/your_arm_driver.py`:
84+
85+
```python
86+
from dimos.hardware.manipulators.base.driver import BaseManipulatorDriver
87+
from dimos.hardware.manipulators.base.spec import ManipulatorCapabilities
88+
from dimos.hardware.manipulators.base.components import (
89+
StandardMotionComponent,
90+
StandardServoComponent,
91+
StandardStatusComponent,
92+
)
93+
from .your_arm_wrapper import YourArmSDKWrapper
94+
95+
class YourArmDriver(BaseManipulatorDriver):
96+
def __init__(self, config: dict):
97+
sdk = YourArmSDKWrapper()
98+
99+
capabilities = ManipulatorCapabilities(
100+
dof=6,
101+
has_gripper=False,
102+
has_force_torque=False,
103+
joint_limits_lower=[-3.14, -2.09, -3.14, -3.14, -3.14, -3.14],
104+
joint_limits_upper=[3.14, 2.09, 3.14, 3.14, 3.14, 3.14],
105+
max_joint_velocity=[2.0] * 6,
106+
max_joint_acceleration=[4.0] * 6,
107+
)
108+
109+
components = [
110+
StandardMotionComponent(),
111+
StandardServoComponent(),
112+
StandardStatusComponent(),
113+
]
114+
115+
super().__init__(sdk, components, config, capabilities)
116+
```
117+
118+
## Component API Decorator
119+
120+
Use `@component_api` to expose methods as RPC endpoints:
121+
122+
```python
123+
from dimos.hardware.manipulators.base.components import component_api
124+
125+
class StandardMotionComponent:
126+
@component_api
127+
def move_joint(self, positions: list[float], velocity: float = 1.0):
128+
"""Auto-exposed as driver.move_joint()"""
129+
...
130+
```
131+
132+
## Threading Architecture
133+
134+
The driver runs **2 threads**:
135+
1. **Control Loop (100Hz)** - Processes commands, reads joint state, publishes feedback
136+
2. **Monitor Loop (10Hz)** - Reads robot state, errors, optional sensors
137+
138+
```
139+
RPC Call → Command Queue → Control Loop → SDK → Hardware
140+
141+
SharedState → LCM Publisher
142+
```
143+
144+
## Testing Your Driver
145+
146+
```python
147+
driver = YourArmDriver({"ip": "192.168.1.100"})
148+
driver.start()
149+
driver.enable_servo()
150+
driver.move_joint([0, 0, 0, 0, 0, 0], velocity=0.5)
151+
state = driver.get_joint_state()
152+
driver.stop()
153+
```
154+
155+
## Common Issues
156+
157+
| Issue | Solution |
158+
|-------|----------|
159+
| Unit mismatch | Verify wrapper converts to radians/meters |
160+
| Commands ignored | Ensure servos are enabled before commanding |
161+
| Velocity not working | Some arms need mode switch via `set_control_mode()` |
162+
163+
## Architecture Details
164+
165+
For complete architecture documentation including full SDK interface specification,
166+
component details, and testing strategies, see:
167+
168+
**[component_based_architecture.md](base/component_based_architecture.md)**
169+
170+
## Reference Implementations
171+
172+
- **XArm**: [xarm/xarm_wrapper.py](xarm/xarm_wrapper.py) - Full-featured wrapper
173+
- **Piper**: [piper/piper_wrapper.py](piper/piper_wrapper.py) - Shows velocity workaround
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright 2025 Dimensional Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Manipulator Hardware Drivers
17+
18+
Drivers for various robotic manipulator arms.
19+
"""
20+
21+
__all__ = []
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2025 Dimensional Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Base framework for generalized manipulator drivers.
16+
17+
This package provides the foundation for building manipulator drivers
18+
that work with any robotic arm (XArm, Piper, UR, Franka, etc.).
19+
"""
20+
21+
from .components import StandardMotionComponent, StandardServoComponent, StandardStatusComponent
22+
from .driver import BaseManipulatorDriver, Command
23+
from .sdk_interface import BaseManipulatorSDK, ManipulatorInfo
24+
from .spec import ManipulatorCapabilities, ManipulatorDriverSpec, RobotState
25+
from .utils import SharedState
26+
27+
__all__ = [
28+
# Driver
29+
"BaseManipulatorDriver",
30+
# SDK Interface
31+
"BaseManipulatorSDK",
32+
"Command",
33+
"ManipulatorCapabilities",
34+
# Spec
35+
"ManipulatorDriverSpec",
36+
"ManipulatorInfo",
37+
"RobotState",
38+
# Utils
39+
"SharedState",
40+
# Components
41+
"StandardMotionComponent",
42+
"StandardServoComponent",
43+
"StandardStatusComponent",
44+
]

0 commit comments

Comments
 (0)