Damiao Omnidirectional AGV Base — DBUS Protocol & Teleop Guide
Complete guide to connecting, commanding, and integrating the Damiao 4-wheel omnidirectional AGV base using the DBUS serial protocol, the Python dami_agent bridge, and the Fearless Platform.
1. Overview
The Damiao AGV (达妙 AGV) is a four-wheel omnidirectional mobile base with independent steering and drive on all four wheels (4WS4WD configuration). Its omnidirectional kinematics — using Mecanum or omni wheels — allow the base to move laterally, diagonally, and rotate in place without any turning radius constraint. This makes it well-suited for manipulation tasks in cluttered environments, mobile manipulation data collection, and whole-body teleoperation setups where precise base positioning is required.
The AGV's open-source firmware communicates via its main controller's UART5 interface (pin PD2, RX), accepting RC receiver-style DBUS/SBUS control frames at 100000 baud with 8 data bits, even parity, and 2 stop bits (8E2). The dami_agent.py bridge script connects a PC via USB-to-TTL adapter to this UART, translates high-level platform commands (move, stop, goal) into properly encoded 18-byte DBUS frames, and forwards them at a configurable control rate — all without modifying the AGV firmware.
The agent simultaneously maintains a WebSocket connection to the Fearless Platform, enabling real-time teleoperation from the browser UI, telemetry reporting, command timeout safety stops, and episode recording for mobile manipulation datasets.
2. Full Specifications
| Property | Value |
|---|---|
| Vendor | Damiao (达妙) |
| Drive configuration | 4-wheel omnidirectional (4WS4WD) |
| Wheel type | Mecanum or omni wheels |
| Control interface | UART5 on main controller (pin PD2, RX) |
| Baud rate | 100000 |
| Serial framing | 8 data bits, even parity, 2 stop bits (8E2) |
| Protocol | DBUS/SBUS-style RC frame (18 bytes) |
| Channels | ch0–ch3 (11-bit, 0–2047, center 1024) + ch4 (center, unused for AGV) + s0/s1 switches |
| Channel → velocity mapping | Vx = sign(ch3 − 1024), Vy = sign(ch2 − 1024), Vw = sign(−(ch0 − 1024)) |
| Amplitude parameter | --amp default 660, range 250–900 (offset from 1024) |
| Control rate | 30 Hz (configurable with --control-hz) |
| Telemetry rate | 8 Hz (configurable with --telemetry-hz) |
| Command timeout | 300 ms (configurable with --cmd-timeout-ms); AGV stops automatically |
| Platform device type | mobile_base |
| Platform capabilities | ["mobile_base", "agv", "telemetry"] |
| Agent module | dami_agent |
| Backend WebSocket URL | ws://localhost:8000 (default) |
| Mock mode | Yes (--mock); serial port not opened, full WebSocket pipeline active |
3. Quick Start
-
Install dependencies.
pip install pyserial websockets
-
Connect the USB-to-TTL adapter. Wire the adapter's TX pin to the AGV main controller's UART5_RX (PD2). Connect GND. Do NOT connect the adapter's RX or 5V to the AGV. Confirm the port appears:
ls /dev/ttyUSB* # Linux -
Create a teleop session on the platform. Open platform.roboticscenter.ai, navigate to the Teleop section, and create a new session. Copy the session ID (format:
RC-XXXX-XXXX). -
Launch the AGV agent.
python3 dami_agent.py \ --session RC-XXXX-XXXX \ --serial-port /dev/ttyUSB0 \ --backend ws://localhost:8000
The agent connects to the platform, registers the AGV node, and begins the control loop at 30 Hz. -
Drive from the browser. Open the session URL in your browser. Use the directional controls to send
movecommands. The AGV responds within one control-loop tick (33 ms at default 30 Hz).
python3 dami_agent.py --session RC-XXXX-XXXX --mock to verify the full command pipeline end-to-end without an AGV or serial connection.
4. DBUS Protocol
The Damiao AGV firmware expects standard DBUS/SBUS RC receiver frames on its UART5 input. The agent encodes velocity commands as RC joystick values in this format without any firmware modification required.
18-byte frame layout
| Bytes | Content | Notes |
|---|---|---|
| 0–5 | ch0, ch1, ch2, ch3 packed (11-bit each) | Little-endian bit-packing; 44 bits total occupying 48 bits |
| 5 (high nibble) | s0 (bits [5:4]), s1 (bits [7:6]) | Switch states; fixed at 1/1 (center) for AGV use |
| 6–15 | Mouse and keyboard fields | Zeroed for AGV; not parsed by AGV firmware |
| 16–17 | ch4 (11-bit) | Fixed at center (1024); not used for AGV motion |
The 11-bit channels are packed consecutively starting from bit 0 of byte 0:
# bit-packing of ch0..ch3 into bytes 0..5 b[0] = ch0 & 0xFF b[1] = (ch0 >> 8 & 0x07) | ((ch1 & 0x1F) << 3) b[2] = (ch1 >> 5 & 0x3F) | ((ch2 & 0x03) << 6) b[3] = ch2 >> 2 & 0xFF b[4] = (ch2 >> 10 & 0x01) | ((ch3 & 0x7F) << 1) b[5] = ch3 >> 7 & 0x0F # upper nibble: switch bits # ch4 in bytes 16..17 b[16] = ch4 & 0xFF b[17] = ch4 >> 8 & 0x07
Channel-to-velocity mapping
The AGV firmware's Control_Task decodes channels as follows. The agent must invert this mapping when encoding velocity commands:
| Channel | Firmware interprets as | Agent encodes from | Formula |
|---|---|---|---|
| ch0 | Yaw (rotation) | vw (angular velocity) | ch0 = 1024 + (-vw) × amp |
| ch1 | Unused | — | Fixed at 1024 |
| ch2 | Lateral (Y) | vy | ch2 = 1024 + vy × amp |
| ch3 | Forward (X) | vx | ch3 = 1024 + vx × amp |
Channel values are clamped to [0, 2047]. At center (1024 on all channels), the AGV holds position. The amp parameter (default 660) sets the magnitude of deflection from center — higher values produce faster motion. The agent accepts vx, vy, and vw as discrete sign values (−1, 0, +1) from the platform command messages.
pyserial.PARITY_EVEN and STOPBITS_TWO.
5. SDK / Python Usage
The dami_agent.py script is the primary interface between the Fearless Platform and the AGV hardware. It runs two async loops — a control loop and a telemetry loop — inside a single WebSocket session.
Agent CLI reference
| Flag | Default | Description |
|---|---|---|
--backend | ws://localhost:8000 | Platform WebSocket base URL |
--session | (required) | Teleop session ID |
--node-id | hostname | AGV node identifier in the session |
--serial-port | /dev/ttyUSB0 | USB-to-TTL adapter device path |
--telemetry-hz | 8.0 | Frequency to send telemetry to platform |
--control-hz | 30.0 | DBUS frame output rate (Hz) |
--cmd-timeout-ms | 300 | Auto-stop if no command received within this window |
--amp | 660 | Joystick amplitude (500–760 recommended) |
--invert-x | false | Flip forward/backward direction |
--invert-y | false | Flip left/right direction |
--invert-z | false | Flip rotation direction |
--mock | false | Skip serial port; validate command pipeline only |
Sending commands programmatically
The agent receives platform commands as JSON messages over its WebSocket. You can send commands from any platform client or script using the session WebSocket. Supported command types:
# Move forward along X axis {"type": "move", "axis": "x", "dir": 1} # Move laterally right (Y axis) {"type": "move", "axis": "y", "dir": 1} # Rotate counter-clockwise (Z axis) {"type": "move", "axis": "z", "dir": -1} # Stop all motion immediately {"type": "stop"} # Goal-based delta movement (x, y, yaw) {"type": "goal", "delta": {"x": 0.5, "y": 0.0, "yaw": 0.0}}
The goal command degrades the continuous delta to a discrete direction sign, matching the agent's discrete velocity model. For smooth continuous control, send repeated move commands at the desired rate and stop when the target is reached.
Shutdown safety
On agent exit (Ctrl+C or WebSocket close), the agent sends three center-position DBUS frames (all channels at 1024) separated by 30 ms delays before closing the serial port. This ensures the AGV firmware receives the stop signal even if the final command was a movement.
6. Platform Integration
The agent connects to /api/teleop/ws on the Fearless Platform backend and registers the AGV as a robot node. The WebSocket handshake:
{
"role": "robot",
"session_id": "RC-XXXX-XXXX",
"member_id": "dami-agv-node",
"node_id": "dami-agv-node",
"node_role": "robot",
"device_type": "mobile_base",
"capabilities": ["mobile_base", "agv", "telemetry"],
"source_host": "hostname"
}
Telemetry format
The agent sends telemetry at the configured --telemetry-hz rate. The joints field reuses the generic telemetry structure to carry the discrete velocity command state:
{
"type": "telemetry",
"member_id": "dami-agv-node",
"joints": {
"vx_cmd": 1.0, // current commanded X velocity sign
"vy_cmd": 0.0,
"vw_cmd": 0.0
},
"feedback": {
"connected": true,
"robot_model": "dami_agv_4ws4wd",
"transport": "serial_dbus",
"serial_port": "/dev/ttyUSB0",
"amp": 660,
"cmd_timeout_ms": 300
},
"ts": 1743680400123
}
Latency ping/pong
The platform sends periodic ping_latency messages. The agent responds with pong_latency immediately, preserving the original ts and id fields. The platform uses these round trips to display round-trip latency in the teleop session header.
Episode recording
When a recording session is active on the platform, all telemetry frames from the AGV node are archived alongside other session nodes (arm, cameras, tactile sensors). The resulting JSONL archive captures the complete mobile manipulation trajectory, including base velocity commands and timing, enabling full replay and training data extraction.
{"type": "ping"} message to keep the connection alive. If the connection closes, the control loop continues running and the AGV will auto-stop after cmd_timeout_ms — it will not run uncontrolled.
7. Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| pyserial.SerialException on open | Port busy, wrong path, or permission denied | Check ls /dev/ttyUSB*. Add user to dialout group: sudo usermod -aG dialout $USER then log out and back in. Pass the correct port with --serial-port /dev/ttyUSBX. |
| AGV does not move on command | Wrong serial framing (8N1 instead of 8E2), wrong baud rate, or TX/RX wired incorrectly | Confirm pyserial is opened with PARITY_EVEN, STOPBITS_TWO, baudrate=100000. Verify the USB-to-TTL TX pin goes to AGV UART5_RX (PD2). Do NOT connect RX-to-TX in loopback. |
| AGV moves briefly then stops | Command timeout triggering (default 300 ms) | The platform is not sending commands fast enough. Check network latency. Increase --cmd-timeout-ms to 500–1000 ms for high-latency links. Ensure the browser teleop panel is actively sending commands. |
| AGV moves in wrong direction | Axis inversion needed for physical installation orientation | Use --invert-x, --invert-y, or --invert-z flags to correct orientation without firmware changes. Test each axis individually first. |
| Agent connects but platform shows no telemetry | Session ID mismatch or WebSocket handshake not completing | Confirm the session ID passed to --session is active in the platform. Check the agent output for "ready" after the handshake. If it prints "handshake failed", the session may have expired — create a new one. |
python3 dami_agent.py --mock --session test-session and your serial port path. Include the USB-to-TTL adapter model.