# GimbalDriver ## 1. High-Level Overview **Purpose**: Implements the ViewLink serial protocol for controlling the ViewPro A40 gimbal. Sends pan/tilt/zoom commands via UART, reads gimbal feedback (current angles), provides PID-based path following, and handles communication integrity. **Architectural Pattern**: Hardware adapter with command queue and PID controller. **Upstream dependencies**: Config helper (UART port, baud rate, PID gains, mock mode), Types helper **Downstream consumers**: ScanController ## 2. Internal Interfaces ### Interface: GimbalDriver | Method | Input | Output | Async | Error Types | |--------|-------|--------|-------|-------------| | `connect(port, baud)` | str, int | bool | No | UARTError | | `disconnect()` | — | — | No | — | | `is_alive()` | — | bool | No | — | | `set_angles(pan, tilt, zoom)` | float, float, float | bool | No | GimbalCommandError | | `get_state()` | — | GimbalState | No | GimbalReadError | | `set_sweep_target(pan)` | float | bool | No | GimbalCommandError | | `zoom_to_poi(pan, tilt, zoom)` | float, float, float | bool | No (blocks ~2s for zoom) | GimbalCommandError, TimeoutError | | `follow_path(direction, pid_error)` | (dx,dy), float | bool | No | GimbalCommandError | | `return_to_sweep()` | — | bool | No | GimbalCommandError | **GimbalState**: ``` pan: float — current pan angle (degrees) tilt: float — current tilt angle (degrees) zoom: float — current zoom level (1-40) last_heartbeat: float — epoch timestamp of last valid response ``` ## 5. Implementation Details **ViewLink Protocol**: - Baud rate: 115200, 8N1 - Command format: per ViewLink Serial Protocol V3.3.3 spec - Implementation note: read full spec during implementation to determine if native checksums exist. If yes, use them. If not, add CRC-16 wrapper. - Retry: up to 3 times on checksum failure with 10ms delay **PID Controller** (for path following): - Dual-axis PID (pan, tilt independently) - Input: error = (path_center - frame_center) in pixels - Output: pan/tilt angular velocity commands - Gains: configurable via YAML, tuned per-camera - Anti-windup: integral clamping - Update rate: 10 Hz (100ms interval) **Mock Mode** (development): - TCP socket client instead of UART - Connects to mock-gimbal service (Docker) - Same interface, simulated delays for zoom transition (1-2s) **Key Dependencies**: | Library | Version | Purpose | |---------|---------|---------| | pyserial | — | UART communication | | crcmod | — | CRC-16 if needed (determined after reading ViewLink spec) | | struct (stdlib) | — | Binary packet packing/unpacking | **Error Handling Strategy**: - UART open failure → GimbalDriver.connect() returns false → gimbal_available=false - Command send failure → retry 3x → GimbalCommandError - No heartbeat for 4s → is_alive() returns false → ScanController sets gimbal_available=false ## 7. Caveats & Edge Cases **Known limitations**: - Zoom transition takes 1-2s physical time (40x optical) - PID gains need tuning on real hardware (bench testing) - Gimbal has physical pan/tilt limits — commands beyond limits are clamped **Physical EMI mitigation** (not software — documented here for reference): - Shielded UART cable, shortest run - Antenna ≥35cm from gimbal - Ferrite beads on cable near Jetson ## 8. Dependency Graph **Must be implemented after**: Config helper, Types helper **Can be implemented in parallel with**: Tier1Detector, Tier2SpatialAnalyzer, VLMClient, OutputManager **Blocks**: ScanController (needs GimbalDriver for scan control) ## 9. Logging Strategy | Log Level | When | Example | |-----------|------|---------| | ERROR | UART open failed, 3x retry exhausted | `UART /dev/ttyTHS1 open failed: Permission denied` | | WARN | Checksum failure (retrying), slow response | `Gimbal CRC failure, retry 2/3` | | INFO | Connected, zoom complete, mode change | `Gimbal connected at /dev/ttyTHS1 115200. Zoom to 20x complete (1.4s)` |