docs: add comprehensive Bee camera system report
This commit is contained in:
parent
5f053ca2df
commit
3577d3ecf2
1 changed files with 810 additions and 0 deletions
810
docs/BEE-CAMERA.md
Normal file
810
docs/BEE-CAMERA.md
Normal file
|
|
@ -0,0 +1,810 @@
|
|||
# Bee Camera System — Full Technical Report
|
||||
|
||||
*Generated: 2026-03-13*
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The Hivemapper Bee dashcam uses an Intel Keem Bay SoC with an integrated Myriad X VPU for camera capture and ML inference. The camera pipeline flows from a Sony IMX378-equivalent sensor through MIPI CSI-2 to the VPU, where DepthAI firmware handles image processing and neural network inference. Frames are written to disk and exposed through multiple odc-api REST endpoints.
|
||||
|
||||
**Key Findings:**
|
||||
- Camera controlled via `depthai_gate.service` (Python/Flask on port 11492)
|
||||
- ML inference handled by `map-ai.service` using the VPU's Neural Compute Engine
|
||||
- Live frames stored in `/tmp/recording/pics/`
|
||||
- Landmark observation images stored in `/data/recording/cached_observations/`
|
||||
- Preview mode restarts the camera-bridge service with different configuration
|
||||
- No direct V4L2 access — all camera access goes through DepthAI pipeline
|
||||
|
||||
---
|
||||
|
||||
## 1. Hardware
|
||||
|
||||
### 1.1 System-on-Chip: Intel Keem Bay
|
||||
|
||||
| Component | Specification |
|
||||
|-----------|---------------|
|
||||
| **SoC** | Intel Keem Bay (RVC2 / Robotics Vision Core 2) |
|
||||
| **CPU** | 4× ARM Cortex-A53 @ 1.5GHz |
|
||||
| **VPU** | Intel Movidius Myriad X (16 SHAVE cores) |
|
||||
| **NPU** | Integrated Neural Compute Engine (hardware inference) |
|
||||
| **RAM** | 4GB LPDDR4 (~3.5GB usable) |
|
||||
| **ISP** | Integrated Image Signal Processor on VPU |
|
||||
| **Process** | 10nm (Intel) |
|
||||
|
||||
**Memory Configuration:**
|
||||
```
|
||||
MemTotal: 3,584,000 kB (~3.5GB)
|
||||
SwapTotal: 2,097,148 kB (~2GB)
|
||||
CmaTotal: 1,408,000 kB (~1.34GB reserved for VPU/camera DMA)
|
||||
```
|
||||
|
||||
### 1.2 Camera Sensor
|
||||
|
||||
| Specification | Value |
|
||||
|---------------|-------|
|
||||
| **Sensor** | Sony IMX378 (or equivalent 12MP) |
|
||||
| **Resolution** | 4056 × 3040 native, downscaled to 2028 × 1024 |
|
||||
| **Interface** | MIPI CSI-2 |
|
||||
| **Frame Rate** | Variable, typically 30 FPS |
|
||||
| **ISP** | On-VPU processing via DepthAI |
|
||||
|
||||
The Bee uses a Luxonis OAK-1 compatible camera module integrated with the Keem Bay SoC. The camera sensor connects directly to the SoC's MIPI CSI-2 interface, which is managed entirely by the DepthAI/Luxonis firmware running on the Myriad X VPU.
|
||||
|
||||
### 1.3 Bus Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Intel Keem Bay SoC │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
|
||||
│ │ ARM Cortex │ │ Myriad X VPU │ │ Neural Compute │ │
|
||||
│ │ A53 (4-core) │ │ (16 SHAVE) │ │ Engine (NCE) │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
|
||||
│ │ │ │ │
|
||||
│ └────────┬────────┴──────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────┴────────┐ │
|
||||
│ │ Internal Bus │ │
|
||||
│ │ (AXI/NoC) │ │
|
||||
│ └────────┬────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────┼─────────────────────────────────────┐ │
|
||||
│ ┌─┴──┐ ┌────┴────┐ ┌────────┐ ┌───────────┐ │ │
|
||||
│ │PCIe│ │ USB │ │ SDIO │ │ MIPI CSI │ │ │
|
||||
│ └──┬─┘ └────┬────┘ └────┬───┘ └─────┬─────┘ │ │
|
||||
└─────┼───────────┼──────────────┼──────────────┼────────┘ │
|
||||
│ │ │ │ │
|
||||
┌─────┴─────┐ ┌───┴───────┐ ┌──┴──┐ ┌────┴─────┐ │
|
||||
│ Marvell │ │ Telit │ │eMMC │ │ Camera │ │
|
||||
│ 88W8997 │ │ LE910C4 │ │Flash│ │ Module │ │
|
||||
│ WiFi/BT │ │ LTE Modem │ │ │ │ (IMX378) │ │
|
||||
└───────────┘ └───────────┘ └─────┘ └──────────┘ │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Kernel / V4L2
|
||||
|
||||
### 2.1 Kernel Modules
|
||||
|
||||
The Bee runs a custom Yocto-based Linux with Intel-specific VPU drivers:
|
||||
|
||||
| Module | Purpose | Status |
|
||||
|--------|---------|--------|
|
||||
| **kmb_cam** (if present) | Keem Bay camera driver | Likely used internally |
|
||||
| **kmb_imx412** (if present) | Sony IMX412 sensor driver | May be loaded for sensor |
|
||||
| **videodev** | V4L2 subsystem | Core video framework |
|
||||
| **v4l2_fwnode** | V4L2 firmware node parsing | Device tree integration |
|
||||
|
||||
**Note:** Standard V4L2 device access (`/dev/video*`) is **not used** for normal operation. The camera is accessed exclusively through the DepthAI XLink protocol running on the VPU. The VPU owns the camera hardware completely.
|
||||
|
||||
### 2.2 VPU Sysfs Interface
|
||||
|
||||
The VPU is controlled via sysfs:
|
||||
```
|
||||
/sys/class/vpu/
|
||||
```
|
||||
|
||||
**Firmware Loading:**
|
||||
- `luxonis_vpu.bin` — DepthAI firmware (Luxonis/OAK)
|
||||
- `vpu_nvr_b0.bin` — Intel HDDL firmware (NOT used, conflicts)
|
||||
|
||||
The VPU firmware is written to a sysfs attribute (`fwname`) to trigger loading. The DepthAI firmware must load first, otherwise the Intel HDDL service (`deviceservice`) grabs the VPU and causes conflicts.
|
||||
|
||||
### 2.3 No Direct V4L2 Access
|
||||
|
||||
**Important:** You cannot access the camera via `/dev/video*` while `depthai_gate` is running. The DepthAI pipeline has exclusive ownership of the camera hardware. To get frames, you must:
|
||||
1. Use the existing depthai_gate/odc-api stack, OR
|
||||
2. Stop depthai_gate and implement your own DepthAI pipeline, OR
|
||||
3. Reverse-engineer XLink and write custom firmware
|
||||
|
||||
---
|
||||
|
||||
## 3. DepthAI Gate
|
||||
|
||||
### 3.1 Service Configuration
|
||||
|
||||
```ini
|
||||
# depthai_gate.service (inferred from analysis)
|
||||
[Unit]
|
||||
Description=DepthAI Camera Gate
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/opt/depthai_gate/run.py
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### 3.2 Technical Details
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Language** | Python 3 + Flask |
|
||||
| **Port** | 11492 (localhost) |
|
||||
| **Tasks** | ~158 threads observed |
|
||||
| **Memory** | ~200MB RSS |
|
||||
| **Location** | `/opt/depthai_gate/` (estimated) |
|
||||
|
||||
### 3.3 Responsibilities
|
||||
|
||||
1. **VPU Firmware Loading** — Writes `luxonis_vpu.bin` to VPU sysfs
|
||||
2. **XLink Connection** — Establishes PCIe XLink to Myriad X VPU
|
||||
3. **DepthAI Pipeline** — Configures camera capture and ISP settings
|
||||
4. **Frame Capture** — Captures frames at configured resolution/framerate
|
||||
5. **Frame Output** — Writes frames to `/tmp/recording/pics/`
|
||||
|
||||
### 3.4 XLink Protocol
|
||||
|
||||
XLink is Luxonis's proprietary protocol for host-to-VPU communication:
|
||||
- **Transport:** PCIe (on Keem Bay) or USB (on desktop OAK devices)
|
||||
- **Channels:** Bidirectional data streams for frames, tensors, and control
|
||||
- **Status Values:**
|
||||
- `0` = Disconnected
|
||||
- `1` = Connecting / Error
|
||||
- `2` = Connected (good)
|
||||
|
||||
**Status Check (from logs):**
|
||||
```
|
||||
xlink_device_status=2 # Healthy
|
||||
vpu_firmware=luxonis_vpu.bin
|
||||
```
|
||||
|
||||
### 3.5 Pipeline Configuration
|
||||
|
||||
The DepthAI pipeline likely includes:
|
||||
- **ColorCamera node** — IMX378 capture at 4K, downscaled to 2028×1024
|
||||
- **ImageManipNode** — Resize, crop, color conversion
|
||||
- **XLinkOut node** — Send frames to host for storage
|
||||
- **NeuralNetwork node** (optional) — On-VPU inference
|
||||
|
||||
Pipeline configs may exist at:
|
||||
- `/opt/depthai_gate/pipeline.json`
|
||||
- `/data/camera_config.json`
|
||||
- Hardcoded in Python
|
||||
|
||||
### 3.6 VPU Conflict Bug
|
||||
|
||||
**Root Cause (identified and fixed):**
|
||||
|
||||
`deviceservice.service` (Intel HDDL / OpenVINO) was racing with `depthai_gate.service`:
|
||||
|
||||
1. HDDL starts at boot, loads `vpu_nvr_b0.bin`
|
||||
2. depthai_gate starts, overwrites with `luxonis_vpu.bin`
|
||||
3. HDDL locked out, retries XLink every 2 seconds forever
|
||||
4. On depthai_gate restart, HDDL grabs VPU first → camera dead
|
||||
5. Watchdog (`secure-wdtclient`) crash loops → memory pressure → OOM
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
systemctl disable --now deviceservice
|
||||
systemctl mask deviceservice # Survives OTA better
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. map-ai Pipeline
|
||||
|
||||
### 4.1 Service Configuration
|
||||
|
||||
```ini
|
||||
# map-ai.service (inferred)
|
||||
[Unit]
|
||||
Description=Map AI Processing
|
||||
After=depthai_gate.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/opt/map-ai/run.py
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### 4.2 Technical Details
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Language** | Python 3 |
|
||||
| **Model Format** | ONNX (via OpenVINO or DepthAI NCE) |
|
||||
| **Input** | Frames from depthai_gate |
|
||||
| **Output** | Detections to Redis, blurred frames to disk |
|
||||
|
||||
### 4.3 Processing Pipeline
|
||||
|
||||
```
|
||||
Frame from depthai → map-ai.py
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────┐
|
||||
│ ML INFERENCE (on VPU) │
|
||||
│ - Road sign classifier │
|
||||
│ - Face detector (privacy) │
|
||||
│ - License plate detector (privacy) │
|
||||
└───────────────────────┬───────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────┐
|
||||
│ PRIVACY PROCESSING │
|
||||
│ - PrivacyBlurNode │
|
||||
│ - Gaussian blur on faces/plates │
|
||||
│ - cv2.imwrite blurred frames │
|
||||
└───────────────────────┬───────────────┘
|
||||
│
|
||||
┌───────────────┴───────────────┐
|
||||
▼ ▼
|
||||
Redis ZSET (detections) Disk (blurred frames)
|
||||
```
|
||||
|
||||
### 4.4 AI Models
|
||||
|
||||
| Model | Location | Purpose |
|
||||
|-------|----------|---------|
|
||||
| Road Signs | `/opt/object-detection/model.blob` or `/data/models/` | Sign classification |
|
||||
| Privacy | `/opt/odc-api/python/` or `/data/models/` | Face/plate detection |
|
||||
| PVC | `/data/recording/models/pvc.onnx` | Unknown (227 bytes — likely index) |
|
||||
|
||||
**Privacy Model Hash:** Stored in FrameKm metadata for verification.
|
||||
|
||||
### 4.5 Redis Integration
|
||||
|
||||
map-ai writes to Redis status keys:
|
||||
```
|
||||
GET MAP_AI_READY → "True"
|
||||
GET EXTERNAL_MODEL_CLASSIFIER_READY → "True"
|
||||
```
|
||||
|
||||
Detection results stored in SQLite, not Redis ZSETs.
|
||||
|
||||
---
|
||||
|
||||
## 5. Frame Storage
|
||||
|
||||
### 5.1 Storage Locations
|
||||
|
||||
| Path | Type | Purpose | Persistence |
|
||||
|------|------|---------|-------------|
|
||||
| `/tmp/recording/pics/` | tmpfs | Live camera frames | Ephemeral |
|
||||
| `/tmp/recording/preview/` | tmpfs | Preview mode frames | Ephemeral |
|
||||
| `/data/recording/cached_observations/` | ext4 | Landmark observation images | Persistent |
|
||||
| `/data/recording/framekm/` | ext4 | FrameKm upload bundles | Persistent |
|
||||
| `/tmp/rgb/` | tmpfs | Frame list files | Ephemeral |
|
||||
|
||||
### 5.2 Frame Format
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Format** | JPEG |
|
||||
| **Resolution** | 2028 × 1024 |
|
||||
| **Quality** | ~85% (estimated ~150-200KB/frame) |
|
||||
| **Color** | RGB |
|
||||
|
||||
### 5.3 Naming Convention
|
||||
|
||||
**Live frames** (`/tmp/recording/pics/`):
|
||||
```
|
||||
{system_time_ms}_{frame_id}_{sequence}.jpg
|
||||
Example: 1709920000123_0001_0042.jpg
|
||||
```
|
||||
|
||||
**Cached observations** (`/data/recording/cached_observations/`):
|
||||
```
|
||||
{timestamp}_{subsecond}_{frame_number}.jpg
|
||||
Example: 1746377552_043000_2945056.jpg
|
||||
```
|
||||
|
||||
### 5.4 Frame Purger
|
||||
|
||||
The `folder_purger` service manages disk space:
|
||||
```bash
|
||||
folder-purger /tmp/recording/pic 400000000 /mnt/data/gps 2000000000 ...
|
||||
```
|
||||
|
||||
When `/tmp/recording/pics/` exceeds 400MB, older frames are deleted.
|
||||
|
||||
### 5.5 Database Schema
|
||||
|
||||
Frames are tracked in SQLite (`/data/recording/odc-api.db` or `data-logger.v2.0.0.db`):
|
||||
|
||||
```sql
|
||||
-- frames table
|
||||
CREATE TABLE frames (
|
||||
system_time INTEGER PRIMARY KEY,
|
||||
image_name TEXT
|
||||
);
|
||||
```
|
||||
|
||||
Landmark observations reference frames:
|
||||
```sql
|
||||
-- observations table (simplified)
|
||||
CREATE TABLE observations (
|
||||
id INTEGER PRIMARY KEY,
|
||||
landmark_id INTEGER,
|
||||
image_name TEXT,
|
||||
x1 REAL, y1 REAL, x2 REAL, y2 REAL, -- bounding box
|
||||
ts INTEGER,
|
||||
...
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. video-processor
|
||||
|
||||
### 6.1 Service Details
|
||||
|
||||
The `video-processor` service is not explicitly documented in the analyzed firmware, but based on naming patterns, it likely handles:
|
||||
|
||||
1. **FrameKm Bundling** — Package frames + metadata for upload
|
||||
2. **Video Encoding** — H.264/H.265 encoding for preview/streaming
|
||||
3. **Frame Sequencing** — Order frames for FrameKm creation
|
||||
|
||||
### 6.2 FrameKm Format
|
||||
|
||||
**Purpose:** Bundle ~1km of driving data for upload to Hivemapper/HERE.
|
||||
|
||||
**Path:** `/data/recording/framekm/`
|
||||
|
||||
**Contents:**
|
||||
```
|
||||
framekm-2024-03-08-12-34-56-abc123.tar
|
||||
├── manifest.json
|
||||
├── frame_0001.jpg
|
||||
├── frame_0002.jpg
|
||||
├── ...
|
||||
├── gnss_auth_buffer.bin
|
||||
└── gnss_auth_signature.bin
|
||||
```
|
||||
|
||||
**Manifest Fields:**
|
||||
```json
|
||||
{
|
||||
"name": "framekm-2024-03-08-12-34-56-abc123",
|
||||
"numFrames": 150,
|
||||
"deviceId": "fvhL2I-iCT",
|
||||
"firmwareVersion": "0.0.1",
|
||||
"privacyModelHash": "sha256:abc123...",
|
||||
"gnssAuthBuffer": "base64...",
|
||||
"gnssAuthSignature": "base64...",
|
||||
"gnssAuthPublicKey": "base64...",
|
||||
"createdAt": 1709920000000
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 Relationship to Camera Frames
|
||||
|
||||
The video-processor does NOT produce the frames we care about for camera access. It only packages existing blurred frames for upload. For raw frame access, focus on `depthai_gate` and the preview system.
|
||||
|
||||
---
|
||||
|
||||
## 7. odc-api Camera Endpoints
|
||||
|
||||
### 7.1 Base URL
|
||||
|
||||
```
|
||||
http://192.168.0.10:5000/api/1/
|
||||
```
|
||||
|
||||
Binds to AP interface (`wlp1s0f0`) only — not accessible from home LAN directly.
|
||||
|
||||
### 7.2 Preview Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/preview/start` | GET | Start preview mode (120s timeout) |
|
||||
| `/preview/stop` | GET | Stop preview mode |
|
||||
| `/preview/status` | GET | Check if preview is active |
|
||||
| `/preview/metadata` | GET | Get latest frame metadata |
|
||||
|
||||
**Preview Implementation (`util/preview.ts`):**
|
||||
|
||||
```typescript
|
||||
export const startPreview = async () => {
|
||||
// Create preview directory
|
||||
await execSync('mkdir /tmp/recording/preview');
|
||||
|
||||
// Write preview config
|
||||
writeFileSync(IMAGER_CONFIG_PATH, JSON.stringify(getPreviewConfig()));
|
||||
|
||||
// Restart camera-bridge with new config
|
||||
await execSync(CMD.STOP_CAMERA); // systemctl stop camera-bridge
|
||||
await sleep(1000);
|
||||
await execSync(CMD.START_CAMERA); // systemctl start camera-bridge
|
||||
};
|
||||
```
|
||||
|
||||
**Preview Timeout:** 120 seconds (auto-stops to preserve 4K quality recording)
|
||||
|
||||
### 7.3 Landmark Image Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/landmarks/images/:id` | GET | Get image paths for landmark |
|
||||
| `/landmarks/:id/chips` | GET | Get chip endpoints for landmark |
|
||||
| `/landmarks/:id/chips/:chip_id` | GET | Get cropped observation image (JPEG) |
|
||||
| `/landmarks/boundingBox/:id` | GET | Get bounding box coordinates |
|
||||
| `/landmarks/upload` | PUT | Upload landmark image to external URL |
|
||||
|
||||
**Image Retrieval Flow:**
|
||||
|
||||
```
|
||||
GET /landmarks/images/123
|
||||
↓
|
||||
Returns: ["/data/recording/cached_observations/1746377552_043000_2945056.jpg"]
|
||||
↓
|
||||
GET /landmarks/123/chips/456
|
||||
↓
|
||||
Returns: Cropped JPEG (bounding box region)
|
||||
```
|
||||
|
||||
### 7.4 Camera Configuration
|
||||
|
||||
**Config Path:** `/opt/camera-bridge/config.json`
|
||||
|
||||
**Commands (from `bee.ts`):**
|
||||
```typescript
|
||||
export const CMD = {
|
||||
RESTART_CAMERA: 'systemctl restart camera-bridge',
|
||||
START_CAMERA: 'systemctl start camera-bridge',
|
||||
STOP_CAMERA: 'systemctl stop camera-bridge',
|
||||
START_PREVIEW: 'systemctl start camera-preview',
|
||||
STOP_PREVIEW: 'systemctl stop camera-preview',
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
### 7.5 Frame Retrieval
|
||||
|
||||
There is **no direct `/camera/frame` endpoint** in the current odc-api. To get a camera frame:
|
||||
|
||||
1. **Via Preview Mode:**
|
||||
- Call `/preview/start`
|
||||
- Read frames from `/tmp/recording/preview/`
|
||||
- Call `/preview/stop` when done
|
||||
|
||||
2. **Via Landmark Images:**
|
||||
- Call `/landmarks/last/N` to get recent detections
|
||||
- Call `/landmarks/images/:id` to get observation image paths
|
||||
- Call `/landmarks/:id/chips/:chip_id` to get cropped JPEG
|
||||
|
||||
3. **Direct File Access (SSH):**
|
||||
- Read from `/tmp/recording/pics/` for latest frames
|
||||
- Read from `/data/recording/cached_observations/` for landmark images
|
||||
|
||||
---
|
||||
|
||||
## 8. Full Data Flow
|
||||
|
||||
### 8.1 Complete Pipeline
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ CAMERA CAPTURE │
|
||||
│ IMX378 Sensor → MIPI CSI-2 → VPU ISP → DepthAI Pipeline │
|
||||
└───────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ DEPTHAI_GATE (port 11492) │
|
||||
│ - XLink communication with Myriad X VPU │
|
||||
│ - Frame capture from DepthAI pipeline │
|
||||
│ - Writes frames to /tmp/recording/pics/ │
|
||||
└───────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┴───────────────┐
|
||||
▼ ▼
|
||||
┌───────────────────────────────┐ ┌──────────────────────────────────────────┐
|
||||
│ RAW FRAME STORAGE │ │ MAP-AI INFERENCE │
|
||||
│ /tmp/recording/pics/ │ │ - Road sign detection (VPU NCE) │
|
||||
│ - Temporary frames │ │ - Privacy blur (faces/plates) │
|
||||
│ - Purged when >400MB │ │ - Outputs to Redis + SQLite │
|
||||
└───────────────────────────────┘ └─────────────────┬────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────┤
|
||||
▼ ▼
|
||||
┌───────────────────────────────────┐ ┌──────────────────────────────────────┐
|
||||
│ CACHED OBSERVATIONS │ │ LANDMARK DATABASE │
|
||||
│ /data/recording/ │ │ /data/recording/odc-api.db │
|
||||
│ cached_observations/ │ │ - landmarks table │
|
||||
│ - Persistent blurred frames │ │ - observations table │
|
||||
│ - Referenced by landmark ID │ │ - frames table │
|
||||
└───────────────────┬───────────────┘ └─────────────────┬────────────────────┘
|
||||
│ │
|
||||
└──────────────┬──────────────────────┘
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ ODC-API (port 5000) │
|
||||
│ - /preview/* — Start/stop preview mode │
|
||||
│ - /landmarks/last/N — Get recent detections │
|
||||
│ - /landmarks/images/:id — Get observation image paths │
|
||||
│ - /landmarks/:id/chips/:chip_id — Get cropped JPEG │
|
||||
└───────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ FRAMEKM BUNDLING │
|
||||
│ hivemapper-data-logger │
|
||||
│ - Collect ~1km of frames + metadata │
|
||||
│ - Bundle with GNSS auth signatures │
|
||||
│ - Store at /data/recording/framekm/ │
|
||||
└───────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ UPLOAD PATH │
|
||||
│ odc-api → mitmdump (port 8888) → Cloudflare Workers → HERE OLP │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8.2 Single Detection Event Trace
|
||||
|
||||
```
|
||||
1. Camera captures frame
|
||||
└── IMX378 → MIPI → VPU ISP → depthai_gate
|
||||
|
||||
2. Frame written to disk
|
||||
└── /tmp/recording/pics/1709920000123_0001_0042.jpg
|
||||
|
||||
3. map-ai reads frame
|
||||
└── Runs road sign classifier on VPU NCE
|
||||
|
||||
4. Detection found (speed limit 35)
|
||||
└── Privacy blur applied to any faces/plates
|
||||
|
||||
5. Observation stored
|
||||
└── SQLite: observations table (landmark_id, bbox, ts, image_name)
|
||||
└── File: /data/recording/cached_observations/...
|
||||
|
||||
6. Landmark created/updated
|
||||
└── SQLite: landmarks table (class_label, lat, lon, confidence)
|
||||
|
||||
7. odc-api exposes data
|
||||
└── GET /landmarks/last/5 returns detection
|
||||
└── GET /landmarks/images/{id} returns image path
|
||||
└── GET /landmarks/{id}/chips/{chip_id} returns cropped JPEG
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Replacement Considerations
|
||||
|
||||
### 9.1 Accessing Frames Without odc-api
|
||||
|
||||
**Option 1: Direct File Read**
|
||||
```bash
|
||||
# SSH to Bee
|
||||
ssh -p 2222 root@localhost # via Lucy tunnel
|
||||
|
||||
# Read latest frames
|
||||
ls -lt /tmp/recording/pics/ | head -10
|
||||
cp /tmp/recording/pics/latest_frame.jpg /tmp/
|
||||
|
||||
# Stream frames (naive)
|
||||
while true; do
|
||||
cp $(ls -t /tmp/recording/pics/*.jpg | head -1) /tmp/current.jpg
|
||||
sleep 0.033 # ~30 FPS
|
||||
done
|
||||
```
|
||||
|
||||
**Pros:** Simple, no service changes
|
||||
**Cons:** Race conditions, no metadata
|
||||
|
||||
**Option 2: Redis Pub/Sub**
|
||||
Subscribe to frame events if depthai_gate publishes them:
|
||||
```python
|
||||
import redis
|
||||
r = redis.Redis()
|
||||
p = r.pubsub()
|
||||
p.subscribe('frame_ready')
|
||||
for message in p.listen():
|
||||
print(message) # Contains frame path or metadata
|
||||
```
|
||||
|
||||
**Pros:** Event-driven, no polling
|
||||
**Cons:** May not exist in current firmware
|
||||
|
||||
### 9.2 Accessing Frames Without depthai_gate
|
||||
|
||||
**Not recommended** — requires implementing your own DepthAI pipeline.
|
||||
|
||||
If you must:
|
||||
1. Stop depthai_gate: `systemctl stop depthai_gate`
|
||||
2. Use Luxonis depthai Python SDK
|
||||
3. Create minimal pipeline:
|
||||
```python
|
||||
import depthai as dai
|
||||
|
||||
pipeline = dai.Pipeline()
|
||||
cam = pipeline.create(dai.node.ColorCamera)
|
||||
cam.setResolution(dai.ColorCameraProperties.SensorResolution.THE_4_K)
|
||||
cam.setIspScale(1, 2) # Downscale to 2028x1024
|
||||
|
||||
xout = pipeline.create(dai.node.XLinkOut)
|
||||
xout.setStreamName("video")
|
||||
cam.video.link(xout.input)
|
||||
|
||||
with dai.Device(pipeline) as device:
|
||||
q = device.getOutputQueue("video")
|
||||
while True:
|
||||
frame = q.get()
|
||||
cv2.imwrite("/tmp/frame.jpg", frame.getCvFrame())
|
||||
```
|
||||
|
||||
**Pros:** Full control over camera
|
||||
**Cons:** Breaks all Hivemapper services, loses ML pipeline
|
||||
|
||||
### 9.3 Minimal Path to JPEG Frame
|
||||
|
||||
**Fastest (with existing stack):**
|
||||
```bash
|
||||
# Via SSH
|
||||
ssh -p 2222 root@localhost 'ls -t /tmp/recording/pics/*.jpg | head -1 | xargs cat' > frame.jpg
|
||||
```
|
||||
|
||||
**Via API (requires preview mode):**
|
||||
```bash
|
||||
curl http://192.168.0.10:5000/api/1/preview/start
|
||||
sleep 2
|
||||
ssh -p 2222 root@localhost 'ls -t /tmp/recording/preview/*.jpg | head -1 | xargs cat' > frame.jpg
|
||||
curl http://192.168.0.10:5000/api/1/preview/stop
|
||||
```
|
||||
|
||||
### 9.4 Building a Custom Camera Interface
|
||||
|
||||
**Requirements:**
|
||||
1. Maintain depthai_gate (or reimplement VPU control)
|
||||
2. Expose a REST endpoint for single-frame capture
|
||||
3. Optionally implement MJPEG streaming
|
||||
|
||||
**Proposed odc-api Addition:**
|
||||
```typescript
|
||||
// routes/camera.ts
|
||||
router.get('/frame', async (req, res) => {
|
||||
const frames = readdirSync('/tmp/recording/pics')
|
||||
.filter(f => f.endsWith('.jpg'))
|
||||
.sort()
|
||||
.reverse();
|
||||
|
||||
if (frames.length === 0) {
|
||||
return res.status(404).send('No frames available');
|
||||
}
|
||||
|
||||
const framePath = join('/tmp/recording/pics', frames[0]);
|
||||
res.sendFile(framePath);
|
||||
});
|
||||
|
||||
router.get('/stream', async (req, res) => {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'multipart/x-mixed-replace; boundary=frame',
|
||||
'Cache-Control': 'no-cache',
|
||||
});
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const frames = readdirSync('/tmp/recording/pics')
|
||||
.filter(f => f.endsWith('.jpg'))
|
||||
.sort()
|
||||
.reverse();
|
||||
|
||||
if (frames.length > 0) {
|
||||
const framePath = join('/tmp/recording/pics', frames[0]);
|
||||
const frameData = readFileSync(framePath);
|
||||
|
||||
res.write('--frame\r\n');
|
||||
res.write('Content-Type: image/jpeg\r\n');
|
||||
res.write(`Content-Length: ${frameData.length}\r\n\r\n`);
|
||||
res.write(frameData);
|
||||
res.write('\r\n');
|
||||
}
|
||||
}, 33); // ~30 FPS
|
||||
|
||||
req.on('close', () => clearInterval(interval));
|
||||
});
|
||||
```
|
||||
|
||||
### 9.5 Architecture for Replacement System
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ VARROA CAMERA SERVICE │
|
||||
├──────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
|
||||
│ │ depthai_gate │ → │ varroa-camera │ → │ HTTP API │ │
|
||||
│ │ (unchanged) │ │ (new service) │ │ (port 80) │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └──────────────┘ │
|
||||
│ ↓ ↓ ↓ │
|
||||
│ /tmp/recording/pics/ Monitor & serve GET /frame │
|
||||
│ frames via inotify GET /stream │
|
||||
│ GET /landmarks │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Open Questions
|
||||
|
||||
| Question | Priority | How to Investigate |
|
||||
|----------|----------|-------------------|
|
||||
| Exact depthai_gate pipeline config | High | SSH in, find config files in /opt/ |
|
||||
| Does depthai_gate publish to Redis? | High | `redis-cli MONITOR` while recording |
|
||||
| Camera-bridge vs depthai_gate relationship | High | Check systemd deps, trace with strace |
|
||||
| Preview config format | Medium | Read `getPreviewConfig()` implementation |
|
||||
| ML model exact location on Bee | Medium | `find /opt /data -name "*.blob" -o -name "*.onnx"` |
|
||||
| Frame timestamp accuracy | Medium | Compare frame timestamps to GNSS time |
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Key File Paths
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `/tmp/recording/pics/` | Live camera frames |
|
||||
| `/tmp/recording/preview/` | Preview mode frames |
|
||||
| `/data/recording/cached_observations/` | Landmark observation images |
|
||||
| `/data/recording/framekm/` | FrameKm upload bundles |
|
||||
| `/data/recording/odc-api.db` | SQLite database |
|
||||
| `/opt/camera-bridge/config.json` | Camera configuration |
|
||||
| `/opt/depthai_gate/` | DepthAI service (estimated) |
|
||||
| `/opt/odc-api/` | Node.js API service |
|
||||
| `/sys/class/vpu/` | VPU sysfs interface |
|
||||
|
||||
## Appendix B: Service Dependencies
|
||||
|
||||
```
|
||||
multi-user.target
|
||||
│
|
||||
├── redis.service [t+2s]
|
||||
│
|
||||
├── depthai_gate.service [t+8s] # MUST start before map-ai
|
||||
│ │
|
||||
│ └── Loads luxonis_vpu.bin
|
||||
│
|
||||
├── map-ai.service [t+12s] # Depends on depthai_gate
|
||||
│ │
|
||||
│ └── Privacy blur, ML inference
|
||||
│
|
||||
├── hivemapper-data-logger.service [t+15s]
|
||||
│
|
||||
└── odc-api.service [t+18s] # REST API
|
||||
```
|
||||
|
||||
## Appendix C: Port Reference
|
||||
|
||||
| Port | Service | Protocol | Binding |
|
||||
|------|---------|----------|---------|
|
||||
| 22 | sshd | TCP | AP only (via socket) |
|
||||
| 5000 | odc-api | HTTP | AP interface |
|
||||
| 6379 | Redis | TCP | localhost |
|
||||
| 8888 | mitmdump | HTTP | localhost |
|
||||
| 11492 | depthai_gate | HTTP/Flask | localhost |
|
||||
|
||||
---
|
||||
|
||||
*End of Report*
|
||||
Loading…
Add table
Add a link
Reference in a new issue