Help us improve
Share bugs, ideas, or general feedback.
From dora-skills
Builds robot control pipelines with dora-rs: serial communication, kinematics (FK/IK), motion planning, and ROS2 bridge integration.
npx claudepluginhub zhanghandong/dora-skills --plugin dora-skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/dora-skills:domain-robotThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Building robot control applications with dora-rs
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Guides systematic root-cause debugging when tests fail, builds break, or unexpected errors occur. Provides a structured triage checklist to preserve evidence, localize, and fix issues instead of guessing.
Share bugs, ideas, or general feedback.
Building robot control applications with dora-rs
Dora supports robot control through:
- id: serial
build: pip install dora-rustypot
path: dora-rustypot
inputs:
command: controller/command
outputs:
- feedback
env:
SERIAL_PORT: /dev/ttyUSB0
BAUD_RATE: "115200"
- id: kinematics
build: pip install dora-pytorch-kinematics
path: dora-pytorch-kinematics
inputs:
joint_positions: arm/positions
target_pose: planner/target
outputs:
- end_effector_pose # FK output
- joint_targets # IK output
env:
URDF_PATH: robot.urdf
Note: dora-ros2-bridge is a Rust library in the main dora repository. It requires building from source. See ROS2 bridge examples.
# ROS2 bridge example (requires Rust build from dora repo)
- id: ros2-bridge
path: dora-ros2-bridge
inputs:
cmd_vel: controller/velocity
outputs:
- odom
- joint_states
env:
ROS_TOPICS_IN: /odom,/joint_states
ROS_TOPICS_OUT: /cmd_vel
nodes:
# Camera for visual feedback
- id: camera
build: pip install opencv-video-capture
path: opencv-video-capture
inputs:
tick: dora/timer/millis/33
outputs:
- image
env:
CAPTURE_PATH: "0"
# Object detection
- id: detector
build: pip install dora-yolo
path: dora-yolo
inputs:
image: camera/image
outputs:
- bbox
# Motion planner
- id: planner
path: ./planner.py
inputs:
bbox: detector/bbox
arm_state: arm/state
outputs:
- target_pose
# Kinematics
- id: kinematics
build: pip install dora-pytorch-kinematics
path: dora-pytorch-kinematics
inputs:
target_pose: planner/target_pose
outputs:
- joint_targets
env:
URDF_PATH: robot.urdf
# Arm controller
- id: arm
build: pip install dora-rustypot
path: dora-rustypot
inputs:
command: kinematics/joint_targets
outputs:
- state
env:
SERIAL_PORT: /dev/ttyUSB0
BAUD_RATE: "1000000"
# Visualization
- id: plot
build: pip install dora-rerun
path: dora-rerun
inputs:
image: camera/image
boxes2d: detector/bbox
nodes:
# Lidar sensor
- id: lidar
path: lidar_driver.py
inputs:
tick: dora/timer/millis/100
outputs:
- scan
# Localization
- id: localization
path: localization.py
inputs:
scan: lidar/scan
odom: chassis/odom
outputs:
- pose
# Path planner
- id: planner
path: path_planner.py
inputs:
pose: localization/pose
goal: user/goal
outputs:
- path
# Motion controller
- id: controller
path: motion_controller.py
inputs:
path: planner/path
pose: localization/pose
outputs:
- cmd_vel
# Chassis
- id: chassis
path: chassis_driver.py
inputs:
cmd_vel: controller/cmd_vel
outputs:
- odom
env:
SERIAL_PORT: /dev/ttyUSB0
# arm_controller.py
import numpy as np
from dora import Node
import serial
node = Node()
# Serial connection to servo controller
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate=115200,
timeout=0.1
)
def send_joint_command(joints):
"""Send joint positions to servo controller"""
# Convert to servo protocol
command = build_command(joints)
ser.write(command)
return ser.read(100) # Read feedback
for event in node:
if event["type"] == "INPUT" and event["id"] == "command":
joints = event["value"] # [j1, j2, j3, j4, j5, j6]
# Send to hardware
feedback = send_joint_command(joints)
# Parse and send feedback
state = parse_feedback(feedback)
node.send_output("state", state)
elif event["type"] == "STOP":
# Safe shutdown - move to home position
send_joint_command([0, 0, 0, 0, 0, 0])
break
ser.close()
# planner.py
import numpy as np
import pyarrow as pa
from dora import Node
node = Node()
current_state = None
target_bbox = None
for event in node:
if event["type"] == "INPUT":
if event["id"] == "arm_state":
current_state = event["value"]
elif event["id"] == "bbox":
# Get detection results
detections = event["value"].to_pylist()
if detections:
# Find target object
target = find_target(detections)
if target and current_state is not None:
# Compute target pose from bbox
target_pose = bbox_to_pose(target)
# Send to kinematics
node.send_output("target_pose", pa.array([target_pose]))
elif event["type"] == "STOP":
break
# kinematics_helper.py
import numpy as np
from pytorch_kinematics import chain
def load_robot(urdf_path):
"""Load robot from URDF"""
return chain.build_chain_from_urdf(urdf_path)
def forward_kinematics(robot, joint_positions):
"""Compute end-effector pose from joint positions"""
ret = robot.forward_kinematics(joint_positions)
return ret['end_effector'].get_matrix()
def inverse_kinematics(robot, target_pose, current_joints):
"""Compute joint positions for target pose"""
from pytorch_kinematics.ik import jacobian_ik
return jacobian_ik(
robot,
target_pose,
current_joints,
max_iterations=100
)
For real-time control, use appropriate timer frequencies:
# Position control (slow)
tick: dora/timer/millis/50 # 20 Hz
# Velocity control (medium)
tick: dora/timer/millis/20 # 50 Hz
# Torque control (fast)
tick: dora/timer/millis/5 # 200 Hz
| Node | Install | Purpose |
|---|---|---|
| dora-rustypot | pip install dora-rustypot | Serial servo control |
| dora-pytorch-kinematics | pip install dora-pytorch-kinematics | FK/IK |
| dora-ros2-bridge | Build from dora repo (Rust) | ROS2 integration |
| robot_descriptions_py | pip install robot_descriptions | URDF models |