275 lines
9.1 KiB
Markdown
275 lines
9.1 KiB
Markdown
# 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://<server-ip>: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://<server-ip>: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 <server-ip> [--port <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 <your-server-ip>
|
|
```
|
|
|
|
3. **Open browser**: navigate to `http://<your-server-ip>: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 <server-ip> --port <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
|
|
```
|
|
|
|
When run manually, the script now auto-loads `/etc/xmonitor/xmonitor.env` if that file exists.
|
|
|
|
### 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`
|
|
|