Saqr/DEPLOY.md
2026-04-12 19:05:32 +04:00

14 KiB

Saqr PPE Detection - Deployment Guide

Unitree G1 Robot + Intel RealSense D435I


Robot Details

Item Value
Robot Unitree G1 Humanoid
IP 192.168.123.164
User unitree
OS Ubuntu 20.04 (aarch64 / Jetson)
Python 3.10 (conda env: teleimager)
Camera Intel RealSense D435I
Serial 243622073459
Port USB 3.2 @ /dev/video0

Step 1: Train the Model (Dev Machine)

cd ~/Robotics_workspace/AI/Saqr
conda activate AI_MSI_yolo
python train.py --dataset dataset --epochs 100 --batch 16

Verify model exists:

ls -lh models/saqr_best.pt
# Expected: ~5.3 MB

Step 2: Deploy to Robot (Dev Machine)

Option A: Auto deploy

cd ~/Robotics_workspace/AI/Saqr
./deploy.sh

Option B: Manual SCP

# Create folders
ssh unitree@192.168.123.164 "mkdir -p ~/Saqr/{models,captures/{SAFE,PARTIAL,UNSAFE},Config,Logs}"

# Copy project files
scp saqr.py saqr_g1_bridge.py detect.py manager.py logger.py gui.py requirements.txt deploy.sh DEPLOY.md \
    unitree@192.168.123.164:~/Saqr/

# Copy config
scp Config/logging.json unitree@192.168.123.164:~/Saqr/Config/

# Copy trained model (5.3 MB)
scp models/saqr_best.pt unitree@192.168.123.164:~/Saqr/models/

Step 3: Install Dependencies (Robot)

ssh unitree@192.168.123.164

Fix system clock (required for SSL/pip):

sudo date -s "2026-04-10 15:00:00"

Install into teleimager conda env:

conda activate teleimager
python -m pip install ultralytics opencv-python-headless numpy PyYAML

If pip fails (SSL errors), install offline from dev machine:

# On dev machine:
mkdir -p /tmp/saqr_pkgs
pip download ultralytics opencv-python-headless numpy PyYAML \
    -d /tmp/saqr_pkgs --python-version 3.10 --platform manylinux2014_aarch64 --only-binary=:all:
scp -r /tmp/saqr_pkgs unitree@192.168.123.164:/tmp/saqr_pkgs

# On robot:
conda activate teleimager
python -m pip install --no-index --find-links=/tmp/saqr_pkgs ultralytics opencv-python-headless numpy PyYAML

Install Jetson GPU PyTorch (for CUDA acceleration):

# Remove pip PyTorch (wrong CUDA version)
python -m pip uninstall torch torchvision -y

# Install Jetson-specific PyTorch for JetPack 5.1 / CUDA 11.4
python -m pip install --no-cache-dir \
  https://developer.download.nvidia.com/compute/redist/jp/v51/pytorch/torch-2.1.0a0+41361538.nv23.06-cp310-cp310-linux_aarch64.whl

python -m pip install --no-cache-dir \
  https://developer.download.nvidia.com/compute/redist/jp/v51/pytorch/torchvision-0.16.1a0+5e8e2f1-cp310-cp310-linux_aarch64.whl

Fix Qt / Display (choose one):

A) At the robot's physical terminal (monitor connected):

xhost +local:
export DISPLAY=:0
export QT_QPA_PLATFORM=xcb

B) Via SSH with X11 forwarding:

# From dev machine:
ssh -X unitree@192.168.123.164
export QT_QPA_PLATFORM=xcb

C) Headless / no display (SSH without -X):

export QT_QPA_PLATFORM=offscreen
# Always add --headless flag when running saqr.py

Make permanent:

echo 'export QT_QPA_PLATFORM=offscreen' >> ~/.bashrc
source ~/.bashrc

Common error: Invalid MIT-MAGIC-COOKIE-1 key or could not connect to display :0 This means you're in SSH without X11 auth. Either use ssh -X, run xhost +local: on the physical terminal, or switch to headless mode.

Fix system clock (required for pip/SSL):

sudo date -s "2026-04-10 16:00:00"

Verify install:

python -c "from ultralytics import YOLO; print('ultralytics OK')"
python -c "import torch; print('CUDA:', torch.cuda.is_available())"
python -c "import cv2; print('opencv OK')"

Step 4: Run PPE Detection (Robot)

conda activate teleimager
cd ~/Saqr

# === WITH DISPLAY (physical monitor on robot) ===
xhost +
export DISPLAY=:0
python saqr.py --source /dev/video2 --model models/saqr_best.pt

# === HEADLESS via SSH (no display, saves captures + CSV) ===
export QT_QPA_PLATFORM=offscreen
python saqr.py --source /dev/video2 --model models/saqr_best.pt --headless

Note: /dev/video2 is the RealSense D435I RGB camera accessed directly via OpenCV V4L2. No pyrealsense2 SDK needed. Pure OpenCV frames (640x480 BGR).

Option B: RealSense SDK (pyrealsense2):

python saqr.py --source realsense --model models/saqr_best.pt --headless
python saqr.py --source realsense:243622073459 --model models/saqr_best.pt --headless

Option C: GUI (dev machine only, not on robot):

# On your dev machine (not the robot):
python gui.py --source 0 --model models/saqr_best.pt

Note: gui.py requires PySide6 and a display. It will NOT work on the headless Jetson robot.

With OpenCV camera index:

python saqr.py --source 0 --model models/saqr_best.pt --headless

With V4L2 device path:

python saqr.py --source /dev/video0 --model models/saqr_best.pt --headless

With GUI (if display connected):

python gui.py --source realsense --model models/saqr_best.pt

Simple detection (no tracking):

python detect.py --source realsense --model models/saqr_best.pt

Step 4b: Run with G1 TTS + Reject Action (Bridge)

saqr_g1_bridge.py spawns saqr.py, parses its event stream, and drives the G1 onboard TTS and the G1 arm action client on each per-person status transition:

Transition TTS (speaker_id=2, English) Arm action
→ UNSAFE "Not safe! Please wear your protective equipment." reject (id=13) + auto release arm
→ SAFE "Safe."
→ PARTIAL

Requires unitree_sdk2py installed on the robot and a reachable DDS bus on eth0. The bridge uses a single ChannelFactoryInitialize for both clients.

conda activate marcus      # or teleimager — whichever env has unitree_sdk2py
cd ~/Saqr
python3 saqr_g1_bridge.py --iface eth0 --source realsense --headless -- --stream 8080

Then open http://192.168.123.164:8080 in your laptop browser.

With live OpenCV window (physical monitor on robot):

xhost +local: >/dev/null 2>&1
DISPLAY=:0 python3 saqr_g1_bridge.py --iface eth0 --source realsense

q in the window quits; Ctrl+C in the terminal is also forwarded to Saqr.

Dry run (no TTS, no motion — just see decisions):

python3 saqr_g1_bridge.py --dry-run --source realsense --headless

Bridge CLI flags:

Flag Default Description
--iface (default DDS) DDS network interface, e.g. eth0
--timeout 10.0 Arm/Audio client timeout (seconds)
--cooldown 8.0 Per-(id, status) seconds before re-triggering
--release-after 2.0 Seconds before auto release arm (0 = never)
--speaker-id 2 G1 TtsMaker speaker_id (2 = English on current firmware)
--dry-run off Parse events but never call the SDK
--source Pass through to saqr (0 / realsense / /dev/video2 / path)
--headless off Pass --headless to saqr
--saqr-conf Pass --conf to saqr
--imgsz Pass --imgsz to saqr
--device Pass --device to saqr (cpu / 0 / cuda:0)
-- <extra> Everything after -- is forwarded raw to saqr

Speaker-id reference

speaker_ids are locked to a language — they do NOT auto-detect input text. On current G1 firmware, speaker_id=0 is Chinese regardless of what you feed it. Speaker 2 was confirmed English by running Sanad mode 6 (voice_example.py 6). If the robot's firmware changes, re-scan:

# On the robot (in a conda env with unitree_sdk2py):
python3 ~/Sanad/voice_example.py 6

and pass the new id with --speaker-id N.

What successful output looks like:

[BRIDGE] G1ArmActionClient ready (iface=eth0)
[BRIDGE] G1 AudioClient ready (speaker_id=2)
[BRIDGE] launching: /.../python3 -u /home/unitree/Saqr/saqr.py --source realsense --headless
...
ID 0001 | NEW | SAFE | wearing: helmet, vest | missing: none | ...
[BRIDGE] tts -> 'Safe.'
ID 0002 | NEW | UNSAFE | wearing: none | missing: vest | ...
[BRIDGE] tts -> 'Not safe! Please wear your protective equipment.'
[BRIDGE] -> reject
[BRIDGE] -> release arm

Step 5: Check Results (Robot)

Live status:

cat ~/Saqr/captures/result.csv

Event history (audit log):

cat ~/Saqr/captures/events.csv

Captured photos:

ls ~/Saqr/captures/SAFE/
ls ~/Saqr/captures/PARTIAL/
ls ~/Saqr/captures/UNSAFE/

Export CSV report:

cd ~/Saqr
python manager.py --export

Download results to dev machine:

# From dev machine
scp -r unitree@192.168.123.164:~/Saqr/captures/ ./captures_from_robot/
scp unitree@192.168.123.164:~/Saqr/captures/events.csv ./events_robot.csv

Camera Source Options

Source Command Description
/dev/video2 --source /dev/video2 RGB camera via OpenCV (recommended)
realsense --source realsense RealSense D435I via pyrealsense2 SDK
realsense:SERIAL --source realsense:243622073459 Specific RealSense by serial
/dev/video4 --source /dev/video4 Second RGB stream (if available)
0 --source 0 First OpenCV camera index
video.mp4 --source video.mp4 Video file
image.jpg --source image.jpg Single image

G1 Robot V4L2 Device Map (RealSense D435I):

/dev/video0  - Stereo module (infrared) - won't open with OpenCV
/dev/video1  - Stereo metadata
/dev/video2  - RGB camera (640x480) ← USE THIS
/dev/video3  - RGB metadata
/dev/video4  - RGB camera (secondary stream)

Detect cameras on robot:

# Find working RGB cameras
python -c "
import cv2
for i in range(10):
    cap = cv2.VideoCapture(f'/dev/video{i}', cv2.CAP_V4L2)
    if cap.isOpened():
        ret, frame = cap.read()
        if ret and frame is not None:
            print(f'/dev/video{i}: {frame.shape} OK')
        else:
            print(f'/dev/video{i}: opened but no frame')
        cap.release()
"

# RealSense devices
rs-enumerate-devices | grep "Serial Number"

Tuning Parameters

Parameter Default Flag Description
Confidence 0.35 --conf 0.35 Lower = more detections, higher = fewer false positives
Max Missing 90 --max-missing 90 Frames before track deleted (~3s at 30fps)
Match Distance 250 --match-distance 250 Pixels for track matching
Confirm Frames 5 --status-confirm-frames 5 Frames to confirm a status change
python saqr.py --source realsense --model models/saqr_best.pt --headless \
    --conf 0.30 --max-missing 120 --match-distance 300 --status-confirm-frames 7

Compliance Rules

Status Condition Color
SAFE Helmet AND vest detected, no violations Green
PARTIAL Only helmet OR only vest detected Yellow
UNSAFE no-helmet or no-vest detected, or nothing detected Red

Output Files

File Location Description
result.csv captures/result.csv Current state of all tracked persons
events.csv captures/events.csv Audit log (NEW / STATUS_CHANGE events)
Person crops captures/SAFE/*.jpg Cropped images of compliant workers
Person crops captures/PARTIAL/*.jpg Workers with incomplete PPE
Person crops captures/UNSAFE/*.jpg Workers violating PPE rules
Logs Logs/Inference/saqr.log Runtime log

Project Files

File Purpose
saqr.py Main PPE tracking + detection (RealSense + OpenCV)
saqr_g1_bridge.py Saqr → G1 bridge (onboard TTS + reject arm action on UNSAFE/SAFE transitions)
detect.py Simple detection without tracking
gui.py PySide6 desktop GUI
manager.py Photo management CLI + CSV export
train.py YOLO model training
logger.py Centralized logging
deploy.sh One-command deploy to robot
Config/logging.json Log settings

Troubleshooting

RealSense not detected

# Check USB connection
lsusb | grep Intel

# Re-enumerate
rs-enumerate-devices | head -10

# Reset USB (if needed)
sudo usbreset /dev/bus/usb/002/002

Camera not opening

# Test RealSense directly
python -c "
import pyrealsense2 as rs
pipe = rs.pipeline()
cfg = rs.config()
cfg.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)
pipe.start(cfg)
frames = pipe.wait_for_frames()
print('Frame:', frames.get_color_frame().get_width(), 'x', frames.get_color_frame().get_height())
pipe.stop()
"

# Test OpenCV fallback
python -c "import cv2; c=cv2.VideoCapture(0); print('OK' if c.isOpened() else 'FAIL'); c.release()"

# Try different source
python saqr.py --source /dev/video0 --model models/saqr_best.pt --headless

ModuleNotFoundError: ultralytics

# Check you're in the right conda env
which python
# Should show: /home/unitree/miniconda3/envs/teleimager/bin/python

# Install to the correct env
python -m pip install ultralytics

System clock wrong (SSL errors)

sudo date -s "2026-04-10 15:00:00"

Model not found

ls ~/Saqr/models/
# Should show: saqr_best.pt (~5.3 MB)

Low FPS on Jetson

# Use smaller confidence to reduce load
python saqr.py --source realsense --conf 0.5 --headless

# Or use headless opencv
export DISPLAY=
python saqr.py --source realsense --headless

Too many duplicate track IDs

# Increase tolerance
python saqr.py --source realsense --max-missing 150 --match-distance 300 --headless