feat: use AprilTag instead of QR code

This commit is contained in:
Schuwi
2025-09-14 18:52:24 +02:00
parent 788ad54724
commit 589c9964aa
600 changed files with 12814 additions and 122 deletions

View File

@@ -0,0 +1,325 @@
# AprilTag Storage Location System Design
## Overview
Implement a hierarchical storage location system with AprilTag tag36h11 generation and assignment capabilities to enable quick component location entry and filtering.
## Database Schema
### 1. Storage Locations Table
```sql
CREATE TABLE storage_locations (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
apriltag_id INTEGER UNIQUE,
parent_id INTEGER REFERENCES storage_locations(id),
level INTEGER NOT NULL DEFAULT 0,
path TEXT NOT NULL, -- Materialized path: "shelf1/drawer2/box3"
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
CONSTRAINT apriltag_id_range CHECK (apriltag_id >= 0 AND apriltag_id <= 586)
);
CREATE INDEX idx_storage_locations_parent_id ON storage_locations(parent_id);
CREATE UNIQUE INDEX idx_storage_locations_apriltag_id ON storage_locations(apriltag_id);
CREATE INDEX idx_storage_locations_path ON storage_locations USING gin(path gin_trgm_ops);
CREATE UNIQUE INDEX idx_storage_locations_name_parent ON storage_locations(name, parent_id);
```
### 2. Modified Components Table
```sql
-- Migration to add storage_location_id to components
ALTER TABLE components
ADD COLUMN storage_location_id INTEGER REFERENCES storage_locations(id),
ADD COLUMN legacy_position VARCHAR(255); -- Keep old position data for migration
-- Move existing position data to legacy_position
UPDATE components SET legacy_position = position;
```
## AprilTag Design Strategy
### AprilTag tag36h11 Family
- **Total tags available**: 587 (IDs 0-586)
- **Format**: Pre-generated SVG files with human-readable ID labels
- **Storage**: `/priv/static/apriltags/tag36h11_id_XXX.svg`
- **Assignment**: One-to-one mapping between AprilTag ID and storage location
### AprilTag Components:
- **AprilTag ID**: Integer (0-586) stored in database
- **SVG File**: Pre-generated with 2-module margin and ID label
- **Human Readable**: "ID XXX" format for easy identification
- **Uniqueness**: Database constraint ensures no duplicate assignments
### Advantages over QR Codes:
1. **Robust Detection**: AprilTags designed for reliable multi-tag detection
2. **Standard Format**: Well-established robotics/AR standard
3. **Better Performance**: More resistant to lighting/angle variations
4. **Multi-tag Support**: Designed specifically for multiple tags in same view
## Multi-AprilTag Detection Strategy
### 1. Spatial Independence
```
Unlike QR codes, AprilTags are designed for multi-tag scenarios:
- Each tag has unique geometric properties
- No interference between nearby tags
- Reliable detection at various scales and angles
```
### 2. Detection Priority
```
Selection Priority:
1. User selection (tap/click on detected tag)
2. Closest tag to center of frame
3. Largest tag in frame (closest to camera)
4. Most recent successfully scanned tag
```
### 3. Visual Feedback
```
Camera Overlay:
- Draw bounding boxes around each detected AprilTag
- Show AprilTag ID and location name
- Highlight "best candidate" with different color
- Allow tap-to-select for disambiguation
```
## Implementation Components
### 1. Elixir Modules
#### Storage Location Schema
```elixir
defmodule ComponentsElixir.Inventory.StorageLocation do
use Ecto.Schema
import Ecto.Changeset
schema "storage_locations" do
field :name, :string
field :description, :string
field :apriltag_id, :integer
field :level, :integer, virtual: true
field :path, :string, virtual: true
field :is_active, :boolean, default: true
belongs_to :parent, __MODULE__
has_many :children, __MODULE__, foreign_key: :parent_id
has_many :components, Component
timestamps()
end
end
```
#### AprilTag Management
```elixir
defmodule ComponentsElixir.AprilTag do
def generate_apriltag_svg(apriltag_id, opts \\ []) do
# Generate SVG with actual AprilTag pattern
# Include human-readable ID below tag
end
def get_apriltag_url(storage_location) do
# Return URL to pre-generated SVG file
end
def available_apriltag_ids() do
# Return list of unused AprilTag IDs (0-586)
end
def generate_all_apriltag_svgs() do
# Pre-generate all 587 SVG files
end
end
```
### 2. Phoenix LiveView Components
#### AprilTag Assignment Interface
```elixir
defmodule ComponentsElixirWeb.StorageLocationsLive do
use ComponentsElixirWeb, :live_view
def mount(_params, _session, socket) do
socket =
socket
|> assign(:scanning, false)
|> assign(:available_apriltag_ids, AprilTag.available_apriltag_ids())
|> assign(:selected_location, nil)
{:ok, socket}
end
def handle_event("assign_apriltag", %{"apriltag_id" => id}, socket) do
# Assign specific AprilTag ID to storage location
end
def handle_event("apriltag_detected", %{"ids" => ids}, socket) do
# Handle multiple AprilTag detection
parsed_tags = Enum.map(ids, &resolve_storage_location/1)
socket =
socket
|> assign(:detected_tags, parsed_tags)
|> maybe_auto_select_location(parsed_tags)
{:noreply, socket}
end
end
```
### 3. JavaScript AprilTag Detection (Future)
#### Camera Integration
```javascript
// assets/js/apriltag_scanner.js
import { AprilTagDetector } from "apriltag-js";
export const AprilTagScanner = {
mounted() {
this.detector = new AprilTagDetector();
this.video = this.el.querySelector('video');
this.canvas = this.el.querySelector('canvas');
this.startCamera();
this.scanLoop();
},
scanLoop() {
if (this.video.readyState === this.video.HAVE_ENOUGH_DATA) {
const detections = this.detector.detect(this.canvas);
if (detections.length > 0) {
const apriltag_ids = detections.map(d => d.id);
this.pushEvent("apriltag_detected", { ids: apriltag_ids });
}
}
requestAnimationFrame(() => this.scanLoop());
}
};
```
## User Experience Flow
### 1. Adding Components with AprilTag Scan
```
1. User clicks "Add Component"
2. Position field shows camera icon
3. Click camera → AprilTag scanner opens
4. Scan storage location AprilTag
5. If multiple tags detected:
- Show overlay with detected locations
- User taps to select specific location
6. Location path auto-filled: "Shelf A → Drawer 2 → Box 5"
7. Component saved with storage_location_id
```
### 2. Storage Location Management
```
1. New "Storage Locations" section in admin
2. Add/edit locations with AprilTag ID selection
3. Auto-assign next available ID or manual selection
4. Download AprilTag SVG with location info
5. Print labels for physical attachment
```
### 3. AprilTag Assignment
```
1. Available AprilTag IDs shown in dropdown (0-586)
2. Current assignments displayed
3. Bulk assignment for initial setup
4. Re-assignment with validation
5. Conflict prevention (one tag per location)
```
## Handling Multiple AprilTags in Same Image
### Strategy 1: Designed for Multi-Tag
- AprilTags inherently support multiple detection
- No spatial interference between tags
- Each tag independently detectable
### Strategy 2: User Selection
- Display all detected tags with IDs
- Show corresponding location names
- Allow tap/click to select intended tag
- Remember user preferences
### Strategy 3: Smart Defaults
- Prioritize by distance to frame center
- Consider tag size (closer = larger)
- Use most recently accessed locations
- Provide manual override always
## Migration Strategy
### Phase 1: Database Migration
1. Run migration to add apriltag_id field
2. Remove qr_code field and constraints
3. Update schema and validation rules
### Phase 2: AprilTag Generation
1. Generate all 587 SVG files using Mix task
2. Serve static files via Phoenix
3. Update UI to show AprilTag instead of QR code
### Phase 3: Assignment Interface
1. Add AprilTag selection to location forms
2. Implement auto-assignment logic
3. Create bulk assignment tools
4. Add conflict detection
### Phase 4: Detection (Future)
1. Integrate AprilTag detection library
2. Implement multi-tag camera interface
3. Add disambiguation UI
4. Test detection robustness
## Technical Dependencies
### Elixir Dependencies
```elixir
# mix.exs - no additional dependencies needed for generation
# Future detection would require:
# {:apriltag, "~> 0.1"} # hypothetical
```
### JavaScript Dependencies (Future Detection)
```javascript
// package.json
{
"apriltag-js": "^1.0.0", // hypothetical
"camera-utils": "^2.0.0" // camera access utilities
}
```
## Database Indexes for Performance
```sql
-- Fast location lookups
CREATE INDEX idx_components_storage_location_id ON components(storage_location_id);
-- Hierarchical queries
CREATE INDEX idx_storage_locations_path_gin ON storage_locations USING gin(path gin_trgm_ops);
-- AprilTag uniqueness and fast lookup
CREATE UNIQUE INDEX idx_storage_locations_apriltag_id ON storage_locations(apriltag_id);
-- Valid AprilTag ID range constraint
ALTER TABLE storage_locations ADD CONSTRAINT apriltag_id_range
CHECK (apriltag_id >= 0 AND apriltag_id <= 586);
```
## Benefits Over QR Code System
1. **Robust Multi-Tag Detection**: AprilTags designed specifically for multiple tags in same view
2. **Better Performance**: More reliable detection under various lighting and angle conditions
3. **Industry Standard**: Widely used in robotics and AR applications
4. **No Encoding Needed**: Simple integer ID mapping instead of complex string encoding
5. **Collision Avoidance**: 587 unique IDs provide ample space without conflicts
6. **Future-Proof**: Ready for advanced detection and tracking features
This design provides a robust foundation for AprilTag-based storage management while leveraging the inherent advantages of AprilTags for multi-tag detection scenarios.

View File

@@ -361,13 +361,7 @@ export const QRScanner = {
```
### JavaScript Dependencies
```json
// package.json
{
"jsqr": "^1.4.0",
"qr-scanner": "^1.4.2"
}
```
???
## Database Indexes for Performance
```sql