Skip to main content

AI Teammate Robot Bridge

PyPI Python

Connects ROS2 robots to the AI Teammate platform via WebSocket + MQTT.

Features

  • rclpy native — Direct DDS participation, no rosbridge required
  • Dual telemetry — MQTT (primary, 1Hz) + WebSocket (fallback)
  • Auto-detection — cmd_vel, camera, IMU, LiDAR topics discovered at startup
  • Camera streaming — RealSense / USB camera auto-detect, on-demand JPEG streaming
  • SLAM mapping — Trajectory collection (5Hz, 5cm spacing) + WiFi fingerprint auto-collection
  • WiFi fingerprinting — Save/localize/list fingerprints with mobile AP blocklist
  • Plugin architecture — Optional AMR Skills for 54+ robot commands
  • Auto-reconnect — WebSocket + MQTT reconnection with exponential backoff
  • Simulator mode — Works without ROS2 for development/testing
  • systemd ready — Auto-restart, boot start with linger

Quick Install (Recommended)

curl -sL "https://ai-teammate.net/api/b2b/install/MY-ROBOT-01?key=YOUR_API_KEY" | bash

This single command will:

  1. Detect ROS2 and auto-configure topics
  2. Install bridge from PyPI + AMR skills (pre-built binary)
  3. Configure MQTT telemetry
  4. Register device with cloud gateway
  5. Create systemd service (auto-restart + boot start)

Get your API key from Control Tower > Devices > + button.

Step-by-Step Setup Guide

Prerequisites

  • Robot PC: Ubuntu 22.04 + ROS2 Humble (or Jazzy)
  • Robot hardware bringup running (motors, LiDAR, IMU topics publishing)
  • Internet connection (WSS + MQTT to ai-teammate.net)

Step 1. Get API Key

  1. Open Control Tower
  2. Go to Devices tab > click + button
  3. Enter a Device ID (e.g. my-robot-01) and copy the generated API Key (dk_...)

Step 2. Run Install Script

SSH into the robot and run:

curl -sL "https://ai-teammate.net/api/b2b/install/my-robot-01?key=dk_YOUR_API_KEY" | bash

The script automatically:

  • Detects ROS2 and discovers topics (cmd_vel, odom, imu, lidar, camera)
  • Installs bridge from PyPI + optional AMR skills binary
  • Generates .env and sensors.yaml with auto-detected topic names
  • Configures MQTT telemetry
  • Registers device with cloud gateway
  • Sets up sudoers for shutdown/reboot (NOPASSWD)
  • Creates systemd service with boot-start enabled

For networks with a proxy:

curl -sL "..." | bash -s -- --proxy=http://proxy:8080

Step 3. Verify Installation

# Check service status
systemctl --user status ai-teammate-bridge

# Watch live logs
tail -f ~/ai-teammate-bridge/bridge.log

# Review detected config
cat ~/ai-teammate-bridge/.env
cat ~/ai-teammate-bridge/sensors.yaml

Step 4. Verify in Control Tower

  1. Open Control Tower > Devices tab
  2. Robot should show as connected with green indicator
  3. Click the robot > Health section should show IMU, odom, lidar as OK
  4. SLAM tab should display sensor data

Troubleshooting

Symptom Cause Fix
sensor: no data (never received) Topic name mismatch Run ros2 topic list to find actual names, update ~/ai-teammate-bridge/.env, then systemctl --user restart ai-teammate-bridge
SHUTDOWN/REBOOT not working Missing sudoers echo "$USER ALL=(ALL) NOPASSWD: /usr/sbin/shutdown, /usr/sbin/reboot" | sudo tee /etc/sudoers.d/ai-teammate-bridge && sudo chmod 440 /etc/sudoers.d/ai-teammate-bridge
Bridge not starting after reboot Linger not enabled loginctl enable-linger $USER
WS connection failed Firewall/proxy Test with curl https://ai-teammate.net/api/health, use --proxy flag if needed
ros2: command not found in service ROS2 env not sourced Ensure /etc/env.sh exists and sources ROS2 setup, or add source /opt/ros/humble/setup.bash to service ExecStart

Manual Topic Override

If auto-detection picks the wrong topic:

# Check what's actually publishing
ros2 topic list | grep -E 'odom|imu|scan|cmd_vel'

# Edit .env
vi ~/ai-teammate-bridge/.env
# Example: ODOM_TOPIC=/base_controller/odom

# Restart
systemctl --user restart ai-teammate-bridge

Common non-standard topic names:

Sensor Standard Variants
Odometry /odom /base_controller/odom, /odometry/filtered, /wheel/odometry
IMU /imu/data /imu/data_raw, /camera/imu, /front_lidar/imu
LiDAR /scan /scan_filtered, /front_lidar/scan
cmd_vel /cmd_vel /base_controller/cmd_vel_unstamped, /cmd_vel_mux/input/teleop

Manual Install

pip3 install ai-teammate-ros2-bridge

Configuration

mkdir -p ~/ai-teammate-bridge && cd ~/ai-teammate-bridge

ai-teammate-bridge init
# → Creates .env with interactive prompts

# Or manually:
cat > .env << EOF
DEVICE_ID=my-robot-01
GATEWAY_URL=wss://ai-teammate.net/gw
API_KEY=dk_YOUR_API_KEY
CONNECTION_MODE=rclpy
STATUS_REPORT_INTERVAL=1.0
CMD_VEL_TOPIC=/cmd_vel
IMU_TOPIC=/imu/data
ODOM_TOPIC=/odom
SCAN_TOPIC=/scan
MQTT_BROKER=ai-teammate.net
MQTT_PORT=1883
# MQTT credentials: provisioned by the one-line installer
# (or ask ops). Never commit real credentials to docs.
MQTT_USER=<ask-ops>
MQTT_PASS=<ask-ops>
EOF

Run

# Source ROS2 environment first
source /opt/ros/humble/setup.bash

ai-teammate-bridge

Environment Variables

Variable Default Description
DEVICE_ID required Unique device identifier
GATEWAY_URL required Device Gateway WebSocket URL
API_KEY required Device API key (dk_xxx)
CONNECTION_MODE rclpy rclpy for ROS2, simulator for testing
STATUS_REPORT_INTERVAL 1.0 Sensor update interval (seconds)
CMD_VEL_TOPIC /cmd_vel ROS2 velocity command topic
IMU_TOPIC /imu/data IMU subscription topic
ODOM_TOPIC /odom Odometry subscription topic
SCAN_TOPIC /scan LiDAR scan topic
MQTT_BROKER (empty=disabled) MQTT broker hostname
MQTT_PORT 1883 MQTT broker port
MQTT_USER robot_bridge MQTT username
MQTT_PASS (empty) MQTT password
MQTT_TRANSPORT tcp tcp or websockets
MQTT_TLS (off) 1 to wrap the connection in TLS
MQTT_WS_PATH /mqtt WebSocket path (only for websockets)
COORD_ENABLED (off) 1/true/yes/on to obey fleet coordination hold leases
COORD_DEGRADED_SPEED 0.15 Forward cap (m/s) while no valid lease is held
FLEET_TENANT (empty) Tenant namespace for the coordination topic — not the same as FLEET_ID, see below
FLEET_ID (from gateway) Fleet id for the broadcast e-stop topic — not the same as FLEET_TENANT, see below

Fleet coordination (교차로 hold lease)

Off by default. When COORD_ENABLED is on, the bridge subscribes to fleet/<FLEET_TENANT>/coordination/hold/<DEVICE_ID> and turns the leases it receives into one forward-speed cap, applied both in-process and in the Nav2 cmd_vel relay.

This is traffic management, not safety — the edge hard-stop (safety.py) runs after it and is never weakened by it. Three states: a live clear lease uncaps, a live hold lease stops the robot, and no valid lease means COORD_DEGRADED_SPEED — "if you don't know, go slow". That fallback is deliberately not a full stop, so a dead cloud cannot brick the fleet; values of 0 or below are rejected at startup (logged, replaced by the default).

FLEET_TENANT must match what the coordination service publishes under, or the robot never receives its leases and stays capped at the degraded speed.

FLEET_TENANT and FLEET_ID are different identifiers. They look alike, they both namespace a fleet/... topic, and setting one to the other's value fails in a way that is hard to trace:

topic carries if wrong
FLEET_TENANT fleet/<tenant>/coordination/hold/<DEVICE_ID> traffic (hold/clear leases) robot silently never receives a lease and just crawls at COORD_DEGRADED_SPEEDno error anywhere
FLEET_ID fleet/<id>/broadcast/stop emergency stop robot does not receive broadcast e-stop

They also default differently. FLEET_ID falls back to the gateway-supplied project_id/fleet_id (ws_client._subscribe_fleet_topic), so it usually works without being set — which is exactly why someone reaches for that same value when filling in FLEET_TENANT. FLEET_TENANT has no fallback: unset means the legacy un-namespaced topic, and any non-matching value means silence. Coordination silence is never read as permission (that is the point of the degraded fallback), so the symptom is a fleet that mysteriously crawls, not one that errors.

Release ordering — bridge before skills. Robots update the skills package by extracting a .so tarball straight into site-packages, which bypasses pip entirely, so the bridge floor declared in ai-teammate-ros2-skills' pyproject.toml is not enforced on the robot. A skills build that imports a symbol this bridge lacks fails at import, and the failure disables every skill command, not just the new one. So: publish and roll out the bridge first, confirm robots picked it up, and only then publish skills. The auto-updater enforces the same order per cycle (_defer_skills_update), and skills carries a fallback for the coordination import specifically, but neither removes the need to ship in this order.

MQTT over WebSocket (broker behind a CDN)

Raw TCP MQTT cannot be proxied by a CDN, so a broker that sits behind one is only reachable over wss on 443. Point the bridge at it like this — the robot then needs a single outbound 443, which most site firewalls already allow:

MQTT_BROKER=mqtt.example.com
MQTT_PORT=443
MQTT_TRANSPORT=websockets
MQTT_TLS=1

The default stays plain TCP on 1883, so bridges already in the field keep working until their env is changed.

Architecture

Robot                              Cloud (ai-teammate.net)
┌──────────────────────┐          ┌─────────────────────┐
│  ai-teammate-bridge  │          │  Device Gateway      │
��  (CLI entry point)   │          │  (port 8003)         │
│                      │          │                      │
│  ���────────────────┐  │  WSS    │  /ws/{device_id}     │
│  │  ws_client.py  │──┼────────▶│    commands ◀──────  │──▶ Control Tower
│  │  DeviceBridge   │◀─┼────────│    responses          │
│  └────────────────┘  │          │                      │
│                      │  MQTT   │  Mosquitto (1883)    │
│  ┌────────────────┐  │────────▶│    telemetry          │
│  │  MQTT publisher │  │         │    position           │
│  └────────────────┘  │         │    health              │
│                      │          └─────────────────────┘
│  ┌────────────────┐  │
│  │  ros2_node.py  │  │  ROS2 DDS (no rosbridge)
│  │  ├─ cmd_vel pub│  │
│  │  ├─ IMU sub    │  │
│  │  ├─ odom sub   │  │
│  │  ├─ scan sub   │  │
│  │  ├─ camera sub │  │  on-demand (auto-stop 30s)
│  │  └─ TF listener│  │  map→base_footprint (5Hz)
│  └────────────────┘  │
│                      │
│  ┌────────────────┐  │
│  │  Skills (opt)  │  │  ai-teammate-ros2-skills
│  │  54+ commands  │  │  (pre-built .so binary)
│  └────────────────┘  │
└──────────────────────┘

Communication Channels

Channel Data Direction Frequency
WebSocket Commands, responses Bidirectional On-demand
WebSocket Camera frames Robot → Cloud 2-5 FPS (on-demand)
WebSocket SLAM map + trajectory Robot → Cloud Every 5s during mapping
MQTT Telemetry (IMU, battery) Robot → Cloud 1 Hz
MQTT Position (x, y, theta) Robot → Cloud 1 Hz (retained)
MQTT Health alerts Robot → Cloud On-event (retained)

Package Structure

ai_teammate_ros2_bridge/
├── cli.py            # Entry point: ai-teammate-bridge command
├── config.py         # Shared state (_ros2_cache), env loading, MQTT config
├── ws_client.py      # DeviceBridge class, WS + MQTT event loop, health checks
├── device_bridge.py  # Command handlers (SLAM, camera, nav, fingerprint, etc.)
├── ros2_node.py      # rclpy spin thread, ROS2 subscriptions, TF listener
├── sensors.py        # Sensor data aggregation, battery fallback
├── sensors_config.py # Hardware abstraction (sensors.yaml)
├── lan_server.py     # LAN WebSocket server for local clients
└── utils.py          # Input sanitization helpers

Systemd Service

The install script creates a systemd service automatically. For user-level services, the unit uses After=basic.target (not network-online.target, which is unavailable in user scope).

# System-level (if sudo available)
sudo systemctl status ai-teammate-bridge
sudo systemctl restart ai-teammate-bridge
sudo journalctl -u ai-teammate-bridge -f

# User-level (no sudo, with loginctl enable-linger)
systemctl --user status ai-teammate-bridge
systemctl --user restart ai-teammate-bridge

Command Dispatch

Commands from the cloud are dispatched in two tiers:

  1. Bridge-first commands — Camera commands (CAPTURE_IMAGE, CAMERA_CAPTURE, START_CAMERA_STREAM, STOP_CAMERA_STREAM) always go to device_bridge.py first because they require access to the shared config._ros2_cache.
  2. Skills-first — All other commands try the optional ai-teammate-ros2-skills plugin first. If the plugin returns unknown_command, the bridge falls back to device_bridge.py.

SLAM Commands

Command Description
SLAM_START Launch slam_toolbox, begin trajectory + WiFi fingerprint collection
SLAM_STOP Stop SLAM, return trajectory/fingerprint counts
SLAM_SAVE Save map, auto-link fingerprints to nearby locations, detect mobile APs

During SLAM mapping, the bridge automatically:

  • Collects trajectory points at 5Hz with 5cm minimum spacing (via TF listener)
  • Scans WiFi fingerprints every 5s with 50cm minimum displacement
  • On SLAM_SAVE: links fingerprints to saved locations within 1.5m as "predicted" source
  • Detects mobile APs (same BSSID at distant locations) and adds them to wifi_blocklist.json

WiFi Fingerprint Commands

Command Description
SAVE_FINGERPRINT Save WiFi scan at current pose. source: verified (default) or predicted
LIST_FINGERPRINTS List all saved fingerprints with pose and AP count
LOCALIZE_FINGERPRINT Match current WiFi scan against saved fingerprints (filters blocked BSSIDs)

Location Commands

Command Description
SAVE_LOCATION Save named location. Supports explicit x, y params (map pin) or uses current robot pose

Camera (On-Demand)

Camera subscriptions are created on-demand and auto-stop after 30s of inactivity to save CPU (~10% reduction). Camera health is excluded from the all_ok flag and alert system — it only reports status when actively streaming.

Capture flow: start_camera() is always called first, then a 10s wait for a fresh frame before capturing.

Supported Platforms

Platform Architecture Python ROS2
Ubuntu 20.04+ x86_64 3.10, 3.12 Humble, Jazzy
Ubuntu 20.04+ aarch64 (Jetson, RPi4+) 3.10, 3.12 Humble, Jazzy
Any Linux x86_64/aarch64 3.10+ Simulator mode (no ROS2)

Requirements

  • Python 3.10+
  • ROS2 Humble or Jazzy (optional — simulator mode works without)
  • Internet access to ai-teammate.net (WSS + MQTT)

Changelog (v2.6.14 — v2.6.24)

v2.6.24

  • feat(radio): Mobile AP detection — same BSSID seen at distant locations during SLAM is auto-blocklisted in wifi_blocklist.json
  • feat(place): SAVE_LOCATION supports explicit x, y coordinates (map pin placement vs robot pose)
  • feat(place): SLAM_SAVE auto-links collected fingerprints to nearby saved locations as "predicted" source

v2.6.23

  • feat(slam): Auto-collect WiFi fingerprints during SLAM mapping (5s interval, 50cm min displacement)

v2.6.22

  • feat(slam): Collect trajectory during mapping via TF listener (5Hz, 5cm spacing), include in map upload metadata

v2.6.21

  • fix(slam): Send initial SLAM map on WS connect regardless of age; flag-based dedup instead of age-only check

v2.6.20

  • fix(stream): camera_stream and follow_state use shared config._ros2_cache instead of local cache

v2.6.19

  • fix(camera): Route camera commands (CAPTURE_IMAGE, CAMERA_CAPTURE, START_CAMERA_STREAM, STOP_CAMERA_STREAM) to bridge first, skip skills — camera needs shared config cache

v2.6.18

  • fix(camera): Always call start_camera() + 10s wait for fresh frame before capture

v2.6.17

  • fix(camera): Use shared config._ros2_cache for CAPTURE_IMAGE instead of local cache

v2.6.16

  • fix(camera): Check frame presence instead of _camera_active flag for capture readiness

v2.6.15

  • fix(camera): Improved capture_image — check start_camera result, 5s wait, better logging

v2.6.14

  • fix(health): Camera (on-demand) excluded from all_ok and health alerts — only reports when active

License

MIT - MobioLabs

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ai_teammate_ros2_bridge-2.6.61.tar.gz (183.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ai_teammate_ros2_bridge-2.6.61-py3-none-any.whl (119.7 kB view details)

Uploaded Python 3

File details

Details for the file ai_teammate_ros2_bridge-2.6.61.tar.gz.

File metadata

  • Download URL: ai_teammate_ros2_bridge-2.6.61.tar.gz
  • Upload date:
  • Size: 183.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for ai_teammate_ros2_bridge-2.6.61.tar.gz
Algorithm Hash digest
SHA256 528621093d0e0576f028555b06ef82815a640f51d63100ab8a4ce4cce8b0c56a
MD5 8f039094ae2bcad640c777ba542f403e
BLAKE2b-256 8cad00db5ab8e133450ced95624dce0619a7337d4f56cfa3673ea146c7f859e5

See more details on using hashes here.

File details

Details for the file ai_teammate_ros2_bridge-2.6.61-py3-none-any.whl.

File metadata

File hashes

Hashes for ai_teammate_ros2_bridge-2.6.61-py3-none-any.whl
Algorithm Hash digest
SHA256 7ca149ae6dfa9da9e05d141b9979a9cb9aae0e24aec18a19b180301f67b45f1c
MD5 bd57755f43511c037f58581b61a3cfde
BLAKE2b-256 d82be3d7f89ae41ef47b29d8adaf0191236beac9c8751abe3c357369e48e29fb

See more details on using hashes here.

Release history Release notifications | RSS feed

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page