# xMonitor — Robot Status Monitor A real-time web dashboard for monitoring a ROS 2 robot's motor, arm, waist, and power status. Data is streamed from the robot controller over WebSocket and displayed in a browser. --- ## Architecture ``` Robot Controller (ROS 2) Monitor Server (PC / Server) ┌──────────────────────────┐ ┌──────────────────────────┐ │ monitor_sender.py │ WebSocket │ main_monitor.py │ │ ─────────────────────── │─────────────▶│ (FastAPI + uvicorn) │ │ Subscribes: │ /ws/robot │ │ │ • /leg/status │ │ Broadcasts to browsers │ │ • /arm/status │ │ via /ws │ │ • /waist/status │ └────────────┬─────────────┘ │ • /power/battery/status │ │ WebSocket /ws │ • /leg/motor_status │ ▼ │ • /arm/motor_status │ ┌──────────────────────────┐ │ • /waist/motor_status │ │ Browser (any device) │ │ │ │ http://:8000 │ │ Sends JSON every 1 s │ └──────────────────────────┘ └──────────────────────────┘ ``` --- ## Requirements ### Monitor Server (PC / any host) | Package | Purpose | |---------|---------| | Python ≥ 3.9 | Runtime | | `fastapi` | Web framework | | `uvicorn` | ASGI server | | `websockets` | (used by sender; not needed on server) | ```bash pip install fastapi uvicorn ``` ### Robot Controller (ROS 2 node) | Package | Purpose | |---------|---------| | Python ≥ 3.9 | Runtime | | ROS 2 (Humble / Iron / …) | Middleware | | `bodyctrl_msgs` | Custom message package | | `websockets` | WebSocket client | | `rclpy` | ROS 2 Python client | ```bash pip install websockets ``` --- ## Monitor Server — `main_monitor.py` ### Start ```bash # Default: listens on all interfaces, port 8000 python main_monitor.py # Or with uvicorn directly (supports --reload for development) uvicorn main_monitor:app --host 0.0.0.0 --port 8000 ``` ### WebSocket endpoints | Endpoint | Direction | Description | |----------|-----------|-------------| | `GET /` | HTTP | Returns the web dashboard HTML | | `WS /ws` | Server → Browser | Pushes robot status to every connected browser | | `WS /ws/robot` | Robot → Server | Receives JSON from `monitor_sender.py` | ### Web GUI Open `http://:8000` in any browser. The dashboard shows: - **Connection status** indicator (green / red dot) - **Last update timestamp** - **Power panel** (two rows): - 主电池 (Master battery): voltage (V), current (A), SOC (%) - 副电池 (Secondary battery): voltage (V), current (A), SOC (%) - Cards turn **orange** (warn) or **red** (alert) when voltage / SOC drops low - **Motor status table** with columns: `ID | Pos (rad) | Speed | Current (A) | Temp (°C) | Motor Temp | MOS Temp | Error` - Groups: 左臂 11-14, 右臂 21-24, 腰部 31-33, 左腿 51-56, 右腿 61-66 - Temperature cells turn **yellow** when > 100 °C, **red** when > 120 °C - Rows with errors are highlighted red; error column shows `✓` when OK - Browser auto-reconnects every 3 s if the WebSocket drops --- ## Robot Sender — `monitor_sender.py` Run this on the robot controller where ROS 2 is running. ### Usage ```bash python3 monitor_sender.py --ip [--port ] ``` | Argument | Default | Description | |----------|---------|-------------| | `--ip` | `10.11.24.86` | IP address of the monitor server | | `--port` | `8000` | TCP port of the monitor server | ### Examples ```bash # Connect to server at 192.168.1.100 on default port 8000 python3 monitor_sender.py --ip 192.168.1.100 # Connect to server at 10.0.0.5 on port 9000 python3 monitor_sender.py --ip 10.0.0.5 --port 9000 ``` ### Subscribed ROS 2 topics | Topic | Message Type | Description | |-------|-------------|-------------| | `/leg/status` | `bodyctrl_msgs/MotorStatusMsg` | Leg motor status (pos / speed / current / temp / error) | | `/arm/status` | `bodyctrl_msgs/MotorStatusMsg` | Arm motor status | | `/waist/status` | `bodyctrl_msgs/MotorStatusMsg` | Waist motor status | | `/leg/motor_status` | `bodyctrl_msgs/MotorStatusMsg1` | Leg motor & MOS temperatures | | `/arm/motor_status` | `bodyctrl_msgs/MotorStatusMsg1` | Arm motor & MOS temperatures | | `/waist/motor_status` | `bodyctrl_msgs/MotorStatusMsg1` | Waist motor & MOS temperatures | | `/power/battery/status` | `bodyctrl_msgs/PowerBatteryStatus` | Battery voltages, currents, SOC | ### Behaviour - All topics are sampled once per second (1 Hz) via a ROS 2 timer, reducing network overhead. - Data from all topics is merged into a single JSON payload and sent over WebSocket. - Motor temperature (`motortemperature`) and MOS temperature (`mostemperature`) from `MotorStatusMsg1` are merged into the corresponding motor entry by `name` ID. - If the WebSocket connection to the server drops, the sender automatically retries every 3 s and clears the internal queue to avoid stale data. ### JSON payload format ```json { "timestamp": "2026-04-05T10:00:00+00:00", "statuses": [ { "name": 51, "pos": -0.2508, "speed": -0.0051, "current": -0.0488, "temperature": 30.0, "motor_temp": 28.5, "mos_temp": 31.2, "error": 0 } ], "power": { "voltage": 52.30, "current": -2.60, "power": 100.0, "little_voltage": 53.10, "little_current": -0.10, "little_power": 94.0 } } ``` --- ## Quick-start (end-to-end) 1. **On the monitor server** (PC on the same LAN): ```bash pip install fastapi uvicorn python main_monitor.py ``` 2. **On the robot controller** (source your ROS 2 workspace first): ```bash source /home/ubuntu/ros2ws/install/setup.bash python3 monitor_sender.py --ip ``` 3. **Open browser**: navigate to `http://:8000` --- ## File Overview | File | Description | |------|-------------| | `main_monitor.py` | FastAPI server — hosts the web GUI and relays data to browsers | | `monitor_sender.py` | ROS 2 node — collects robot data and streams it to the server | | `README.md` | This file | | `scripts/start_monitor_sender.sh` | Bootstrap script for hotspot connect, environment setup, and sender launch | | `deploy/xmonitor.env.example` | Template for `/etc/xmonitor/xmonitor.env` | | `deploy/xmonitor-sender.service` | `systemd` service template for robot-side autostart | --- ## Robot Sender Autostart (Ubuntu + systemd) For field deployment on the robot controller, this repo now includes: - `scripts/start_monitor_sender.sh` - `deploy/xmonitor.env.example` - `deploy/xmonitor-sender.service` The design stays intentionally small: - the startup script keeps trying to join your phone hotspot with `nmcli` - it waits until the PC hotspot IP is reachable - it sources the ROS setup and `.venv` - it launches `python3 monitor_sender.py --ip --port ` - `systemd` restarts the process after crashes, while logs go to both `journalctl` and `~/xMonitor/logs/` ### Install on the Ubuntu robot device Assuming the repo is deployed at `/home/ubuntu/xMonitor`: ```bash cd /home/ubuntu/xMonitor sudo install -d -m 700 /etc/xmonitor sudo cp deploy/xmonitor.env.example /etc/xmonitor/xmonitor.env sudo chmod 600 /etc/xmonitor/xmonitor.env sudoedit /etc/xmonitor/xmonitor.env chmod +x scripts/start_monitor_sender.sh sudo cp deploy/xmonitor-sender.service /etc/systemd/system/xmonitor-sender.service sudo systemctl daemon-reload sudo systemctl enable --now xmonitor-sender.service ``` If you deploy the repo somewhere other than `/home/ubuntu/xMonitor`, update these together: - `User=` in `deploy/xmonitor-sender.service` - `WorkingDirectory=` in `deploy/xmonitor-sender.service` - `ExecStart=` in `deploy/xmonitor-sender.service` - `APP_DIR=` and `RUN_USER=` in `/etc/xmonitor/xmonitor.env` ### Runtime checks ```bash # Run the bootstrap script manually for troubleshooting bash /home/ubuntu/xMonitor/scripts/start_monitor_sender.sh # Service state systemctl status xmonitor-sender.service # Live logs from systemd journalctl -u xmonitor-sender.service -f # Per-run log files ls /home/ubuntu/xMonitor/logs ``` ### Behavior - Hotspot connect retry: every 5 seconds until the target SSID is connected - Server reachability retry: every 5 seconds until `SERVER_IP` answers `ping` - Python process crash: `systemd` restarts after 5 seconds - WebSocket drop after startup: handled by `monitor_sender.py`, which already retries every 3 seconds - Per-run log files are pruned automatically; default retention is the latest 20 files via `LOG_RETENTION_COUNT`