# Next Session: Streaming Nav — Close-Range Tuning + Telegram E2E

## Context

Session 79 implemented and deployed streaming vision navigation. Pi streams MJPEG + WS sensors to Panda. Panda runs continuous VLM loop (NavController), drives Pi directly. Annie is a commander (start/poll/stop). 5 commits shipped, 29 new tests, 46+593 passing.

**E2E results:** 3/4 runs found the red ball (2 goal_reached at 14 and 65 cycles). 1 run over-rotated at close range — VLM confused reddish furniture with the ball when ball exited frame during centering turn.

## What to Fix

### 1. Close-range over-rotation (HIGH)

**Problem:** When robot gets very close to the ball, a centering turn moves the ball out of frame. VLM then sees reddish furniture and keeps centering in the same direction → spins in circles.

**Fix:** Cap consecutive same-direction centering turns. After 8 centering turns without any forward/approaching command, force a short forward (0.2s) to change perspective.

In `services/panda_nav/server.py`, add tracking after the search rotation block:

```python
# After the existing search rotation tracking block, add:
if result.get("reason") == "centering":
    self._consecutive_centering += 1
    if self._consecutive_centering >= 8:
        # Force forward to change perspective
        result = {"command": "forward", "duration_s": 0.2, "reason": "centering_nudge"}
        self._consecutive_centering = 0
elif result.get("reason") == "approaching":
    self._consecutive_centering = 0
```

Add `self._consecutive_centering = 0` to `__init__` and `start()`.

### 2. Telegram E2E (MEDIUM)

**Not yet tested:** Annie commander pattern through Telegram. `NAV_STREAMING=1` is set in telegram bot env. Send "find the red ball" via Telegram and verify the full chain: Telegram → bot → robot_tools → _navigate_streaming → Panda → Pi.

### 3. Panda systemd unit (LOW)

`panda-nav` currently runs as nohup. Create a proper systemd user service so it survives reboots. Template:

```ini
[Unit]
Description=Panda Nav VLM Server
After=network.target docker.service

[Service]
Type=simple
WorkingDirectory=/home/rajesh/workplace/her/her-os/services/panda_nav
Environment=NAV_TOKEN=
Environment=LLAMA_SERVER_URL=http://localhost:11435
Environment=NAV_VLM_MODEL=gemma-4-E2B-it
Environment=NAV_STREAMING=1
Environment=PI_BASE_URL=http://192.168.68.61:8080
Environment=PI_AUTH_TOKEN=8cX80yIBws1PfBjFuvPz0k9egPSZD0LvS02oUD6ijfg
Environment=SETTLE_AFTER_TURN_S=2.0
ExecStart=/usr/bin/python3 -m uvicorn server:app --host 0.0.0.0 --port 11436
Restart=on-failure
RestartSec=5

[Install]
WantedBy=default.target
```

### 4. Pipeline VLM with driving (FUTURE)

Start next VLM inference while robot is still executing previous drive command. Eliminates 120ms VLM wait from critical path. Architectural change — do after stabilization.

## Current Deployment State

All 3 machines are live with streaming nav:

| Machine | Service | Status | Key env vars |
|---------|---------|--------|-------------|
| Pi (192.168.68.61) | turbopi-server (systemd) | Running | ROBOT_API_TOKEN set |
| Panda (192.168.68.57) | panda-nav (nohup) | Running | NAV_STREAMING=1, PI_AUTH_TOKEN set, SETTLE_AFTER_TURN_S=2.0 |
| Panda (192.168.68.57) | panda-llamacpp (Docker) | Running | Port 11435 |
| Titan (192.168.68.52) | telegram-bot (nohup) | Running | NAV_STREAMING=1 |

**To restart panda-nav after code changes:**
```bash
ssh rajesh@192.168.68.57
fuser -k 11436/tcp
docker start panda-llamacpp  # in case it got killed
cd ~/workplace/her/her-os && git pull origin main
cd services/panda_nav
NAV_TOKEN='' LLAMA_SERVER_URL='http://localhost:11435' NAV_VLM_MODEL='gemma-4-E2B-it' \
NAV_STREAMING=1 PI_BASE_URL='http://192.168.68.61:8080' \
PI_AUTH_TOKEN='8cX80yIBws1PfBjFuvPz0k9egPSZD0LvS02oUD6ijfg' \
nohup python3 -m uvicorn server:app --host 0.0.0.0 --port 11436 >> /tmp/panda-nav.log 2>&1 &
```

## Tuning Constants (current values)

```python
SEARCH_ANGLE_DEG = 15        # search rotation per cycle
ANGLE_SMALL = 12             # centering when far
ANGLE_MEDIUM = 8             # centering when medium
ANGLE_LARGE = 5              # centering when close
FWD_SMALL = 1.0              # forward when far (SMALL size)
FWD_MEDIUM = 0.3             # forward when medium
SETTLE_AFTER_TURN_S = 2.0    # camera stabilization after turn (env configurable)
MIN_CYCLE_INTERVAL_S = 0.1   # rate floor
SEARCH_GIVE_UP_THRESHOLD = 12
STUCK_ESCAPE_THRESHOLD = 3
HEARTBEAT_TIMEOUT_S = 30.0
```

## Commits from Session 79

1. `d4c2502` — main streaming nav (10 files, 1516 insertions)
2. `52314b1` — httpx timeout compat (MjpegClient silent failure fix)
3. `00a2857` — VLM hysteresis (3 consecutive NONE before search)
4. `9fc56c3` — halved turn angles + 300ms settle
5. `9f46406` — settle 300ms→2s, configurable via env
