# Annie Robot Car — Architecture

## Overview

Annie can control a TurboPi robot car via voice, Telegram, and text commands.
The architecture is **single-tier**: Annie's brain on Titan sends HTTP commands
to a Raspberry Pi 5 "dumb actuator." The Pi has no local LLM reasoning —
Gemma 4 E2B is used only for photo description via Ollama.

## Network Topology

```
Titan (192.168.68.52)          Pi 5 (192.168.68.61)         Panda (192.168.68.105)
├── Annie brain (Gemma 4 26B)  ├── turbopi-server :8080     ├── Phone loop
├── Context Engine             ├── Ollama (gemma4-car)      └── WhatsApp agent
└── Dashboard                  └── TurboPi SDK (UART→STM32)
```

## Components

### Pi-side: `services/turbopi-server/main.py`
- FastAPI server wrapping HiwonderSDK via UART (`/dev/ttyAMA0`)
- 10 endpoints: `/drive`, `/estop`, `/look`, `/photo`, `/photo/describe`,
  `/distance`, `/battery`, `/buzzer`, `/oled`, `/health`
- Bearer token auth on all endpoints except `/health`
- Mecanum wheel omnidirectional movement (11 actions)
- Camera servo pan/tilt (PWM channels 1+2)
- Sonar distance sensor (I2C)
- Deployed as systemd unit with ExecStartPre motor-zero

### Annie-side: `services/annie-voice/robot_tools.py`
- HTTP client with split timeouts (connect=2s, read=5s, vision=180s)
- 4 tools: `drive_robot`, `robot_photo`, `robot_look`, `robot_status`
- Events emitted as `chariot` creature with `complete` event type
- Gated by `ROBOT_ENABLED` env flag, Telegram-only initially

### Creature: `chariot`
- Zone: acting
- Service: annie-voice
- Process: vehicle-control
- Accent: burnt orange (r:220, g:120, b:40)

## Safety Stack (5 layers)

| Layer | Mechanism | Response Time |
|-------|-----------|---------------|
| 1 | ESTOP endpoint (bypasses async lock) | <100ms |
| 2 | Dead-man's switch (10s no command) | 10s |
| 3 | Duration clamp (max 5s per command) | Immediate |
| 4 | Rate limit (10 drive/min, cooldown) | Immediate |
| 5 | systemd ExecStartPre motor-zero | On restart |

## Motor Control

Uses mecanum wheel kinematics from `MecanumChassis.set_velocity(velocity, direction, angular_rate)`:
- Direction 90°=forward, 270°=backward, 0°=right, 180°=left
- Angular rate enables in-place rotation
- 11 actions: forward, backward, left, right, strafe_left, strafe_right,
  spin_left, spin_right, stop, drift_left, drift_right
- Literal type validation prevents LLM from hallucinating invalid actions

## Benchmark Results (Pi 5, Gemma 4 E2B, think:false)

| Test | Tokens | tok/s | Total |
|------|--------|-------|-------|
| Math (7*8) | 3 | 9.76 | 16.3s (cold) |
| Reasoning (150/2.5) | 3 | 11.54 | 3.3s |
| Haiku | 20 | 7.15 | 4.6s |
| Longer text | 37 | 7.07 | 7.8s |
| Vision | 21 | 6.67 | 123.2s |

Production Modelfile: `gemma4-car` (FROM gemma4:e2b, temp=0.7, num_predict=200, robot system prompt).

## Power Supply

**3x SupTronics X-UPS1** stacked in parallel, powering Pi 5 via 5V connector.

| Spec | Value |
|------|-------|
| Board | SupTronics X-UPS1 (Geekworm) |
| Cells per board | 4x 18650 Li-Ion (3.7V) |
| Total cells | **12x 18650** (3 boards stacked) |
| Est. capacity | 36,000-42,000 mAh @ 3.7V (**133-155 Wh**) |
| Output voltage | 5.1V ±5% |
| Max output current | 3A per board, **9A total** (parallel) |
| Input voltage | 6-18V DC (5.5x2.1mm jack) |
| Charging current | 2.3-3.2A per board |
| Low battery cutoff | ≤2.4V per cell |

**Estimated runtime:**
- Pi 5 idle (~4W): ~33-39 hours
- Pi 5 active + Ollama (~8W): ~17-19 hours
- Pi 5 + Ollama vision peak (~12W): ~11-13 hours

**Notes:**
- 12V and 5V outputs cannot be used simultaneously — using 5V only
- All 12V OFF jumpers must be shorted when stacking
- Boards wired in parallel (outputs combined)
- Pi 5 showed throttle warning `0x50000` (under-voltage occurred) — may be boot surge transient

## Hardware Calibration (verified on hardware)

| Parameter | Value |
|-----------|-------|
| Pan servo | Channel 2, center=1500, range 1000-2000 |
| Tilt servo | Channel 1, center=1500, range 1200-1800 |
| Angular rate sign | Negative = counter-clockwise (left), positive = clockwise (right) |
| Rotation speed | 360° in 5s at speed 40 (~72°/s) |
| Strafe | Mechanically correct but impractical with 12-battery weight |
| Sonar | Always reads 99999 (I2C issue — needs investigation) |
| Battery voltage | Returns None from SDK (wiring issue — needs investigation) |

## Future Extensions

1. **Local E2B safety loop**: Pi runs obstacle detection + emergency stop locally
2. **Hailo YOLO**: 310 FPS object detection for real-time obstacle avoidance
3. **Voice mode**: Expand `channels` from `("telegram",)` to include `"voice"`
4. **Patrol mode**: Scheduled autonomous routes with camera monitoring
5. **Follow-me**: Person tracking via Hailo YOLO + motor control
6. **Intercom on wheels**: Drive to location, initiate voice call
