Add humanoid robotics guide, SVG diagrams, and NO ASCII rule
- Add comprehensive humanoid robotics guide (humanoid.md) with: - Supported robot kits (Unitree, UBTECH, Poppy, InMoov, ROBOTIS) - BASIC keywords for servo/movement control - Computer vision integration (face, object, gesture detection) - LLM-driven conversational movement - Build guides for beginner to advanced - Safety considerations and complete reception robot example - Create humanoid-architecture.svg with theme transparency - Update PROMPT.md with NO ASCII DIAGRAMS rule - Update devices README to include humanoid robotics section - SVG diagrams for chapter 13 devices - Update documentation style guide with theme transparency
131
PROMPT.md
|
|
@ -328,18 +328,135 @@ When documenting features, verify against actual source:
|
|||
|
||||
---
|
||||
|
||||
## Diagram Guidelines
|
||||
## NO ASCII DIAGRAMS — MANDATORY
|
||||
|
||||
For SVG diagrams in `src/assets/`:
|
||||
**NEVER use ASCII art diagrams in documentation. ALL diagrams must be SVG.**
|
||||
|
||||
### Prohibited ASCII Patterns
|
||||
|
||||
```
|
||||
- Transparent background
|
||||
- Dual-theme support (light/dark CSS)
|
||||
- Width: 1040-1400px
|
||||
- Font: Arial, sans-serif
|
||||
- Colors: Blue #4A90E2, Orange #F5A623, Purple #BD10E0, Green #7ED321
|
||||
❌ NEVER USE:
|
||||
┌─────────┐ ╔═══════╗ +-------+
|
||||
│ Box │ ║ Box ║ | Box |
|
||||
└─────────┘ ╚═══════╝ +-------+
|
||||
|
||||
├── folder/ │ → ▶ ──►
|
||||
│ └── file ▼ ← ◀ ◄──
|
||||
```
|
||||
|
||||
### What to Use Instead
|
||||
|
||||
| Instead of... | Use... |
|
||||
|---------------|--------|
|
||||
| ASCII box diagrams | SVG diagrams in `assets/` |
|
||||
| ASCII flow charts | SVG with arrows and boxes |
|
||||
| ASCII directory trees | Markdown tables or SVG |
|
||||
| ASCII tables with borders | Markdown tables |
|
||||
| ASCII progress indicators | SVG or text description |
|
||||
|
||||
### Directory Trees → Tables
|
||||
|
||||
```markdown
|
||||
❌ WRONG:
|
||||
mybot.gbai/
|
||||
├── mybot.gbdialog/
|
||||
│ └── start.bas
|
||||
└── mybot.gbot/
|
||||
└── config.csv
|
||||
|
||||
✅ CORRECT:
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| `mybot.gbai/` | Root package |
|
||||
| `mybot.gbdialog/` | BASIC scripts |
|
||||
| `mybot.gbdialog/start.bas` | Entry point |
|
||||
| `mybot.gbot/` | Configuration |
|
||||
| `mybot.gbot/config.csv` | Bot settings |
|
||||
```
|
||||
|
||||
### Rationale
|
||||
|
||||
- ASCII breaks in different fonts/editors
|
||||
- Not accessible for screen readers
|
||||
- Cannot adapt to dark/light themes
|
||||
- Looks unprofessional in modern docs
|
||||
- SVGs scale perfectly at any size
|
||||
|
||||
---
|
||||
|
||||
## SVG Diagram Guidelines — Theme Transparency
|
||||
|
||||
All SVG diagrams MUST be theme-agnostic and work with light/dark modes.
|
||||
|
||||
### Required Structure
|
||||
|
||||
Every SVG must include:
|
||||
|
||||
1. **CSS classes for text** (not hardcoded colors)
|
||||
2. **Dark mode media query** in `<style>` block
|
||||
3. **Standard gradient definitions** in `<defs>`
|
||||
|
||||
### Required CSS Block
|
||||
|
||||
```xml
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### Standard Gradients (use these IDs)
|
||||
|
||||
| Gradient ID | Colors | Purpose |
|
||||
|-------------|--------|---------|
|
||||
| `primaryGrad` | `#6366F1` → `#8B5CF6` | Primary/purple elements |
|
||||
| `cyanGrad` | `#06B6D4` → `#0EA5E9` | Cyan/info elements |
|
||||
| `greenGrad` | `#10B981` → `#34D399` | Success/green elements |
|
||||
| `orangeGrad` | `#F59E0B` → `#FBBF24` | Warning/orange elements |
|
||||
|
||||
### Background Rules
|
||||
|
||||
- Use `fill="#FAFBFC"` for main background (not pure white)
|
||||
- Add dot pattern overlay: `fill="url(#dots)"` with `opacity="0.08"`
|
||||
- Cards use `fill="#FFFFFF"` with `stroke="#E2E8F0"` for definition
|
||||
- Use `filter="url(#cardShadow)"` for card depth
|
||||
|
||||
### DO NOT
|
||||
|
||||
- ❌ Hardcode text colors without CSS class
|
||||
- ❌ Use pure black (`#000000`) for text
|
||||
- ❌ Forget the `@media (prefers-color-scheme: dark)` block
|
||||
- ❌ Create new gradient IDs when standard ones exist
|
||||
- ❌ Use ASCII art diagrams when SVG is appropriate
|
||||
|
||||
### DO
|
||||
|
||||
- ✅ Use CSS classes for ALL text elements
|
||||
- ✅ Include dark mode media query in every SVG
|
||||
- ✅ Use standard gradient IDs consistently
|
||||
- ✅ Test SVGs in both light and dark browser modes
|
||||
- ✅ Convert ASCII diagrams to proper SVGs
|
||||
- ✅ Place SVGs in `src/assets/chapter-XX/` directories
|
||||
|
||||
### Dimensions
|
||||
|
||||
- Width: 600-1200px (responsive)
|
||||
- Use `style="max-width: 100%; height: auto;"` when embedding
|
||||
|
||||
### Reference
|
||||
|
||||
See `src/16-appendix-docs-style/svg.md` for complete guidelines.
|
||||
|
||||
---
|
||||
|
||||
## Translation Workflow
|
||||
|
|
|
|||
21
README.md
|
|
@ -10,26 +10,7 @@ A strongly-typed, self-hosted conversational platform focused on convention over
|
|||
|
||||
## 🏗️ Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ General Bots Platform │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ botapp │ │ botui │ │ botserver │ │
|
||||
│ │ (Tauri) │───▶│ (Pure Web) │───▶│ (API) │ │
|
||||
│ │ Desktop │ │ HTMX/HTML │ │ Rust │ │
|
||||
│ └─────────────┘ └─────────────┘ └──────┬──────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────┴───────────┐ │
|
||||
│ │ │ │
|
||||
│ ┌─────▼─────┐ ┌──────▼─────┐ │
|
||||
│ │ botlib │ │ botbook │ │
|
||||
│ │ (Shared) │ │ (Docs) │ │
|
||||
│ └───────────┘ └────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="src/assets/platform-architecture.svg" alt="General Bots Platform Architecture" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -6,24 +6,7 @@
|
|||
|
||||
---
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ 📚 ADD DOCUMENTS TO KNOWLEDGE BASE │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Step │───▶│ Step │───▶│ Step │───▶│ Step │ │ │
|
||||
│ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │
|
||||
│ │ │Prepare │ │ Upload │ │ Index │ │ Test │ │ │
|
||||
│ │ │ Docs │ │ Files │ │ KB │ │ KB │ │ │
|
||||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../../assets/chapter-04/step-flow-4-steps.svg" alt="Add Documents to KB - 4 Step Process" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -57,36 +40,7 @@ Before you begin, make sure you have:
|
|||
|
||||
A **Knowledge Base (KB)** is a collection of documents that your bot uses to answer questions. When a user asks something, the bot searches through these documents to find relevant information.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ HOW KNOWLEDGE BASE WORKS │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ User asks: "What is our refund policy?" │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 🔍 Semantic Search │ │
|
||||
│ │ Searches through all documents in the knowledge base │ │
|
||||
│ └────────────────────────┬────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────────┼───────────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │policies │ │ FAQ │ │ terms │ │
|
||||
│ │ .pdf │ │ .docx │ │ .md │ │
|
||||
│ └────┬────┘ └─────────┘ └─────────┘ │
|
||||
│ │ │
|
||||
│ ▼ Found match! │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ "Refunds are available within 30 days of purchase..." │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ Bot answers with context from the document │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../../assets/chapter-04/kb-semantic-search-flow.svg" alt="Knowledge Base Semantic Search Flow" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -6,24 +6,7 @@
|
|||
|
||||
---
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ 📱 CONNECT WHATSAPP TO YOUR BOT │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Step │───▶│ Step │───▶│ Step │───▶│ Step │ │ │
|
||||
│ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │
|
||||
│ │ │ Meta │ │ Create │ │Configure│ │ Test │ │ │
|
||||
│ │ │ Account │ │ App │ │ Bot │ │ Channel │ │ │
|
||||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../../assets/chapter-04/step-flow-4-steps.svg" alt="Connect WhatsApp - 4 Step Process" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -56,38 +39,7 @@ Before you begin, make sure you have:
|
|||
|
||||
## Understanding WhatsApp Integration
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ HOW WHATSAPP INTEGRATION WORKS │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ User sends message on WhatsApp │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ WhatsApp Cloud API │ │
|
||||
│ │ (Meta's servers receive message) │ │
|
||||
│ └────────────────────────┬────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ │ Webhook │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ General Bots Server │ │
|
||||
│ │ (Your bot processes the message) │ │
|
||||
│ └────────────────────────┬────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ │ API Call │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ WhatsApp Cloud API │ │
|
||||
│ │ (Sends reply to user) │ │
|
||||
│ └────────────────────────┬────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ User receives bot response on WhatsApp │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../../assets/chapter-04/whatsapp-integration-flow.svg" alt="WhatsApp Integration Flow" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -13,26 +13,7 @@ General Bots can run on any device, from mobile phones to minimal embedded hardw
|
|||
- **Education** - Classroom assistants, lab equipment interfaces
|
||||
- **Healthcare** - Patient check-in, medication reminders
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Embedded GB Architecture │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Display │ │ botserver │ │ llama.cpp │ │
|
||||
│ │ LCD/OLED │────▶│ (Rust) │────▶│ (Local) │ │
|
||||
│ │ TFT/HDMI │ │ Port 8088 │ │ Port 8080 │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
│ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │
|
||||
│ │ Keyboard │ │ SQLite │ │ TinyLlama │ │
|
||||
│ │ Buttons │ │ (Data) │ │ GGUF │ │
|
||||
│ │ Touch │ │ │ │ (~700MB) │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-13/embedded-architecture.svg" alt="Embedded GB Architecture" style="max-width: 100%; height: auto;">
|
||||
|
||||
## What's in This Chapter
|
||||
|
||||
|
|
@ -43,6 +24,10 @@ General Bots can run on any device, from mobile phones to minimal embedded hardw
|
|||
- [Supported Hardware](./hardware.md) - SBCs, displays, and peripherals
|
||||
- [Quick Start](./quick-start.md) - Deploy in 5 minutes
|
||||
- [Local LLM](./local-llm.md) - Offline AI with llama.cpp
|
||||
- [Buying Guide](./buying-guide.md) - Choose your first SBC
|
||||
|
||||
### Robotics
|
||||
- [Humanoid Robots](./humanoid.md) - Build intelligent humanoids with CV, LLM, and movement control
|
||||
|
||||
### Deployment Options
|
||||
|
||||
|
|
@ -52,3 +37,4 @@ General Bots can run on any device, from mobile phones to minimal embedded hardw
|
|||
| **Raspberry Pi** | IoT, displays, terminals | 1GB+ RAM |
|
||||
| **Orange Pi** | Full offline AI | 4GB+ RAM for LLM |
|
||||
| **Industrial** | Factory, retail, healthcare | Any ARM/x86 SBC |
|
||||
| **Humanoid Robots** | Service, reception, research | Servo kit + compute board |
|
||||
|
|
|
|||
|
|
@ -16,24 +16,7 @@ A Single Board Computer (SBC) is a complete computer on a single circuit board.
|
|||
|
||||
### Decision Flowchart
|
||||
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ What's your budget? │
|
||||
└───────────┬─────────────┘
|
||||
│
|
||||
┌───────────────────┼───────────────────┐
|
||||
▼ ▼ ▼
|
||||
Under $30 $30-80 $80-150
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ Orange Pi │ │ Raspberry Pi │ │ Orange Pi 5 │
|
||||
│ Zero 3 │ │ 4 / 5 │ │ (with NPU) │
|
||||
│ │ │ │ │ │
|
||||
│ Basic display │ │ Full desktop │ │ Local AI/LLM │
|
||||
│ Simple tasks │ │ Most projects │ │ Edge AI │
|
||||
└───────────────┘ └───────────────┘ └───────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-13/budget-decision-tree.svg" alt="Hardware Budget Decision Tree" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Recommended Starter Kits
|
||||
|
||||
|
|
@ -107,65 +90,61 @@ This board has a 6 TOPS NPU for accelerated AI inference!
|
|||
|
||||
Perfect first project - monitor and log temperature!
|
||||
|
||||
```
|
||||
□ Orange Pi Zero 3 (1GB) $20
|
||||
□ 16GB microSD card $5
|
||||
□ 5V 2A power supply $5
|
||||
□ DHT22 temperature sensor $6
|
||||
□ 0.96" OLED display (I2C) $6
|
||||
□ Jumper wires (female-female) $3
|
||||
─────────────
|
||||
Total: $45
|
||||
```
|
||||
| Item | Price |
|
||||
|------|-------|
|
||||
| Orange Pi Zero 3 (1GB) | $20 |
|
||||
| 16GB microSD card | $5 |
|
||||
| 5V 2A power supply | $5 |
|
||||
| DHT22 temperature sensor | $6 |
|
||||
| 0.96" OLED display (I2C) | $6 |
|
||||
| Jumper wires (female-female) | $3 |
|
||||
| **Total** | **$45** |
|
||||
|
||||
### Smart Doorbell ($70)
|
||||
|
||||
AI-powered doorbell with notifications!
|
||||
|
||||
```
|
||||
□ Raspberry Pi Zero 2 W $15
|
||||
□ Pi Camera Module $25
|
||||
□ Push button $1
|
||||
□ Piezo buzzer $2
|
||||
□ LED (with resistor) $1
|
||||
□ 16GB microSD card $5
|
||||
□ 5V 2.5A power supply $8
|
||||
□ Case $5
|
||||
□ Jumper wires $3
|
||||
─────────────
|
||||
Total: $70
|
||||
```
|
||||
| Item | Price |
|
||||
|------|-------|
|
||||
| Raspberry Pi Zero 2 W | $15 |
|
||||
| Pi Camera Module | $25 |
|
||||
| Push button | $1 |
|
||||
| Piezo buzzer | $2 |
|
||||
| LED (with resistor) | $1 |
|
||||
| 16GB microSD card | $5 |
|
||||
| 5V 2.5A power supply | $8 |
|
||||
| Case | $5 |
|
||||
| Jumper wires | $3 |
|
||||
| **Total** | **$70** |
|
||||
|
||||
### Offline AI Assistant ($150)
|
||||
|
||||
Run AI completely offline - no internet needed!
|
||||
|
||||
```
|
||||
□ Orange Pi 5 (8GB RAM) $89
|
||||
□ 128GB NVMe SSD $20
|
||||
□ 12V 3A power supply $12
|
||||
□ 7" HDMI touchscreen $45
|
||||
□ USB microphone $10
|
||||
□ Case with fan $15
|
||||
□ Jumper wires $3
|
||||
─────────────
|
||||
Total: ~$195
|
||||
```
|
||||
| Item | Price |
|
||||
|------|-------|
|
||||
| Orange Pi 5 (8GB RAM) | $89 |
|
||||
| 128GB NVMe SSD | $20 |
|
||||
| 12V 3A power supply | $12 |
|
||||
| 7" HDMI touchscreen | $45 |
|
||||
| USB microphone | $10 |
|
||||
| Case with fan | $15 |
|
||||
| Jumper wires | $3 |
|
||||
| **Total** | **~$195** |
|
||||
|
||||
### Voice-Controlled Lights ($55)
|
||||
|
||||
Control your lights by talking!
|
||||
|
||||
```
|
||||
□ Raspberry Pi 4 (2GB) $35
|
||||
□ 4-channel relay module $6
|
||||
□ USB microphone $8
|
||||
□ 16GB microSD card $5
|
||||
□ 5V 3A power supply $8
|
||||
□ Jumper wires $3
|
||||
─────────────
|
||||
Total: ~$65
|
||||
```
|
||||
| Item | Price |
|
||||
|------|-------|
|
||||
| Raspberry Pi 4 (2GB) | $35 |
|
||||
| 4-channel relay module | $6 |
|
||||
| USB microphone | $8 |
|
||||
| 16GB microSD card | $5 |
|
||||
| 5V 3A power supply | $8 |
|
||||
| Jumper wires | $3 |
|
||||
| **Total** | **~$65** |
|
||||
|
||||
## Where to Buy (By Region)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,22 +39,7 @@
|
|||
|
||||
The Orange Pi 5 with RK3588S is ideal for embedded LLM:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Orange Pi 5 - Best for Offline AI │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ CPU: Rockchip RK3588S (4x A76 + 4x A55) │
|
||||
│ NPU: 6 TOPS (Neural Processing Unit) │
|
||||
│ GPU: Mali-G610 MP4 │
|
||||
│ RAM: 4GB / 8GB / 16GB LPDDR4X │
|
||||
│ Storage: M.2 NVMe + eMMC + microSD │
|
||||
│ │
|
||||
│ LLM Performance: │
|
||||
│ ├─ TinyLlama 1.1B Q4: ~8-12 tokens/sec │
|
||||
│ ├─ Phi-2 2.7B Q4: ~4-6 tokens/sec │
|
||||
│ └─ With NPU (rkllm): ~20-30 tokens/sec │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-13/orange-pi-5-specs.svg" alt="Orange Pi 5 Specifications" style="max-width: 100%; height: auto;">
|
||||
|
||||
## Displays
|
||||
|
||||
|
|
@ -68,13 +53,7 @@ For text-only interfaces:
|
|||
| HD44780 20x4 | 20 chars × 4 lines | I2C/GPIO | More context |
|
||||
| LCD2004 | 20 chars × 4 lines | I2C | Industrial |
|
||||
|
||||
**Example output on 16x2:**
|
||||
```
|
||||
┌────────────────┐
|
||||
│> How can I help│
|
||||
│< Processing... │
|
||||
└────────────────┘
|
||||
```
|
||||
**Example output on 16x2:** Simple text display showing user prompt and bot status.
|
||||
|
||||
### OLED Displays
|
||||
|
||||
|
|
@ -138,17 +117,7 @@ For low-power, readable in sunlight:
|
|||
|
||||
### Buttons & GPIO
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Simple 4-Button Interface │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ [◄ PREV] [▲ UP] [▼ DOWN] [► SELECT] │
|
||||
│ │
|
||||
│ GPIO 17 GPIO 27 GPIO 22 GPIO 23 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-13/gpio-button-interface.svg" alt="GPIO Button Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
## Enclosures
|
||||
|
||||
|
|
|
|||
654
src/13-devices/humanoid.md
Normal file
|
|
@ -0,0 +1,654 @@
|
|||
# Humanoid Robotics with General Bots
|
||||
|
||||
Build custom humanoid robots powered by General Bots, using computer vision from botmodels, LLM intelligence from botserver, and BASIC keywords for movement control.
|
||||
|
||||
## Overview
|
||||
|
||||
General Bots transforms humanoid robot kits into intelligent conversational assistants capable of:
|
||||
|
||||
- **Natural conversation** via LLM integration
|
||||
- **Visual recognition** using botmodels CV pipelines
|
||||
- **Expressive movement** through BASIC keyword commands
|
||||
- **Autonomous behavior** with scheduled tasks and triggers
|
||||
|
||||
<img src="../assets/chapter-13/humanoid-architecture.svg" alt="Humanoid Robot Architecture" style="max-width: 100%; height: auto;">
|
||||
|
||||
## Supported Robot Kits
|
||||
|
||||
### Consumer Kits
|
||||
|
||||
| Kit | DOF | Height | Price | Best For |
|
||||
|-----|-----|--------|-------|----------|
|
||||
| **Unitree G1** | 23 | 127cm | $16,000 | Research, commercial |
|
||||
| **Unitree H1** | 19 | 180cm | $90,000 | Industrial, research |
|
||||
| **UBTECH Walker S** | 41 | 145cm | Contact | Enterprise service |
|
||||
| **Fourier GR-1** | 40 | 165cm | $100,000+ | Research |
|
||||
| **Agility Digit** | 16 | 175cm | Lease | Warehouse, logistics |
|
||||
|
||||
### DIY/Maker Kits
|
||||
|
||||
| Kit | DOF | Height | Price | Best For |
|
||||
|-----|-----|--------|-------|----------|
|
||||
| **Poppy Humanoid** | 25 | 83cm | $8,000 | Education, research |
|
||||
| **InMoov** | 22+ | 170cm | $1,500+ | Makers, open source |
|
||||
| **Reachy** | 14 | Torso | $15,000 | HRI research |
|
||||
| **NimbRo-OP2X** | 20 | 95cm | $30,000 | RoboCup, research |
|
||||
| **ROBOTIS OP3** | 20 | 51cm | $11,000 | Education, competition |
|
||||
|
||||
### Affordable Entry Points
|
||||
|
||||
| Kit | DOF | Height | Price | Best For |
|
||||
|-----|-----|--------|-------|----------|
|
||||
| **ROBOTIS MINI** | 16 | 27cm | $500 | Learning, hobby |
|
||||
| **Lynxmotion SES-V2** | 17 | 40cm | $800 | Education |
|
||||
| **Hiwonder TonyPi** | 16 | 35cm | $400 | Raspberry Pi projects |
|
||||
| **XGO-Mini** | 12 | 20cm | $350 | Quadruped + arm |
|
||||
|
||||
## Architecture
|
||||
|
||||
### System Components
|
||||
|
||||
| Component | Role | Technology |
|
||||
|-----------|------|------------|
|
||||
| **botserver** | Brain - LLM, conversation, decisions | Rust, Port 8088 |
|
||||
| **botmodels** | Eyes - CV, face recognition, object detection | Python, ONNX |
|
||||
| **BASIC Runtime** | Motor control - movement sequences | Keywords → servo commands |
|
||||
| **Hardware Bridge** | Translation layer | GPIO, Serial, CAN bus |
|
||||
|
||||
### Communication Flow
|
||||
|
||||
| Step | From | To | Protocol |
|
||||
|------|------|----|----------|
|
||||
| 1 | Microphone | botserver | Audio stream / STT |
|
||||
| 2 | botserver | LLM | HTTP API |
|
||||
| 3 | LLM response | BASIC interpreter | Internal |
|
||||
| 4 | BASIC keywords | Hardware bridge | Serial/GPIO |
|
||||
| 5 | Hardware bridge | Servos | PWM/CAN |
|
||||
| 6 | Camera | botmodels | Video stream |
|
||||
| 7 | botmodels | botserver | Detection events |
|
||||
|
||||
## Hardware Setup
|
||||
|
||||
### Computing Options
|
||||
|
||||
| Option | Specs | Use Case |
|
||||
|--------|-------|----------|
|
||||
| **Jetson Orin Nano** | 8GB, 40 TOPS | Full CV + local LLM |
|
||||
| **Raspberry Pi 5** | 8GB, no NPU | Cloud LLM, basic CV |
|
||||
| **Orange Pi 5** | 8GB, 6 TOPS NPU | Local LLM, basic CV |
|
||||
| **Intel NUC** | 32GB, x86 | Maximum performance |
|
||||
|
||||
### Servo Controllers
|
||||
|
||||
| Controller | Channels | Interface | Best For |
|
||||
|------------|----------|-----------|----------|
|
||||
| **PCA9685** | 16 PWM | I2C | Small robots |
|
||||
| **Pololu Maestro** | 6-24 | USB/Serial | Hobby servos |
|
||||
| **Dynamixel U2D2** | 254 | USB/TTL/RS485 | Dynamixel servos |
|
||||
| **ODrive** | 2 BLDC | USB/CAN | High-torque joints |
|
||||
|
||||
### Sensor Integration
|
||||
|
||||
| Sensor | Purpose | Interface |
|
||||
|--------|---------|-----------|
|
||||
| USB Camera | Vision, face detection | USB/CSI |
|
||||
| IMU (MPU6050) | Balance, orientation | I2C |
|
||||
| ToF (VL53L0X) | Distance sensing | I2C |
|
||||
| Microphone Array | Voice input, direction | USB/I2S |
|
||||
| Force Sensors | Grip feedback | ADC |
|
||||
|
||||
## BASIC Keywords for Robotics
|
||||
|
||||
### Servo Control
|
||||
|
||||
```basic
|
||||
' Initialize servo controller
|
||||
SERVO INIT "PCA9685", address: 0x40
|
||||
|
||||
' Move single servo
|
||||
SERVO MOVE channel, angle, speed
|
||||
SERVO MOVE 0, 90, 50 ' Channel 0 to 90° at speed 50
|
||||
|
||||
' Move multiple servos simultaneously
|
||||
SERVO SYNC moves
|
||||
SERVO SYNC [[0, 90], [1, 45], [2, 135]]
|
||||
|
||||
' Read servo position
|
||||
position = SERVO READ channel
|
||||
```
|
||||
|
||||
### Predefined Movements
|
||||
|
||||
```basic
|
||||
' Humanoid poses
|
||||
ROBOT POSE "stand"
|
||||
ROBOT POSE "sit"
|
||||
ROBOT POSE "wave"
|
||||
ROBOT POSE "bow"
|
||||
ROBOT POSE "point", direction: "left"
|
||||
|
||||
' Walking
|
||||
ROBOT WALK steps, direction
|
||||
ROBOT WALK 5, "forward"
|
||||
ROBOT WALK 3, "backward"
|
||||
ROBOT TURN degrees
|
||||
ROBOT TURN 90 ' Turn right 90°
|
||||
ROBOT TURN -45 ' Turn left 45°
|
||||
|
||||
' Arm movements
|
||||
ROBOT ARM "left", action: "raise"
|
||||
ROBOT ARM "right", action: "extend"
|
||||
ROBOT GRIP "left", "open"
|
||||
ROBOT GRIP "right", "close"
|
||||
|
||||
' Head movements
|
||||
ROBOT HEAD "nod"
|
||||
ROBOT HEAD "shake"
|
||||
ROBOT HEAD "look", pan: 30, tilt: -15
|
||||
```
|
||||
|
||||
### Motion Sequences
|
||||
|
||||
```basic
|
||||
' Define custom sequence
|
||||
SEQUENCE DEFINE "greeting"
|
||||
ROBOT POSE "stand"
|
||||
WAIT 500
|
||||
ROBOT HEAD "look", pan: 0, tilt: 0
|
||||
ROBOT ARM "right", action: "wave"
|
||||
WAIT 1000
|
||||
ROBOT POSE "bow"
|
||||
WAIT 800
|
||||
ROBOT POSE "stand"
|
||||
SEQUENCE END
|
||||
|
||||
' Play sequence
|
||||
SEQUENCE PLAY "greeting"
|
||||
|
||||
' Interrupt sequence
|
||||
SEQUENCE STOP
|
||||
```
|
||||
|
||||
### Balance and Safety
|
||||
|
||||
```basic
|
||||
' Enable balance control
|
||||
ROBOT BALANCE ON
|
||||
ROBOT BALANCE OFF
|
||||
|
||||
' Set safety limits
|
||||
ROBOT LIMITS torque: 80, speed: 70
|
||||
|
||||
' Emergency stop
|
||||
ROBOT STOP
|
||||
|
||||
' Check stability
|
||||
stable = ROBOT STABLE
|
||||
IF NOT stable THEN
|
||||
ROBOT POSE "stable"
|
||||
END IF
|
||||
```
|
||||
|
||||
## Computer Vision Integration
|
||||
|
||||
### Face Detection and Recognition
|
||||
|
||||
```basic
|
||||
' Start face detection
|
||||
CV START "face_detection"
|
||||
|
||||
' Wait for face
|
||||
face = CV DETECT "face", timeout: 5000
|
||||
|
||||
IF face THEN
|
||||
' Get face details
|
||||
name = face.identity
|
||||
emotion = face.emotion
|
||||
direction = face.direction
|
||||
|
||||
' React to person
|
||||
ROBOT HEAD "look", pan: direction.x, tilt: direction.y
|
||||
|
||||
IF name <> "unknown" THEN
|
||||
TALK "Hello " + name + "!"
|
||||
SEQUENCE PLAY "greeting"
|
||||
ELSE
|
||||
TALK "Hello! I don't think we've met."
|
||||
END IF
|
||||
END IF
|
||||
```
|
||||
|
||||
### Object Detection
|
||||
|
||||
```basic
|
||||
' Detect objects in view
|
||||
objects = CV DETECT "objects"
|
||||
|
||||
FOR EACH obj IN objects
|
||||
IF obj.class = "cup" THEN
|
||||
' Point at the cup
|
||||
ROBOT ARM "right", action: "point"
|
||||
ROBOT HEAD "look", pan: obj.x, tilt: obj.y
|
||||
TALK "I see a " + obj.color + " cup"
|
||||
END IF
|
||||
NEXT
|
||||
```
|
||||
|
||||
### Gesture Recognition
|
||||
|
||||
```basic
|
||||
' Enable gesture detection
|
||||
CV START "gesture_detection"
|
||||
|
||||
' React to gestures
|
||||
gesture = CV DETECT "gesture", timeout: 10000
|
||||
|
||||
SELECT CASE gesture.type
|
||||
CASE "wave"
|
||||
SEQUENCE PLAY "wave_back"
|
||||
CASE "thumbs_up"
|
||||
ROBOT POSE "thumbs_up"
|
||||
TALK "Great!"
|
||||
CASE "come_here"
|
||||
ROBOT WALK 3, "forward"
|
||||
CASE "stop"
|
||||
ROBOT STOP
|
||||
END SELECT
|
||||
```
|
||||
|
||||
### Following Behavior
|
||||
|
||||
```basic
|
||||
' Follow a person
|
||||
CV START "person_tracking"
|
||||
|
||||
WHILE TRUE
|
||||
person = CV TRACK "person"
|
||||
|
||||
IF person THEN
|
||||
' Keep person centered
|
||||
IF person.x < -20 THEN
|
||||
ROBOT TURN -10
|
||||
ELSE IF person.x > 20 THEN
|
||||
ROBOT TURN 10
|
||||
END IF
|
||||
|
||||
' Maintain distance
|
||||
IF person.distance < 1.0 THEN
|
||||
ROBOT WALK 1, "backward"
|
||||
ELSE IF person.distance > 2.0 THEN
|
||||
ROBOT WALK 1, "forward"
|
||||
END IF
|
||||
ELSE
|
||||
' Lost tracking, look around
|
||||
ROBOT HEAD "look", pan: 45, tilt: 0
|
||||
WAIT 1000
|
||||
ROBOT HEAD "look", pan: -45, tilt: 0
|
||||
WAIT 1000
|
||||
END IF
|
||||
|
||||
WAIT 100
|
||||
WEND
|
||||
```
|
||||
|
||||
## LLM-Driven Behavior
|
||||
|
||||
### Conversational Movement
|
||||
|
||||
```basic
|
||||
' System prompt for embodied AI
|
||||
system_prompt = "You are a helpful humanoid robot assistant. "
|
||||
system_prompt = system_prompt + "When responding, include movement commands in brackets. "
|
||||
system_prompt = system_prompt + "Available: [wave], [nod], [shake_head], [bow], [point_left], [point_right], [think]"
|
||||
|
||||
SET SYSTEM PROMPT system_prompt
|
||||
|
||||
' Process conversation with movements
|
||||
response = LLM "How can I help you today?"
|
||||
|
||||
' Parse and execute movements
|
||||
movements = EXTRACT_BRACKETS response
|
||||
FOR EACH move IN movements
|
||||
SELECT CASE move
|
||||
CASE "wave"
|
||||
SEQUENCE PLAY "wave"
|
||||
CASE "nod"
|
||||
ROBOT HEAD "nod"
|
||||
CASE "shake_head"
|
||||
ROBOT HEAD "shake"
|
||||
CASE "bow"
|
||||
ROBOT POSE "bow"
|
||||
CASE "point_left"
|
||||
ROBOT ARM "left", action: "point"
|
||||
CASE "point_right"
|
||||
ROBOT ARM "right", action: "point"
|
||||
CASE "think"
|
||||
ROBOT HEAD "look", pan: 15, tilt: 15
|
||||
END SELECT
|
||||
NEXT
|
||||
|
||||
' Speak cleaned response
|
||||
clean_response = REMOVE_BRACKETS response
|
||||
TALK clean_response
|
||||
```
|
||||
|
||||
### Autonomous Behavior Loop
|
||||
|
||||
```basic
|
||||
' Main behavior loop
|
||||
WHILE TRUE
|
||||
' Check for voice input
|
||||
input = HEAR timeout: 5000
|
||||
|
||||
IF input <> "" THEN
|
||||
' Process with LLM
|
||||
response = LLM input
|
||||
ProcessResponse response
|
||||
ELSE
|
||||
' Idle behaviors
|
||||
idle_action = RANDOM 1, 5
|
||||
|
||||
SELECT CASE idle_action
|
||||
CASE 1
|
||||
' Look around
|
||||
ROBOT HEAD "look", pan: RANDOM -30, 30, tilt: 0
|
||||
CASE 2
|
||||
' Check for faces
|
||||
face = CV DETECT "face", timeout: 1000
|
||||
IF face THEN
|
||||
TALK "Hello there!"
|
||||
SEQUENCE PLAY "greeting"
|
||||
END IF
|
||||
CASE 3
|
||||
' Slight movement
|
||||
ROBOT POSE "relax"
|
||||
CASE 4, 5
|
||||
' Do nothing
|
||||
END SELECT
|
||||
END IF
|
||||
|
||||
WAIT 1000
|
||||
WEND
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Robot Definition File
|
||||
|
||||
Create `robot.csv` in your `.gbot` folder:
|
||||
|
||||
| name | value |
|
||||
|------|-------|
|
||||
| robot_type | humanoid |
|
||||
| controller | PCA9685 |
|
||||
| controller_address | 0x40 |
|
||||
| servo_count | 16 |
|
||||
| balance_enabled | true |
|
||||
| cv_enabled | true |
|
||||
| cv_model | yolov8n |
|
||||
| face_model | arcface |
|
||||
|
||||
### Servo Mapping
|
||||
|
||||
Create `servos.csv` to map joints to channels:
|
||||
|
||||
| joint | channel | min_angle | max_angle | home | inverted |
|
||||
|-------|---------|-----------|-----------|------|----------|
|
||||
| head_pan | 0 | -90 | 90 | 0 | false |
|
||||
| head_tilt | 1 | -45 | 45 | 0 | false |
|
||||
| left_shoulder | 2 | -90 | 180 | 0 | false |
|
||||
| left_elbow | 3 | 0 | 135 | 90 | false |
|
||||
| left_wrist | 4 | -90 | 90 | 0 | false |
|
||||
| right_shoulder | 5 | -90 | 180 | 0 | true |
|
||||
| right_elbow | 6 | 0 | 135 | 90 | true |
|
||||
| right_wrist | 7 | -90 | 90 | 0 | true |
|
||||
| left_hip | 8 | -45 | 45 | 0 | false |
|
||||
| left_knee | 9 | 0 | 90 | 0 | false |
|
||||
| left_ankle | 10 | -30 | 30 | 0 | false |
|
||||
| right_hip | 11 | -45 | 45 | 0 | true |
|
||||
| right_knee | 12 | 0 | 90 | 0 | true |
|
||||
| right_ankle | 13 | -30 | 30 | 0 | true |
|
||||
| left_grip | 14 | 0 | 90 | 0 | false |
|
||||
| right_grip | 15 | 0 | 90 | 0 | false |
|
||||
|
||||
### Motion Files
|
||||
|
||||
Create `motions.csv` for predefined sequences:
|
||||
|
||||
| name | frames |
|
||||
|------|--------|
|
||||
| wave | [{"t":0,"joints":{"right_shoulder":90}},{"t":500,"joints":{"right_shoulder":120}},{"t":1000,"joints":{"right_shoulder":90}}] |
|
||||
| bow | [{"t":0,"joints":{"head_tilt":0}},{"t":500,"joints":{"head_tilt":-30}},{"t":1500,"joints":{"head_tilt":0}}] |
|
||||
| nod | [{"t":0,"joints":{"head_tilt":0}},{"t":200,"joints":{"head_tilt":-15}},{"t":400,"joints":{"head_tilt":10}},{"t":600,"joints":{"head_tilt":0}}] |
|
||||
|
||||
## Build Guides
|
||||
|
||||
### Beginner: Desktop Companion
|
||||
|
||||
**Budget: ~$600**
|
||||
|
||||
| Component | Model | Price |
|
||||
|-----------|-------|-------|
|
||||
| Robot Kit | ROBOTIS MINI | $500 |
|
||||
| Compute | Raspberry Pi 5 4GB | $60 |
|
||||
| Camera | Pi Camera v3 | $25 |
|
||||
| Microphone | USB mic | $15 |
|
||||
|
||||
**Capabilities:**
|
||||
- Voice conversation via cloud LLM
|
||||
- Basic face detection
|
||||
- Gesture responses
|
||||
- Desktop assistant
|
||||
|
||||
### Intermediate: Service Robot
|
||||
|
||||
**Budget: ~$10,000**
|
||||
|
||||
| Component | Model | Price |
|
||||
|-----------|-------|-------|
|
||||
| Robot Kit | Poppy Humanoid | $8,000 |
|
||||
| Compute | Jetson Orin Nano | $500 |
|
||||
| Camera | Intel RealSense D435 | $300 |
|
||||
| Microphone | ReSpeaker Array | $100 |
|
||||
| Speakers | USB powered | $50 |
|
||||
|
||||
**Capabilities:**
|
||||
- Local LLM (Llama 3.2 3B)
|
||||
- Full body tracking
|
||||
- Object manipulation
|
||||
- Walking and navigation
|
||||
|
||||
### Advanced: Research Platform
|
||||
|
||||
**Budget: ~$20,000+**
|
||||
|
||||
| Component | Model | Price |
|
||||
|-----------|-------|-------|
|
||||
| Robot Kit | Unitree G1 EDU | $16,000 |
|
||||
| Compute | Onboard + Workstation | $3,000 |
|
||||
| Sensors | LiDAR + Depth cameras | $1,000 |
|
||||
|
||||
**Capabilities:**
|
||||
- Full autonomous navigation
|
||||
- Complex manipulation
|
||||
- Multi-modal interaction
|
||||
- Research-grade motion control
|
||||
|
||||
## Safety Considerations
|
||||
|
||||
### Physical Safety
|
||||
|
||||
- Always have emergency stop accessible
|
||||
- Set conservative torque limits initially
|
||||
- Test movements at reduced speed first
|
||||
- Ensure stable base before walking
|
||||
- Keep clear area around robot
|
||||
|
||||
### Software Safety
|
||||
|
||||
```basic
|
||||
' Always wrap motion code in error handling
|
||||
ON ERROR GOTO SafetyStop
|
||||
|
||||
' Motion code here...
|
||||
|
||||
END
|
||||
|
||||
SafetyStop:
|
||||
ROBOT STOP
|
||||
ROBOT POSE "safe"
|
||||
LOG ERROR "Emergency stop: " + ERROR MESSAGE
|
||||
TALK "I need to stop for safety"
|
||||
```
|
||||
|
||||
### Operating Guidelines
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Unknown obstacle detected | Stop, assess, navigate |
|
||||
| Balance threshold exceeded | Widen stance or sit |
|
||||
| Person too close | Step back, warn verbally |
|
||||
| Battery low | Announce, return to charger |
|
||||
| Communication lost | Safe pose, wait for reconnect |
|
||||
|
||||
## Example: Reception Robot
|
||||
|
||||
Complete example of a reception desk humanoid:
|
||||
|
||||
```basic
|
||||
' reception-robot.bas
|
||||
' A humanoid reception assistant
|
||||
|
||||
' Initialize systems
|
||||
SERVO INIT "PCA9685", address: 0x40
|
||||
CV START "face_detection"
|
||||
ROBOT POSE "stand"
|
||||
ROBOT BALANCE ON
|
||||
|
||||
' Set personality
|
||||
SET SYSTEM PROMPT "You are a friendly reception robot at TechCorp. "
|
||||
SET SYSTEM PROMPT SYSTEM PROMPT + "Greet visitors, answer questions about the company, "
|
||||
SET SYSTEM PROMPT SYSTEM PROMPT + "and direct them to the right department. "
|
||||
SET SYSTEM PROMPT SYSTEM PROMPT + "Be warm and professional. Use [wave], [nod], [point_left], [point_right] for gestures."
|
||||
|
||||
' Load company knowledge
|
||||
USE KB "company-info"
|
||||
|
||||
' Main loop
|
||||
WHILE TRUE
|
||||
' Watch for approaching people
|
||||
face = CV DETECT "face", timeout: 2000
|
||||
|
||||
IF face THEN
|
||||
' Turn to face person
|
||||
ROBOT HEAD "look", pan: face.direction.x, tilt: face.direction.y
|
||||
|
||||
' Check if known
|
||||
IF face.identity <> "unknown" THEN
|
||||
TALK "Welcome back, " + face.identity + "!"
|
||||
SEQUENCE PLAY "wave"
|
||||
ELSE
|
||||
TALK "Hello! Welcome to TechCorp. How can I help you today?"
|
||||
SEQUENCE PLAY "greeting"
|
||||
END IF
|
||||
|
||||
' Enter conversation mode
|
||||
ConversationLoop
|
||||
END IF
|
||||
|
||||
' Idle animation
|
||||
IdleBehavior
|
||||
|
||||
WAIT 500
|
||||
WEND
|
||||
|
||||
SUB ConversationLoop
|
||||
silence_count = 0
|
||||
|
||||
WHILE silence_count < 3
|
||||
input = HEAR timeout: 10000
|
||||
|
||||
IF input <> "" THEN
|
||||
silence_count = 0
|
||||
|
||||
' Process with LLM
|
||||
response = LLM input
|
||||
|
||||
' Execute gestures
|
||||
ExecuteGestures response
|
||||
|
||||
' Speak response
|
||||
TALK REMOVE_BRACKETS response
|
||||
|
||||
' Check for directions
|
||||
IF CONTAINS response, "point_left" THEN
|
||||
TALK "The elevators are to your left"
|
||||
ELSE IF CONTAINS response, "point_right" THEN
|
||||
TALK "You'll find that department to your right"
|
||||
END IF
|
||||
ELSE
|
||||
silence_count = silence_count + 1
|
||||
|
||||
IF silence_count = 2 THEN
|
||||
TALK "Is there anything else I can help with?"
|
||||
ROBOT HEAD "tilt", angle: 10
|
||||
END IF
|
||||
END IF
|
||||
WEND
|
||||
|
||||
TALK "Have a great day!"
|
||||
SEQUENCE PLAY "wave"
|
||||
END SUB
|
||||
|
||||
SUB IdleBehavior
|
||||
action = RANDOM 1, 10
|
||||
|
||||
SELECT CASE action
|
||||
CASE 1
|
||||
ROBOT HEAD "look", pan: RANDOM -20, 20, tilt: 0
|
||||
CASE 2
|
||||
ROBOT POSE "relax"
|
||||
CASE 3, 4, 5, 6, 7, 8, 9, 10
|
||||
' Mostly stay still
|
||||
END SELECT
|
||||
END SUB
|
||||
|
||||
SUB ExecuteGestures response
|
||||
IF CONTAINS response, "[wave]" THEN
|
||||
SEQUENCE PLAY "wave"
|
||||
END IF
|
||||
IF CONTAINS response, "[nod]" THEN
|
||||
ROBOT HEAD "nod"
|
||||
END IF
|
||||
IF CONTAINS response, "[point_left]" THEN
|
||||
ROBOT ARM "left", action: "point"
|
||||
END IF
|
||||
IF CONTAINS response, "[point_right]" THEN
|
||||
ROBOT ARM "right", action: "point"
|
||||
END IF
|
||||
END SUB
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
### Robot Kits
|
||||
|
||||
- [Unitree Robotics](https://www.unitree.com/) - G1, H1 humanoids
|
||||
- [ROBOTIS](https://www.robotis.com/) - OP3, MINI, Dynamixel
|
||||
- [Poppy Project](https://www.poppy-project.org/) - Open source humanoid
|
||||
- [InMoov](https://inmoov.fr/) - 3D printable humanoid
|
||||
- [Pollen Robotics](https://www.pollen-robotics.com/) - Reachy
|
||||
|
||||
### Components
|
||||
|
||||
- [Dynamixel](https://www.robotis.us/dynamixel/) - Smart servos
|
||||
- [ODrive](https://odriverobotics.com/) - BLDC motor control
|
||||
- [Intel RealSense](https://www.intelrealsense.com/) - Depth cameras
|
||||
- [NVIDIA Jetson](https://developer.nvidia.com/embedded/jetson-modules) - Edge AI
|
||||
|
||||
### Learning
|
||||
|
||||
- [ROS 2 Humanoid](https://docs.ros.org/) - Robot Operating System
|
||||
- [MoveIt](https://moveit.ros.org/) - Motion planning
|
||||
- [OpenCV](https://opencv.org/) - Computer vision
|
||||
- [MediaPipe](https://mediapipe.dev/) - Body/hand tracking
|
||||
|
|
@ -4,24 +4,7 @@ Run AI inference completely offline on embedded devices. No internet, no API cos
|
|||
|
||||
## Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Local LLM Architecture │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ User Input ──▶ botserver ──▶ llama.cpp ──▶ Response │
|
||||
│ │ │ │
|
||||
│ │ ┌────┴────┐ │
|
||||
│ │ │ Model │ │
|
||||
│ │ │ GGUF │ │
|
||||
│ │ │ (Q4_K) │ │
|
||||
│ │ └─────────┘ │
|
||||
│ │ │
|
||||
│ SQLite DB │
|
||||
│ (sessions) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-13/local-llm-architecture.svg" alt="Local LLM Architecture" style="max-width: 100%; height: auto;">
|
||||
|
||||
## Recommended Models
|
||||
|
||||
|
|
|
|||
|
|
@ -6,27 +6,7 @@ Deploy General Bots as the primary interface on Android and HarmonyOS devices, t
|
|||
|
||||
BotDevice transforms any Android or HarmonyOS device into a dedicated General Bots system, removing manufacturer bloatware and installing GB as the default launcher.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ BotDevice Architecture │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ BotDevice App (Tauri) │ │
|
||||
│ ├──────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ botui/ui/suite │ Tauri Android │ src/lib.rs (Rust) │ │
|
||||
│ │ (Web Interface) │ (WebView + NDK) │ (Backend + Hardware) │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────┴────────────────────────────┐ │
|
||||
│ │ Android/HarmonyOS System │ │
|
||||
│ │ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Camera │ │ GPS │ │ WiFi │ │ Storage │ │ │
|
||||
│ │ └─────────┘ └──────────┘ └────────┘ └─────────┘ │ │
|
||||
│ └───────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-13/botdevice-architecture.svg" alt="BotDevice Architecture" style="max-width: 100%; height: auto;">
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
|
|
@ -226,38 +206,23 @@ adb reboot
|
|||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
botdevice/
|
||||
├── Cargo.toml # Rust/Tauri dependencies
|
||||
├── tauri.conf.json # Tauri config → botui/ui/suite
|
||||
├── build.rs # Build script
|
||||
├── src/lib.rs # Android entry point
|
||||
│
|
||||
├── icons/
|
||||
│ ├── gb-bot.svg # Source icon
|
||||
│ ├── icon.png # Main icon (512x512)
|
||||
│ └── */ic_launcher.png # Icons by density
|
||||
│
|
||||
├── scripts/
|
||||
│ ├── generate-icons.sh # Generate PNGs from SVG
|
||||
│ └── create-bootanimation.sh
|
||||
│
|
||||
├── capabilities/
|
||||
│ └── default.json # Tauri permissions
|
||||
│
|
||||
├── gen/android/ # Generated Android project
|
||||
│ └── app/src/main/
|
||||
│ ├── AndroidManifest.xml
|
||||
│ └── res/values/themes.xml
|
||||
│
|
||||
└── rom/ # Installation tools
|
||||
├── install.sh # Interactive installer
|
||||
├── scripts/
|
||||
│ ├── debloat.sh # Remove bloatware
|
||||
│ └── build-magisk-module.sh
|
||||
└── gsi/
|
||||
└── README.md # GSI instructions
|
||||
```
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| `Cargo.toml` | Rust/Tauri dependencies |
|
||||
| `tauri.conf.json` | Tauri config → botui/ui/suite |
|
||||
| `build.rs` | Build script |
|
||||
| `src/lib.rs` | Android entry point |
|
||||
| `icons/gb-bot.svg` | Source icon |
|
||||
| `icons/icon.png` | Main icon (512x512) |
|
||||
| `icons/*/ic_launcher.png` | Icons by density |
|
||||
| `scripts/generate-icons.sh` | Generate PNGs from SVG |
|
||||
| `scripts/create-bootanimation.sh` | Boot animation generator |
|
||||
| `capabilities/default.json` | Tauri permissions |
|
||||
| `gen/android/` | Generated Android project |
|
||||
| `rom/install.sh` | Interactive installer |
|
||||
| `rom/scripts/debloat.sh` | Remove bloatware |
|
||||
| `rom/scripts/build-magisk-module.sh` | Magisk module builder |
|
||||
| `rom/gsi/README.md` | GSI instructions |
|
||||
|
||||
## Offline Mode
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,104 @@ Use standard HTML image syntax with responsive styling:
|
|||
|
||||
---
|
||||
|
||||
## SVG Theme Transparency Guidelines
|
||||
|
||||
All SVGs MUST be theme-agnostic and work with light/dark modes. Follow these requirements:
|
||||
|
||||
### Required CSS Media Query
|
||||
|
||||
Every SVG must include a `<style>` block with `prefers-color-scheme` support:
|
||||
|
||||
```xml
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### Background Transparency
|
||||
|
||||
- Use `fill="#FAFBFC"` for light backgrounds (subtle, not pure white)
|
||||
- Add dot pattern overlay for texture: `fill="url(#dots)"` with low opacity
|
||||
- Cards use `fill="#FFFFFF"` with border strokes for definition
|
||||
- **NEVER** use pure black (`#000000`) backgrounds
|
||||
|
||||
### Color Palette (Theme-Safe)
|
||||
|
||||
| Purpose | Light Mode | Dark Mode Adaptation |
|
||||
|---------|------------|---------------------|
|
||||
| Title text | `#1E1B4B` | `#F1F5F9` (via CSS) |
|
||||
| Main text | `#334155` | `#E2E8F0` (via CSS) |
|
||||
| Secondary text | `#64748B` | `#94A3B8` (via CSS) |
|
||||
| Mono/code text | `#475569` | `#CBD5E1` (via CSS) |
|
||||
| Card backgrounds | `#FFFFFF` | Keep white (contrast) |
|
||||
| Borders | `#E2E8F0` | Keep (works both) |
|
||||
|
||||
### Standard Gradients
|
||||
|
||||
Use these gradient IDs consistently across all SVGs:
|
||||
|
||||
```xml
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
```
|
||||
|
||||
### DO NOT
|
||||
|
||||
- ❌ Hardcode text colors without CSS class
|
||||
- ❌ Use pure black (`#000000`) or pure white (`#FFFFFF`) for text
|
||||
- ❌ Forget the `@media (prefers-color-scheme: dark)` block
|
||||
- ❌ Use opaque backgrounds that don't adapt
|
||||
- ❌ Create new gradient IDs when standard ones exist
|
||||
|
||||
### DO
|
||||
|
||||
- ✅ Use CSS classes for all text elements
|
||||
- ✅ Include dark mode media query in every SVG
|
||||
- ✅ Use standard gradient IDs from the palette
|
||||
- ✅ Test SVGs in both light and dark browser modes
|
||||
- ✅ Use subtle shadows with low opacity (`0.15`)
|
||||
- ✅ Keep white cards for contrast in both modes
|
||||
|
||||
---
|
||||
|
||||
## Conversation Examples (WhatsApp Style)
|
||||
|
||||
All conversation examples throughout the book use the WhatsApp-style HTML format. This provides a familiar, visually consistent representation of bot interactions.
|
||||
|
|
|
|||
144
src/assets/chapter-04/kb-semantic-search-flow.svg
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
|
||||
</marker>
|
||||
|
||||
<marker id="arrowGreen" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#10B981"/>
|
||||
</marker>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="900" height="550" fill="#FAFBFC"/>
|
||||
<rect width="900" height="550" fill="url(#dots)"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">How Knowledge Base Works</text>
|
||||
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Semantic search through your documents</text>
|
||||
|
||||
<!-- Main Container -->
|
||||
<rect x="30" y="75" width="840" height="450" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- Step 1: User Question -->
|
||||
<g transform="translate(350, 95)">
|
||||
<rect x="0" y="0" width="200" height="60" rx="8" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="25" rx="8" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="20" width="200" height="5" fill="url(#cyanGrad)"/>
|
||||
<text x="100" y="17" text-anchor="middle" font-size="10" font-weight="600" class="white-text">User asks:</text>
|
||||
<text x="100" y="45" text-anchor="middle" font-size="10" class="mono-text">"What is our refund policy?"</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow down -->
|
||||
<path d="M450 160 L450 190" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- Step 2: Semantic Search -->
|
||||
<g transform="translate(300, 200)">
|
||||
<rect x="0" y="0" width="300" height="70" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="300" height="28" rx="10" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="22" width="300" height="6" fill="url(#primaryGrad)"/>
|
||||
<text x="150" y="19" text-anchor="middle" font-size="12" font-weight="600" class="white-text">🔍 Semantic Search</text>
|
||||
<text x="150" y="50" text-anchor="middle" font-size="10" class="secondary-text">Searches through all documents in the knowledge base</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow down splitting into 3 -->
|
||||
<path d="M450 275 L450 305" stroke="#8B5CF6" stroke-width="2" fill="none"/>
|
||||
<path d="M200 305 L700 305" stroke="#8B5CF6" stroke-width="2" fill="none"/>
|
||||
<path d="M200 305 L200 330" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
<path d="M450 305 L450 330" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
<path d="M700 305 L700 330" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- Document Cards -->
|
||||
<!-- policies.pdf -->
|
||||
<g transform="translate(120, 340)">
|
||||
<rect x="0" y="0" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<text x="80" y="25" text-anchor="middle" font-size="22">📄</text>
|
||||
<text x="80" y="48" text-anchor="middle" font-size="11" font-weight="500" class="main-text">policies.pdf</text>
|
||||
<text x="80" y="62" text-anchor="middle" font-size="9" class="secondary-text">Refund terms</text>
|
||||
</g>
|
||||
|
||||
<!-- FAQ.docx -->
|
||||
<g transform="translate(370, 340)">
|
||||
<rect x="0" y="0" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<text x="80" y="25" text-anchor="middle" font-size="22">📄</text>
|
||||
<text x="80" y="48" text-anchor="middle" font-size="11" font-weight="500" class="main-text">FAQ.docx</text>
|
||||
<text x="80" y="62" text-anchor="middle" font-size="9" class="secondary-text">Common questions</text>
|
||||
</g>
|
||||
|
||||
<!-- terms.md -->
|
||||
<g transform="translate(620, 340)">
|
||||
<rect x="0" y="0" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<text x="80" y="25" text-anchor="middle" font-size="22">📄</text>
|
||||
<text x="80" y="48" text-anchor="middle" font-size="11" font-weight="500" class="main-text">terms.md</text>
|
||||
<text x="80" y="62" text-anchor="middle" font-size="9" class="secondary-text">Legal terms</text>
|
||||
</g>
|
||||
|
||||
<!-- Found match indicator -->
|
||||
<path d="M200 415 L200 440" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
|
||||
<text x="200" y="432" text-anchor="middle" font-size="9" font-weight="600" style="fill:#10B981;">Found match!</text>
|
||||
|
||||
<!-- Result Box -->
|
||||
<g transform="translate(80, 455)">
|
||||
<rect x="0" y="0" width="740" height="55" rx="8" fill="#F0FDF4" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<text x="20" y="25" font-size="11" font-weight="600" class="main-text">✅ Match Found:</text>
|
||||
<text x="20" y="43" font-size="10" class="mono-text">"Refunds are available within 30 days of purchase. Contact support@company.com to initiate..."</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(60, 110)">
|
||||
<rect x="0" y="0" width="160" height="80" rx="6" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
|
||||
<text x="80" y="18" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Process Flow</text>
|
||||
|
||||
<circle cx="20" cy="38" r="6" fill="url(#cyanGrad)"/>
|
||||
<text x="35" y="42" font-size="9" class="secondary-text">User query</text>
|
||||
|
||||
<circle cx="20" cy="58" r="6" fill="url(#primaryGrad)"/>
|
||||
<text x="35" y="62" font-size="9" class="secondary-text">Vector search</text>
|
||||
|
||||
<circle cx="100" cy="58" r="6" fill="url(#greenGrad)"/>
|
||||
<text x="115" y="62" font-size="9" class="secondary-text">Result</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.7 KiB |
110
src/assets/chapter-04/step-flow-4-steps.svg
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<svg width="900" height="200" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<marker id="arrowGray" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#94A3B8"/>
|
||||
</marker>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="900" height="200" fill="#FAFBFC"/>
|
||||
<rect width="900" height="200" fill="url(#dots)"/>
|
||||
|
||||
<!-- Main Container -->
|
||||
<rect x="20" y="20" width="860" height="160" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- Step 1 -->
|
||||
<g transform="translate(60, 50)">
|
||||
<rect x="0" y="0" width="150" height="100" rx="10" fill="#FFFFFF" stroke="#06B6D4" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="150" height="32" rx="10" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="26" width="150" height="6" fill="url(#cyanGrad)"/>
|
||||
<text x="75" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Step 1</text>
|
||||
<text x="75" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">Prepare</text>
|
||||
<text x="75" y="75" text-anchor="middle" font-size="9" class="secondary-text">Gather requirements</text>
|
||||
<text x="75" y="90" text-anchor="middle" font-size="9" class="secondary-text">and resources</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 1→2 -->
|
||||
<path d="M215 100 L260 100" stroke="#94A3B8" stroke-width="2" fill="none" marker-end="url(#arrowGray)"/>
|
||||
|
||||
<!-- Step 2 -->
|
||||
<g transform="translate(270, 50)">
|
||||
<rect x="0" y="0" width="150" height="100" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="150" height="32" rx="10" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="26" width="150" height="6" fill="url(#primaryGrad)"/>
|
||||
<text x="75" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Step 2</text>
|
||||
<text x="75" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">Configure</text>
|
||||
<text x="75" y="75" text-anchor="middle" font-size="9" class="secondary-text">Set up settings</text>
|
||||
<text x="75" y="90" text-anchor="middle" font-size="9" class="secondary-text">and connections</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 2→3 -->
|
||||
<path d="M425 100 L470 100" stroke="#94A3B8" stroke-width="2" fill="none" marker-end="url(#arrowGray)"/>
|
||||
|
||||
<!-- Step 3 -->
|
||||
<g transform="translate(480, 50)">
|
||||
<rect x="0" y="0" width="150" height="100" rx="10" fill="#FFFFFF" stroke="#F59E0B" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="150" height="32" rx="10" fill="url(#orangeGrad)"/>
|
||||
<rect x="0" y="26" width="150" height="6" fill="url(#orangeGrad)"/>
|
||||
<text x="75" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Step 3</text>
|
||||
<text x="75" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">Deploy</text>
|
||||
<text x="75" y="75" text-anchor="middle" font-size="9" class="secondary-text">Execute the</text>
|
||||
<text x="75" y="90" text-anchor="middle" font-size="9" class="secondary-text">implementation</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 3→4 -->
|
||||
<path d="M635 100 L680 100" stroke="#94A3B8" stroke-width="2" fill="none" marker-end="url(#arrowGray)"/>
|
||||
|
||||
<!-- Step 4 -->
|
||||
<g transform="translate(690, 50)">
|
||||
<rect x="0" y="0" width="150" height="100" rx="10" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="150" height="32" rx="10" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="26" width="150" height="6" fill="url(#greenGrad)"/>
|
||||
<text x="75" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Step 4</text>
|
||||
<text x="75" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">Test</text>
|
||||
<text x="75" y="75" text-anchor="middle" font-size="9" class="secondary-text">Verify everything</text>
|
||||
<text x="75" y="90" text-anchor="middle" font-size="9" class="secondary-text">works correctly</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6 KiB |
152
src/assets/chapter-04/whatsapp-integration-flow.svg
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<svg width="900" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="whatsappGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#25D366;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#128C7E;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
|
||||
</marker>
|
||||
|
||||
<marker id="arrowGreen" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#25D366"/>
|
||||
</marker>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="900" height="500" fill="#FAFBFC"/>
|
||||
<rect width="900" height="500" fill="url(#dots)"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">How WhatsApp Integration Works</text>
|
||||
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Message flow between WhatsApp and General Bots</text>
|
||||
|
||||
<!-- Main Container -->
|
||||
<rect x="30" y="75" width="840" height="400" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- Step 1: User sends message -->
|
||||
<g transform="translate(80, 105)">
|
||||
<rect x="0" y="0" width="200" height="80" rx="10" fill="#FFFFFF" stroke="#25D366" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="30" rx="10" fill="url(#whatsappGrad)"/>
|
||||
<rect x="0" y="24" width="200" height="6" fill="url(#whatsappGrad)"/>
|
||||
<text x="100" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">📱 User</text>
|
||||
<text x="100" y="52" text-anchor="middle" font-size="10" class="main-text">Sends message on</text>
|
||||
<text x="100" y="68" text-anchor="middle" font-size="10" font-weight="500" class="main-text">WhatsApp</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 1 -->
|
||||
<path d="M285 145 L340 145" stroke="#25D366" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
|
||||
<text x="312" y="138" text-anchor="middle" font-size="8" class="secondary-text">1</text>
|
||||
|
||||
<!-- Step 2: WhatsApp Cloud API receives -->
|
||||
<g transform="translate(350, 105)">
|
||||
<rect x="0" y="0" width="200" height="80" rx="10" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="30" rx="10" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="24" width="200" height="6" fill="url(#cyanGrad)"/>
|
||||
<text x="100" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">WhatsApp Cloud API</text>
|
||||
<text x="100" y="52" text-anchor="middle" font-size="10" class="main-text">Meta's servers</text>
|
||||
<text x="100" y="68" text-anchor="middle" font-size="10" class="secondary-text">receive message</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 2 (webhook) -->
|
||||
<path d="M450 190 L450 230" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
<text x="470" y="215" font-size="9" class="mono-text">Webhook</text>
|
||||
<text x="435" y="215" text-anchor="middle" font-size="8" class="secondary-text">2</text>
|
||||
|
||||
<!-- Step 3: General Bots Server -->
|
||||
<g transform="translate(300, 240)">
|
||||
<rect x="0" y="0" width="300" height="90" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="300" height="32" rx="10" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="26" width="300" height="6" fill="url(#primaryGrad)"/>
|
||||
<text x="150" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">General Bots Server</text>
|
||||
<text x="150" y="55" text-anchor="middle" font-size="10" class="main-text">Your bot processes the message</text>
|
||||
<text x="150" y="72" text-anchor="middle" font-size="9" class="secondary-text">LLM • Knowledge Base • BASIC Scripts</text>
|
||||
<text x="150" y="85" text-anchor="middle" font-size="9" class="mono-text">botserver:8080</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 3 (API call) -->
|
||||
<path d="M450 335 L450 375" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
<text x="470" y="360" font-size="9" class="mono-text">API Call</text>
|
||||
<text x="435" y="360" text-anchor="middle" font-size="8" class="secondary-text">3</text>
|
||||
|
||||
<!-- Step 4: WhatsApp Cloud API sends reply -->
|
||||
<g transform="translate(350, 385)">
|
||||
<rect x="0" y="0" width="200" height="70" rx="10" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="28" rx="10" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="22" width="200" height="6" fill="url(#cyanGrad)"/>
|
||||
<text x="100" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">WhatsApp Cloud API</text>
|
||||
<text x="100" y="48" text-anchor="middle" font-size="10" class="main-text">Sends reply to user</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 4 (back to user) -->
|
||||
<path d="M345 420 L285 420 L285 185 L180 185" stroke="#25D366" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
|
||||
<text x="265" y="300" text-anchor="middle" font-size="8" class="secondary-text">4</text>
|
||||
|
||||
<!-- Info Box: Required Configuration -->
|
||||
<g transform="translate(620, 105)">
|
||||
<rect x="0" y="0" width="220" height="180" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
|
||||
<text x="110" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Required Configuration</text>
|
||||
|
||||
<text x="15" y="48" font-size="9" font-weight="600" class="secondary-text">Meta Developer Portal:</text>
|
||||
<text x="15" y="63" font-size="9" class="mono-text">• Phone Number ID</text>
|
||||
<text x="15" y="78" font-size="9" class="mono-text">• Access Token</text>
|
||||
<text x="15" y="93" font-size="9" class="mono-text">• Webhook URL</text>
|
||||
|
||||
<text x="15" y="118" font-size="9" font-weight="600" class="secondary-text">Bot Settings:</text>
|
||||
<text x="15" y="133" font-size="9" class="mono-text">• whatsapp_phone_id</text>
|
||||
<text x="15" y="148" font-size="9" class="mono-text">• whatsapp_token</text>
|
||||
<text x="15" y="163" font-size="9" class="mono-text">• webhook_verify_token</text>
|
||||
</g>
|
||||
|
||||
<!-- Info Box: Message Types -->
|
||||
<g transform="translate(620, 300)">
|
||||
<rect x="0" y="0" width="220" height="145" rx="8" fill="#ECFDF5" stroke="#A7F3D0" stroke-width="1"/>
|
||||
<text x="110" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Supported Messages</text>
|
||||
|
||||
<text x="15" y="45" font-size="9" class="secondary-text">✅ Text messages</text>
|
||||
<text x="15" y="62" font-size="9" class="secondary-text">✅ Images & documents</text>
|
||||
<text x="15" y="79" font-size="9" class="secondary-text">✅ Location sharing</text>
|
||||
<text x="15" y="96" font-size="9" class="secondary-text">✅ Interactive buttons</text>
|
||||
<text x="15" y="113" font-size="9" class="secondary-text">✅ Template messages</text>
|
||||
<text x="15" y="130" font-size="9" class="secondary-text">✅ Voice messages</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.7 KiB |
159
src/assets/chapter-13/botdevice-architecture.svg
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
<svg width="900" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="pinkGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#EC4899;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#F472B6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
|
||||
</marker>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="900" height="500" fill="#FAFBFC"/>
|
||||
<rect width="900" height="500" fill="url(#dots)"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">BotDevice Architecture</text>
|
||||
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Mobile app deployment with Tauri for Android/HarmonyOS</text>
|
||||
|
||||
<!-- Main Container -->
|
||||
<rect x="30" y="75" width="840" height="400" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- BotDevice App Container -->
|
||||
<g transform="translate(60, 100)">
|
||||
<rect x="0" y="0" width="780" height="130" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="780" height="35" rx="10" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="28" width="780" height="7" fill="url(#primaryGrad)"/>
|
||||
<text x="390" y="24" text-anchor="middle" font-size="13" font-weight="600" class="white-text">BotDevice App (Tauri)</text>
|
||||
|
||||
<!-- Three columns inside -->
|
||||
<!-- botui/ui/suite -->
|
||||
<g transform="translate(20, 50)">
|
||||
<rect x="0" y="0" width="230" height="65" rx="6" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
|
||||
<text x="115" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">botui/ui/suite</text>
|
||||
<text x="115" y="40" text-anchor="middle" font-size="10" class="secondary-text">(Web Interface)</text>
|
||||
<text x="115" y="55" text-anchor="middle" font-size="9" class="mono-text">HTML + HTMX + CSS</text>
|
||||
</g>
|
||||
|
||||
<!-- Tauri Android -->
|
||||
<g transform="translate(270, 50)">
|
||||
<rect x="0" y="0" width="230" height="65" rx="6" fill="#F0FDF4" stroke="#A7F3D0" stroke-width="1"/>
|
||||
<text x="115" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Tauri Android</text>
|
||||
<text x="115" y="40" text-anchor="middle" font-size="10" class="secondary-text">(WebView + NDK)</text>
|
||||
<text x="115" y="55" text-anchor="middle" font-size="9" class="mono-text">Native bridge</text>
|
||||
</g>
|
||||
|
||||
<!-- src/lib.rs -->
|
||||
<g transform="translate(520, 50)">
|
||||
<rect x="0" y="0" width="240" height="65" rx="6" fill="#FEF3C7" stroke="#FCD34D" stroke-width="1"/>
|
||||
<text x="120" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">src/lib.rs (Rust)</text>
|
||||
<text x="120" y="40" text-anchor="middle" font-size="10" class="secondary-text">(Backend + Hardware)</text>
|
||||
<text x="120" y="55" text-anchor="middle" font-size="9" class="mono-text">Business logic</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Connector line down -->
|
||||
<path d="M450 235 L450 265" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- Android/HarmonyOS System Container -->
|
||||
<g transform="translate(60, 275)">
|
||||
<rect x="0" y="0" width="780" height="180" rx="10" fill="#FFFFFF" stroke="#64748B" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="780" height="35" rx="10" fill="#64748B"/>
|
||||
<rect x="0" y="28" width="780" height="7" fill="#64748B"/>
|
||||
<text x="390" y="24" text-anchor="middle" font-size="13" font-weight="600" class="white-text">Android / HarmonyOS System</text>
|
||||
|
||||
<!-- Hardware capabilities row -->
|
||||
<!-- Camera -->
|
||||
<g transform="translate(30, 55)">
|
||||
<rect x="0" y="0" width="160" height="105" rx="8" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="24" width="160" height="6" fill="url(#cyanGrad)"/>
|
||||
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Camera</text>
|
||||
|
||||
<text x="80" y="52" text-anchor="middle" font-size="20">📷</text>
|
||||
<text x="80" y="75" text-anchor="middle" font-size="10" class="main-text">Photo/Video</text>
|
||||
<text x="80" y="92" text-anchor="middle" font-size="9" class="mono-text">QR Scanning</text>
|
||||
</g>
|
||||
|
||||
<!-- GPS -->
|
||||
<g transform="translate(210, 55)">
|
||||
<rect x="0" y="0" width="160" height="105" rx="8" fill="#FFFFFF" stroke="#34D399" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="24" width="160" height="6" fill="url(#greenGrad)"/>
|
||||
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">GPS</text>
|
||||
|
||||
<text x="80" y="52" text-anchor="middle" font-size="20">📍</text>
|
||||
<text x="80" y="75" text-anchor="middle" font-size="10" class="main-text">Location</text>
|
||||
<text x="80" y="92" text-anchor="middle" font-size="9" class="mono-text">Geofencing</text>
|
||||
</g>
|
||||
|
||||
<!-- WiFi -->
|
||||
<g transform="translate(390, 55)">
|
||||
<rect x="0" y="0" width="160" height="105" rx="8" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="24" width="160" height="6" fill="url(#primaryGrad)"/>
|
||||
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">WiFi</text>
|
||||
|
||||
<text x="80" y="52" text-anchor="middle" font-size="20">📶</text>
|
||||
<text x="80" y="75" text-anchor="middle" font-size="10" class="main-text">Connectivity</text>
|
||||
<text x="80" y="92" text-anchor="middle" font-size="9" class="mono-text">Network API</text>
|
||||
</g>
|
||||
|
||||
<!-- Storage -->
|
||||
<g transform="translate(570, 55)">
|
||||
<rect x="0" y="0" width="190" height="105" rx="8" fill="#FFFFFF" stroke="#F59E0B" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="190" height="30" rx="8" fill="url(#orangeGrad)"/>
|
||||
<rect x="0" y="24" width="190" height="6" fill="url(#orangeGrad)"/>
|
||||
<text x="95" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Storage</text>
|
||||
|
||||
<text x="95" y="52" text-anchor="middle" font-size="20">💾</text>
|
||||
<text x="95" y="75" text-anchor="middle" font-size="10" class="main-text">Local Files</text>
|
||||
<text x="95" y="92" text-anchor="middle" font-size="9" class="mono-text">SQLite / Assets</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
152
src/assets/chapter-13/budget-decision-tree.svg
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<marker id="arrowGray" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#94A3B8"/>
|
||||
</marker>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="800" height="450" fill="#FAFBFC"/>
|
||||
<rect width="800" height="450" fill="url(#dots)"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="400" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">Hardware Budget Decision Tree</text>
|
||||
<text x="400" y="55" text-anchor="middle" font-size="12" class="secondary-text">Choose the right single-board computer for your project</text>
|
||||
|
||||
<!-- Decision Node: Budget Question -->
|
||||
<g transform="translate(300, 80)">
|
||||
<rect x="0" y="0" width="200" height="50" rx="25" fill="url(#primaryGrad)" filter="url(#cardShadow)"/>
|
||||
<text x="100" y="30" text-anchor="middle" font-size="13" font-weight="600" class="white-text">What's your budget?</text>
|
||||
</g>
|
||||
|
||||
<!-- Connector Lines from Decision -->
|
||||
<g stroke="#94A3B8" stroke-width="2" fill="none">
|
||||
<!-- Left branch -->
|
||||
<path d="M350 130 L350 160 L150 160 L150 190" marker-end="url(#arrowGray)"/>
|
||||
<!-- Center branch -->
|
||||
<path d="M400 130 L400 190" marker-end="url(#arrowGray)"/>
|
||||
<!-- Right branch -->
|
||||
<path d="M450 130 L450 160 L650 160 L650 190" marker-end="url(#arrowGray)"/>
|
||||
</g>
|
||||
|
||||
<!-- Budget Labels on Lines -->
|
||||
<text x="200" y="155" text-anchor="middle" font-size="11" font-weight="600" class="secondary-text">Under $30</text>
|
||||
<text x="400" y="175" text-anchor="middle" font-size="11" font-weight="600" class="secondary-text">$30 - $80</text>
|
||||
<text x="600" y="155" text-anchor="middle" font-size="11" font-weight="600" class="secondary-text">$80 - $150</text>
|
||||
|
||||
<!-- Option 1: Orange Pi Zero 3 -->
|
||||
<g transform="translate(50, 200)">
|
||||
<rect x="0" y="0" width="200" height="140" rx="10" fill="#FFFFFF" stroke="#FCD34D" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="35" rx="10" fill="url(#orangeGrad)"/>
|
||||
<rect x="0" y="28" width="200" height="7" fill="url(#orangeGrad)"/>
|
||||
<text x="100" y="24" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Orange Pi Zero 3</text>
|
||||
|
||||
<text x="100" y="60" text-anchor="middle" font-size="18" font-weight="700" class="main-text">~$20</text>
|
||||
|
||||
<text x="15" y="85" font-size="10" class="secondary-text">• 1GB RAM</text>
|
||||
<text x="15" y="100" font-size="10" class="secondary-text">• Basic display</text>
|
||||
<text x="15" y="115" font-size="10" class="secondary-text">• Simple tasks</text>
|
||||
|
||||
<rect x="15" y="122" width="170" height="10" rx="3" fill="#FEF3C7"/>
|
||||
<text x="100" y="130" text-anchor="middle" font-size="8" font-weight="500" class="secondary-text">Entry Level</text>
|
||||
</g>
|
||||
|
||||
<!-- Option 2: Raspberry Pi 4/5 -->
|
||||
<g transform="translate(300, 200)">
|
||||
<rect x="0" y="0" width="200" height="140" rx="10" fill="#FFFFFF" stroke="#A7F3D0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="35" rx="10" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="28" width="200" height="7" fill="url(#greenGrad)"/>
|
||||
<text x="100" y="24" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Raspberry Pi 4 / 5</text>
|
||||
|
||||
<text x="100" y="60" text-anchor="middle" font-size="18" font-weight="700" class="main-text">$35 - $80</text>
|
||||
|
||||
<text x="15" y="85" font-size="10" class="secondary-text">• 2-8GB RAM</text>
|
||||
<text x="15" y="100" font-size="10" class="secondary-text">• Full desktop</text>
|
||||
<text x="15" y="115" font-size="10" class="secondary-text">• Most projects</text>
|
||||
|
||||
<rect x="15" y="122" width="170" height="10" rx="3" fill="#ECFDF5"/>
|
||||
<text x="100" y="130" text-anchor="middle" font-size="8" font-weight="500" class="secondary-text">Recommended</text>
|
||||
</g>
|
||||
|
||||
<!-- Option 3: Orange Pi 5 -->
|
||||
<g transform="translate(550, 200)">
|
||||
<rect x="0" y="0" width="200" height="140" rx="10" fill="#FFFFFF" stroke="#C7D2FE" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="35" rx="10" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="28" width="200" height="7" fill="url(#primaryGrad)"/>
|
||||
<text x="100" y="24" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Orange Pi 5</text>
|
||||
|
||||
<text x="100" y="60" text-anchor="middle" font-size="18" font-weight="700" class="main-text">~$89</text>
|
||||
|
||||
<text x="15" y="85" font-size="10" class="secondary-text">• 8-16GB RAM + NPU</text>
|
||||
<text x="15" y="100" font-size="10" class="secondary-text">• Local AI / LLM</text>
|
||||
<text x="15" y="115" font-size="10" class="secondary-text">• Edge AI ready</text>
|
||||
|
||||
<rect x="15" y="122" width="170" height="10" rx="3" fill="#EEF2FF"/>
|
||||
<text x="100" y="130" text-anchor="middle" font-size="8" font-weight="500" class="secondary-text">Best for Local AI</text>
|
||||
</g>
|
||||
|
||||
<!-- Use Case Icons/Labels -->
|
||||
<g transform="translate(50, 365)">
|
||||
<rect x="0" y="0" width="200" height="60" rx="8" fill="#FFF7ED" stroke="#FDBA74" stroke-width="1"/>
|
||||
<text x="100" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Best For</text>
|
||||
<text x="100" y="40" text-anchor="middle" font-size="9" class="secondary-text">Sensors, displays</text>
|
||||
<text x="100" y="52" text-anchor="middle" font-size="9" class="secondary-text">Simple automation</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(300, 365)">
|
||||
<rect x="0" y="0" width="200" height="60" rx="8" fill="#F0FDF4" stroke="#86EFAC" stroke-width="1"/>
|
||||
<text x="100" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Best For</text>
|
||||
<text x="100" y="40" text-anchor="middle" font-size="9" class="secondary-text">General projects</text>
|
||||
<text x="100" y="52" text-anchor="middle" font-size="9" class="secondary-text">Home assistant, kiosk</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(550, 365)">
|
||||
<rect x="0" y="0" width="200" height="60" rx="8" fill="#EEF2FF" stroke="#A5B4FC" stroke-width="1"/>
|
||||
<text x="100" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Best For</text>
|
||||
<text x="100" y="40" text-anchor="middle" font-size="9" class="secondary-text">Offline LLM, AI voice</text>
|
||||
<text x="100" y="52" text-anchor="middle" font-size="9" class="secondary-text">Edge computing</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.3 KiB |
164
src/assets/chapter-13/embedded-architecture.svg
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
<svg width="900" height="400" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
|
||||
</marker>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="900" height="400" fill="#FAFBFC"/>
|
||||
<rect width="900" height="400" fill="url(#dots)"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">Embedded GB Architecture</text>
|
||||
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Single-board computer deployment with local LLM</text>
|
||||
|
||||
<!-- Main Container -->
|
||||
<rect x="30" y="75" width="840" height="290" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- Display Section -->
|
||||
<g transform="translate(60, 100)">
|
||||
<rect x="0" y="0" width="160" height="110" rx="8" fill="#FFFFFF" stroke="#C7D2FE" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="22" width="160" height="8" fill="url(#cyanGrad)"/>
|
||||
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Display</text>
|
||||
|
||||
<text x="80" y="55" text-anchor="middle" font-size="10" class="main-text">LCD / OLED</text>
|
||||
<text x="80" y="72" text-anchor="middle" font-size="10" class="main-text">TFT / HDMI</text>
|
||||
<text x="80" y="95" text-anchor="middle" font-size="9" class="mono-text">SPI / I2C</text>
|
||||
</g>
|
||||
|
||||
<!-- BotServer Section -->
|
||||
<g transform="translate(280, 100)">
|
||||
<rect x="0" y="0" width="160" height="110" rx="8" fill="#FFFFFF" stroke="#DDD6FE" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="22" width="160" height="8" fill="url(#primaryGrad)"/>
|
||||
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botserver</text>
|
||||
|
||||
<text x="80" y="55" text-anchor="middle" font-size="10" class="main-text">(Rust)</text>
|
||||
<text x="80" y="72" text-anchor="middle" font-size="10" font-weight="500" class="main-text">Port 8088</text>
|
||||
<text x="80" y="95" text-anchor="middle" font-size="9" class="mono-text">ARM64 Binary</text>
|
||||
</g>
|
||||
|
||||
<!-- llama.cpp Section -->
|
||||
<g transform="translate(500, 100)">
|
||||
<rect x="0" y="0" width="160" height="110" rx="8" fill="#FFFFFF" stroke="#A7F3D0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="22" width="160" height="8" fill="url(#greenGrad)"/>
|
||||
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">llama.cpp</text>
|
||||
|
||||
<text x="80" y="55" text-anchor="middle" font-size="10" class="main-text">(Local)</text>
|
||||
<text x="80" y="72" text-anchor="middle" font-size="10" font-weight="500" class="main-text">Port 8080</text>
|
||||
<text x="80" y="95" text-anchor="middle" font-size="9" class="mono-text">GGUF Models</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrows between top row -->
|
||||
<g stroke="#8B5CF6" stroke-width="2" fill="none">
|
||||
<path d="M225 155 L275 155" marker-end="url(#arrowPurple)"/>
|
||||
<path d="M445 155 L495 155" marker-end="url(#arrowPurple)"/>
|
||||
</g>
|
||||
|
||||
<!-- Input Section -->
|
||||
<g transform="translate(60, 240)">
|
||||
<rect x="0" y="0" width="160" height="100" rx="8" fill="#FFFFFF" stroke="#FCD34D" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#orangeGrad)"/>
|
||||
<rect x="0" y="22" width="160" height="8" fill="url(#orangeGrad)"/>
|
||||
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Input</text>
|
||||
|
||||
<text x="80" y="52" text-anchor="middle" font-size="10" class="main-text">Keyboard</text>
|
||||
<text x="80" y="68" text-anchor="middle" font-size="10" class="main-text">Buttons</text>
|
||||
<text x="80" y="84" text-anchor="middle" font-size="10" class="main-text">Touch</text>
|
||||
</g>
|
||||
|
||||
<!-- SQLite Section -->
|
||||
<g transform="translate(280, 240)">
|
||||
<rect x="0" y="0" width="160" height="100" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="160" height="30" rx="8" fill="#64748B"/>
|
||||
<rect x="0" y="22" width="160" height="8" fill="#64748B"/>
|
||||
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">SQLite</text>
|
||||
|
||||
<text x="80" y="52" text-anchor="middle" font-size="10" class="main-text">(Data)</text>
|
||||
<text x="80" y="72" text-anchor="middle" font-size="9" class="mono-text">Sessions</text>
|
||||
<text x="80" y="88" text-anchor="middle" font-size="9" class="mono-text">Conversations</text>
|
||||
</g>
|
||||
|
||||
<!-- Model Section -->
|
||||
<g transform="translate(500, 240)">
|
||||
<rect x="0" y="0" width="160" height="100" rx="8" fill="#FFFFFF" stroke="#A7F3D0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="22" width="160" height="8" fill="url(#greenGrad)"/>
|
||||
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">TinyLlama</text>
|
||||
|
||||
<text x="80" y="52" text-anchor="middle" font-size="10" class="main-text">GGUF Model</text>
|
||||
<text x="80" y="72" text-anchor="middle" font-size="9" class="mono-text">Q4_K_M</text>
|
||||
<text x="80" y="88" text-anchor="middle" font-size="9" class="mono-text">~700MB</text>
|
||||
</g>
|
||||
|
||||
<!-- Vertical Arrows -->
|
||||
<g stroke="#8B5CF6" stroke-width="2" fill="none">
|
||||
<path d="M140 215 L140 235" marker-end="url(#arrowPurple)"/>
|
||||
<path d="M360 215 L360 235" marker-end="url(#arrowPurple)"/>
|
||||
<path d="M580 215 L580 235" marker-end="url(#arrowPurple)"/>
|
||||
</g>
|
||||
|
||||
<!-- Hardware specs badge -->
|
||||
<g transform="translate(700, 120)">
|
||||
<rect x="0" y="0" width="150" height="200" rx="8" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
|
||||
<text x="75" y="25" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Requirements</text>
|
||||
|
||||
<text x="15" y="55" font-size="9" class="secondary-text">CPU</text>
|
||||
<text x="15" y="70" font-size="10" class="mono-text">ARM64 / x86</text>
|
||||
|
||||
<text x="15" y="95" font-size="9" class="secondary-text">RAM</text>
|
||||
<text x="15" y="110" font-size="10" class="mono-text">2GB+ min</text>
|
||||
|
||||
<text x="15" y="135" font-size="9" class="secondary-text">Storage</text>
|
||||
<text x="15" y="150" font-size="10" class="mono-text">16GB+ SD</text>
|
||||
|
||||
<text x="15" y="175" font-size="9" class="secondary-text">Power</text>
|
||||
<text x="15" y="190" font-size="10" class="mono-text">5V 2-3A</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
89
src/assets/chapter-13/gpio-button-interface.svg
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<svg width="600" height="300" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="600" height="300" fill="#FAFBFC"/>
|
||||
<rect width="600" height="300" fill="url(#dots)"/>
|
||||
|
||||
<!-- Main Card -->
|
||||
<rect x="30" y="20" width="540" height="260" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- Header -->
|
||||
<rect x="30" y="20" width="540" height="45" rx="12" fill="url(#primaryGrad)"/>
|
||||
<rect x="30" y="52" width="540" height="13" fill="url(#primaryGrad)"/>
|
||||
<text x="300" y="50" text-anchor="middle" font-size="15" font-weight="600" class="white-text">Simple 4-Button Interface</text>
|
||||
|
||||
<!-- Buttons Container -->
|
||||
<rect x="60" y="90" width="480" height="120" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
|
||||
|
||||
<!-- Button 1: PREV -->
|
||||
<g transform="translate(85, 110)">
|
||||
<rect x="0" y="0" width="90" height="55" rx="8" fill="url(#orangeGrad)" filter="url(#cardShadow)"/>
|
||||
<text x="45" y="28" text-anchor="middle" font-size="16" font-weight="700" class="white-text">◄ PREV</text>
|
||||
<text x="45" y="45" text-anchor="middle" font-size="9" class="white-text">GPIO 17</text>
|
||||
</g>
|
||||
|
||||
<!-- Button 2: UP -->
|
||||
<g transform="translate(195, 110)">
|
||||
<rect x="0" y="0" width="90" height="55" rx="8" fill="url(#orangeGrad)" filter="url(#cardShadow)"/>
|
||||
<text x="45" y="28" text-anchor="middle" font-size="16" font-weight="700" class="white-text">▲ UP</text>
|
||||
<text x="45" y="45" text-anchor="middle" font-size="9" class="white-text">GPIO 27</text>
|
||||
</g>
|
||||
|
||||
<!-- Button 3: DOWN -->
|
||||
<g transform="translate(305, 110)">
|
||||
<rect x="0" y="0" width="90" height="55" rx="8" fill="url(#orangeGrad)" filter="url(#cardShadow)"/>
|
||||
<text x="45" y="28" text-anchor="middle" font-size="16" font-weight="700" class="white-text">▼ DOWN</text>
|
||||
<text x="45" y="45" text-anchor="middle" font-size="9" class="white-text">GPIO 22</text>
|
||||
</g>
|
||||
|
||||
<!-- Button 4: SELECT -->
|
||||
<g transform="translate(415, 110)">
|
||||
<rect x="0" y="0" width="100" height="55" rx="8" fill="url(#primaryGrad)" filter="url(#cardShadow)"/>
|
||||
<text x="50" y="28" text-anchor="middle" font-size="16" font-weight="700" class="white-text">► SELECT</text>
|
||||
<text x="50" y="45" text-anchor="middle" font-size="9" class="white-text">GPIO 23</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(60, 225)">
|
||||
<rect x="0" y="0" width="480" height="40" rx="6" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
|
||||
<text x="20" y="25" font-size="10" class="secondary-text">Pull-up resistors enabled</text>
|
||||
<text x="170" y="25" font-size="10" class="secondary-text">•</text>
|
||||
<text x="190" y="25" font-size="10" class="secondary-text">Active LOW (pressed = 0)</text>
|
||||
<text x="350" y="25" font-size="10" class="secondary-text">•</text>
|
||||
<text x="370" y="25" font-size="10" class="secondary-text">Debounce: 50ms</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
234
src/assets/chapter-13/humanoid-architecture.svg
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
<svg width="1000" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="pinkGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#EC4899;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#F472B6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
|
||||
</marker>
|
||||
|
||||
<marker id="arrowCyan" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#06B6D4"/>
|
||||
</marker>
|
||||
|
||||
<marker id="arrowGreen" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#10B981"/>
|
||||
</marker>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1000" height="600" fill="#FAFBFC"/>
|
||||
<rect width="1000" height="600" fill="url(#dots)"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="500" y="35" text-anchor="middle" font-size="22" font-weight="600" class="title-text">Humanoid Robot Architecture</text>
|
||||
<text x="500" y="57" text-anchor="middle" font-size="12" class="secondary-text">General Bots + Computer Vision + LLM + Movement Control</text>
|
||||
|
||||
<!-- Main Container -->
|
||||
<rect x="25" y="75" width="950" height="500" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- Left Column: Sensors -->
|
||||
<g transform="translate(50, 100)">
|
||||
<text x="80" y="0" text-anchor="middle" font-size="12" font-weight="600" class="secondary-text">SENSORS</text>
|
||||
|
||||
<!-- Camera -->
|
||||
<rect x="0" y="15" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#06B6D4" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="15" width="160" height="25" rx="8" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="35" width="160" height="5" fill="url(#cyanGrad)"/>
|
||||
<text x="80" y="33" text-anchor="middle" font-size="10" font-weight="600" class="white-text">📷 Camera</text>
|
||||
<text x="80" y="58" text-anchor="middle" font-size="9" class="main-text">RGB + Depth</text>
|
||||
<text x="80" y="73" text-anchor="middle" font-size="8" class="mono-text">USB / CSI</text>
|
||||
|
||||
<!-- Microphone -->
|
||||
<rect x="0" y="100" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#06B6D4" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="100" width="160" height="25" rx="8" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="120" width="160" height="5" fill="url(#cyanGrad)"/>
|
||||
<text x="80" y="118" text-anchor="middle" font-size="10" font-weight="600" class="white-text">🎤 Microphone</text>
|
||||
<text x="80" y="143" text-anchor="middle" font-size="9" class="main-text">Voice Input</text>
|
||||
<text x="80" y="158" text-anchor="middle" font-size="8" class="mono-text">USB / I2S</text>
|
||||
|
||||
<!-- IMU -->
|
||||
<rect x="0" y="185" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#06B6D4" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="185" width="160" height="25" rx="8" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="205" width="160" height="5" fill="url(#cyanGrad)"/>
|
||||
<text x="80" y="203" text-anchor="middle" font-size="10" font-weight="600" class="white-text">⚖️ IMU</text>
|
||||
<text x="80" y="228" text-anchor="middle" font-size="9" class="main-text">Balance + Orientation</text>
|
||||
<text x="80" y="243" text-anchor="middle" font-size="8" class="mono-text">I2C</text>
|
||||
</g>
|
||||
|
||||
<!-- Center Column: Processing -->
|
||||
<g transform="translate(280, 100)">
|
||||
<text x="200" y="0" text-anchor="middle" font-size="12" font-weight="600" class="secondary-text">PROCESSING</text>
|
||||
|
||||
<!-- botmodels (CV) -->
|
||||
<rect x="0" y="15" width="180" height="90" rx="10" fill="#FFFFFF" stroke="#EC4899" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="15" width="180" height="30" rx="10" fill="url(#pinkGrad)"/>
|
||||
<rect x="0" y="40" width="180" height="5" fill="url(#pinkGrad)"/>
|
||||
<text x="90" y="36" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botmodels</text>
|
||||
<text x="90" y="65" text-anchor="middle" font-size="10" class="main-text">Computer Vision</text>
|
||||
<text x="90" y="82" text-anchor="middle" font-size="9" class="secondary-text">Face • Object • Gesture</text>
|
||||
<text x="90" y="97" text-anchor="middle" font-size="8" class="mono-text">ONNX / Python</text>
|
||||
|
||||
<!-- botserver (Brain) -->
|
||||
<rect x="0" y="120" width="180" height="110" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="120" width="180" height="30" rx="10" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="145" width="180" height="5" fill="url(#primaryGrad)"/>
|
||||
<text x="90" y="141" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botserver</text>
|
||||
<text x="90" y="168" text-anchor="middle" font-size="10" class="main-text">Brain / LLM</text>
|
||||
<text x="90" y="185" text-anchor="middle" font-size="9" class="secondary-text">Conversation • Decisions</text>
|
||||
<text x="90" y="202" text-anchor="middle" font-size="9" class="secondary-text">Knowledge • Memory</text>
|
||||
<text x="90" y="220" text-anchor="middle" font-size="8" class="mono-text">Rust / Port 8088</text>
|
||||
|
||||
<!-- BASIC Runtime -->
|
||||
<rect x="0" y="245" width="180" height="90" rx="10" fill="#FFFFFF" stroke="#F59E0B" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="245" width="180" height="30" rx="10" fill="url(#orangeGrad)"/>
|
||||
<rect x="0" y="270" width="180" height="5" fill="url(#orangeGrad)"/>
|
||||
<text x="90" y="266" text-anchor="middle" font-size="11" font-weight="600" class="white-text">BASIC Runtime</text>
|
||||
<text x="90" y="295" text-anchor="middle" font-size="10" class="main-text">Movement Control</text>
|
||||
<text x="90" y="312" text-anchor="middle" font-size="9" class="secondary-text">SERVO • ROBOT • SEQUENCE</text>
|
||||
<text x="90" y="327" text-anchor="middle" font-size="8" class="mono-text">Keywords → Commands</text>
|
||||
|
||||
<!-- Hardware Bridge -->
|
||||
<rect x="200" y="120" width="180" height="90" rx="10" fill="#FFFFFF" stroke="#64748B" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="200" y="120" width="180" height="30" rx="10" fill="#64748B"/>
|
||||
<rect x="200" y="145" width="180" height="5" fill="#64748B"/>
|
||||
<text x="290" y="141" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Hardware Bridge</text>
|
||||
<text x="290" y="168" text-anchor="middle" font-size="10" class="main-text">Protocol Translation</text>
|
||||
<text x="290" y="185" text-anchor="middle" font-size="9" class="secondary-text">GPIO • Serial • CAN</text>
|
||||
<text x="290" y="200" text-anchor="middle" font-size="8" class="mono-text">PCA9685 / Dynamixel</text>
|
||||
</g>
|
||||
|
||||
<!-- Right Column: Actuators -->
|
||||
<g transform="translate(720, 100)">
|
||||
<text x="110" y="0" text-anchor="middle" font-size="12" font-weight="600" class="secondary-text">ACTUATORS</text>
|
||||
|
||||
<!-- Servos -->
|
||||
<rect x="0" y="15" width="220" height="90" rx="8" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="15" width="220" height="25" rx="8" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="35" width="220" height="5" fill="url(#greenGrad)"/>
|
||||
<text x="110" y="33" text-anchor="middle" font-size="10" font-weight="600" class="white-text">⚙️ Servos / Motors</text>
|
||||
<text x="110" y="58" text-anchor="middle" font-size="9" class="main-text">Joint Movement</text>
|
||||
<text x="110" y="73" text-anchor="middle" font-size="8" class="secondary-text">Head • Arms • Legs • Grippers</text>
|
||||
<text x="110" y="90" text-anchor="middle" font-size="8" class="mono-text">PWM / TTL / RS485</text>
|
||||
|
||||
<!-- Speaker -->
|
||||
<rect x="0" y="120" width="220" height="70" rx="8" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="120" width="220" height="25" rx="8" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="140" width="220" height="5" fill="url(#greenGrad)"/>
|
||||
<text x="110" y="138" text-anchor="middle" font-size="10" font-weight="600" class="white-text">🔊 Speaker</text>
|
||||
<text x="110" y="163" text-anchor="middle" font-size="9" class="main-text">Voice Output (TTS)</text>
|
||||
<text x="110" y="178" text-anchor="middle" font-size="8" class="mono-text">USB / Audio Jack</text>
|
||||
|
||||
<!-- Display -->
|
||||
<rect x="0" y="205" width="220" height="70" rx="8" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="205" width="220" height="25" rx="8" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="225" width="220" height="5" fill="url(#greenGrad)"/>
|
||||
<text x="110" y="223" text-anchor="middle" font-size="10" font-weight="600" class="white-text">🖥️ Display</text>
|
||||
<text x="110" y="248" text-anchor="middle" font-size="9" class="main-text">Face / Status / Emotions</text>
|
||||
<text x="110" y="263" text-anchor="middle" font-size="8" class="mono-text">HDMI / LCD / OLED</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrows: Sensors → Processing -->
|
||||
<path d="M215 145 L275 85" stroke="#06B6D4" stroke-width="2" fill="none" marker-end="url(#arrowCyan)"/>
|
||||
<path d="M215 230 L275 175" stroke="#06B6D4" stroke-width="2" fill="none" marker-end="url(#arrowCyan)"/>
|
||||
<path d="M215 315 L275 290" stroke="#06B6D4" stroke-width="2" fill="none" marker-end="url(#arrowCyan)"/>
|
||||
|
||||
<!-- Arrows: Between Processing -->
|
||||
<path d="M370 200 L370 235" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
<path d="M465 265 L475 215" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- Arrows: Processing → Actuators -->
|
||||
<path d="M660 210 L715 145" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
|
||||
<path d="M660 210 L715 220" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
|
||||
<path d="M660 210 L715 290" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
|
||||
|
||||
<!-- Bottom: Flow Summary -->
|
||||
<g transform="translate(50, 460)">
|
||||
<rect x="0" y="0" width="900" height="95" rx="10" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
|
||||
<text x="450" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Data Flow</text>
|
||||
|
||||
<!-- Flow steps -->
|
||||
<g transform="translate(30, 40)">
|
||||
<rect x="0" y="0" width="140" height="40" rx="6" fill="#EFF6FF" stroke="#BFDBFE" stroke-width="1"/>
|
||||
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">1. Voice / Vision</text>
|
||||
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">Input sensors</text>
|
||||
</g>
|
||||
|
||||
<text x="185" y="60" font-size="16" class="secondary-text">→</text>
|
||||
|
||||
<g transform="translate(200, 40)">
|
||||
<rect x="0" y="0" width="140" height="40" rx="6" fill="#F5F3FF" stroke="#DDD6FE" stroke-width="1"/>
|
||||
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">2. CV Analysis</text>
|
||||
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">botmodels</text>
|
||||
</g>
|
||||
|
||||
<text x="355" y="60" font-size="16" class="secondary-text">→</text>
|
||||
|
||||
<g transform="translate(370, 40)">
|
||||
<rect x="0" y="0" width="140" height="40" rx="6" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
|
||||
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">3. LLM Decision</text>
|
||||
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">botserver</text>
|
||||
</g>
|
||||
|
||||
<text x="525" y="60" font-size="16" class="secondary-text">→</text>
|
||||
|
||||
<g transform="translate(540, 40)">
|
||||
<rect x="0" y="0" width="140" height="40" rx="6" fill="#FEF3C7" stroke="#FCD34D" stroke-width="1"/>
|
||||
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">4. BASIC Script</text>
|
||||
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">Movement cmds</text>
|
||||
</g>
|
||||
|
||||
<text x="695" y="60" font-size="16" class="secondary-text">→</text>
|
||||
|
||||
<g transform="translate(710, 40)">
|
||||
<rect x="0" y="0" width="140" height="40" rx="6" fill="#ECFDF5" stroke="#A7F3D0" stroke-width="1"/>
|
||||
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">5. Physical Action</text>
|
||||
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">Servos + Voice</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
182
src/assets/chapter-13/local-llm-architecture.svg
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<svg width="900" height="400" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="flowGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:0.3" />
|
||||
<stop offset="50%" style="stop-color:#8B5CF6;stop-opacity:0.6" />
|
||||
<stop offset="100%" style="stop-color:#A855F7;stop-opacity:0.3" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
|
||||
</marker>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="900" height="400" fill="#FAFBFC"/>
|
||||
<rect width="900" height="400" fill="url(#dots)"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">Local LLM Architecture</text>
|
||||
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Offline inference with llama.cpp on embedded devices</text>
|
||||
|
||||
<!-- Main Container -->
|
||||
<rect x="30" y="75" width="840" height="295" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- Main Flow: User Input → botserver → llama.cpp → Response -->
|
||||
|
||||
<!-- User Input -->
|
||||
<g transform="translate(60, 105)">
|
||||
<rect x="0" y="0" width="130" height="80" rx="8" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="130" height="28" rx="8" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="22" width="130" height="6" fill="url(#cyanGrad)"/>
|
||||
<text x="65" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">User Input</text>
|
||||
|
||||
<text x="65" y="52" text-anchor="middle" font-size="10" class="main-text">Text / Voice</text>
|
||||
<text x="65" y="68" text-anchor="middle" font-size="9" class="mono-text">HTTP / WebSocket</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 1 -->
|
||||
<path d="M195 145 L240 145" stroke="url(#flowGrad)" stroke-width="3" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- BotServer -->
|
||||
<g transform="translate(250, 95)">
|
||||
<rect x="0" y="0" width="170" height="100" rx="8" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="170" height="28" rx="8" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="22" width="170" height="6" fill="url(#primaryGrad)"/>
|
||||
<text x="85" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botserver</text>
|
||||
|
||||
<text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">Rust Runtime</text>
|
||||
<text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">Session Manager</text>
|
||||
<text x="85" y="84" text-anchor="middle" font-size="9" class="mono-text">Port 8088</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 2 -->
|
||||
<path d="M425 145 L475 145" stroke="url(#flowGrad)" stroke-width="3" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- llama.cpp -->
|
||||
<g transform="translate(485, 95)">
|
||||
<rect x="0" y="0" width="170" height="100" rx="8" fill="#FFFFFF" stroke="#34D399" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="170" height="28" rx="8" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="22" width="170" height="6" fill="url(#greenGrad)"/>
|
||||
<text x="85" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">llama.cpp</text>
|
||||
|
||||
<text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">Local Inference</text>
|
||||
<text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">OpenAI API compat</text>
|
||||
<text x="85" y="84" text-anchor="middle" font-size="9" class="mono-text">Port 8080</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow 3 -->
|
||||
<path d="M660 145 L710 145" stroke="url(#flowGrad)" stroke-width="3" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- Response -->
|
||||
<g transform="translate(720, 105)">
|
||||
<rect x="0" y="0" width="130" height="80" rx="8" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="130" height="28" rx="8" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="22" width="130" height="6" fill="url(#cyanGrad)"/>
|
||||
<text x="65" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Response</text>
|
||||
|
||||
<text x="65" y="52" text-anchor="middle" font-size="10" class="main-text">Generated Text</text>
|
||||
<text x="65" y="68" text-anchor="middle" font-size="9" class="mono-text">Streaming</text>
|
||||
</g>
|
||||
|
||||
<!-- Lower components: SQLite and Model -->
|
||||
|
||||
<!-- Vertical connector from botserver -->
|
||||
<path d="M335 200 L335 240" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- SQLite DB -->
|
||||
<g transform="translate(250, 250)">
|
||||
<rect x="0" y="0" width="170" height="90" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="170" height="28" rx="8" fill="#64748B"/>
|
||||
<rect x="0" y="22" width="170" height="6" fill="#64748B"/>
|
||||
<text x="85" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">SQLite DB</text>
|
||||
|
||||
<text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">(sessions)</text>
|
||||
<text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">Conversations</text>
|
||||
<text x="85" y="82" text-anchor="middle" font-size="9" class="mono-text">Context history</text>
|
||||
</g>
|
||||
|
||||
<!-- Vertical connector from llama.cpp -->
|
||||
<path d="M570 200 L570 240" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- Model GGUF -->
|
||||
<g transform="translate(485, 250)">
|
||||
<rect x="0" y="0" width="170" height="90" rx="8" fill="#F0FDF4" stroke="#A7F3D0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="170" height="28" rx="8" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="22" width="170" height="6" fill="url(#greenGrad)"/>
|
||||
<text x="85" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Model (GGUF)</text>
|
||||
|
||||
<text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">Quantized Model</text>
|
||||
<text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">Q4_K_M format</text>
|
||||
<text x="85" y="82" text-anchor="middle" font-size="9" class="mono-text">700MB - 4GB</text>
|
||||
</g>
|
||||
|
||||
<!-- Info box -->
|
||||
<g transform="translate(60, 230)">
|
||||
<rect x="0" y="0" width="160" height="110" rx="8" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
|
||||
<text x="80" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Offline Benefits</text>
|
||||
|
||||
<text x="15" y="45" font-size="9" class="secondary-text">✓ No internet needed</text>
|
||||
<text x="15" y="62" font-size="9" class="secondary-text">✓ Data stays local</text>
|
||||
<text x="15" y="79" font-size="9" class="secondary-text">✓ Zero API costs</text>
|
||||
<text x="15" y="96" font-size="9" class="secondary-text">✓ ~2-30 tok/sec</text>
|
||||
</g>
|
||||
|
||||
<!-- Model recommendations -->
|
||||
<g transform="translate(690, 230)">
|
||||
<rect x="0" y="0" width="160" height="110" rx="8" fill="#FEF3C7" stroke="#FCD34D" stroke-width="1"/>
|
||||
<text x="80" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Recommended</text>
|
||||
|
||||
<text x="15" y="45" font-size="9" class="secondary-text">2GB RAM:</text>
|
||||
<text x="75" y="45" font-size="9" class="mono-text">TinyLlama</text>
|
||||
<text x="15" y="62" font-size="9" class="secondary-text">4GB RAM:</text>
|
||||
<text x="75" y="62" font-size="9" class="mono-text">Phi-2</text>
|
||||
<text x="15" y="79" font-size="9" class="secondary-text">8GB RAM:</text>
|
||||
<text x="75" y="79" font-size="9" class="mono-text">Llama-3-8B</text>
|
||||
<text x="15" y="96" font-size="9" class="secondary-text">16GB RAM:</text>
|
||||
<text x="75" y="96" font-size="9" class="mono-text">Mistral-7B</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.7 KiB |
128
src/assets/chapter-13/orange-pi-5-specs.svg
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
<svg width="700" height="380" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="700" height="380" fill="#FAFBFC"/>
|
||||
<rect width="700" height="380" fill="url(#dots)"/>
|
||||
|
||||
<!-- Main Card -->
|
||||
<rect x="30" y="20" width="640" height="340" rx="12" fill="#FFFFFF" stroke="#FCD34D" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- Header -->
|
||||
<rect x="30" y="20" width="640" height="50" rx="12" fill="url(#orangeGrad)"/>
|
||||
<rect x="30" y="55" width="640" height="15" fill="url(#orangeGrad)"/>
|
||||
<text x="350" y="52" text-anchor="middle" font-size="18" font-weight="600" class="white-text">Orange Pi 5 — Best for Offline AI</text>
|
||||
|
||||
<!-- Badge -->
|
||||
<rect x="550" y="30" width="100" height="25" rx="12" fill="#FFFFFF" opacity="0.9"/>
|
||||
<text x="600" y="48" text-anchor="middle" font-size="11" font-weight="600" style="fill:#F59E0B;">NPU Ready</text>
|
||||
|
||||
<!-- Specs Grid -->
|
||||
<!-- Row 1 -->
|
||||
<g transform="translate(50, 90)">
|
||||
<rect x="0" y="0" width="180" height="65" rx="8" fill="#FFF7ED" stroke="#FDBA74" stroke-width="1"/>
|
||||
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">CPU</text>
|
||||
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">Rockchip RK3588S</text>
|
||||
<text x="15" y="55" font-size="10" class="mono-text">4x A76 + 4x A55</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(250, 90)">
|
||||
<rect x="0" y="0" width="180" height="65" rx="8" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
|
||||
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">NPU</text>
|
||||
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">6 TOPS</text>
|
||||
<text x="15" y="55" font-size="10" class="mono-text">Neural Processing Unit</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(450, 90)">
|
||||
<rect x="0" y="0" width="200" height="65" rx="8" fill="#F0FDF4" stroke="#86EFAC" stroke-width="1"/>
|
||||
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">GPU</text>
|
||||
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">Mali-G610 MP4</text>
|
||||
<text x="15" y="55" font-size="10" class="mono-text">OpenGL ES 3.2</text>
|
||||
</g>
|
||||
|
||||
<!-- Row 2 -->
|
||||
<g transform="translate(50, 170)">
|
||||
<rect x="0" y="0" width="180" height="65" rx="8" fill="#F5F3FF" stroke="#DDD6FE" stroke-width="1"/>
|
||||
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">RAM</text>
|
||||
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">4GB / 8GB / 16GB</text>
|
||||
<text x="15" y="55" font-size="10" class="mono-text">LPDDR4X</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(250, 170)">
|
||||
<rect x="0" y="0" width="180" height="65" rx="8" fill="#ECFDF5" stroke="#A7F3D0" stroke-width="1"/>
|
||||
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">Storage</text>
|
||||
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">M.2 NVMe + eMMC</text>
|
||||
<text x="15" y="55" font-size="10" class="mono-text">+ microSD slot</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(450, 170)">
|
||||
<rect x="0" y="0" width="200" height="65" rx="8" fill="#FEF3C7" stroke="#FCD34D" stroke-width="1"/>
|
||||
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">Price</text>
|
||||
<text x="15" y="40" font-size="16" font-weight="700" class="main-text">~$89</text>
|
||||
<text x="15" y="55" font-size="10" class="mono-text">8GB model</text>
|
||||
</g>
|
||||
|
||||
<!-- LLM Performance Section -->
|
||||
<rect x="50" y="250" width="600" height="95" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
|
||||
<text x="70" y="275" font-size="12" font-weight="600" class="main-text">LLM Performance</text>
|
||||
|
||||
<!-- Performance Bars -->
|
||||
<g transform="translate(70, 290)">
|
||||
<text x="0" y="12" font-size="10" class="secondary-text">TinyLlama 1.1B Q4</text>
|
||||
<rect x="140" y="2" width="200" height="14" rx="4" fill="#E2E8F0"/>
|
||||
<rect x="140" y="2" width="160" height="14" rx="4" fill="url(#greenGrad)"/>
|
||||
<text x="350" y="12" font-size="10" font-weight="500" class="mono-text">~8-12 tok/s</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(70, 312)">
|
||||
<text x="0" y="12" font-size="10" class="secondary-text">Phi-2 2.7B Q4</text>
|
||||
<rect x="140" y="2" width="200" height="14" rx="4" fill="#E2E8F0"/>
|
||||
<rect x="140" y="2" width="80" height="14" rx="4" fill="url(#orangeGrad)"/>
|
||||
<text x="350" y="12" font-size="10" font-weight="500" class="mono-text">~4-6 tok/s</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(70, 334)">
|
||||
<text x="0" y="12" font-size="10" class="secondary-text">With NPU (rkllm)</text>
|
||||
<rect x="140" y="2" width="200" height="14" rx="4" fill="#E2E8F0"/>
|
||||
<rect x="140" y="2" width="200" height="14" rx="4" fill="url(#primaryGrad)"/>
|
||||
<text x="350" y="12" font-size="10" font-weight="500" class="mono-text">~20-30 tok/s</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
162
src/assets/platform-architecture.svg
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
<svg width="900" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
|
||||
</filter>
|
||||
|
||||
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
|
||||
</marker>
|
||||
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
|
||||
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.title-text { fill: #F1F5F9; }
|
||||
.main-text { fill: #E2E8F0; }
|
||||
.secondary-text { fill: #94A3B8; }
|
||||
.mono-text { fill: #CBD5E1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="900" height="500" fill="#FAFBFC"/>
|
||||
<rect width="900" height="500" fill="url(#dots)"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="40" text-anchor="middle" font-size="22" font-weight="600" class="title-text">General Bots Platform</text>
|
||||
<text x="450" y="62" text-anchor="middle" font-size="12" class="secondary-text">Enterprise-Grade LLM Orchestrator & AI Automation Platform</text>
|
||||
|
||||
<!-- Main Container -->
|
||||
<rect x="30" y="85" width="840" height="385" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
|
||||
<!-- Top Row: botapp → botui → botserver -->
|
||||
|
||||
<!-- botapp -->
|
||||
<g transform="translate(70, 120)">
|
||||
<rect x="0" y="0" width="200" height="100" rx="10" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="32" rx="10" fill="url(#cyanGrad)"/>
|
||||
<rect x="0" y="25" width="200" height="7" fill="url(#cyanGrad)"/>
|
||||
<text x="100" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botapp</text>
|
||||
|
||||
<text x="100" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(Tauri)</text>
|
||||
<text x="100" y="72" text-anchor="middle" font-size="10" class="secondary-text">Desktop</text>
|
||||
<text x="100" y="88" text-anchor="middle" font-size="9" class="mono-text">Native file access</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow botapp → botui -->
|
||||
<path d="M275 170 L330 170" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- botui -->
|
||||
<g transform="translate(340, 120)">
|
||||
<rect x="0" y="0" width="200" height="100" rx="10" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="32" rx="10" fill="url(#greenGrad)"/>
|
||||
<rect x="0" y="25" width="200" height="7" fill="url(#greenGrad)"/>
|
||||
<text x="100" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botui</text>
|
||||
|
||||
<text x="100" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(Pure Web)</text>
|
||||
<text x="100" y="72" text-anchor="middle" font-size="10" class="secondary-text">HTMX/HTML</text>
|
||||
<text x="100" y="88" text-anchor="middle" font-size="9" class="mono-text">Suite & Minimal UI</text>
|
||||
</g>
|
||||
|
||||
<!-- Arrow botui → botserver -->
|
||||
<path d="M545 170 L600 170" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- botserver -->
|
||||
<g transform="translate(610, 120)">
|
||||
<rect x="0" y="0" width="200" height="100" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="200" height="32" rx="10" fill="url(#primaryGrad)"/>
|
||||
<rect x="0" y="25" width="200" height="7" fill="url(#primaryGrad)"/>
|
||||
<text x="100" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botserver</text>
|
||||
|
||||
<text x="100" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(API)</text>
|
||||
<text x="100" y="72" text-anchor="middle" font-size="10" class="secondary-text">Rust</text>
|
||||
<text x="100" y="88" text-anchor="middle" font-size="9" class="mono-text">LLM Orchestration</text>
|
||||
</g>
|
||||
|
||||
<!-- Vertical connector from botserver -->
|
||||
<path d="M710 225 L710 280" stroke="#8B5CF6" stroke-width="2" fill="none"/>
|
||||
|
||||
<!-- Horizontal split line -->
|
||||
<path d="M450 280 L710 280" stroke="#8B5CF6" stroke-width="2" fill="none"/>
|
||||
|
||||
<!-- Vertical lines down to bottom components -->
|
||||
<path d="M450 280 L450 320" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
<path d="M710 280 L710 320" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
|
||||
|
||||
<!-- Bottom Row: botlib and botbook -->
|
||||
|
||||
<!-- botlib -->
|
||||
<g transform="translate(340, 330)">
|
||||
<rect x="0" y="0" width="220" height="100" rx="10" fill="#FFFFFF" stroke="#F59E0B" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="220" height="32" rx="10" fill="url(#orangeGrad)"/>
|
||||
<rect x="0" y="25" width="220" height="7" fill="url(#orangeGrad)"/>
|
||||
<text x="110" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botlib</text>
|
||||
|
||||
<text x="110" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(Shared)</text>
|
||||
<text x="110" y="72" text-anchor="middle" font-size="10" class="secondary-text">Rust Library</text>
|
||||
<text x="110" y="88" text-anchor="middle" font-size="9" class="mono-text">Common types, HTTP client</text>
|
||||
</g>
|
||||
|
||||
<!-- botbook -->
|
||||
<g transform="translate(600, 330)">
|
||||
<rect x="0" y="0" width="220" height="100" rx="10" fill="#FFFFFF" stroke="#64748B" stroke-width="2" filter="url(#cardShadow)"/>
|
||||
<rect x="0" y="0" width="220" height="32" rx="10" fill="#64748B"/>
|
||||
<rect x="0" y="25" width="220" height="7" fill="#64748B"/>
|
||||
<text x="110" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botbook</text>
|
||||
|
||||
<text x="110" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(Docs)</text>
|
||||
<text x="110" y="72" text-anchor="middle" font-size="10" class="secondary-text">mdBook</text>
|
||||
<text x="110" y="88" text-anchor="middle" font-size="9" class="mono-text">Documentation</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend on left side -->
|
||||
<g transform="translate(70, 280)">
|
||||
<rect x="0" y="0" width="220" height="150" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
|
||||
<text x="110" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Components</text>
|
||||
|
||||
<rect x="15" y="38" width="12" height="12" rx="2" fill="url(#cyanGrad)"/>
|
||||
<text x="35" y="48" font-size="10" class="secondary-text">Desktop wrapper</text>
|
||||
|
||||
<rect x="15" y="58" width="12" height="12" rx="2" fill="url(#greenGrad)"/>
|
||||
<text x="35" y="68" font-size="10" class="secondary-text">Web interface</text>
|
||||
|
||||
<rect x="15" y="78" width="12" height="12" rx="2" fill="url(#primaryGrad)"/>
|
||||
<text x="35" y="88" font-size="10" class="secondary-text">Core API server</text>
|
||||
|
||||
<rect x="15" y="98" width="12" height="12" rx="2" fill="url(#orangeGrad)"/>
|
||||
<text x="35" y="108" font-size="10" class="secondary-text">Shared library</text>
|
||||
|
||||
<rect x="15" y="118" width="12" height="12" rx="2" fill="#64748B"/>
|
||||
<text x="35" y="128" font-size="10" class="secondary-text">Documentation</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |