Spaces:
Configuration error
Configuration error
Commit
·
57c13e3
1
Parent(s):
9ce2f95
major refactor of the deepdrone repo, to be a CLI agent
Browse files- .env-example +0 -8
- README.md +181 -36
- drone/__init__.py +4 -2
- drone/cli.py +280 -0
- drone/config.py +183 -0
- drone/drone_chat_interface.py +543 -0
- drone/drone_tools.py +342 -0
- drone/interactive_setup.py +414 -0
- drone/llm_interface.py +227 -0
- drone/terminal_chat.py +496 -0
- eeprom.bin +3 -0
- main.py +35 -210
- mav.parm +1349 -0
- {drone → misc}/drone_chat.py +0 -0
- {drone → misc}/hf_model.py +0 -0
- requirements.txt +8 -4
- setup.py +30 -8
- simulate_drone.py +234 -0
- terrain/S36E149.DAT +0 -0
- test_ollama_features.py +92 -0
- tests/__init__.py +0 -8
- tests/test_agent_mission.py +0 -179
- tests/test_connection.py +0 -33
- tests/test_mission.py +0 -136
- tests/test_mission_planning.py +0 -131
- tests/test_prompt_examples.md +0 -120
- tests/test_simple_agent.py +0 -75
.env-example
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
# DeepDrone Environment Variables
|
2 |
-
# Make a copy of this file and name it '.env', then fill in your API tokens
|
3 |
-
|
4 |
-
# Hugging Face API token (required for AI functionality)
|
5 |
-
# Get your token at https://huggingface.co/settings/tokens
|
6 |
-
HF_TOKEN=your_huggingface_token_here
|
7 |
-
|
8 |
-
# Add any additional API keys or environment variables below
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
@@ -1,53 +1,198 @@
|
|
1 |
-
|
2 |
-
title: DeepDrone
|
3 |
-
emoji: 🚁
|
4 |
-
colorFrom: green
|
5 |
-
colorTo: green
|
6 |
-
sdk: streamlit
|
7 |
-
sdk_version: 1.41.1
|
8 |
-
app_file: main.py
|
9 |
-
pinned: false
|
10 |
-
---
|
11 |
|
12 |
-
|
13 |
|
14 |
-
|
15 |
|
16 |
-
|
17 |
-
|
|
|
|
|
|
|
|
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
-
|
|
|
|
|
|
|
|
|
21 |
|
22 |
-
|
23 |
-
- **Visualizations**: Generate flight paths and sensor readings visualizations
|
24 |
-
- **Maintenance Recommendations**: Get maintenance suggestions based on flight hours
|
25 |
-
- **Mission Planning**: Generate mission plans for various drone operations
|
26 |
-
- **Real Drone Control**: Connect to and control real drones using DroneKit
|
27 |
-
- Take off and land
|
28 |
-
- Navigate to GPS coordinates
|
29 |
-
- Return to home
|
30 |
-
- Execute waypoint missions
|
31 |
-
- Monitor battery and location
|
32 |
|
33 |
-
|
34 |
|
35 |
-
1. Clone
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
|
41 |
-
|
|
|
|
|
|
|
42 |
|
43 |
-
|
|
|
|
|
|
|
44 |
|
45 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
```
|
50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
```
|
52 |
|
53 |
This script fixes the "AttributeError: module 'collections' has no attribute 'MutableMapping'" error by patching the DroneKit library to use collections.abc instead of collections.
|
|
|
1 |
+
# 🚁 DeepDrone - AI-Powered Drone Control Terminal
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
+
A powerful terminal-based application for controlling drones using various AI models including OpenAI, Anthropic, Hugging Face, and local Ollama models. Built with DroneKit integration for real drone control.
|
4 |
|
5 |
+
## ✨ Features
|
6 |
|
7 |
+
### 🤖 Multi-Model AI Support
|
8 |
+
- **OpenAI**: GPT-3.5, GPT-4, and other OpenAI models
|
9 |
+
- **Anthropic**: Claude 3 models
|
10 |
+
- **Ollama**: Local models (Llama 3.1, Codestral, etc.)
|
11 |
+
- **Hugging Face**: Any model available through their API
|
12 |
+
- **LiteLLM**: Unified interface for all providers
|
13 |
|
14 |
+
### 🚁 Drone Control & Operations
|
15 |
+
- **Real Drone Control**: Connect to and control real drones using DroneKit
|
16 |
+
- **Flight Operations**: Take off, land, navigate to GPS coordinates
|
17 |
+
- **Mission Planning**: Execute complex waypoint missions
|
18 |
+
- **Safety Features**: Return to home, emergency stop
|
19 |
+
- **Telemetry**: Monitor battery, location, and flight status
|
20 |
|
21 |
+
### 💻 Terminal Interface
|
22 |
+
- **Rich CLI**: Beautiful command-line interface with colors and formatting
|
23 |
+
- **Interactive Chat**: Natural language conversation with AI models
|
24 |
+
- **Model Management**: Easy switching between different AI models
|
25 |
+
- **Configuration**: Persistent settings and API key management
|
26 |
|
27 |
+
## 🚀 Quick Start
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
+
### Installation
|
30 |
|
31 |
+
1. **Clone the repository**
|
32 |
+
```bash
|
33 |
+
git clone <repository-url>
|
34 |
+
cd deepdrone
|
35 |
+
```
|
36 |
|
37 |
+
2. **Install dependencies**
|
38 |
+
```bash
|
39 |
+
pip install -r requirements.txt
|
40 |
+
```
|
41 |
|
42 |
+
3. **Start the interactive session**
|
43 |
+
```bash
|
44 |
+
python main.py
|
45 |
+
```
|
46 |
|
47 |
+
### Interactive Setup
|
48 |
+
|
49 |
+
When you run `python main.py`, DeepDrone will guide you through:
|
50 |
+
|
51 |
+
1. **🔮 Provider Selection** - Choose from OpenAI, Anthropic, Google, Meta, Mistral, or Ollama
|
52 |
+
2. **🤖 Model Selection** - Pick from popular models or enter your own
|
53 |
+
3. **🔑 API Key Entry** - Securely enter your API credentials (Ollama skips this)
|
54 |
+
4. **✅ Connection Test** - Verify everything works
|
55 |
+
5. **🚁 Simulator Setup** - Instructions for drone connection
|
56 |
+
6. **💬 Chat Interface** - Start controlling your drone with natural language
|
57 |
+
|
58 |
+
### Command Line Usage (Advanced)
|
59 |
+
|
60 |
+
For advanced users who prefer command-line options:
|
61 |
+
|
62 |
+
```bash
|
63 |
+
# List available models
|
64 |
+
python main.py models list
|
65 |
+
|
66 |
+
# Add API keys for cloud models
|
67 |
+
python main.py models set-key gpt-3.5-turbo
|
68 |
+
|
69 |
+
# Start a specific chat session
|
70 |
+
python main.py chat -m gpt-3.5-turbo
|
71 |
+
|
72 |
+
# Check Ollama models (for local AI)
|
73 |
+
python main.py ollama check
|
74 |
|
75 |
+
# View configuration
|
76 |
+
python main.py config
|
77 |
+
```
|
78 |
+
|
79 |
+
## 🛠️ Configuration
|
80 |
+
|
81 |
+
### Setting Up AI Models
|
82 |
+
|
83 |
+
#### OpenAI Models
|
84 |
+
```bash
|
85 |
+
# Add your OpenAI API key
|
86 |
+
python main.py models set-key gpt-3.5-turbo
|
87 |
+
# Get API key from: https://platform.openai.com/api-keys
|
88 |
+
```
|
89 |
+
|
90 |
+
#### Anthropic Models
|
91 |
+
```bash
|
92 |
+
# Add your Anthropic API key
|
93 |
+
python main.py models set-key claude-3-sonnet
|
94 |
+
# Get API key from: https://console.anthropic.com/
|
95 |
+
```
|
96 |
|
97 |
+
#### Local Ollama Models
|
98 |
+
```bash
|
99 |
+
# First install and run Ollama: https://ollama.ai
|
100 |
+
ollama pull llama3.1
|
101 |
+
ollama pull codestral
|
102 |
+
|
103 |
+
# Check available models
|
104 |
+
python main.py ollama check
|
105 |
```
|
106 |
+
|
107 |
+
### Drone Connection
|
108 |
+
|
109 |
+
#### Using the Built-in Simulator
|
110 |
+
|
111 |
+
**🎯 Quick Start:**
|
112 |
+
1. Open a new terminal and run:
|
113 |
+
```bash
|
114 |
+
python simulate_drone.py
|
115 |
+
```
|
116 |
+
2. The simulator will display a connection string like `udp:127.0.0.1:14550`
|
117 |
+
3. In DeepDrone chat, use that connection string to connect
|
118 |
+
|
119 |
+
#### Real Drone Connections
|
120 |
+
|
121 |
+
For real drones:
|
122 |
+
- **Serial**: `/dev/ttyACM0` (Linux) or `COM3` (Windows)
|
123 |
+
- **TCP**: `tcp:192.168.1.100:5760`
|
124 |
+
- **UDP**: `udp:127.0.0.1:14550`
|
125 |
+
|
126 |
+
#### Professional Simulation
|
127 |
+
|
128 |
+
For advanced simulation, install ArduPilot SITL:
|
129 |
+
```bash
|
130 |
+
# Get installation help
|
131 |
+
python simulate_drone.py --install-help
|
132 |
+
```
|
133 |
+
|
134 |
+
## 🎯 Usage Examples
|
135 |
+
|
136 |
+
### Basic Chat
|
137 |
+
```bash
|
138 |
+
# Start chat with GPT-3.5
|
139 |
+
python main.py chat -m gpt-3.5-turbo
|
140 |
+
|
141 |
+
# Start chat with local Ollama model
|
142 |
+
python main.py chat -m llama3.1
|
143 |
+
```
|
144 |
+
|
145 |
+
### Interactive Chat Session
|
146 |
+
|
147 |
+
Once in the chat interface, you can control your drone with natural language:
|
148 |
+
|
149 |
+
```
|
150 |
+
╭─────────────── DeepDrone Control Center ───────────────╮
|
151 |
+
│ 🚁 DEEPDRONE CHAT INTERFACE │
|
152 |
+
│ │
|
153 |
+
│ AI Model: claude-3-5-sonnet (anthropic) │
|
154 |
+
│ Drone Connection: udp:127.0.0.1:14550 │
|
155 |
+
│ Status: Ready for commands │
|
156 |
+
╰────────────────────────────────────────────────────────╯
|
157 |
+
|
158 |
+
🚁 DeepDrone> Connect to the drone simulator and take off to 30 meters
|
159 |
+
|
160 |
+
🤖 DeepDrone AI: I'll connect to the simulator and take off to 30 meters...
|
161 |
+
[Executes Python code to control drone]
|
162 |
+
✅ Connected and airborne at 30 meters!
|
163 |
+
|
164 |
+
🚁 DeepDrone> Fly in a square pattern with 50 meter sides
|
165 |
+
|
166 |
+
🤖 DeepDrone AI: I'll create a square flight pattern...
|
167 |
+
[Plans and executes waypoint mission]
|
168 |
+
✅ Square pattern completed!
|
169 |
+
|
170 |
+
🚁 DeepDrone> Return home and land
|
171 |
+
|
172 |
+
🤖 DeepDrone AI: Returning to launch point and landing safely...
|
173 |
+
✅ Mission complete, drone landed safely!
|
174 |
+
```
|
175 |
+
|
176 |
+
### Model Management
|
177 |
+
```bash
|
178 |
+
# Add a custom model
|
179 |
+
python main.py models add my-gpt4 openai gpt-4 --max-tokens 4096
|
180 |
+
|
181 |
+
# Remove a model
|
182 |
+
python main.py models remove my-gpt4
|
183 |
+
|
184 |
+
# View configuration
|
185 |
+
python main.py config
|
186 |
+
```
|
187 |
+
|
188 |
+
## 🛡️ DroneKit Integration
|
189 |
+
|
190 |
+
### Python 3.10+ Compatibility
|
191 |
+
|
192 |
+
If you're using Python 3.10 or newer, run the patch script:
|
193 |
+
|
194 |
+
```bash
|
195 |
+
python drone/dronekit_patch.py
|
196 |
```
|
197 |
|
198 |
This script fixes the "AttributeError: module 'collections' has no attribute 'MutableMapping'" error by patching the DroneKit library to use collections.abc instead of collections.
|
drone/__init__.py
CHANGED
@@ -4,9 +4,11 @@ Drone control and interface module.
|
|
4 |
This package contains all the drone-related functionality including:
|
5 |
- DroneKit integration
|
6 |
- Drone control and mission planning
|
7 |
-
-
|
|
|
8 |
"""
|
9 |
|
10 |
# Import main components for easier access
|
11 |
from .drone_control import DroneController, connect_drone, disconnect_drone, takeoff, land, return_home
|
12 |
-
from .
|
|
|
|
4 |
This package contains all the drone-related functionality including:
|
5 |
- DroneKit integration
|
6 |
- Drone control and mission planning
|
7 |
+
- Terminal interface for natural language interactions with the drone
|
8 |
+
- LiteLLM and Ollama integration for various AI models
|
9 |
"""
|
10 |
|
11 |
# Import main components for easier access
|
12 |
from .drone_control import DroneController, connect_drone, disconnect_drone, takeoff, land, return_home
|
13 |
+
from .config import config_manager
|
14 |
+
from .drone_tools import DroneToolsManager
|
drone/cli.py
ADDED
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Command Line Interface for DeepDrone terminal application.
|
3 |
+
"""
|
4 |
+
|
5 |
+
import typer
|
6 |
+
from rich.console import Console
|
7 |
+
from rich.table import Table
|
8 |
+
from rich.panel import Panel
|
9 |
+
from rich.prompt import Prompt, Confirm
|
10 |
+
from rich import print as rprint
|
11 |
+
from typing import Optional
|
12 |
+
import getpass
|
13 |
+
|
14 |
+
from .config import config_manager, ModelConfig
|
15 |
+
# Import will be done inside functions to avoid circular imports
|
16 |
+
|
17 |
+
app = typer.Typer(
|
18 |
+
name="deepdrone",
|
19 |
+
help="🚁 DeepDrone - AI-Powered Drone Control Terminal",
|
20 |
+
add_completion=False
|
21 |
+
)
|
22 |
+
|
23 |
+
console = Console()
|
24 |
+
|
25 |
+
@app.command()
|
26 |
+
def chat(
|
27 |
+
model: Optional[str] = typer.Option(None, "--model", "-m", help="Model to use for chat"),
|
28 |
+
connection: Optional[str] = typer.Option(None, "--connection", "-c", help="Drone connection string")
|
29 |
+
):
|
30 |
+
"""Start interactive chat with drone AI."""
|
31 |
+
|
32 |
+
# Show welcome banner
|
33 |
+
console.print(Panel.fit(
|
34 |
+
"[bold green]🚁 DEEPDRONE TERMINAL[/bold green]\n"
|
35 |
+
"[dim]AI-Powered Drone Control System[/dim]",
|
36 |
+
border_style="bright_green"
|
37 |
+
))
|
38 |
+
|
39 |
+
# Select model if not provided
|
40 |
+
if not model:
|
41 |
+
model = select_model()
|
42 |
+
if not model:
|
43 |
+
return
|
44 |
+
|
45 |
+
# Validate model exists
|
46 |
+
model_config = config_manager.get_model(model)
|
47 |
+
if not model_config:
|
48 |
+
console.print(f"[red]Error: Model '{model}' not found[/red]")
|
49 |
+
console.print("Use 'deepdrone models list' to see available models")
|
50 |
+
return
|
51 |
+
|
52 |
+
# Check if model needs API key
|
53 |
+
if model_config.provider in ["openai", "anthropic", "huggingface"] and not model_config.api_key:
|
54 |
+
console.print(f"[yellow]Model '{model}' requires an API key[/yellow]")
|
55 |
+
if Confirm.ask("Would you like to set it now?"):
|
56 |
+
set_api_key_interactive(model)
|
57 |
+
# Reload model config after setting API key
|
58 |
+
model_config = config_manager.get_model(model)
|
59 |
+
|
60 |
+
# Start chat
|
61 |
+
try:
|
62 |
+
from .terminal_chat import TerminalDroneChat
|
63 |
+
chat_session = TerminalDroneChat(model_config, connection)
|
64 |
+
chat_session.start()
|
65 |
+
except KeyboardInterrupt:
|
66 |
+
console.print("\n[yellow]Chat session ended[/yellow]")
|
67 |
+
except Exception as e:
|
68 |
+
console.print(f"[red]Error starting chat: {e}[/red]")
|
69 |
+
|
70 |
+
# Create models subcommand group
|
71 |
+
models_app = typer.Typer(help="Manage AI models")
|
72 |
+
app.add_typer(models_app, name="models")
|
73 |
+
|
74 |
+
@models_app.command("list")
|
75 |
+
def list_models():
|
76 |
+
"""List all available models."""
|
77 |
+
models = config_manager.list_models()
|
78 |
+
|
79 |
+
if not models:
|
80 |
+
console.print("[yellow]No models configured[/yellow]")
|
81 |
+
return
|
82 |
+
|
83 |
+
table = Table(title="Available Models")
|
84 |
+
table.add_column("Name", style="cyan", no_wrap=True)
|
85 |
+
table.add_column("Provider", style="magenta")
|
86 |
+
table.add_column("Model ID", style="blue")
|
87 |
+
table.add_column("API Key", style="green")
|
88 |
+
table.add_column("Status", style="yellow")
|
89 |
+
|
90 |
+
for name in models:
|
91 |
+
config = config_manager.get_model(name)
|
92 |
+
api_key_status = "✓" if config.api_key else "✗"
|
93 |
+
|
94 |
+
# Check status
|
95 |
+
if config.provider == "ollama":
|
96 |
+
status = "Local"
|
97 |
+
elif config.api_key:
|
98 |
+
status = "Ready"
|
99 |
+
else:
|
100 |
+
status = "Needs API Key"
|
101 |
+
|
102 |
+
table.add_row(
|
103 |
+
name,
|
104 |
+
config.provider,
|
105 |
+
config.model_id,
|
106 |
+
api_key_status,
|
107 |
+
status
|
108 |
+
)
|
109 |
+
|
110 |
+
console.print(table)
|
111 |
+
|
112 |
+
# Show usage hint
|
113 |
+
console.print("\n[dim]Use 'deepdrone chat -m <model_name>' to start chatting[/dim]")
|
114 |
+
|
115 |
+
@models_app.command("add")
|
116 |
+
def add_model(
|
117 |
+
name: str = typer.Argument(..., help="Name for the model"),
|
118 |
+
provider: str = typer.Argument(..., help="Provider (openai, anthropic, ollama, etc.)"),
|
119 |
+
model_id: str = typer.Argument(..., help="Model ID/identifier"),
|
120 |
+
base_url: Optional[str] = typer.Option(None, "--base-url", help="Base URL for API"),
|
121 |
+
max_tokens: int = typer.Option(2048, "--max-tokens", help="Maximum tokens"),
|
122 |
+
temperature: float = typer.Option(0.7, "--temperature", help="Temperature setting")
|
123 |
+
):
|
124 |
+
"""Add a new model configuration."""
|
125 |
+
|
126 |
+
model_config = ModelConfig(
|
127 |
+
name=name,
|
128 |
+
provider=provider,
|
129 |
+
model_id=model_id,
|
130 |
+
base_url=base_url,
|
131 |
+
max_tokens=max_tokens,
|
132 |
+
temperature=temperature
|
133 |
+
)
|
134 |
+
|
135 |
+
config_manager.add_model(model_config)
|
136 |
+
console.print(f"[green]Model '{name}' added successfully[/green]")
|
137 |
+
|
138 |
+
# Ask for API key if needed
|
139 |
+
if provider in ["openai", "anthropic", "huggingface"]:
|
140 |
+
if Confirm.ask(f"Would you like to set the API key for '{name}' now?"):
|
141 |
+
set_api_key_interactive(name)
|
142 |
+
|
143 |
+
@models_app.command("remove")
|
144 |
+
def remove_model(name: str = typer.Argument(..., help="Name of model to remove")):
|
145 |
+
"""Remove a model configuration."""
|
146 |
+
|
147 |
+
if not config_manager.get_model(name):
|
148 |
+
console.print(f"[red]Model '{name}' not found[/red]")
|
149 |
+
return
|
150 |
+
|
151 |
+
if Confirm.ask(f"Are you sure you want to remove model '{name}'?"):
|
152 |
+
if config_manager.remove_model(name):
|
153 |
+
console.print(f"[green]Model '{name}' removed successfully[/green]")
|
154 |
+
else:
|
155 |
+
console.print(f"[red]Failed to remove model '{name}'[/red]")
|
156 |
+
|
157 |
+
@models_app.command("set-key")
|
158 |
+
def set_api_key(
|
159 |
+
model: str = typer.Argument(..., help="Model name"),
|
160 |
+
key: Optional[str] = typer.Option(None, "--key", help="API key (will prompt if not provided)")
|
161 |
+
):
|
162 |
+
"""Set API key for a model."""
|
163 |
+
set_api_key_interactive(model, key)
|
164 |
+
|
165 |
+
@app.command("config")
|
166 |
+
def show_config():
|
167 |
+
"""Show current configuration."""
|
168 |
+
|
169 |
+
console.print(Panel.fit(
|
170 |
+
f"[bold]Configuration Directory:[/bold] {config_manager.settings.config_dir}\n"
|
171 |
+
f"[bold]Models File:[/bold] {config_manager.settings.models_file}\n"
|
172 |
+
f"[bold]Default Model:[/bold] {config_manager.settings.default_model}\n"
|
173 |
+
f"[bold]Default Connection:[/bold] {config_manager.settings.drone.default_connection_string}",
|
174 |
+
title="DeepDrone Configuration",
|
175 |
+
border_style="blue"
|
176 |
+
))
|
177 |
+
|
178 |
+
# Create ollama subcommand group
|
179 |
+
ollama_app = typer.Typer(help="Ollama-specific commands")
|
180 |
+
app.add_typer(ollama_app, name="ollama")
|
181 |
+
|
182 |
+
@ollama_app.command("check")
|
183 |
+
def check_ollama():
|
184 |
+
"""Check if Ollama is running and list available models."""
|
185 |
+
try:
|
186 |
+
import ollama
|
187 |
+
|
188 |
+
# Try to connect to Ollama
|
189 |
+
models = ollama.list()
|
190 |
+
|
191 |
+
if not hasattr(models, 'models') or not models.models:
|
192 |
+
console.print("[yellow]Ollama is running but no models are installed[/yellow]")
|
193 |
+
console.print("Install a model with: ollama pull llama3.1")
|
194 |
+
return
|
195 |
+
|
196 |
+
table = Table(title="Ollama Models")
|
197 |
+
table.add_column("Name", style="cyan")
|
198 |
+
table.add_column("Size", style="blue")
|
199 |
+
table.add_column("Modified", style="green")
|
200 |
+
|
201 |
+
for model in models.models:
|
202 |
+
table.add_row(
|
203 |
+
model.model,
|
204 |
+
f"{model.size / (1024**3):.1f} GB" if hasattr(model, 'size') else "Unknown",
|
205 |
+
str(model.modified_at)[:19] if hasattr(model, 'modified_at') else 'Unknown'
|
206 |
+
)
|
207 |
+
|
208 |
+
console.print(table)
|
209 |
+
console.print("\n[dim]Use 'deepdrone models add <name> ollama <model_name>' to add to DeepDrone[/dim]")
|
210 |
+
|
211 |
+
except ImportError:
|
212 |
+
console.print("[red]Ollama Python package not installed[/red]")
|
213 |
+
console.print("Install with: pip install ollama")
|
214 |
+
except Exception as e:
|
215 |
+
console.print(f"[red]Error connecting to Ollama: {e}[/red]")
|
216 |
+
console.print("Make sure Ollama is running: ollama serve")
|
217 |
+
|
218 |
+
def select_model() -> Optional[str]:
|
219 |
+
"""Interactive model selection."""
|
220 |
+
models = config_manager.list_models()
|
221 |
+
|
222 |
+
if not models:
|
223 |
+
console.print("[red]No models configured[/red]")
|
224 |
+
console.print("Use 'deepdrone models add' to add a model")
|
225 |
+
return None
|
226 |
+
|
227 |
+
console.print("\n[bold]Available Models:[/bold]")
|
228 |
+
for i, model_name in enumerate(models, 1):
|
229 |
+
config = config_manager.get_model(model_name)
|
230 |
+
status = "✓" if config.api_key or config.provider == "ollama" else "⚠ (needs API key)"
|
231 |
+
console.print(f" {i}. {model_name} ({config.provider}) {status}")
|
232 |
+
|
233 |
+
while True:
|
234 |
+
try:
|
235 |
+
choice = Prompt.ask(
|
236 |
+
"\nSelect model",
|
237 |
+
choices=[str(i) for i in range(1, len(models) + 1)] + models,
|
238 |
+
default="1"
|
239 |
+
)
|
240 |
+
|
241 |
+
if choice.isdigit():
|
242 |
+
return models[int(choice) - 1]
|
243 |
+
elif choice in models:
|
244 |
+
return choice
|
245 |
+
except (ValueError, IndexError):
|
246 |
+
console.print("[red]Invalid selection[/red]")
|
247 |
+
|
248 |
+
def set_api_key_interactive(model_name: str, api_key: Optional[str] = None):
|
249 |
+
"""Set API key interactively."""
|
250 |
+
model_config = config_manager.get_model(model_name)
|
251 |
+
if not model_config:
|
252 |
+
console.print(f"[red]Model '{model_name}' not found[/red]")
|
253 |
+
return
|
254 |
+
|
255 |
+
if model_config.provider == "ollama":
|
256 |
+
console.print(f"[yellow]Model '{model_name}' is an Ollama model and doesn't need an API key[/yellow]")
|
257 |
+
return
|
258 |
+
|
259 |
+
if not api_key:
|
260 |
+
console.print(f"\n[bold]Setting API key for {model_name} ({model_config.provider})[/bold]")
|
261 |
+
|
262 |
+
if model_config.provider == "openai":
|
263 |
+
console.print("Get your OpenAI API key from: https://platform.openai.com/api-keys")
|
264 |
+
elif model_config.provider == "anthropic":
|
265 |
+
console.print("Get your Anthropic API key from: https://console.anthropic.com/")
|
266 |
+
elif model_config.provider == "huggingface":
|
267 |
+
console.print("Get your Hugging Face token from: https://huggingface.co/settings/tokens")
|
268 |
+
|
269 |
+
api_key = getpass.getpass("Enter API key (hidden): ")
|
270 |
+
|
271 |
+
if api_key.strip():
|
272 |
+
if config_manager.set_api_key(model_name, api_key.strip()):
|
273 |
+
console.print(f"[green]API key set for '{model_name}'[/green]")
|
274 |
+
else:
|
275 |
+
console.print(f"[red]Failed to set API key for '{model_name}'[/red]")
|
276 |
+
else:
|
277 |
+
console.print("[yellow]No API key provided[/yellow]")
|
278 |
+
|
279 |
+
if __name__ == "__main__":
|
280 |
+
app()
|
drone/config.py
ADDED
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Configuration management for DeepDrone terminal application.
|
3 |
+
"""
|
4 |
+
|
5 |
+
import os
|
6 |
+
import json
|
7 |
+
from pathlib import Path
|
8 |
+
from typing import Dict, Optional, List
|
9 |
+
from pydantic import BaseModel, Field
|
10 |
+
from pydantic_settings import BaseSettings
|
11 |
+
|
12 |
+
class ModelConfig(BaseModel):
|
13 |
+
"""Configuration for a specific model."""
|
14 |
+
name: str
|
15 |
+
provider: str # 'openai', 'anthropic', 'ollama', 'huggingface', etc.
|
16 |
+
api_key: Optional[str] = None
|
17 |
+
base_url: Optional[str] = None
|
18 |
+
model_id: str
|
19 |
+
max_tokens: int = 2048
|
20 |
+
temperature: float = 0.7
|
21 |
+
|
22 |
+
class DroneConfig(BaseModel):
|
23 |
+
"""Configuration for drone connection."""
|
24 |
+
default_connection_string: str = "udp:127.0.0.1:14550"
|
25 |
+
timeout: int = 30
|
26 |
+
default_altitude: float = 30.0
|
27 |
+
max_altitude: float = 100.0
|
28 |
+
|
29 |
+
class AppSettings(BaseSettings):
|
30 |
+
"""Main application settings."""
|
31 |
+
|
32 |
+
# File paths
|
33 |
+
config_dir: Path = Field(default_factory=lambda: Path.home() / ".deepdrone")
|
34 |
+
models_file: Path = Field(default_factory=lambda: Path.home() / ".deepdrone" / "models.json")
|
35 |
+
|
36 |
+
# Default model
|
37 |
+
default_model: str = "gpt-3.5-turbo"
|
38 |
+
|
39 |
+
# Drone settings
|
40 |
+
drone: DroneConfig = Field(default_factory=DroneConfig)
|
41 |
+
|
42 |
+
# Terminal settings
|
43 |
+
show_thinking: bool = True
|
44 |
+
auto_save_chat: bool = True
|
45 |
+
chat_history_limit: int = 100
|
46 |
+
|
47 |
+
class Config:
|
48 |
+
env_prefix = "DEEPDRONE_"
|
49 |
+
env_file = ".env"
|
50 |
+
extra = "ignore" # Ignore extra environment variables
|
51 |
+
|
52 |
+
class ConfigManager:
|
53 |
+
"""Manages application configuration and model settings."""
|
54 |
+
|
55 |
+
def __init__(self):
|
56 |
+
self.settings = AppSettings()
|
57 |
+
self.models: Dict[str, ModelConfig] = {}
|
58 |
+
self._ensure_config_dir()
|
59 |
+
self._load_models()
|
60 |
+
|
61 |
+
def _ensure_config_dir(self):
|
62 |
+
"""Ensure configuration directory exists."""
|
63 |
+
self.settings.config_dir.mkdir(exist_ok=True)
|
64 |
+
|
65 |
+
def _load_models(self):
|
66 |
+
"""Load model configurations from file."""
|
67 |
+
if self.settings.models_file.exists():
|
68 |
+
try:
|
69 |
+
with open(self.settings.models_file, 'r') as f:
|
70 |
+
models_data = json.load(f)
|
71 |
+
self.models = {
|
72 |
+
name: ModelConfig(**config)
|
73 |
+
for name, config in models_data.items()
|
74 |
+
}
|
75 |
+
except Exception as e:
|
76 |
+
print(f"Error loading models config: {e}")
|
77 |
+
self.models = {}
|
78 |
+
else:
|
79 |
+
# Create default models
|
80 |
+
self._create_default_models()
|
81 |
+
|
82 |
+
def _create_default_models(self):
|
83 |
+
"""Create default model configurations."""
|
84 |
+
self.models = {
|
85 |
+
"gpt-3.5-turbo": ModelConfig(
|
86 |
+
name="gpt-3.5-turbo",
|
87 |
+
provider="openai",
|
88 |
+
model_id="gpt-3.5-turbo",
|
89 |
+
max_tokens=2048,
|
90 |
+
temperature=0.7
|
91 |
+
),
|
92 |
+
"gpt-4": ModelConfig(
|
93 |
+
name="gpt-4",
|
94 |
+
provider="openai",
|
95 |
+
model_id="gpt-4",
|
96 |
+
max_tokens=2048,
|
97 |
+
temperature=0.7
|
98 |
+
),
|
99 |
+
"claude-3-sonnet": ModelConfig(
|
100 |
+
name="claude-3-sonnet",
|
101 |
+
provider="anthropic",
|
102 |
+
model_id="claude-3-sonnet-20240229",
|
103 |
+
max_tokens=2048,
|
104 |
+
temperature=0.7
|
105 |
+
),
|
106 |
+
"llama3.1": ModelConfig(
|
107 |
+
name="llama3.1",
|
108 |
+
provider="ollama",
|
109 |
+
model_id="llama3.1:latest",
|
110 |
+
base_url="http://localhost:11434",
|
111 |
+
max_tokens=2048,
|
112 |
+
temperature=0.7
|
113 |
+
),
|
114 |
+
"codestral": ModelConfig(
|
115 |
+
name="codestral",
|
116 |
+
provider="ollama",
|
117 |
+
model_id="codestral:latest",
|
118 |
+
base_url="http://localhost:11434",
|
119 |
+
max_tokens=2048,
|
120 |
+
temperature=0.7
|
121 |
+
)
|
122 |
+
}
|
123 |
+
self.save_models()
|
124 |
+
|
125 |
+
def save_models(self):
|
126 |
+
"""Save model configurations to file."""
|
127 |
+
try:
|
128 |
+
models_data = {
|
129 |
+
name: config.model_dump()
|
130 |
+
for name, config in self.models.items()
|
131 |
+
}
|
132 |
+
with open(self.settings.models_file, 'w') as f:
|
133 |
+
json.dump(models_data, f, indent=2)
|
134 |
+
except Exception as e:
|
135 |
+
print(f"Error saving models config: {e}")
|
136 |
+
|
137 |
+
def add_model(self, config: ModelConfig):
|
138 |
+
"""Add a new model configuration."""
|
139 |
+
self.models[config.name] = config
|
140 |
+
self.save_models()
|
141 |
+
|
142 |
+
def remove_model(self, name: str) -> bool:
|
143 |
+
"""Remove a model configuration."""
|
144 |
+
if name in self.models:
|
145 |
+
del self.models[name]
|
146 |
+
self.save_models()
|
147 |
+
return True
|
148 |
+
return False
|
149 |
+
|
150 |
+
def get_model(self, name: str) -> Optional[ModelConfig]:
|
151 |
+
"""Get a model configuration by name."""
|
152 |
+
return self.models.get(name)
|
153 |
+
|
154 |
+
def list_models(self) -> List[str]:
|
155 |
+
"""List all available model names."""
|
156 |
+
return list(self.models.keys())
|
157 |
+
|
158 |
+
def set_api_key(self, model_name: str, api_key: str) -> bool:
|
159 |
+
"""Set API key for a model."""
|
160 |
+
if model_name in self.models:
|
161 |
+
self.models[model_name].api_key = api_key
|
162 |
+
self.save_models()
|
163 |
+
return True
|
164 |
+
return False
|
165 |
+
|
166 |
+
def get_ollama_models(self) -> List[str]:
|
167 |
+
"""Get list of available Ollama models."""
|
168 |
+
ollama_models = []
|
169 |
+
for name, config in self.models.items():
|
170 |
+
if config.provider == "ollama":
|
171 |
+
ollama_models.append(name)
|
172 |
+
return ollama_models
|
173 |
+
|
174 |
+
def get_api_models(self) -> List[str]:
|
175 |
+
"""Get list of models that require API keys."""
|
176 |
+
api_models = []
|
177 |
+
for name, config in self.models.items():
|
178 |
+
if config.provider in ["openai", "anthropic", "huggingface"]:
|
179 |
+
api_models.append(name)
|
180 |
+
return api_models
|
181 |
+
|
182 |
+
# Global config manager instance
|
183 |
+
config_manager = ConfigManager()
|
drone/drone_chat_interface.py
ADDED
@@ -0,0 +1,543 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Interactive drone chat interface - similar to Claude Code environment.
|
3 |
+
"""
|
4 |
+
|
5 |
+
import os
|
6 |
+
import sys
|
7 |
+
import time
|
8 |
+
import threading
|
9 |
+
from typing import List, Dict, Any, Optional
|
10 |
+
from rich.console import Console
|
11 |
+
from rich.panel import Panel
|
12 |
+
from rich.text import Text
|
13 |
+
from rich.markdown import Markdown
|
14 |
+
from rich.layout import Layout
|
15 |
+
from rich.live import Live
|
16 |
+
from rich.spinner import Spinner
|
17 |
+
from rich.table import Table
|
18 |
+
from rich.align import Align
|
19 |
+
from prompt_toolkit import prompt
|
20 |
+
from prompt_toolkit.history import InMemoryHistory
|
21 |
+
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
22 |
+
from prompt_toolkit.completion import WordCompleter
|
23 |
+
from prompt_toolkit.styles import Style
|
24 |
+
from prompt_toolkit.shortcuts import confirm
|
25 |
+
import re
|
26 |
+
|
27 |
+
from .config import ModelConfig
|
28 |
+
from .llm_interface import LLMInterface
|
29 |
+
from .drone_tools import DroneToolsManager
|
30 |
+
|
31 |
+
class DroneChatInterface:
|
32 |
+
"""Interactive chat interface for drone control with AI."""
|
33 |
+
|
34 |
+
def __init__(self, model_config: ModelConfig, connection_string: Optional[str] = None):
|
35 |
+
self.console = Console()
|
36 |
+
self.model_config = model_config
|
37 |
+
self.connection_string = connection_string or "udp:127.0.0.1:14550"
|
38 |
+
|
39 |
+
# Initialize components
|
40 |
+
self.llm = LLMInterface(model_config)
|
41 |
+
self.drone_tools = DroneToolsManager(self.connection_string)
|
42 |
+
|
43 |
+
# Chat state
|
44 |
+
self.chat_history: List[Dict[str, str]] = []
|
45 |
+
self.session_active = True
|
46 |
+
|
47 |
+
# Setup prompt components
|
48 |
+
self.history = InMemoryHistory()
|
49 |
+
self.completer = WordCompleter([
|
50 |
+
'connect', 'takeoff', 'land', 'fly', 'goto', 'mission', 'status',
|
51 |
+
'battery', 'location', 'return', 'home', 'help', 'quit', 'exit',
|
52 |
+
'emergency', 'stop', 'altitude', 'waypoint', 'navigate'
|
53 |
+
])
|
54 |
+
|
55 |
+
self.style = Style.from_dict({
|
56 |
+
'prompt': '#00ff00 bold',
|
57 |
+
'input': '#ffffff',
|
58 |
+
'completion-menu.completion': 'bg:#333333 #ffffff',
|
59 |
+
'completion-menu.completion.current': 'bg:#00ff00 #000000 bold',
|
60 |
+
})
|
61 |
+
|
62 |
+
# Status tracking
|
63 |
+
self.last_status_update = time.time()
|
64 |
+
self.status_thread = None
|
65 |
+
self.status_running = False
|
66 |
+
|
67 |
+
def start(self):
|
68 |
+
"""Start the interactive chat session."""
|
69 |
+
self._show_welcome()
|
70 |
+
self._start_status_monitor()
|
71 |
+
|
72 |
+
try:
|
73 |
+
while self.session_active:
|
74 |
+
try:
|
75 |
+
# Get user input
|
76 |
+
user_input = self._get_user_input()
|
77 |
+
|
78 |
+
if not user_input.strip():
|
79 |
+
continue
|
80 |
+
|
81 |
+
# Handle special commands
|
82 |
+
if self._handle_special_commands(user_input):
|
83 |
+
continue
|
84 |
+
|
85 |
+
# Process the message
|
86 |
+
self._process_message(user_input)
|
87 |
+
|
88 |
+
except KeyboardInterrupt:
|
89 |
+
self._handle_exit()
|
90 |
+
break
|
91 |
+
except EOFError:
|
92 |
+
self._handle_exit()
|
93 |
+
break
|
94 |
+
|
95 |
+
finally:
|
96 |
+
self._cleanup()
|
97 |
+
|
98 |
+
def _show_welcome(self):
|
99 |
+
"""Show welcome message and status."""
|
100 |
+
welcome_panel = Panel(
|
101 |
+
f"""[bold green]🚁 DEEPDRONE CHAT INTERFACE[/bold green]
|
102 |
+
|
103 |
+
[bold]AI Model:[/bold] {self.model_config.name} ({self.model_config.provider})
|
104 |
+
[bold]Model ID:[/bold] {self.model_config.model_id}
|
105 |
+
[bold]Drone Connection:[/bold] {self.connection_string}
|
106 |
+
[bold]Status:[/bold] Ready for commands
|
107 |
+
|
108 |
+
[bold cyan]🎯 Available Commands:[/bold cyan]
|
109 |
+
• Natural language: "Connect to drone and take off to 30 meters"
|
110 |
+
• Status commands: "Show battery status", "What's my location?"
|
111 |
+
• Mission commands: "Fly in a square pattern", "Return home"
|
112 |
+
• Help: Type 'help' for more information
|
113 |
+
• Exit: Type 'quit' or 'exit' to end session
|
114 |
+
|
115 |
+
[dim]Type your commands below. The AI will interpret and execute drone operations.[/dim]""",
|
116 |
+
title="[bold green]DeepDrone Control Center[/bold green]",
|
117 |
+
border_style="bright_green",
|
118 |
+
padding=(1, 2)
|
119 |
+
)
|
120 |
+
|
121 |
+
self.console.print(welcome_panel)
|
122 |
+
self.console.print()
|
123 |
+
|
124 |
+
def _get_user_input(self) -> str:
|
125 |
+
"""Get user input."""
|
126 |
+
try:
|
127 |
+
# Use simple input() for better compatibility
|
128 |
+
return input("🚁 DeepDrone> ")
|
129 |
+
except (KeyboardInterrupt, EOFError):
|
130 |
+
return '/quit'
|
131 |
+
|
132 |
+
def _handle_special_commands(self, command: str) -> bool:
|
133 |
+
"""Handle special commands. Returns True if handled."""
|
134 |
+
cmd = command.strip().lower()
|
135 |
+
|
136 |
+
if cmd in ['/quit', '/exit', 'quit', 'exit']:
|
137 |
+
self.session_active = False
|
138 |
+
return True
|
139 |
+
|
140 |
+
elif cmd in ['/help', 'help']:
|
141 |
+
self._show_help()
|
142 |
+
return True
|
143 |
+
|
144 |
+
elif cmd in ['/status', 'status']:
|
145 |
+
self._show_detailed_status()
|
146 |
+
return True
|
147 |
+
|
148 |
+
elif cmd in ['/clear', 'clear']:
|
149 |
+
self.console.clear()
|
150 |
+
self._show_welcome()
|
151 |
+
return True
|
152 |
+
|
153 |
+
elif cmd.startswith('/connect'):
|
154 |
+
parts = cmd.split()
|
155 |
+
if len(parts) > 1:
|
156 |
+
self.connection_string = parts[1]
|
157 |
+
self._connect_drone_direct()
|
158 |
+
else:
|
159 |
+
self.console.print("[red]Usage: /connect <connection_string>[/red]")
|
160 |
+
return True
|
161 |
+
|
162 |
+
elif cmd in ['/disconnect', 'disconnect']:
|
163 |
+
self._disconnect_drone_direct()
|
164 |
+
return True
|
165 |
+
|
166 |
+
elif cmd in ['/emergency', 'emergency']:
|
167 |
+
self._emergency_stop()
|
168 |
+
return True
|
169 |
+
|
170 |
+
elif cmd in ['/ollama', 'ollama']:
|
171 |
+
self._show_ollama_status()
|
172 |
+
return True
|
173 |
+
|
174 |
+
return False
|
175 |
+
|
176 |
+
def _process_message(self, user_message: str):
|
177 |
+
"""Process user message with AI and execute drone commands."""
|
178 |
+
# Add to history
|
179 |
+
self.chat_history.append({"role": "user", "content": user_message})
|
180 |
+
|
181 |
+
# Show user message
|
182 |
+
self.console.print(Panel(
|
183 |
+
user_message,
|
184 |
+
title="[bold blue]You[/bold blue]",
|
185 |
+
border_style="blue",
|
186 |
+
padding=(0, 1)
|
187 |
+
))
|
188 |
+
|
189 |
+
# Generate AI response
|
190 |
+
with Live(
|
191 |
+
Spinner("dots", text="[green]🤖 AI is analyzing your request...[/green]"),
|
192 |
+
console=self.console,
|
193 |
+
transient=True
|
194 |
+
) as live:
|
195 |
+
|
196 |
+
try:
|
197 |
+
# Create system prompt for drone operations
|
198 |
+
system_prompt = self._create_drone_system_prompt()
|
199 |
+
|
200 |
+
# Prepare messages for AI
|
201 |
+
messages = [{"role": "system", "content": system_prompt}]
|
202 |
+
messages.extend(self.chat_history[-10:]) # Last 10 messages for context
|
203 |
+
|
204 |
+
# Get AI response
|
205 |
+
ai_response = self.llm.chat(messages)
|
206 |
+
|
207 |
+
live.stop()
|
208 |
+
|
209 |
+
# Process the response for drone commands
|
210 |
+
self._process_ai_response(ai_response)
|
211 |
+
|
212 |
+
# Add to history
|
213 |
+
self.chat_history.append({"role": "assistant", "content": ai_response})
|
214 |
+
|
215 |
+
except Exception as e:
|
216 |
+
live.stop()
|
217 |
+
self.console.print(f"[red]❌ Error processing request: {e}[/red]")
|
218 |
+
|
219 |
+
def _create_drone_system_prompt(self) -> str:
|
220 |
+
"""Create system prompt for drone operations."""
|
221 |
+
return f"""You are DeepDrone AI, an advanced drone control assistant. You can control real drones through Python code.
|
222 |
+
|
223 |
+
Current drone status:
|
224 |
+
- Connected: {'Yes' if self.drone_tools.is_connected() else 'No'}
|
225 |
+
- Connection: {self.connection_string}
|
226 |
+
- Mission active: {'Yes' if self.drone_tools.mission_in_progress else 'No'}
|
227 |
+
|
228 |
+
Available drone functions (use these in Python code blocks):
|
229 |
+
- connect_drone(connection_string): Connect to drone
|
230 |
+
- takeoff(altitude): Take off to specified altitude in meters
|
231 |
+
- land(): Land the drone
|
232 |
+
- return_home(): Return to launch point
|
233 |
+
- fly_to(lat, lon, alt): Fly to GPS coordinates
|
234 |
+
- get_location(): Get current GPS position
|
235 |
+
- get_battery(): Get battery status
|
236 |
+
- execute_mission(waypoints): Execute mission with waypoints list
|
237 |
+
- disconnect_drone(): Disconnect from drone
|
238 |
+
|
239 |
+
When user asks for drone operations:
|
240 |
+
1. Explain what you'll do
|
241 |
+
2. Provide Python code in ```python code blocks
|
242 |
+
3. The code will be executed automatically
|
243 |
+
4. Provide status updates
|
244 |
+
|
245 |
+
Example response:
|
246 |
+
"I'll connect to the drone and take off to 30 meters altitude.
|
247 |
+
|
248 |
+
```python
|
249 |
+
# Connect to the drone
|
250 |
+
connect_drone('{self.connection_string}')
|
251 |
+
|
252 |
+
# Take off to 30 meters
|
253 |
+
takeoff(30)
|
254 |
+
|
255 |
+
# Get status
|
256 |
+
location = get_location()
|
257 |
+
battery = get_battery()
|
258 |
+
print(f"Location: {{location}}")
|
259 |
+
print(f"Battery: {{battery}}")
|
260 |
+
```
|
261 |
+
|
262 |
+
The drone should now be airborne at 30 meters altitude."
|
263 |
+
|
264 |
+
Always prioritize safety and explain each operation clearly."""
|
265 |
+
|
266 |
+
def _process_ai_response(self, response: str):
|
267 |
+
"""Process AI response and execute any drone commands."""
|
268 |
+
# Show AI response
|
269 |
+
if any(marker in response for marker in ['**', '*', '```', '#', '-', '1.']):
|
270 |
+
content = Markdown(response)
|
271 |
+
else:
|
272 |
+
content = Text(response)
|
273 |
+
|
274 |
+
self.console.print(Panel(
|
275 |
+
content,
|
276 |
+
title="[bold green]🤖 DeepDrone AI[/bold green]",
|
277 |
+
border_style="green",
|
278 |
+
padding=(0, 1)
|
279 |
+
))
|
280 |
+
|
281 |
+
# Extract and execute Python code blocks
|
282 |
+
code_blocks = self._extract_code_blocks(response)
|
283 |
+
if code_blocks:
|
284 |
+
self.console.print(Panel(
|
285 |
+
"[yellow]🔧 Executing drone operations...[/yellow]",
|
286 |
+
border_style="yellow"
|
287 |
+
))
|
288 |
+
|
289 |
+
for i, code in enumerate(code_blocks, 1):
|
290 |
+
self.console.print(f"[dim]Executing code block {i}...[/dim]")
|
291 |
+
try:
|
292 |
+
result = self._execute_drone_code(code)
|
293 |
+
if result:
|
294 |
+
self.console.print(Panel(
|
295 |
+
f"[green]✅ Execution Result:[/green]\n{result}",
|
296 |
+
border_style="green"
|
297 |
+
))
|
298 |
+
except Exception as e:
|
299 |
+
self.console.print(Panel(
|
300 |
+
f"[red]❌ Execution Error:[/red]\n{str(e)}",
|
301 |
+
border_style="red"
|
302 |
+
))
|
303 |
+
|
304 |
+
def _extract_code_blocks(self, text: str) -> List[str]:
|
305 |
+
"""Extract Python code blocks from markdown text."""
|
306 |
+
pattern = r'```(?:python)?\n(.*?)\n```'
|
307 |
+
matches = re.findall(pattern, text, re.DOTALL)
|
308 |
+
return [match.strip() for match in matches if match.strip()]
|
309 |
+
|
310 |
+
def _execute_drone_code(self, code: str) -> str:
|
311 |
+
"""Execute drone code safely."""
|
312 |
+
# Create safe execution environment
|
313 |
+
safe_globals = {
|
314 |
+
'__builtins__': {
|
315 |
+
'print': print,
|
316 |
+
'len': len,
|
317 |
+
'str': str,
|
318 |
+
'int': int,
|
319 |
+
'float': float,
|
320 |
+
'dict': dict,
|
321 |
+
'list': list,
|
322 |
+
'range': range,
|
323 |
+
},
|
324 |
+
'connect_drone': self.drone_tools.connect_drone,
|
325 |
+
'disconnect_drone': self.drone_tools.disconnect_drone,
|
326 |
+
'takeoff': self.drone_tools.takeoff,
|
327 |
+
'land': self.drone_tools.land,
|
328 |
+
'return_home': self.drone_tools.return_home,
|
329 |
+
'fly_to': self.drone_tools.fly_to,
|
330 |
+
'get_location': self.drone_tools.get_location,
|
331 |
+
'get_battery': self.drone_tools.get_battery,
|
332 |
+
'execute_mission': self.drone_tools.execute_mission,
|
333 |
+
'time': time,
|
334 |
+
}
|
335 |
+
|
336 |
+
# Capture output
|
337 |
+
output_lines = []
|
338 |
+
|
339 |
+
def capture_print(*args, **kwargs):
|
340 |
+
output_lines.append(' '.join(str(arg) for arg in args))
|
341 |
+
|
342 |
+
safe_globals['print'] = capture_print
|
343 |
+
|
344 |
+
# Execute code
|
345 |
+
exec(code, safe_globals)
|
346 |
+
|
347 |
+
return '\n'.join(output_lines) if output_lines else "Code executed successfully"
|
348 |
+
|
349 |
+
def _show_help(self):
|
350 |
+
"""Show help information."""
|
351 |
+
help_text = """[bold cyan]🚁 DeepDrone Help[/bold cyan]
|
352 |
+
|
353 |
+
[bold]Natural Language Commands:[/bold]
|
354 |
+
• "Connect to the drone simulator"
|
355 |
+
• "Take off to 30 meters altitude"
|
356 |
+
• "Fly to coordinates 37.7749, -122.4194"
|
357 |
+
• "Show me the current battery status"
|
358 |
+
• "Execute a square flight pattern"
|
359 |
+
• "Return home and land safely"
|
360 |
+
|
361 |
+
[bold]Direct Commands:[/bold]
|
362 |
+
• [cyan]/status[/cyan] - Show detailed system status
|
363 |
+
• [cyan]/connect <connection>[/cyan] - Connect to specific drone
|
364 |
+
• [cyan]/disconnect[/cyan] - Disconnect from drone
|
365 |
+
• [cyan]/emergency[/cyan] - Emergency stop
|
366 |
+
• [cyan]/ollama[/cyan] - Show Ollama status and models
|
367 |
+
• [cyan]/clear[/cyan] - Clear screen
|
368 |
+
• [cyan]/help[/cyan] - Show this help
|
369 |
+
• [cyan]/quit[/cyan] - Exit DeepDrone
|
370 |
+
|
371 |
+
[bold]Example Session:[/bold]
|
372 |
+
[dim]🚁 DeepDrone> Connect to simulator and take off to 20 meters
|
373 |
+
🤖 AI: I'll connect to the simulator and take off to 20 meters...
|
374 |
+
🚁 DeepDrone> Fly in a circle with 50 meter radius
|
375 |
+
🤖 AI: I'll create a circular flight pattern...[/dim]
|
376 |
+
|
377 |
+
[bold]Tips:[/bold]
|
378 |
+
• Use arrow keys to navigate command history
|
379 |
+
• Tab completion available for common commands
|
380 |
+
• AI understands natural language - be conversational!
|
381 |
+
• Always prioritize safety in flight operations"""
|
382 |
+
|
383 |
+
self.console.print(Panel(
|
384 |
+
help_text,
|
385 |
+
title="[bold]DeepDrone Help[/bold]",
|
386 |
+
border_style="cyan",
|
387 |
+
padding=(1, 2)
|
388 |
+
))
|
389 |
+
|
390 |
+
def _show_detailed_status(self):
|
391 |
+
"""Show detailed system and drone status."""
|
392 |
+
status = self.drone_tools.get_status()
|
393 |
+
|
394 |
+
# Create status table
|
395 |
+
table = Table(title="System Status", show_header=True, header_style="bold magenta")
|
396 |
+
table.add_column("Component", style="cyan", width=20)
|
397 |
+
table.add_column("Status", style="white")
|
398 |
+
table.add_column("Details", style="yellow")
|
399 |
+
|
400 |
+
# AI Model status
|
401 |
+
table.add_row("AI Model", "✅ Online", f"{self.model_config.name} ({self.model_config.provider})")
|
402 |
+
|
403 |
+
# Drone connection
|
404 |
+
conn_status = "✅ Connected" if status["connected"] else "❌ Disconnected"
|
405 |
+
table.add_row("Drone Connection", conn_status, self.connection_string)
|
406 |
+
|
407 |
+
# Mission status
|
408 |
+
mission_status = "🚁 Active" if status["mission_in_progress"] else "⏸️ Standby"
|
409 |
+
table.add_row("Mission", mission_status, status.get("phase", "N/A"))
|
410 |
+
|
411 |
+
# Current status
|
412 |
+
table.add_row("System Status", status["status"], "")
|
413 |
+
|
414 |
+
if status["connected"]:
|
415 |
+
location = status.get("location", {})
|
416 |
+
battery = status.get("battery", {})
|
417 |
+
|
418 |
+
if not location.get("error"):
|
419 |
+
lat = location.get("latitude", "N/A")
|
420 |
+
lon = location.get("longitude", "N/A")
|
421 |
+
alt = location.get("altitude", "N/A")
|
422 |
+
table.add_row("Location", "📍 GPS Lock", f"Lat: {lat}, Lon: {lon}, Alt: {alt}m")
|
423 |
+
|
424 |
+
if not battery.get("error"):
|
425 |
+
voltage = battery.get("voltage", "N/A")
|
426 |
+
level = battery.get("level", "N/A")
|
427 |
+
table.add_row("Battery", "🔋 Monitoring", f"Voltage: {voltage}V, Level: {level}%")
|
428 |
+
|
429 |
+
self.console.print(table)
|
430 |
+
|
431 |
+
# Show recent log entries
|
432 |
+
if status.get("log_entries"):
|
433 |
+
self.console.print("\n[bold]Recent Activity:[/bold]")
|
434 |
+
for entry in status["log_entries"][-5:]:
|
435 |
+
self.console.print(f"[dim]{entry}[/dim]")
|
436 |
+
|
437 |
+
def _connect_drone_direct(self):
|
438 |
+
"""Connect to drone directly."""
|
439 |
+
with Live(
|
440 |
+
Spinner("dots", text=f"Connecting to {self.connection_string}..."),
|
441 |
+
console=self.console,
|
442 |
+
transient=True
|
443 |
+
) as live:
|
444 |
+
|
445 |
+
success = self.drone_tools.connect_drone(self.connection_string)
|
446 |
+
live.stop()
|
447 |
+
|
448 |
+
if success:
|
449 |
+
self.console.print(f"[green]✅ Connected to drone at {self.connection_string}[/green]")
|
450 |
+
else:
|
451 |
+
self.console.print(f"[red]❌ Failed to connect to {self.connection_string}[/red]")
|
452 |
+
|
453 |
+
def _disconnect_drone_direct(self):
|
454 |
+
"""Disconnect from drone directly."""
|
455 |
+
if self.drone_tools.is_connected():
|
456 |
+
self.drone_tools.disconnect_drone()
|
457 |
+
self.console.print("[yellow]📡 Disconnected from drone[/yellow]")
|
458 |
+
else:
|
459 |
+
self.console.print("[yellow]No active drone connection[/yellow]")
|
460 |
+
|
461 |
+
def _emergency_stop(self):
|
462 |
+
"""Emergency stop operation."""
|
463 |
+
if self.drone_tools.is_connected():
|
464 |
+
self.console.print("[red]🚨 EMERGENCY STOP INITIATED[/red]")
|
465 |
+
self.drone_tools.emergency_stop()
|
466 |
+
else:
|
467 |
+
self.console.print("[yellow]No active drone connection for emergency stop[/yellow]")
|
468 |
+
|
469 |
+
def _start_status_monitor(self):
|
470 |
+
"""Start background status monitoring."""
|
471 |
+
self.status_running = True
|
472 |
+
self.status_thread = threading.Thread(target=self._status_monitor_loop, daemon=True)
|
473 |
+
self.status_thread.start()
|
474 |
+
|
475 |
+
def _status_monitor_loop(self):
|
476 |
+
"""Background status monitoring loop."""
|
477 |
+
while self.status_running and self.session_active:
|
478 |
+
try:
|
479 |
+
time.sleep(5) # Update every 5 seconds
|
480 |
+
# Could update status bar here if needed
|
481 |
+
except Exception:
|
482 |
+
break
|
483 |
+
|
484 |
+
def _handle_exit(self):
|
485 |
+
"""Handle session exit."""
|
486 |
+
self.console.print("\n[yellow]🚁 Shutting down DeepDrone...[/yellow]")
|
487 |
+
|
488 |
+
if self.drone_tools.is_connected():
|
489 |
+
from rich.prompt import Confirm
|
490 |
+
if Confirm.ask("Disconnect from drone before exit?", default=True):
|
491 |
+
self.drone_tools.disconnect_drone()
|
492 |
+
|
493 |
+
self.session_active = False
|
494 |
+
|
495 |
+
def _show_ollama_status(self):
|
496 |
+
"""Show Ollama status and available models."""
|
497 |
+
try:
|
498 |
+
import ollama
|
499 |
+
|
500 |
+
# Check connection
|
501 |
+
try:
|
502 |
+
models_response = ollama.list()
|
503 |
+
available_models = models_response.models if hasattr(models_response, 'models') else []
|
504 |
+
|
505 |
+
self.console.print("[bold green]✅ Ollama Status: Connected[/bold green]\n")
|
506 |
+
|
507 |
+
if available_models:
|
508 |
+
self.console.print(f"[bold]📚 Available Models ({len(available_models)}):[/bold]")
|
509 |
+
for model in available_models:
|
510 |
+
name = model.model
|
511 |
+
size = model.size if hasattr(model, 'size') else 0
|
512 |
+
size_gb = size / (1024**3) if size else 0
|
513 |
+
modified = str(model.modified_at)[:19] if hasattr(model, 'modified_at') else 'Unknown'
|
514 |
+
|
515 |
+
self.console.print(f" • [green]{name}[/green] ([blue]{size_gb:.1f} GB[/blue]) - {modified}")
|
516 |
+
else:
|
517 |
+
self.console.print("[yellow]📭 No models installed[/yellow]")
|
518 |
+
self.console.print("\n💡 Install a model with:")
|
519 |
+
self.console.print(" [cyan]ollama pull llama3.1[/cyan]")
|
520 |
+
self.console.print(" [cyan]ollama pull codestral[/cyan]")
|
521 |
+
self.console.print(" [cyan]ollama pull qwen2.5-coder[/cyan]")
|
522 |
+
|
523 |
+
self.console.print(f"\n[dim]Current model: {self.model_config.model_id}[/dim]")
|
524 |
+
|
525 |
+
except Exception as e:
|
526 |
+
self.console.print("[red]❌ Ollama Status: Not connected[/red]")
|
527 |
+
self.console.print(f"[red]Error: {e}[/red]\n")
|
528 |
+
self.console.print("💡 Make sure Ollama is running:")
|
529 |
+
self.console.print(" [cyan]ollama serve[/cyan]\n")
|
530 |
+
self.console.print("📥 Download Ollama from:")
|
531 |
+
self.console.print(" [cyan]https://ollama.com/download[/cyan]")
|
532 |
+
|
533 |
+
except ImportError:
|
534 |
+
self.console.print("[red]❌ Ollama package not installed[/red]")
|
535 |
+
self.console.print("Install with: [cyan]pip install ollama[/cyan]")
|
536 |
+
|
537 |
+
def _cleanup(self):
|
538 |
+
"""Cleanup resources."""
|
539 |
+
self.status_running = False
|
540 |
+
if self.drone_tools.is_connected():
|
541 |
+
self.drone_tools.disconnect_drone()
|
542 |
+
|
543 |
+
self.console.print("[green]🚁 DeepDrone session ended. Fly safe![/green]")
|
drone/drone_tools.py
ADDED
@@ -0,0 +1,342 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Drone tools manager for terminal interface.
|
3 |
+
Adapts the existing drone control functionality for the terminal application.
|
4 |
+
"""
|
5 |
+
|
6 |
+
import time
|
7 |
+
import logging
|
8 |
+
from typing import Dict, List, Optional, Any
|
9 |
+
from .drone_control import DroneController
|
10 |
+
|
11 |
+
# Set up logging
|
12 |
+
logging.basicConfig(level=logging.INFO)
|
13 |
+
logger = logging.getLogger(__name__)
|
14 |
+
|
15 |
+
class DroneToolsManager:
|
16 |
+
"""Manages drone operations for the terminal interface."""
|
17 |
+
|
18 |
+
def __init__(self, connection_string: Optional[str] = None):
|
19 |
+
self.controller = DroneController(connection_string)
|
20 |
+
self.connected = False
|
21 |
+
self.mission_in_progress = False
|
22 |
+
|
23 |
+
# Status tracking
|
24 |
+
self.status = "STANDBY"
|
25 |
+
self.phase = ""
|
26 |
+
self.log_entries = []
|
27 |
+
|
28 |
+
def connect_drone(self, connection_string: str, timeout: int = 30) -> bool:
|
29 |
+
"""Connect to a drone."""
|
30 |
+
try:
|
31 |
+
self._update_status("CONNECTING", f"Connecting to {connection_string}")
|
32 |
+
|
33 |
+
success = self.controller.connect_to_drone(connection_string, timeout)
|
34 |
+
if success:
|
35 |
+
self.connected = True
|
36 |
+
self._update_status("CONNECTED", "Successfully connected to drone")
|
37 |
+
|
38 |
+
# Get initial status
|
39 |
+
location = self.get_location()
|
40 |
+
battery = self.get_battery()
|
41 |
+
|
42 |
+
logger.info(f"Connected to drone. Location: {location}, Battery: {battery}")
|
43 |
+
return True
|
44 |
+
else:
|
45 |
+
self._update_status("ERROR", "Failed to connect to drone")
|
46 |
+
return False
|
47 |
+
|
48 |
+
except Exception as e:
|
49 |
+
self._update_status("ERROR", f"Connection error: {str(e)}")
|
50 |
+
logger.error(f"Connection error: {e}")
|
51 |
+
return False
|
52 |
+
|
53 |
+
def disconnect_drone(self):
|
54 |
+
"""Disconnect from the drone."""
|
55 |
+
try:
|
56 |
+
self._update_status("DISCONNECTING", "Disconnecting from drone")
|
57 |
+
self.controller.disconnect()
|
58 |
+
self.connected = False
|
59 |
+
self.mission_in_progress = False
|
60 |
+
self._update_status("STANDBY", "Disconnected from drone")
|
61 |
+
logger.info("Disconnected from drone")
|
62 |
+
except Exception as e:
|
63 |
+
logger.error(f"Disconnect error: {e}")
|
64 |
+
|
65 |
+
def takeoff(self, altitude: float) -> bool:
|
66 |
+
"""Take off to specified altitude."""
|
67 |
+
if not self._ensure_connected():
|
68 |
+
return False
|
69 |
+
|
70 |
+
try:
|
71 |
+
self._update_status("TAKING OFF", f"Taking off to {altitude} meters")
|
72 |
+
self.mission_in_progress = True
|
73 |
+
|
74 |
+
success = self.controller.arm_and_takeoff(altitude)
|
75 |
+
if success:
|
76 |
+
self._update_status("AIRBORNE", f"Reached altitude of {altitude} meters")
|
77 |
+
return True
|
78 |
+
else:
|
79 |
+
self._update_status("ERROR", "Takeoff failed")
|
80 |
+
self.mission_in_progress = False
|
81 |
+
return False
|
82 |
+
|
83 |
+
except Exception as e:
|
84 |
+
self._update_status("ERROR", f"Takeoff error: {str(e)}")
|
85 |
+
self.mission_in_progress = False
|
86 |
+
logger.error(f"Takeoff error: {e}")
|
87 |
+
return False
|
88 |
+
|
89 |
+
def land(self) -> bool:
|
90 |
+
"""Land the drone."""
|
91 |
+
if not self._ensure_connected():
|
92 |
+
return False
|
93 |
+
|
94 |
+
try:
|
95 |
+
self._update_status("LANDING", "Landing drone")
|
96 |
+
|
97 |
+
success = self.controller.land()
|
98 |
+
if success:
|
99 |
+
self._update_status("LANDED", "Drone has landed")
|
100 |
+
self.mission_in_progress = False
|
101 |
+
return True
|
102 |
+
else:
|
103 |
+
self._update_status("ERROR", "Landing failed")
|
104 |
+
return False
|
105 |
+
|
106 |
+
except Exception as e:
|
107 |
+
self._update_status("ERROR", f"Landing error: {str(e)}")
|
108 |
+
logger.error(f"Landing error: {e}")
|
109 |
+
return False
|
110 |
+
|
111 |
+
def return_home(self) -> bool:
|
112 |
+
"""Return to launch/home location."""
|
113 |
+
if not self._ensure_connected():
|
114 |
+
return False
|
115 |
+
|
116 |
+
try:
|
117 |
+
self._update_status("RETURNING", "Returning to launch point")
|
118 |
+
|
119 |
+
success = self.controller.return_to_launch()
|
120 |
+
if success:
|
121 |
+
self._update_status("RETURNING", "Drone is returning to launch point")
|
122 |
+
return True
|
123 |
+
else:
|
124 |
+
self._update_status("ERROR", "Return to home failed")
|
125 |
+
return False
|
126 |
+
|
127 |
+
except Exception as e:
|
128 |
+
self._update_status("ERROR", f"Return error: {str(e)}")
|
129 |
+
logger.error(f"Return error: {e}")
|
130 |
+
return False
|
131 |
+
|
132 |
+
def fly_to(self, latitude: float, longitude: float, altitude: float) -> bool:
|
133 |
+
"""Fly to specific GPS coordinates."""
|
134 |
+
if not self._ensure_connected():
|
135 |
+
return False
|
136 |
+
|
137 |
+
try:
|
138 |
+
self._update_status(
|
139 |
+
"NAVIGATING",
|
140 |
+
f"Flying to lat:{latitude:.4f}, lon:{longitude:.4f}, alt:{altitude}m"
|
141 |
+
)
|
142 |
+
|
143 |
+
success = self.controller.goto_location(latitude, longitude, altitude)
|
144 |
+
if success:
|
145 |
+
return True
|
146 |
+
else:
|
147 |
+
self._update_status("ERROR", "Navigation failed")
|
148 |
+
return False
|
149 |
+
|
150 |
+
except Exception as e:
|
151 |
+
self._update_status("ERROR", f"Navigation error: {str(e)}")
|
152 |
+
logger.error(f"Navigation error: {e}")
|
153 |
+
return False
|
154 |
+
|
155 |
+
def get_location(self) -> Dict[str, Any]:
|
156 |
+
"""Get current GPS location."""
|
157 |
+
if not self._ensure_connected():
|
158 |
+
return {"error": "Not connected to drone"}
|
159 |
+
|
160 |
+
try:
|
161 |
+
return self.controller.get_current_location()
|
162 |
+
except Exception as e:
|
163 |
+
logger.error(f"Location error: {e}")
|
164 |
+
return {"error": str(e)}
|
165 |
+
|
166 |
+
def get_battery(self) -> Dict[str, Any]:
|
167 |
+
"""Get battery status."""
|
168 |
+
if not self._ensure_connected():
|
169 |
+
return {"error": "Not connected to drone"}
|
170 |
+
|
171 |
+
try:
|
172 |
+
return self.controller.get_battery_status()
|
173 |
+
except Exception as e:
|
174 |
+
logger.error(f"Battery error: {e}")
|
175 |
+
return {"error": str(e)}
|
176 |
+
|
177 |
+
def execute_mission(self, waypoints: List[Dict[str, float]]) -> bool:
|
178 |
+
"""Execute a mission with multiple waypoints."""
|
179 |
+
if not self._ensure_connected():
|
180 |
+
return False
|
181 |
+
|
182 |
+
if not waypoints:
|
183 |
+
logger.error("No waypoints provided")
|
184 |
+
return False
|
185 |
+
|
186 |
+
try:
|
187 |
+
self._update_status("MISSION", f"Starting mission with {len(waypoints)} waypoints")
|
188 |
+
self.mission_in_progress = True
|
189 |
+
|
190 |
+
# Upload mission
|
191 |
+
upload_success = self.controller.upload_mission(waypoints)
|
192 |
+
if not upload_success:
|
193 |
+
self._update_status("ERROR", "Failed to upload mission")
|
194 |
+
self.mission_in_progress = False
|
195 |
+
return False
|
196 |
+
|
197 |
+
# Execute mission
|
198 |
+
execute_success = self.controller.execute_mission()
|
199 |
+
if execute_success:
|
200 |
+
self._update_status("EXECUTING", "Mission execution started")
|
201 |
+
|
202 |
+
# Simulate mission progress updates
|
203 |
+
for i, wp in enumerate(waypoints, 1):
|
204 |
+
self._update_status(
|
205 |
+
"EXECUTING",
|
206 |
+
f"Waypoint {i}/{len(waypoints)}: {wp['lat']:.4f}, {wp['lon']:.4f}"
|
207 |
+
)
|
208 |
+
time.sleep(1) # Brief pause between waypoints
|
209 |
+
|
210 |
+
self._update_status("MISSION COMPLETE", "All waypoints reached")
|
211 |
+
return True
|
212 |
+
else:
|
213 |
+
self._update_status("ERROR", "Failed to start mission execution")
|
214 |
+
self.mission_in_progress = False
|
215 |
+
return False
|
216 |
+
|
217 |
+
except Exception as e:
|
218 |
+
self._update_status("ERROR", f"Mission error: {str(e)}")
|
219 |
+
self.mission_in_progress = False
|
220 |
+
logger.error(f"Mission error: {e}")
|
221 |
+
return False
|
222 |
+
|
223 |
+
def get_status(self) -> Dict[str, Any]:
|
224 |
+
"""Get current drone and system status."""
|
225 |
+
status_info = {
|
226 |
+
"connected": self.connected,
|
227 |
+
"mission_in_progress": self.mission_in_progress,
|
228 |
+
"status": self.status,
|
229 |
+
"phase": self.phase,
|
230 |
+
"log_entries": self.log_entries[-5:], # Last 5 entries
|
231 |
+
}
|
232 |
+
|
233 |
+
if self.connected:
|
234 |
+
try:
|
235 |
+
status_info["location"] = self.get_location()
|
236 |
+
status_info["battery"] = self.get_battery()
|
237 |
+
|
238 |
+
if hasattr(self.controller, 'vehicle') and self.controller.vehicle:
|
239 |
+
status_info["mode"] = str(self.controller.vehicle.mode.name)
|
240 |
+
status_info["armed"] = self.controller.vehicle.armed
|
241 |
+
status_info["system_status"] = str(self.controller.vehicle.system_status.state)
|
242 |
+
|
243 |
+
except Exception as e:
|
244 |
+
status_info["telemetry_error"] = str(e)
|
245 |
+
|
246 |
+
return status_info
|
247 |
+
|
248 |
+
def is_connected(self) -> bool:
|
249 |
+
"""Check if connected to drone."""
|
250 |
+
return self.connected
|
251 |
+
|
252 |
+
def emergency_stop(self):
|
253 |
+
"""Emergency stop - immediately land or RTL."""
|
254 |
+
if not self.connected:
|
255 |
+
return
|
256 |
+
|
257 |
+
try:
|
258 |
+
self._update_status("EMERGENCY", "Emergency stop initiated")
|
259 |
+
|
260 |
+
# Try RTL first, then land
|
261 |
+
if not self.return_home():
|
262 |
+
self.land()
|
263 |
+
|
264 |
+
self.mission_in_progress = False
|
265 |
+
|
266 |
+
except Exception as e:
|
267 |
+
logger.error(f"Emergency stop error: {e}")
|
268 |
+
|
269 |
+
def set_airspeed(self, speed: float) -> bool:
|
270 |
+
"""Set target airspeed."""
|
271 |
+
if not self._ensure_connected():
|
272 |
+
return False
|
273 |
+
|
274 |
+
try:
|
275 |
+
return self.controller.set_airspeed(speed)
|
276 |
+
except Exception as e:
|
277 |
+
logger.error(f"Airspeed error: {e}")
|
278 |
+
return False
|
279 |
+
|
280 |
+
def get_telemetry(self) -> Dict[str, Any]:
|
281 |
+
"""Get comprehensive telemetry data."""
|
282 |
+
if not self._ensure_connected():
|
283 |
+
return {"error": "Not connected to drone"}
|
284 |
+
|
285 |
+
try:
|
286 |
+
telemetry = {}
|
287 |
+
|
288 |
+
# Basic info
|
289 |
+
telemetry["location"] = self.get_location()
|
290 |
+
telemetry["battery"] = self.get_battery()
|
291 |
+
|
292 |
+
# Vehicle-specific data if available
|
293 |
+
if hasattr(self.controller, 'vehicle') and self.controller.vehicle:
|
294 |
+
vehicle = self.controller.vehicle
|
295 |
+
|
296 |
+
telemetry.update({
|
297 |
+
"mode": str(vehicle.mode.name),
|
298 |
+
"armed": vehicle.armed,
|
299 |
+
"system_status": str(vehicle.system_status.state),
|
300 |
+
"airspeed": vehicle.airspeed,
|
301 |
+
"groundspeed": vehicle.groundspeed,
|
302 |
+
"heading": vehicle.heading,
|
303 |
+
})
|
304 |
+
|
305 |
+
# GPS info
|
306 |
+
if vehicle.gps_0:
|
307 |
+
telemetry["gps"] = {
|
308 |
+
"fix_type": vehicle.gps_0.fix_type,
|
309 |
+
"satellites_visible": vehicle.gps_0.satellites_visible,
|
310 |
+
"eph": vehicle.gps_0.eph,
|
311 |
+
"epv": vehicle.gps_0.epv,
|
312 |
+
}
|
313 |
+
|
314 |
+
return telemetry
|
315 |
+
|
316 |
+
except Exception as e:
|
317 |
+
logger.error(f"Telemetry error: {e}")
|
318 |
+
return {"error": str(e)}
|
319 |
+
|
320 |
+
def _ensure_connected(self) -> bool:
|
321 |
+
"""Ensure drone is connected."""
|
322 |
+
if not self.connected:
|
323 |
+
logger.error("Not connected to drone")
|
324 |
+
return False
|
325 |
+
return True
|
326 |
+
|
327 |
+
def _update_status(self, status: str, phase: str = ""):
|
328 |
+
"""Update system status and log entry."""
|
329 |
+
self.status = status
|
330 |
+
self.phase = phase
|
331 |
+
|
332 |
+
# Create log entry
|
333 |
+
timestamp = time.strftime("%H:%M:%S")
|
334 |
+
log_entry = f"[{timestamp}] {status}: {phase}" if phase else f"[{timestamp}] {status}"
|
335 |
+
|
336 |
+
self.log_entries.append(log_entry)
|
337 |
+
|
338 |
+
# Keep only last 50 entries
|
339 |
+
if len(self.log_entries) > 50:
|
340 |
+
self.log_entries = self.log_entries[-50:]
|
341 |
+
|
342 |
+
logger.info(log_entry)
|
drone/interactive_setup.py
ADDED
@@ -0,0 +1,414 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Interactive setup and chat interface for DeepDrone.
|
3 |
+
"""
|
4 |
+
|
5 |
+
import os
|
6 |
+
import sys
|
7 |
+
import asyncio
|
8 |
+
from typing import Dict, Optional, Tuple, List
|
9 |
+
from rich.console import Console
|
10 |
+
from rich.panel import Panel
|
11 |
+
from rich.text import Text
|
12 |
+
from rich.align import Align
|
13 |
+
from rich.prompt import Prompt, Confirm
|
14 |
+
from rich.table import Table
|
15 |
+
from rich.live import Live
|
16 |
+
from rich.layout import Layout
|
17 |
+
from rich.spinner import Spinner
|
18 |
+
from prompt_toolkit import prompt
|
19 |
+
from prompt_toolkit.shortcuts import radiolist_dialog, input_dialog, message_dialog
|
20 |
+
from prompt_toolkit.styles import Style
|
21 |
+
import getpass
|
22 |
+
|
23 |
+
from .config import ModelConfig
|
24 |
+
from .drone_chat_interface import DroneChatInterface
|
25 |
+
|
26 |
+
console = Console()
|
27 |
+
|
28 |
+
# Provider configurations
|
29 |
+
PROVIDERS = {
|
30 |
+
"OpenAI": {
|
31 |
+
"name": "openai",
|
32 |
+
"models": ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-3.5-turbo"],
|
33 |
+
"api_key_url": "https://platform.openai.com/api-keys",
|
34 |
+
"description": "GPT models from OpenAI"
|
35 |
+
},
|
36 |
+
"Anthropic": {
|
37 |
+
"name": "anthropic",
|
38 |
+
"models": ["claude-3-5-sonnet-20241022", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"],
|
39 |
+
"api_key_url": "https://console.anthropic.com/",
|
40 |
+
"description": "Claude models from Anthropic"
|
41 |
+
},
|
42 |
+
"Google": {
|
43 |
+
"name": "vertex_ai",
|
44 |
+
"models": ["gemini-1.5-pro", "gemini-1.5-flash", "gemini-pro"],
|
45 |
+
"api_key_url": "https://console.cloud.google.com/",
|
46 |
+
"description": "Gemini models from Google"
|
47 |
+
},
|
48 |
+
"Meta": {
|
49 |
+
"name": "openai", # Using OpenAI format for Llama models via providers
|
50 |
+
"models": ["meta-llama/Meta-Llama-3.1-70B-Instruct", "meta-llama/Meta-Llama-3.1-8B-Instruct"],
|
51 |
+
"api_key_url": "https://together.ai/ or https://replicate.com/",
|
52 |
+
"description": "Llama models from Meta (via Together.ai/Replicate)"
|
53 |
+
},
|
54 |
+
"Mistral": {
|
55 |
+
"name": "mistral",
|
56 |
+
"models": ["mistral-large-latest", "mistral-medium-latest", "mistral-small-latest"],
|
57 |
+
"api_key_url": "https://console.mistral.ai/",
|
58 |
+
"description": "Mistral AI models"
|
59 |
+
},
|
60 |
+
"Ollama": {
|
61 |
+
"name": "ollama",
|
62 |
+
"models": ["llama3.1:latest", "codestral:latest", "qwen2.5-coder:latest", "phi3:latest"],
|
63 |
+
"api_key_url": "https://ollama.ai/ (No API key needed - runs locally)",
|
64 |
+
"description": "Local models via Ollama (no API key required)"
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
def show_welcome_banner():
|
69 |
+
"""Display the welcome banner."""
|
70 |
+
banner = """
|
71 |
+
╔══════════════════════════════════════════════════════════╗
|
72 |
+
║ ║
|
73 |
+
║ 🚁 DEEPDRONE AI CONTROL SYSTEM 🚁 ║
|
74 |
+
║ ║
|
75 |
+
║ Advanced Drone Control with AI Integration ║
|
76 |
+
║ ║
|
77 |
+
╚══════════════════════════════════════════════════════════╝
|
78 |
+
"""
|
79 |
+
|
80 |
+
console.print(Panel(
|
81 |
+
Align.center(Text(banner.strip(), style="bold green")),
|
82 |
+
border_style="bright_green",
|
83 |
+
padding=(1, 2)
|
84 |
+
))
|
85 |
+
|
86 |
+
def select_provider() -> Optional[Tuple[str, Dict]]:
|
87 |
+
"""Interactive provider selection."""
|
88 |
+
console.print("\n[bold cyan]📡 Select AI Provider[/bold cyan]\n")
|
89 |
+
|
90 |
+
# Create provider table for display
|
91 |
+
table = Table(show_header=True, header_style="bold magenta")
|
92 |
+
table.add_column("№", style="bright_green", width=3)
|
93 |
+
table.add_column("Provider", style="cyan", width=12)
|
94 |
+
table.add_column("Description", style="white")
|
95 |
+
table.add_column("Example Models", style="yellow")
|
96 |
+
|
97 |
+
provider_list = list(PROVIDERS.items())
|
98 |
+
|
99 |
+
for i, (name, config) in enumerate(provider_list, 1):
|
100 |
+
example_models = ", ".join(config["models"][:2])
|
101 |
+
if len(config["models"]) > 2:
|
102 |
+
example_models += "..."
|
103 |
+
table.add_row(str(i), name, config["description"], example_models)
|
104 |
+
|
105 |
+
console.print(table)
|
106 |
+
console.print()
|
107 |
+
|
108 |
+
try:
|
109 |
+
from rich.prompt import IntPrompt
|
110 |
+
|
111 |
+
choice = IntPrompt.ask(
|
112 |
+
"Select provider by number",
|
113 |
+
choices=[str(i) for i in range(1, len(provider_list) + 1)],
|
114 |
+
default=1
|
115 |
+
)
|
116 |
+
|
117 |
+
provider_name, provider_config = provider_list[choice - 1]
|
118 |
+
return provider_name, provider_config
|
119 |
+
|
120 |
+
except KeyboardInterrupt:
|
121 |
+
console.print("\n[yellow]Selection cancelled[/yellow]")
|
122 |
+
return None
|
123 |
+
|
124 |
+
def get_available_ollama_models() -> List[str]:
|
125 |
+
"""Get list of locally available Ollama models."""
|
126 |
+
try:
|
127 |
+
import ollama
|
128 |
+
models = ollama.list()
|
129 |
+
# The models are returned as Model objects with a 'model' attribute
|
130 |
+
return [model.model for model in models.models] if hasattr(models, 'models') else []
|
131 |
+
except ImportError:
|
132 |
+
return []
|
133 |
+
except Exception as e:
|
134 |
+
# For debugging, you can uncomment the next line
|
135 |
+
# print(f"Error getting Ollama models: {e}")
|
136 |
+
return []
|
137 |
+
|
138 |
+
def install_ollama_model(model_name: str) -> bool:
|
139 |
+
"""Install an Ollama model."""
|
140 |
+
try:
|
141 |
+
import ollama
|
142 |
+
console.print(f"[yellow]📥 Installing {model_name}... This may take a few minutes.[/yellow]")
|
143 |
+
|
144 |
+
with Live(
|
145 |
+
Spinner("dots", text=f"Installing {model_name}..."),
|
146 |
+
console=console,
|
147 |
+
transient=True
|
148 |
+
) as live:
|
149 |
+
ollama.pull(model_name)
|
150 |
+
live.stop()
|
151 |
+
|
152 |
+
console.print(f"[green]✅ Successfully installed {model_name}[/green]")
|
153 |
+
return True
|
154 |
+
except ImportError:
|
155 |
+
console.print("[red]❌ Ollama package not installed[/red]")
|
156 |
+
return False
|
157 |
+
except Exception as e:
|
158 |
+
console.print(f"[red]❌ Failed to install {model_name}: {e}[/red]")
|
159 |
+
return False
|
160 |
+
|
161 |
+
def get_model_name(provider_name: str, provider_config: Dict) -> Optional[str]:
|
162 |
+
"""Get model name from user."""
|
163 |
+
console.print(f"\n[bold cyan]🤖 Select Model for {provider_name}[/bold cyan]\n")
|
164 |
+
|
165 |
+
# Special handling for Ollama
|
166 |
+
if provider_name.lower() == "ollama":
|
167 |
+
# Check if Ollama is running and get local models
|
168 |
+
local_models = get_available_ollama_models()
|
169 |
+
|
170 |
+
if local_models:
|
171 |
+
console.print("[bold green]✅ Local Ollama models found:[/bold green]")
|
172 |
+
for i, model in enumerate(local_models, 1):
|
173 |
+
console.print(f" {i}. [green]{model}[/green]")
|
174 |
+
|
175 |
+
console.print("\n[bold]Popular models (if not installed locally):[/bold]")
|
176 |
+
start_idx = len(local_models) + 1
|
177 |
+
for i, model in enumerate(provider_config["models"], start_idx):
|
178 |
+
console.print(f" {i}. [blue]{model}[/blue] [dim](will be downloaded)[/dim]")
|
179 |
+
|
180 |
+
all_options = local_models + provider_config["models"]
|
181 |
+
|
182 |
+
else:
|
183 |
+
console.print("[yellow]⚠️ No local Ollama models found or Ollama not running[/yellow]")
|
184 |
+
console.print("Make sure Ollama is running: [cyan]ollama serve[/cyan]\n")
|
185 |
+
console.print("[bold]Popular models (will be downloaded):[/bold]")
|
186 |
+
all_options = provider_config["models"]
|
187 |
+
for i, model in enumerate(all_options, 1):
|
188 |
+
console.print(f" {i}. [blue]{model}[/blue] [dim](will be downloaded)[/dim]")
|
189 |
+
|
190 |
+
console.print(f"\n[dim]Download from: {provider_config['api_key_url']}[/dim]\n")
|
191 |
+
|
192 |
+
try:
|
193 |
+
from rich.prompt import Prompt
|
194 |
+
|
195 |
+
result = Prompt.ask(
|
196 |
+
"Enter model name or number from list above",
|
197 |
+
default="1"
|
198 |
+
)
|
199 |
+
|
200 |
+
if result:
|
201 |
+
# Check if user entered a number (selecting from list)
|
202 |
+
try:
|
203 |
+
choice_num = int(result.strip())
|
204 |
+
if 1 <= choice_num <= len(all_options):
|
205 |
+
selected_model = all_options[choice_num - 1]
|
206 |
+
|
207 |
+
# Check if model needs to be installed
|
208 |
+
if selected_model not in local_models:
|
209 |
+
console.print(f"[yellow]Model '{selected_model}' not found locally.[/yellow]")
|
210 |
+
from rich.prompt import Confirm
|
211 |
+
if Confirm.ask(f"Would you like to install {selected_model}?", default=True):
|
212 |
+
if install_ollama_model(selected_model):
|
213 |
+
return selected_model
|
214 |
+
else:
|
215 |
+
return None
|
216 |
+
else:
|
217 |
+
console.print("[yellow]Model installation cancelled[/yellow]")
|
218 |
+
return None
|
219 |
+
|
220 |
+
return selected_model
|
221 |
+
except ValueError:
|
222 |
+
pass
|
223 |
+
|
224 |
+
# User entered a custom model name
|
225 |
+
model_name = result.strip()
|
226 |
+
if model_name not in local_models:
|
227 |
+
console.print(f"[yellow]Model '{model_name}' not found locally.[/yellow]")
|
228 |
+
from rich.prompt import Confirm
|
229 |
+
if Confirm.ask(f"Would you like to install {model_name}?", default=True):
|
230 |
+
if install_ollama_model(model_name):
|
231 |
+
return model_name
|
232 |
+
else:
|
233 |
+
return None
|
234 |
+
else:
|
235 |
+
console.print("[yellow]Model installation cancelled[/yellow]")
|
236 |
+
return None
|
237 |
+
|
238 |
+
return model_name
|
239 |
+
|
240 |
+
return None
|
241 |
+
|
242 |
+
except KeyboardInterrupt:
|
243 |
+
console.print("\n[yellow]Input cancelled[/yellow]")
|
244 |
+
return None
|
245 |
+
|
246 |
+
else:
|
247 |
+
# Standard handling for other providers
|
248 |
+
console.print("[bold]Popular models for this provider:[/bold]")
|
249 |
+
for i, model in enumerate(provider_config["models"], 1):
|
250 |
+
console.print(f" {i}. [green]{model}[/green]")
|
251 |
+
|
252 |
+
console.print(f"\n[dim]Get API key from: {provider_config['api_key_url']}[/dim]\n")
|
253 |
+
|
254 |
+
try:
|
255 |
+
from rich.prompt import Prompt
|
256 |
+
|
257 |
+
result = Prompt.ask(
|
258 |
+
"Enter model name or number from list above",
|
259 |
+
default="1"
|
260 |
+
)
|
261 |
+
|
262 |
+
if result:
|
263 |
+
# Check if user entered a number (selecting from list)
|
264 |
+
try:
|
265 |
+
choice_num = int(result.strip())
|
266 |
+
if 1 <= choice_num <= len(provider_config["models"]):
|
267 |
+
return provider_config["models"][choice_num - 1]
|
268 |
+
except ValueError:
|
269 |
+
pass
|
270 |
+
|
271 |
+
# Return the entered model name
|
272 |
+
return result.strip()
|
273 |
+
|
274 |
+
return None
|
275 |
+
|
276 |
+
except KeyboardInterrupt:
|
277 |
+
console.print("\n[yellow]Input cancelled[/yellow]")
|
278 |
+
return None
|
279 |
+
|
280 |
+
def get_api_key(provider_name: str, model_name: str) -> Optional[str]:
|
281 |
+
"""Get API key from user."""
|
282 |
+
console.print(f"\n[bold cyan]🔑 API Key for {provider_name}[/bold cyan]\n")
|
283 |
+
console.print(f"Model: [green]{model_name}[/green]")
|
284 |
+
console.print(f"Provider: [blue]{provider_name}[/blue]\n")
|
285 |
+
|
286 |
+
# Ollama doesn't need an API key
|
287 |
+
if provider_name.lower() == "ollama":
|
288 |
+
console.print("[green]✅ Ollama runs locally - no API key required![/green]")
|
289 |
+
console.print("[dim]Make sure Ollama is running: ollama serve[/dim]\n")
|
290 |
+
return "local" # Return a placeholder value
|
291 |
+
|
292 |
+
try:
|
293 |
+
# Use getpass for secure password input (works in all environments)
|
294 |
+
api_key = getpass.getpass("Enter your API key (hidden): ")
|
295 |
+
|
296 |
+
if api_key and api_key.strip():
|
297 |
+
return api_key.strip()
|
298 |
+
|
299 |
+
console.print("[yellow]No API key provided[/yellow]")
|
300 |
+
return None
|
301 |
+
|
302 |
+
except KeyboardInterrupt:
|
303 |
+
console.print("\n[yellow]Input cancelled[/yellow]")
|
304 |
+
return None
|
305 |
+
|
306 |
+
def test_model_connection(model_config: ModelConfig) -> bool:
|
307 |
+
"""Test if the model configuration works."""
|
308 |
+
console.print(f"\n[yellow]🔍 Testing connection to {model_config.name}...[/yellow]")
|
309 |
+
|
310 |
+
try:
|
311 |
+
from .llm_interface import LLMInterface
|
312 |
+
|
313 |
+
with Live(
|
314 |
+
Spinner("dots", text="Testing API connection..."),
|
315 |
+
console=console,
|
316 |
+
transient=True
|
317 |
+
) as live:
|
318 |
+
llm = LLMInterface(model_config)
|
319 |
+
result = llm.test_connection()
|
320 |
+
|
321 |
+
live.stop()
|
322 |
+
|
323 |
+
if result["success"]:
|
324 |
+
console.print("[green]✅ Connection successful![/green]")
|
325 |
+
console.print(f"[dim]Response: {result['response'][:100]}...[/dim]\n")
|
326 |
+
return True
|
327 |
+
else:
|
328 |
+
console.print(f"[red]❌ Connection failed: {result['error']}[/red]\n")
|
329 |
+
return False
|
330 |
+
|
331 |
+
except Exception as e:
|
332 |
+
console.print(f"[red]❌ Error testing connection: {e}[/red]\n")
|
333 |
+
return False
|
334 |
+
|
335 |
+
def start_interactive_session():
|
336 |
+
"""Start the interactive setup and chat session."""
|
337 |
+
try:
|
338 |
+
# Show welcome banner
|
339 |
+
show_welcome_banner()
|
340 |
+
|
341 |
+
# Step 1: Select provider
|
342 |
+
console.print("[bold]Step 1: Choose your AI provider[/bold]\n")
|
343 |
+
provider_result = select_provider()
|
344 |
+
if not provider_result:
|
345 |
+
console.print("[yellow]Setup cancelled. Goodbye![/yellow]")
|
346 |
+
return
|
347 |
+
|
348 |
+
provider_name, provider_config = provider_result
|
349 |
+
|
350 |
+
# Step 2: Get model name
|
351 |
+
console.print(f"[bold]Step 2: Select model for {provider_name}[/bold]")
|
352 |
+
model_name = get_model_name(provider_name, provider_config)
|
353 |
+
if not model_name:
|
354 |
+
console.print("[yellow]Setup cancelled. Goodbye![/yellow]")
|
355 |
+
return
|
356 |
+
|
357 |
+
# Step 3: Get API key
|
358 |
+
console.print("[bold]Step 3: Enter API key[/bold]")
|
359 |
+
api_key = get_api_key(provider_name, model_name)
|
360 |
+
if not api_key:
|
361 |
+
console.print("[yellow]Setup cancelled. Goodbye![/yellow]")
|
362 |
+
return
|
363 |
+
|
364 |
+
# Create model configuration
|
365 |
+
base_url = None
|
366 |
+
if provider_name.lower() == "ollama":
|
367 |
+
base_url = "http://localhost:11434"
|
368 |
+
|
369 |
+
model_config = ModelConfig(
|
370 |
+
name=f"{provider_name.lower()}-session",
|
371 |
+
provider=provider_config["name"],
|
372 |
+
model_id=model_name,
|
373 |
+
api_key=api_key,
|
374 |
+
base_url=base_url,
|
375 |
+
max_tokens=2048,
|
376 |
+
temperature=0.7
|
377 |
+
)
|
378 |
+
|
379 |
+
# Step 4: Test connection
|
380 |
+
console.print("[bold]Step 4: Testing connection[/bold]")
|
381 |
+
if not test_model_connection(model_config):
|
382 |
+
if not Confirm.ask("Connection test failed. Continue anyway?"):
|
383 |
+
console.print("[yellow]Setup cancelled. Goodbye![/yellow]")
|
384 |
+
return
|
385 |
+
|
386 |
+
# Step 5: Show simulator information
|
387 |
+
console.print("[bold yellow]🚁 Drone Connection Setup[/bold yellow]\n")
|
388 |
+
console.print("To control a drone, you'll need a connection. You can:")
|
389 |
+
console.print(" 1. [green]Use Simulator[/green]: Run [cyan]python simulate_drone.py[/cyan] in another terminal")
|
390 |
+
console.print(" 2. [blue]Real Drone[/blue]: Connect via USB, TCP, or UDP")
|
391 |
+
console.print(" 3. [dim]SITL[/dim]: Use ArduPilot Software-in-the-Loop\n")
|
392 |
+
|
393 |
+
console.print("💡 [bold]Quick Start with Simulator:[/bold]")
|
394 |
+
console.print(" • Open a new terminal and run: [cyan]python simulate_drone.py[/cyan]")
|
395 |
+
console.print(" • It will show you the connection string (e.g., udp:127.0.0.1:14550)")
|
396 |
+
console.print(" • Use that connection string in the chat interface\n")
|
397 |
+
|
398 |
+
# Step 6: Start chat
|
399 |
+
console.print("[bold green]🚀 Starting DeepDrone chat session...[/bold green]\n")
|
400 |
+
|
401 |
+
# Small delay
|
402 |
+
import time
|
403 |
+
time.sleep(1)
|
404 |
+
|
405 |
+
# Start the chat interface
|
406 |
+
chat_interface = DroneChatInterface(model_config)
|
407 |
+
chat_interface.start()
|
408 |
+
|
409 |
+
except KeyboardInterrupt:
|
410 |
+
console.print("\n[yellow]🚁 DeepDrone session interrupted. Goodbye![/yellow]")
|
411 |
+
sys.exit(0)
|
412 |
+
except Exception as e:
|
413 |
+
console.print(f"[red]❌ Error in interactive session: {e}[/red]")
|
414 |
+
sys.exit(1)
|
drone/llm_interface.py
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
LLM Interface for DeepDrone supporting LiteLLM and Ollama.
|
3 |
+
"""
|
4 |
+
|
5 |
+
import os
|
6 |
+
from typing import List, Dict, Any, Optional
|
7 |
+
import json
|
8 |
+
import logging
|
9 |
+
|
10 |
+
from .config import ModelConfig
|
11 |
+
|
12 |
+
# Set up logging
|
13 |
+
logging.basicConfig(level=logging.INFO)
|
14 |
+
logger = logging.getLogger(__name__)
|
15 |
+
|
16 |
+
class LLMInterface:
|
17 |
+
"""Interface for interacting with various LLM providers."""
|
18 |
+
|
19 |
+
def __init__(self, model_config: ModelConfig):
|
20 |
+
self.model_config = model_config
|
21 |
+
self._setup_client()
|
22 |
+
|
23 |
+
def _setup_client(self):
|
24 |
+
"""Set up the appropriate client based on model provider."""
|
25 |
+
if self.model_config.provider == "ollama":
|
26 |
+
self._setup_ollama()
|
27 |
+
else:
|
28 |
+
self._setup_litellm()
|
29 |
+
|
30 |
+
def _setup_ollama(self):
|
31 |
+
"""Set up Ollama client."""
|
32 |
+
try:
|
33 |
+
import ollama
|
34 |
+
self.client = ollama
|
35 |
+
self.client_type = "ollama"
|
36 |
+
|
37 |
+
# Test connection
|
38 |
+
try:
|
39 |
+
models = self.client.list()
|
40 |
+
available_models = models.models if hasattr(models, 'models') else []
|
41 |
+
logger.info(f"Connected to Ollama. Available models: {len(available_models)}")
|
42 |
+
|
43 |
+
# Check if the requested model is available
|
44 |
+
model_names = [model.model for model in available_models]
|
45 |
+
if self.model_config.model_id not in model_names:
|
46 |
+
logger.warning(f"Model '{self.model_config.model_id}' not found locally. Available models: {model_names}")
|
47 |
+
|
48 |
+
except Exception as e:
|
49 |
+
logger.warning(f"Could not connect to Ollama: {e}")
|
50 |
+
logger.info("Make sure Ollama is running: ollama serve")
|
51 |
+
|
52 |
+
except ImportError:
|
53 |
+
raise ImportError("Ollama package not installed. Install with: pip install ollama")
|
54 |
+
|
55 |
+
def _setup_litellm(self):
|
56 |
+
"""Set up LiteLLM client."""
|
57 |
+
try:
|
58 |
+
import litellm
|
59 |
+
|
60 |
+
# Set API key in environment if provided (skip for local/placeholder keys)
|
61 |
+
if self.model_config.api_key and self.model_config.api_key != "local":
|
62 |
+
if self.model_config.provider == "openai":
|
63 |
+
os.environ["OPENAI_API_KEY"] = self.model_config.api_key
|
64 |
+
elif self.model_config.provider == "anthropic":
|
65 |
+
os.environ["ANTHROPIC_API_KEY"] = self.model_config.api_key
|
66 |
+
elif self.model_config.provider == "huggingface":
|
67 |
+
os.environ["HUGGINGFACE_API_KEY"] = self.model_config.api_key
|
68 |
+
elif self.model_config.provider == "mistral":
|
69 |
+
os.environ["MISTRAL_API_KEY"] = self.model_config.api_key
|
70 |
+
elif self.model_config.provider == "vertex_ai":
|
71 |
+
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = self.model_config.api_key
|
72 |
+
|
73 |
+
# Set base URL if provided
|
74 |
+
if self.model_config.base_url:
|
75 |
+
litellm.api_base = self.model_config.base_url
|
76 |
+
|
77 |
+
self.client = litellm
|
78 |
+
self.client_type = "litellm"
|
79 |
+
|
80 |
+
logger.info(f"Set up LiteLLM for {self.model_config.provider}")
|
81 |
+
|
82 |
+
except ImportError:
|
83 |
+
raise ImportError("LiteLLM package not installed. Install with: pip install litellm")
|
84 |
+
|
85 |
+
def chat(self, messages: List[Dict[str, str]]) -> str:
|
86 |
+
"""Send chat messages and get response."""
|
87 |
+
try:
|
88 |
+
if self.client_type == "ollama":
|
89 |
+
return self._chat_ollama(messages)
|
90 |
+
else:
|
91 |
+
return self._chat_litellm(messages)
|
92 |
+
except Exception as e:
|
93 |
+
logger.error(f"Chat error: {e}")
|
94 |
+
return f"Error communicating with {self.model_config.provider}: {str(e)}"
|
95 |
+
|
96 |
+
def _chat_ollama(self, messages: List[Dict[str, str]]) -> str:
|
97 |
+
"""Chat using Ollama."""
|
98 |
+
try:
|
99 |
+
# Convert messages to Ollama format
|
100 |
+
prompt = self._messages_to_prompt(messages)
|
101 |
+
|
102 |
+
response = self.client.generate(
|
103 |
+
model=self.model_config.model_id,
|
104 |
+
prompt=prompt,
|
105 |
+
options={
|
106 |
+
'temperature': self.model_config.temperature,
|
107 |
+
'num_predict': self.model_config.max_tokens,
|
108 |
+
}
|
109 |
+
)
|
110 |
+
|
111 |
+
return response['response']
|
112 |
+
|
113 |
+
except Exception as e:
|
114 |
+
error_str = str(e).lower()
|
115 |
+
|
116 |
+
if "model not found" in error_str or "model does not exist" in error_str:
|
117 |
+
available_models = []
|
118 |
+
try:
|
119 |
+
models = self.client.list()
|
120 |
+
available_models = [m.model for m in models.models] if hasattr(models, 'models') else []
|
121 |
+
except:
|
122 |
+
pass
|
123 |
+
|
124 |
+
error_msg = f"❌ Model '{self.model_config.model_id}' not found in Ollama.\n\n"
|
125 |
+
|
126 |
+
if available_models:
|
127 |
+
error_msg += f"📋 Available local models:\n"
|
128 |
+
for model in available_models:
|
129 |
+
error_msg += f" • {model}\n"
|
130 |
+
error_msg += f"\n💡 To install {self.model_config.model_id}, run:\n"
|
131 |
+
error_msg += f" ollama pull {self.model_config.model_id}\n"
|
132 |
+
else:
|
133 |
+
error_msg += "📭 No models found locally.\n\n"
|
134 |
+
error_msg += f"💡 To install {self.model_config.model_id}, run:\n"
|
135 |
+
error_msg += f" ollama pull {self.model_config.model_id}\n\n"
|
136 |
+
error_msg += "🎯 Popular models to try:\n"
|
137 |
+
error_msg += " • ollama pull llama3.1\n"
|
138 |
+
error_msg += " • ollama pull codestral\n"
|
139 |
+
error_msg += " • ollama pull qwen2.5-coder\n"
|
140 |
+
|
141 |
+
return error_msg
|
142 |
+
|
143 |
+
elif "connection" in error_str or "refused" in error_str:
|
144 |
+
return "❌ Cannot connect to Ollama.\n\n💡 Make sure Ollama is running:\n ollama serve\n\n📥 Download Ollama from: https://ollama.com/download"
|
145 |
+
|
146 |
+
return f"❌ Ollama error: {str(e)}"
|
147 |
+
|
148 |
+
def _chat_litellm(self, messages: List[Dict[str, str]]) -> str:
|
149 |
+
"""Chat using LiteLLM."""
|
150 |
+
try:
|
151 |
+
response = self.client.completion(
|
152 |
+
model=self.model_config.model_id,
|
153 |
+
messages=messages,
|
154 |
+
max_tokens=self.model_config.max_tokens,
|
155 |
+
temperature=self.model_config.temperature,
|
156 |
+
)
|
157 |
+
|
158 |
+
return response.choices[0].message.content
|
159 |
+
|
160 |
+
except Exception as e:
|
161 |
+
if "api key" in str(e).lower():
|
162 |
+
return f"API key error for {self.model_config.provider}. Please set your API key with: deepdrone models set-key {self.model_config.name}"
|
163 |
+
elif "quota" in str(e).lower() or "billing" in str(e).lower():
|
164 |
+
return f"Billing/quota error for {self.model_config.provider}. Please check your account."
|
165 |
+
elif "model" in str(e).lower() and "not found" in str(e).lower():
|
166 |
+
return f"Model '{self.model_config.model_id}' not found for {self.model_config.provider}."
|
167 |
+
|
168 |
+
raise e
|
169 |
+
|
170 |
+
def _messages_to_prompt(self, messages: List[Dict[str, str]]) -> str:
|
171 |
+
"""Convert messages to a single prompt for models that don't support chat format."""
|
172 |
+
prompt_parts = []
|
173 |
+
|
174 |
+
for message in messages:
|
175 |
+
role = message["role"]
|
176 |
+
content = message["content"]
|
177 |
+
|
178 |
+
if role == "system":
|
179 |
+
prompt_parts.append(f"System: {content}")
|
180 |
+
elif role == "user":
|
181 |
+
prompt_parts.append(f"Human: {content}")
|
182 |
+
elif role == "assistant":
|
183 |
+
prompt_parts.append(f"Assistant: {content}")
|
184 |
+
|
185 |
+
prompt_parts.append("Assistant: ")
|
186 |
+
|
187 |
+
return "\n\n".join(prompt_parts)
|
188 |
+
|
189 |
+
def test_connection(self) -> Dict[str, Any]:
|
190 |
+
"""Test connection to the LLM service."""
|
191 |
+
try:
|
192 |
+
test_messages = [
|
193 |
+
{"role": "user", "content": "Hello, please respond with 'Connection test successful'"}
|
194 |
+
]
|
195 |
+
|
196 |
+
response = self.chat(test_messages)
|
197 |
+
|
198 |
+
return {
|
199 |
+
"success": True,
|
200 |
+
"response": response,
|
201 |
+
"provider": self.model_config.provider,
|
202 |
+
"model": self.model_config.model_id
|
203 |
+
}
|
204 |
+
|
205 |
+
except Exception as e:
|
206 |
+
return {
|
207 |
+
"success": False,
|
208 |
+
"error": str(e),
|
209 |
+
"provider": self.model_config.provider,
|
210 |
+
"model": self.model_config.model_id
|
211 |
+
}
|
212 |
+
|
213 |
+
def get_model_info(self) -> Dict[str, Any]:
|
214 |
+
"""Get information about the current model."""
|
215 |
+
info = {
|
216 |
+
"name": self.model_config.name,
|
217 |
+
"provider": self.model_config.provider,
|
218 |
+
"model_id": self.model_config.model_id,
|
219 |
+
"max_tokens": self.model_config.max_tokens,
|
220 |
+
"temperature": self.model_config.temperature,
|
221 |
+
"client_type": self.client_type,
|
222 |
+
}
|
223 |
+
|
224 |
+
if self.model_config.base_url:
|
225 |
+
info["base_url"] = self.model_config.base_url
|
226 |
+
|
227 |
+
return info
|
drone/terminal_chat.py
ADDED
@@ -0,0 +1,496 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Terminal-based chat interface for DeepDrone with LiteLLM and Ollama support.
|
3 |
+
"""
|
4 |
+
|
5 |
+
import asyncio
|
6 |
+
from typing import List, Dict, Any, Optional
|
7 |
+
from rich.console import Console
|
8 |
+
from rich.panel import Panel
|
9 |
+
from rich.text import Text
|
10 |
+
from rich.markdown import Markdown
|
11 |
+
from rich.live import Live
|
12 |
+
from rich.spinner import Spinner
|
13 |
+
from prompt_toolkit import prompt
|
14 |
+
from prompt_toolkit.shortcuts import message_dialog
|
15 |
+
from prompt_toolkit.styles import Style
|
16 |
+
import json
|
17 |
+
import time
|
18 |
+
|
19 |
+
from .config import ModelConfig
|
20 |
+
from .llm_interface import LLMInterface
|
21 |
+
from .drone_tools import DroneToolsManager
|
22 |
+
|
23 |
+
class TerminalDroneChat:
|
24 |
+
"""Terminal-based chat interface for drone control."""
|
25 |
+
|
26 |
+
def __init__(self, model_config: ModelConfig, connection_string: Optional[str] = None):
|
27 |
+
self.console = Console()
|
28 |
+
self.model_config = model_config
|
29 |
+
self.connection_string = connection_string
|
30 |
+
self.chat_history: List[Dict[str, str]] = []
|
31 |
+
|
32 |
+
# Initialize LLM interface
|
33 |
+
self.llm = LLMInterface(model_config)
|
34 |
+
|
35 |
+
# Initialize drone tools
|
36 |
+
self.drone_tools = DroneToolsManager(connection_string)
|
37 |
+
|
38 |
+
# Chat settings
|
39 |
+
self.show_thinking = True
|
40 |
+
self.max_history = 50
|
41 |
+
|
42 |
+
# Style for prompt
|
43 |
+
self.prompt_style = Style.from_dict({
|
44 |
+
'prompt': '#00ff00 bold',
|
45 |
+
'input': '#ffffff',
|
46 |
+
})
|
47 |
+
|
48 |
+
def start(self):
|
49 |
+
"""Start the interactive chat session."""
|
50 |
+
self._show_welcome()
|
51 |
+
|
52 |
+
try:
|
53 |
+
while True:
|
54 |
+
user_input = self._get_user_input()
|
55 |
+
|
56 |
+
if not user_input.strip():
|
57 |
+
continue
|
58 |
+
|
59 |
+
# Handle special commands
|
60 |
+
if user_input.startswith('/'):
|
61 |
+
if self._handle_command(user_input):
|
62 |
+
continue
|
63 |
+
else:
|
64 |
+
break
|
65 |
+
|
66 |
+
# Process user message
|
67 |
+
self._process_message(user_input)
|
68 |
+
|
69 |
+
except KeyboardInterrupt:
|
70 |
+
self._show_goodbye()
|
71 |
+
except EOFError:
|
72 |
+
self._show_goodbye()
|
73 |
+
|
74 |
+
def _show_welcome(self):
|
75 |
+
"""Show welcome message and model info."""
|
76 |
+
welcome_text = f"""
|
77 |
+
[bold green]🚁 DEEPDRONE TERMINAL ACTIVE[/bold green]
|
78 |
+
|
79 |
+
[bold]Model:[/bold] {self.model_config.name} ({self.model_config.provider})
|
80 |
+
[bold]Connection:[/bold] {self.connection_string or 'Not connected'}
|
81 |
+
[bold]Status:[/bold] Ready for commands
|
82 |
+
|
83 |
+
[dim]Type your commands or questions. Use /help for available commands.[/dim]
|
84 |
+
[dim]Type /quit to exit.[/dim]
|
85 |
+
"""
|
86 |
+
|
87 |
+
self.console.print(Panel(
|
88 |
+
welcome_text.strip(),
|
89 |
+
border_style="bright_green",
|
90 |
+
padding=(1, 2)
|
91 |
+
))
|
92 |
+
|
93 |
+
def _show_goodbye(self):
|
94 |
+
"""Show goodbye message."""
|
95 |
+
self.console.print("\n[yellow]🚁 DeepDrone session ended. Fly safe![/yellow]")
|
96 |
+
|
97 |
+
# Disconnect from drone if connected
|
98 |
+
if self.drone_tools.is_connected():
|
99 |
+
self.console.print("[dim]Disconnecting from drone...[/dim]")
|
100 |
+
self.drone_tools.disconnect()
|
101 |
+
|
102 |
+
def _get_user_input(self) -> str:
|
103 |
+
"""Get user input with custom prompt."""
|
104 |
+
try:
|
105 |
+
return prompt(
|
106 |
+
[('class:prompt', '🚁 DeepDrone> ')],
|
107 |
+
style=self.prompt_style
|
108 |
+
)
|
109 |
+
except (KeyboardInterrupt, EOFError):
|
110 |
+
return '/quit'
|
111 |
+
|
112 |
+
def _handle_command(self, command: str) -> bool:
|
113 |
+
"""Handle special commands. Returns True to continue, False to quit."""
|
114 |
+
cmd_parts = command[1:].split()
|
115 |
+
if not cmd_parts:
|
116 |
+
return True
|
117 |
+
|
118 |
+
cmd = cmd_parts[0].lower()
|
119 |
+
|
120 |
+
if cmd in ['quit', 'exit', 'q']:
|
121 |
+
return False
|
122 |
+
|
123 |
+
elif cmd == 'help':
|
124 |
+
self._show_help()
|
125 |
+
|
126 |
+
elif cmd == 'clear':
|
127 |
+
self.console.clear()
|
128 |
+
self._show_welcome()
|
129 |
+
|
130 |
+
elif cmd == 'history':
|
131 |
+
self._show_history()
|
132 |
+
|
133 |
+
elif cmd == 'status':
|
134 |
+
self._show_status()
|
135 |
+
|
136 |
+
elif cmd == 'connect':
|
137 |
+
if len(cmd_parts) > 1:
|
138 |
+
self._connect_drone(cmd_parts[1])
|
139 |
+
else:
|
140 |
+
self.console.print("[red]Usage: /connect <connection_string>[/red]")
|
141 |
+
|
142 |
+
elif cmd == 'disconnect':
|
143 |
+
self._disconnect_drone()
|
144 |
+
|
145 |
+
elif cmd == 'models':
|
146 |
+
self._show_model_info()
|
147 |
+
|
148 |
+
else:
|
149 |
+
self.console.print(f"[red]Unknown command: {command}[/red]")
|
150 |
+
self.console.print("Type /help for available commands")
|
151 |
+
|
152 |
+
return True
|
153 |
+
|
154 |
+
def _process_message(self, user_message: str):
|
155 |
+
"""Process user message and generate response."""
|
156 |
+
# Add user message to history
|
157 |
+
self.chat_history.append({"role": "user", "content": user_message})
|
158 |
+
|
159 |
+
# Show user message
|
160 |
+
self.console.print(Panel(
|
161 |
+
user_message,
|
162 |
+
title="[bold blue]You[/bold blue]",
|
163 |
+
border_style="blue",
|
164 |
+
padding=(0, 1)
|
165 |
+
))
|
166 |
+
|
167 |
+
# Check if this requires drone tools
|
168 |
+
requires_tools = self._message_requires_tools(user_message)
|
169 |
+
|
170 |
+
# Generate response
|
171 |
+
with Live(
|
172 |
+
Spinner("dots", text="[green]DeepDrone is thinking...[/green]"),
|
173 |
+
console=self.console,
|
174 |
+
transient=True
|
175 |
+
) as live:
|
176 |
+
|
177 |
+
try:
|
178 |
+
if requires_tools:
|
179 |
+
response = self._process_with_tools(user_message, live)
|
180 |
+
else:
|
181 |
+
response = self._process_simple_chat(user_message)
|
182 |
+
|
183 |
+
live.stop()
|
184 |
+
|
185 |
+
# Show response
|
186 |
+
self._show_response(response)
|
187 |
+
|
188 |
+
# Add to history
|
189 |
+
self.chat_history.append({"role": "assistant", "content": response})
|
190 |
+
|
191 |
+
# Trim history if too long
|
192 |
+
if len(self.chat_history) > self.max_history:
|
193 |
+
self.chat_history = self.chat_history[-self.max_history:]
|
194 |
+
|
195 |
+
except Exception as e:
|
196 |
+
live.stop()
|
197 |
+
self.console.print(f"[red]Error: {e}[/red]")
|
198 |
+
|
199 |
+
def _message_requires_tools(self, message: str) -> bool:
|
200 |
+
"""Check if message requires drone tools."""
|
201 |
+
tool_keywords = [
|
202 |
+
'connect', 'takeoff', 'land', 'fly', 'goto', 'mission',
|
203 |
+
'battery', 'location', 'status', 'arm', 'disarm', 'rtl',
|
204 |
+
'return', 'home', 'altitude', 'waypoint', 'navigate'
|
205 |
+
]
|
206 |
+
|
207 |
+
message_lower = message.lower()
|
208 |
+
return any(keyword in message_lower for keyword in tool_keywords)
|
209 |
+
|
210 |
+
def _process_with_tools(self, message: str, live: Live) -> str:
|
211 |
+
"""Process message that may require drone tools."""
|
212 |
+
# Update status
|
213 |
+
live.update(Spinner("dots", text="[green]Analyzing command and planning actions...[/green]"))
|
214 |
+
|
215 |
+
# Create system prompt with tool information
|
216 |
+
system_prompt = self._create_system_prompt_with_tools()
|
217 |
+
|
218 |
+
# Prepare messages
|
219 |
+
messages = [{"role": "system", "content": system_prompt}]
|
220 |
+
messages.extend(self.chat_history[-10:]) # Last 10 messages for context
|
221 |
+
messages.append({"role": "user", "content": message})
|
222 |
+
|
223 |
+
# Get LLM response
|
224 |
+
response = self.llm.chat(messages)
|
225 |
+
|
226 |
+
# Check if response contains tool calls
|
227 |
+
if self._response_has_tool_calls(response):
|
228 |
+
live.update(Spinner("dots", text="[yellow]Executing drone operations...[/yellow]"))
|
229 |
+
response = self._execute_tool_calls(response)
|
230 |
+
|
231 |
+
return response
|
232 |
+
|
233 |
+
def _process_simple_chat(self, message: str) -> str:
|
234 |
+
"""Process simple chat message without tools."""
|
235 |
+
system_prompt = """You are DeepDrone, an AI assistant specialized in drone operations and flight control.
|
236 |
+
|
237 |
+
You help users with:
|
238 |
+
- Drone flight planning and mission design
|
239 |
+
- Understanding drone systems and components
|
240 |
+
- Troubleshooting flight issues
|
241 |
+
- Safety protocols and regulations
|
242 |
+
- Data analysis from drone flights
|
243 |
+
|
244 |
+
Be concise, helpful, and focus on drone-related topics. If asked about your identity,
|
245 |
+
clearly state that you are DeepDrone, a specialized drone AI assistant."""
|
246 |
+
|
247 |
+
messages = [{"role": "system", "content": system_prompt}]
|
248 |
+
messages.extend(self.chat_history[-10:])
|
249 |
+
messages.append({"role": "user", "content": message})
|
250 |
+
|
251 |
+
return self.llm.chat(messages)
|
252 |
+
|
253 |
+
def _create_system_prompt_with_tools(self) -> str:
|
254 |
+
"""Create system prompt with tool information."""
|
255 |
+
return """You are DeepDrone, an AI assistant that can control real drones through Python code.
|
256 |
+
|
257 |
+
Available drone control functions:
|
258 |
+
- connect_drone(connection_string): Connect to drone
|
259 |
+
- disconnect_drone(): Disconnect from drone
|
260 |
+
- takeoff(altitude): Take off to specified altitude in meters
|
261 |
+
- land(): Land the drone
|
262 |
+
- return_home(): Return to launch point
|
263 |
+
- fly_to(lat, lon, alt): Fly to GPS coordinates
|
264 |
+
- get_location(): Get current GPS position
|
265 |
+
- get_battery(): Get battery status
|
266 |
+
- execute_mission(waypoints): Execute mission with list of waypoints
|
267 |
+
|
268 |
+
When user requests drone operations, write Python code using these functions.
|
269 |
+
Always explain what you're doing and provide status updates.
|
270 |
+
|
271 |
+
Example:
|
272 |
+
```python
|
273 |
+
# Connect to drone simulator
|
274 |
+
connect_drone('udp:127.0.0.1:14550')
|
275 |
+
|
276 |
+
# Take off to 30 meters
|
277 |
+
takeoff(30)
|
278 |
+
|
279 |
+
# Fly to a specific location
|
280 |
+
fly_to(37.7749, -122.4194, 30)
|
281 |
+
|
282 |
+
# Return home
|
283 |
+
return_home()
|
284 |
+
|
285 |
+
# Disconnect
|
286 |
+
disconnect_drone()
|
287 |
+
```
|
288 |
+
|
289 |
+
Be safety-conscious and explain each operation."""
|
290 |
+
|
291 |
+
def _response_has_tool_calls(self, response: str) -> bool:
|
292 |
+
"""Check if response contains Python code blocks."""
|
293 |
+
return "```python" in response or "```" in response
|
294 |
+
|
295 |
+
def _execute_tool_calls(self, response: str) -> str:
|
296 |
+
"""Execute tool calls found in response."""
|
297 |
+
# Extract Python code blocks
|
298 |
+
code_blocks = self._extract_code_blocks(response)
|
299 |
+
|
300 |
+
results = []
|
301 |
+
for code in code_blocks:
|
302 |
+
try:
|
303 |
+
result = self._execute_code_block(code)
|
304 |
+
results.append(f"✅ Executed: {result}")
|
305 |
+
except Exception as e:
|
306 |
+
results.append(f"❌ Error: {e}")
|
307 |
+
|
308 |
+
# Append execution results to response
|
309 |
+
if results:
|
310 |
+
response += "\n\n**Execution Results:**\n" + "\n".join(results)
|
311 |
+
|
312 |
+
return response
|
313 |
+
|
314 |
+
def _extract_code_blocks(self, text: str) -> List[str]:
|
315 |
+
"""Extract Python code blocks from markdown text."""
|
316 |
+
code_blocks = []
|
317 |
+
lines = text.split('\n')
|
318 |
+
in_code_block = False
|
319 |
+
current_block = []
|
320 |
+
|
321 |
+
for line in lines:
|
322 |
+
if line.strip().startswith('```python') or line.strip().startswith('```'):
|
323 |
+
if in_code_block:
|
324 |
+
# End of code block
|
325 |
+
if current_block:
|
326 |
+
code_blocks.append('\n'.join(current_block))
|
327 |
+
current_block = []
|
328 |
+
in_code_block = False
|
329 |
+
else:
|
330 |
+
# Start of code block
|
331 |
+
in_code_block = True
|
332 |
+
elif in_code_block:
|
333 |
+
current_block.append(line)
|
334 |
+
|
335 |
+
return code_blocks
|
336 |
+
|
337 |
+
def _execute_code_block(self, code: str) -> str:
|
338 |
+
"""Execute a code block using drone tools."""
|
339 |
+
# Create a safe execution environment
|
340 |
+
safe_globals = {
|
341 |
+
'connect_drone': self.drone_tools.connect_drone,
|
342 |
+
'disconnect_drone': self.drone_tools.disconnect_drone,
|
343 |
+
'takeoff': self.drone_tools.takeoff,
|
344 |
+
'land': self.drone_tools.land,
|
345 |
+
'return_home': self.drone_tools.return_home,
|
346 |
+
'fly_to': self.drone_tools.fly_to,
|
347 |
+
'get_location': self.drone_tools.get_location,
|
348 |
+
'get_battery': self.drone_tools.get_battery,
|
349 |
+
'execute_mission': self.drone_tools.execute_mission,
|
350 |
+
}
|
351 |
+
|
352 |
+
# Capture output
|
353 |
+
output = []
|
354 |
+
|
355 |
+
def capture_print(*args, **kwargs):
|
356 |
+
output.append(' '.join(str(arg) for arg in args))
|
357 |
+
|
358 |
+
safe_globals['print'] = capture_print
|
359 |
+
|
360 |
+
# Execute code
|
361 |
+
exec(code, safe_globals)
|
362 |
+
|
363 |
+
return '\n'.join(output) if output else "Command executed successfully"
|
364 |
+
|
365 |
+
def _show_response(self, response: str):
|
366 |
+
"""Show AI response with formatting."""
|
367 |
+
# Parse as markdown if it contains markdown elements
|
368 |
+
if any(marker in response for marker in ['**', '*', '```', '#', '-', '1.']):
|
369 |
+
content = Markdown(response)
|
370 |
+
else:
|
371 |
+
content = Text(response)
|
372 |
+
|
373 |
+
self.console.print(Panel(
|
374 |
+
content,
|
375 |
+
title="[bold green]🚁 DeepDrone[/bold green]",
|
376 |
+
border_style="green",
|
377 |
+
padding=(0, 1)
|
378 |
+
))
|
379 |
+
|
380 |
+
def _show_help(self):
|
381 |
+
"""Show help message."""
|
382 |
+
help_text = """
|
383 |
+
[bold]Available Commands:[/bold]
|
384 |
+
|
385 |
+
[bold cyan]/help[/bold cyan] - Show this help message
|
386 |
+
[bold cyan]/quit[/bold cyan] - Exit the application
|
387 |
+
[bold cyan]/clear[/bold cyan] - Clear the screen
|
388 |
+
[bold cyan]/history[/bold cyan] - Show chat history
|
389 |
+
[bold cyan]/status[/bold cyan] - Show system status
|
390 |
+
[bold cyan]/connect <connection>[/bold cyan] - Connect to drone
|
391 |
+
[bold cyan]/disconnect[/bold cyan] - Disconnect from drone
|
392 |
+
[bold cyan]/models[/bold cyan] - Show current model info
|
393 |
+
|
394 |
+
[bold]Drone Commands (natural language):[/bold]
|
395 |
+
- "Connect to simulator at udp:127.0.0.1:14550"
|
396 |
+
- "Take off to 30 meters"
|
397 |
+
- "Fly to coordinates 37.7749, -122.4194 at 50 meters"
|
398 |
+
- "Show current location and battery status"
|
399 |
+
- "Execute a square flight pattern"
|
400 |
+
- "Return home and land"
|
401 |
+
|
402 |
+
[bold]Example Conversation:[/bold]
|
403 |
+
[dim]You: Connect to the drone simulator
|
404 |
+
DeepDrone: I'll connect to the simulator for you...
|
405 |
+
You: Take off to 20 meters and fly in a circle
|
406 |
+
DeepDrone: Taking off to 20 meters and executing circular pattern...[/dim]
|
407 |
+
"""
|
408 |
+
|
409 |
+
self.console.print(Panel(
|
410 |
+
help_text.strip(),
|
411 |
+
title="[bold]DeepDrone Help[/bold]",
|
412 |
+
border_style="cyan"
|
413 |
+
))
|
414 |
+
|
415 |
+
def _show_history(self):
|
416 |
+
"""Show chat history."""
|
417 |
+
if not self.chat_history:
|
418 |
+
self.console.print("[yellow]No chat history available[/yellow]")
|
419 |
+
return
|
420 |
+
|
421 |
+
self.console.print("[bold]Chat History:[/bold]\n")
|
422 |
+
|
423 |
+
for i, msg in enumerate(self.chat_history[-10:], 1): # Last 10 messages
|
424 |
+
role_color = "blue" if msg["role"] == "user" else "green"
|
425 |
+
role_name = "You" if msg["role"] == "user" else "DeepDrone"
|
426 |
+
|
427 |
+
self.console.print(f"[{role_color}]{i}. {role_name}:[/{role_color}] {msg['content'][:100]}...")
|
428 |
+
|
429 |
+
def _show_status(self):
|
430 |
+
"""Show system status."""
|
431 |
+
drone_status = "Connected" if self.drone_tools.is_connected() else "Disconnected"
|
432 |
+
drone_color = "green" if self.drone_tools.is_connected() else "red"
|
433 |
+
|
434 |
+
status_text = f"""
|
435 |
+
[bold]Model:[/bold] {self.model_config.name} ({self.model_config.provider})
|
436 |
+
[bold]Drone Status:[/bold] [{drone_color}]{drone_status}[/{drone_color}]
|
437 |
+
[bold]Connection:[/bold] {self.connection_string or 'None'}
|
438 |
+
[bold]Chat History:[/bold] {len(self.chat_history)} messages
|
439 |
+
"""
|
440 |
+
|
441 |
+
if self.drone_tools.is_connected():
|
442 |
+
try:
|
443 |
+
location = self.drone_tools.get_location()
|
444 |
+
battery = self.drone_tools.get_battery()
|
445 |
+
status_text += f"""
|
446 |
+
[bold]Location:[/bold] {location}
|
447 |
+
[bold]Battery:[/bold] {battery}
|
448 |
+
"""
|
449 |
+
except Exception as e:
|
450 |
+
status_text += f"\n[yellow]Could not get drone telemetry: {e}[/yellow]"
|
451 |
+
|
452 |
+
self.console.print(Panel(
|
453 |
+
status_text.strip(),
|
454 |
+
title="[bold]System Status[/bold]",
|
455 |
+
border_style="yellow"
|
456 |
+
))
|
457 |
+
|
458 |
+
def _connect_drone(self, connection_string: str):
|
459 |
+
"""Connect to drone."""
|
460 |
+
self.console.print(f"[yellow]Connecting to drone at {connection_string}...[/yellow]")
|
461 |
+
|
462 |
+
try:
|
463 |
+
if self.drone_tools.connect_drone(connection_string):
|
464 |
+
self.connection_string = connection_string
|
465 |
+
self.console.print("[green]✅ Connected to drone successfully[/green]")
|
466 |
+
else:
|
467 |
+
self.console.print("[red]❌ Failed to connect to drone[/red]")
|
468 |
+
except Exception as e:
|
469 |
+
self.console.print(f"[red]❌ Connection error: {e}[/red]")
|
470 |
+
|
471 |
+
def _disconnect_drone(self):
|
472 |
+
"""Disconnect from drone."""
|
473 |
+
if self.drone_tools.is_connected():
|
474 |
+
self.drone_tools.disconnect_drone()
|
475 |
+
self.console.print("[yellow]Disconnected from drone[/yellow]")
|
476 |
+
else:
|
477 |
+
self.console.print("[yellow]No drone connection to disconnect[/yellow]")
|
478 |
+
|
479 |
+
def _show_model_info(self):
|
480 |
+
"""Show current model information."""
|
481 |
+
info_text = f"""
|
482 |
+
[bold]Name:[/bold] {self.model_config.name}
|
483 |
+
[bold]Provider:[/bold] {self.model_config.provider}
|
484 |
+
[bold]Model ID:[/bold] {self.model_config.model_id}
|
485 |
+
[bold]Max Tokens:[/bold] {self.model_config.max_tokens}
|
486 |
+
[bold]Temperature:[/bold] {self.model_config.temperature}
|
487 |
+
"""
|
488 |
+
|
489 |
+
if self.model_config.base_url:
|
490 |
+
info_text += f"\n[bold]Base URL:[/bold] {self.model_config.base_url}"
|
491 |
+
|
492 |
+
self.console.print(Panel(
|
493 |
+
info_text.strip(),
|
494 |
+
title="[bold]Current Model[/bold]",
|
495 |
+
border_style="magenta"
|
496 |
+
))
|
eeprom.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:fdba0627b42b0ddda657d4b38e46469a0b19a87dae8daa00c2da6f5f2db26bd3
|
3 |
+
size 16384
|
main.py
CHANGED
@@ -1,218 +1,43 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
from drone import drone_chat
|
5 |
-
|
6 |
-
# Load environment variables from .env file
|
7 |
-
load_dotenv()
|
8 |
-
|
9 |
-
# Add dark theme CSS right at the beginning
|
10 |
-
st.markdown(
|
11 |
-
"""
|
12 |
-
<style>
|
13 |
-
/* Dark theme for the entire app, applied immediately */
|
14 |
-
.stApp, body, [data-testid="stAppViewContainer"], .main, .block-container, [data-testid="stHeader"] {
|
15 |
-
background-color: #000000 !important;
|
16 |
-
color: #00ff00 !important;
|
17 |
-
}
|
18 |
-
|
19 |
-
/* Dark styling for all inputs */
|
20 |
-
[data-testid="stTextInput"] > div, .stTextInput > div {
|
21 |
-
background-color: #1E1E1E !important;
|
22 |
-
color: #00ff00 !important;
|
23 |
-
border: 1px solid #00ff00 !important;
|
24 |
-
}
|
25 |
-
|
26 |
-
[data-testid="stTextInput"] input, .stTextInput input {
|
27 |
-
color: #00ff00 !important;
|
28 |
-
background-color: #1E1E1E !important;
|
29 |
-
}
|
30 |
-
</style>
|
31 |
-
""",
|
32 |
-
unsafe_allow_html=True
|
33 |
-
)
|
34 |
|
35 |
-
|
|
|
|
|
36 |
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
def show_auth_screen():
|
41 |
-
"""Display the authentication screen with DeepDrone information"""
|
42 |
-
|
43 |
-
# Military-style header
|
44 |
-
st.markdown("<h1 class='glow-text' style='text-align: center; color: #00ff00; font-family: \"Courier New\", monospace; margin-top: 0; margin-bottom: 10px;'>DEEPDRONE COMMAND CENTER</h1>", unsafe_allow_html=True)
|
45 |
-
st.markdown("<p class='subheader glow-text' style='text-align: center; margin-bottom: 5px;'>SECURE TACTICAL OPERATIONS INTERFACE</p>", unsafe_allow_html=True)
|
46 |
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
st.markdown("<div style='text-align: center'>", unsafe_allow_html=True)
|
51 |
-
st.markdown("<h2 style='color: #00ff00; font-family: \"Courier New\", monospace; text-shadow: 0 0 5px #00ff00;'>SYSTEM AUTHENTICATION REQUIRED</h2>", unsafe_allow_html=True)
|
52 |
-
|
53 |
-
# System status information in a more compact layout
|
54 |
-
cols = st.columns(2)
|
55 |
-
with cols[0]:
|
56 |
-
st.markdown("""
|
57 |
-
<div style='font-family: "Courier New", monospace; color: #00dd00;'>
|
58 |
-
<b>SYSTEM STATUS:</b> STANDBY<br>
|
59 |
-
<b>DATABASE:</b> CONNECTED<br>
|
60 |
-
<b>SECURITY:</b> ENABLED
|
61 |
-
</div>
|
62 |
-
""", unsafe_allow_html=True)
|
63 |
-
|
64 |
-
with cols[1]:
|
65 |
-
st.markdown("""
|
66 |
-
<div style='font-family: "Courier New", monospace; color: #00dd00;'>
|
67 |
-
<b>PROTOCOL:</b> HF-AUTH-1<br>
|
68 |
-
<b>ENCRYPTION:</b> AES-256<br>
|
69 |
-
<b>AI MODULE:</b> OFFLINE
|
70 |
-
</div>
|
71 |
-
""", unsafe_allow_html=True)
|
72 |
-
|
73 |
-
# Compact information about DeepDrone
|
74 |
-
st.markdown("""
|
75 |
-
<div style='font-family: "Courier New", monospace; color: #00ff00; text-align: left; margin: 15px 0;'>
|
76 |
-
<p><b>DEEPDRONE</b> is an advanced command and control system for drone operations:</p>
|
77 |
-
|
78 |
-
<ul style='color: #00ff00; margin: 8px 0; padding-left: 20px;'>
|
79 |
-
<li>Real-time <b>flight data analysis</b> and visualization</li>
|
80 |
-
<li>Comprehensive <b>sensor monitoring</b> with anomaly detection</li>
|
81 |
-
<li>AI-powered <b>mission planning</b> and execution</li>
|
82 |
-
<li>Predictive <b>maintenance scheduling</b> and diagnostics</li>
|
83 |
-
</ul>
|
84 |
-
</div>
|
85 |
-
""", unsafe_allow_html=True)
|
86 |
-
|
87 |
-
st.markdown("<hr style='border: 1px solid #00ff00; margin: 10px 0;'>", unsafe_allow_html=True)
|
88 |
-
|
89 |
-
# Token input with custom styling
|
90 |
-
st.markdown("<h3 style='color: #00ff00; font-family: \"Courier New\", monospace; text-shadow: 0 0 5px #00ff00;'>ENTER HUGGING FACE AUTHENTICATION TOKEN FOR THE LLM TO RUN:</h3>", unsafe_allow_html=True)
|
91 |
-
|
92 |
-
# Create a container with dark background for the input
|
93 |
-
st.markdown("<div style='background-color: #0A0A0A; padding: 10px; border-radius: 5px;'>", unsafe_allow_html=True)
|
94 |
-
api_key = st.text_input("HF Token", type="password", placeholder="Enter Hugging Face API token...", label_visibility="collapsed")
|
95 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
96 |
-
|
97 |
-
# Submit button with custom styling
|
98 |
-
if st.button("AUTHORIZE ACCESS"):
|
99 |
-
if api_key:
|
100 |
-
os.environ["HF_TOKEN"] = api_key
|
101 |
-
st.markdown("<div style='color: #00ff00; background-color: rgba(0, 128, 0, 0.2); padding: 10px; border: 1px solid #00ff00; border-radius: 5px;'>AUTHENTICATION SUCCESSFUL - INITIALIZING SYSTEM</div>", unsafe_allow_html=True)
|
102 |
-
st.session_state['authenticated'] = True
|
103 |
-
st.rerun()
|
104 |
-
|
105 |
-
st.markdown("</div>", unsafe_allow_html=True) # Close text-align center div
|
106 |
-
st.markdown("</div>", unsafe_allow_html=True) # Close auth-container div
|
107 |
|
108 |
def main():
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
[data-testid="stTextInput"] input {
|
134 |
-
color: #00ff00 !important;
|
135 |
-
background-color: #1E1E1E !important;
|
136 |
-
}
|
137 |
-
|
138 |
-
/* Dark buttons */
|
139 |
-
.stButton > button {
|
140 |
-
background-color: #0A0A0A !important;
|
141 |
-
color: #00ff00 !important;
|
142 |
-
border: 1px solid #00ff00 !important;
|
143 |
-
border-radius: 2px !important;
|
144 |
-
font-family: "Courier New", monospace !important;
|
145 |
-
}
|
146 |
-
|
147 |
-
.stButton > button:hover {
|
148 |
-
background-color: #00ff00 !important;
|
149 |
-
color: #000000 !important;
|
150 |
-
}
|
151 |
-
|
152 |
-
/* Success message styling */
|
153 |
-
.element-container [data-testid="stAlert"] {
|
154 |
-
background-color: rgba(0, 128, 0, 0.2) !important;
|
155 |
-
color: #00ff00 !important;
|
156 |
-
border: 1px solid #00ff00 !important;
|
157 |
-
}
|
158 |
-
|
159 |
-
/* Header styling */
|
160 |
-
h1, h2, h3, h4, h5, h6, p, span, div, label {
|
161 |
-
color: #00ff00 !important;
|
162 |
-
}
|
163 |
-
|
164 |
-
/* Centered container for HF token */
|
165 |
-
.auth-container {
|
166 |
-
display: flex;
|
167 |
-
flex-direction: column;
|
168 |
-
align-items: center;
|
169 |
-
justify-content: center;
|
170 |
-
height: auto;
|
171 |
-
min-height: 400px;
|
172 |
-
max-width: 90vh;
|
173 |
-
width: 70%;
|
174 |
-
margin: 20px auto;
|
175 |
-
padding: 30px;
|
176 |
-
border: 1px solid #00ff00;
|
177 |
-
border-radius: 10px;
|
178 |
-
background-color: #0A0A0A !important;
|
179 |
-
overflow-y: auto;
|
180 |
-
}
|
181 |
-
|
182 |
-
/* Hide Streamlit's default footer */
|
183 |
-
footer, header {
|
184 |
-
visibility: hidden !important;
|
185 |
-
display: none !important;
|
186 |
-
}
|
187 |
-
|
188 |
-
/* Remove white background from all components */
|
189 |
-
.block-container, .main {
|
190 |
-
background-color: #000000 !important;
|
191 |
-
}
|
192 |
-
|
193 |
-
/* Add a slight glow effect to green text */
|
194 |
-
.glow-text {
|
195 |
-
text-shadow: 0 0 5px #00ff00 !important;
|
196 |
-
}
|
197 |
-
|
198 |
-
/* Custom select styling */
|
199 |
-
[data-testid="stSelectbox"] {
|
200 |
-
background-color: #1E1E1E !important;
|
201 |
-
color: #00ff00 !important;
|
202 |
-
border: 1px solid #00ff00 !important;
|
203 |
-
}
|
204 |
-
</style>
|
205 |
-
""",
|
206 |
-
unsafe_allow_html=True
|
207 |
-
)
|
208 |
-
|
209 |
-
# Check if user is authenticated
|
210 |
-
if not os.environ.get("HF_TOKEN") and not st.session_state.get('authenticated', False):
|
211 |
-
show_auth_screen()
|
212 |
-
return
|
213 |
-
|
214 |
-
# Run the drone chat application
|
215 |
-
drone_chat.main()
|
216 |
|
217 |
if __name__ == "__main__":
|
218 |
main()
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
DeepDrone Terminal Application - AI-Powered Drone Control System
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
+
A terminal-based application for controlling drones using various AI models
|
6 |
+
including OpenAI, Anthropic, Hugging Face, and local Ollama models.
|
7 |
+
"""
|
8 |
|
9 |
+
import sys
|
10 |
+
import os
|
11 |
+
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
+
# Add the current directory to Python path for imports
|
14 |
+
current_dir = Path(__file__).parent
|
15 |
+
sys.path.insert(0, str(current_dir))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
def main():
|
18 |
+
"""Main entry point for the DeepDrone terminal application."""
|
19 |
+
try:
|
20 |
+
# Load environment variables if .env file exists
|
21 |
+
env_file = current_dir / ".env"
|
22 |
+
if env_file.exists():
|
23 |
+
from dotenv import load_dotenv
|
24 |
+
load_dotenv(env_file)
|
25 |
+
|
26 |
+
# If no arguments provided, start interactive mode
|
27 |
+
if len(sys.argv) == 1:
|
28 |
+
from drone.interactive_setup import start_interactive_session
|
29 |
+
start_interactive_session()
|
30 |
+
else:
|
31 |
+
# Run the Typer CLI application for other commands
|
32 |
+
from drone.cli import app
|
33 |
+
app()
|
34 |
+
|
35 |
+
except KeyboardInterrupt:
|
36 |
+
print("\n🚁 DeepDrone session interrupted. Goodbye!")
|
37 |
+
sys.exit(0)
|
38 |
+
except Exception as e:
|
39 |
+
print(f"❌ Error starting DeepDrone: {e}")
|
40 |
+
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
if __name__ == "__main__":
|
43 |
main()
|
mav.parm
ADDED
@@ -0,0 +1,1349 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
ACRO_BAL_PITCH 1.000000
|
2 |
+
ACRO_BAL_ROLL 1.000000
|
3 |
+
ACRO_OPTIONS 0
|
4 |
+
ACRO_RP_EXPO 0.300000
|
5 |
+
ACRO_RP_RATE 360.000000
|
6 |
+
ACRO_RP_RATE_TC 0.000000
|
7 |
+
ACRO_THR_MID 0.000000
|
8 |
+
ACRO_TRAINER 2
|
9 |
+
ACRO_Y_EXPO 0.000000
|
10 |
+
ACRO_Y_RATE 202.500000
|
11 |
+
ACRO_Y_RATE_TC 0.000000
|
12 |
+
ADSB_TYPE 0
|
13 |
+
AHRS_COMP_BETA 0.100000
|
14 |
+
AHRS_EKF_TYPE 3
|
15 |
+
AHRS_GPS_GAIN 1.000000
|
16 |
+
AHRS_GPS_MINSATS 6
|
17 |
+
AHRS_GPS_USE 1
|
18 |
+
AHRS_OPTIONS 0
|
19 |
+
AHRS_ORIENTATION 0
|
20 |
+
AHRS_RP_P 0.200000
|
21 |
+
AHRS_TRIM_X 0.000000
|
22 |
+
AHRS_TRIM_Y 0.000000
|
23 |
+
AHRS_TRIM_Z 0.000000
|
24 |
+
AHRS_WIND_MAX 0
|
25 |
+
AHRS_YAW_P 0.200000
|
26 |
+
ANGLE_MAX 3000
|
27 |
+
ARMING_ACCTHRESH 0.750000
|
28 |
+
ARMING_CHECK 1
|
29 |
+
ARMING_MAGTHRESH 100
|
30 |
+
ARMING_MIS_ITEMS 0
|
31 |
+
ARMING_NEED_LOC 0
|
32 |
+
ARMING_OPTIONS 0
|
33 |
+
ARMING_RUDDER 2
|
34 |
+
ARSPD_ENABLE 0
|
35 |
+
ATC_ACCEL_P_MAX 110000.000000
|
36 |
+
ATC_ACCEL_R_MAX 110000.000000
|
37 |
+
ATC_ACCEL_Y_MAX 27000.000000
|
38 |
+
ATC_ANGLE_BOOST 1
|
39 |
+
ATC_ANG_LIM_TC 1.000000
|
40 |
+
ATC_ANG_PIT_P 4.500000
|
41 |
+
ATC_ANG_RLL_P 4.500000
|
42 |
+
ATC_ANG_YAW_P 4.500000
|
43 |
+
ATC_INPUT_TC 0.150000
|
44 |
+
ATC_LAND_P_MULT 1.000000
|
45 |
+
ATC_LAND_R_MULT 1.000000
|
46 |
+
ATC_LAND_Y_MULT 1.000000
|
47 |
+
ATC_RATE_FF_ENAB 1
|
48 |
+
ATC_RATE_P_MAX 0.000000
|
49 |
+
ATC_RATE_R_MAX 0.000000
|
50 |
+
ATC_RATE_Y_MAX 0.000000
|
51 |
+
ATC_RAT_PIT_D 0.003600
|
52 |
+
ATC_RAT_PIT_D_FF 0.000000
|
53 |
+
ATC_RAT_PIT_FF 0.000000
|
54 |
+
ATC_RAT_PIT_FLTD 20.000000
|
55 |
+
ATC_RAT_PIT_FLTE 0.000000
|
56 |
+
ATC_RAT_PIT_FLTT 20.000000
|
57 |
+
ATC_RAT_PIT_I 0.135000
|
58 |
+
ATC_RAT_PIT_IMAX 0.500000
|
59 |
+
ATC_RAT_PIT_NEF 0
|
60 |
+
ATC_RAT_PIT_NTF 0
|
61 |
+
ATC_RAT_PIT_P 0.135000
|
62 |
+
ATC_RAT_PIT_PDMX 0.000000
|
63 |
+
ATC_RAT_PIT_SMAX 0.000000
|
64 |
+
ATC_RAT_RLL_D 0.003600
|
65 |
+
ATC_RAT_RLL_D_FF 0.000000
|
66 |
+
ATC_RAT_RLL_FF 0.000000
|
67 |
+
ATC_RAT_RLL_FLTD 20.000000
|
68 |
+
ATC_RAT_RLL_FLTE 0.000000
|
69 |
+
ATC_RAT_RLL_FLTT 20.000000
|
70 |
+
ATC_RAT_RLL_I 0.135000
|
71 |
+
ATC_RAT_RLL_IMAX 0.500000
|
72 |
+
ATC_RAT_RLL_NEF 0
|
73 |
+
ATC_RAT_RLL_NTF 0
|
74 |
+
ATC_RAT_RLL_P 0.135000
|
75 |
+
ATC_RAT_RLL_PDMX 0.000000
|
76 |
+
ATC_RAT_RLL_SMAX 0.000000
|
77 |
+
ATC_RAT_YAW_D 0.000000
|
78 |
+
ATC_RAT_YAW_D_FF 0.000000
|
79 |
+
ATC_RAT_YAW_FF 0.000000
|
80 |
+
ATC_RAT_YAW_FLTD 20.000000
|
81 |
+
ATC_RAT_YAW_FLTE 2.500000
|
82 |
+
ATC_RAT_YAW_FLTT 20.000000
|
83 |
+
ATC_RAT_YAW_I 0.020000
|
84 |
+
ATC_RAT_YAW_IMAX 0.500000
|
85 |
+
ATC_RAT_YAW_NEF 0
|
86 |
+
ATC_RAT_YAW_NTF 0
|
87 |
+
ATC_RAT_YAW_P 0.300000
|
88 |
+
ATC_RAT_YAW_PDMX 0.000000
|
89 |
+
ATC_RAT_YAW_SMAX 0.000000
|
90 |
+
ATC_SLEW_YAW 6000.000000
|
91 |
+
ATC_THR_G_BOOST 0.000000
|
92 |
+
ATC_THR_MIX_MAN 0.100000
|
93 |
+
ATC_THR_MIX_MAX 0.500000
|
94 |
+
ATC_THR_MIX_MIN 0.100000
|
95 |
+
AUTOTUNE_AGGR 0.075000
|
96 |
+
AUTOTUNE_AXES 7
|
97 |
+
AUTOTUNE_MIN_D 0.000500
|
98 |
+
AUTO_OPTIONS 0
|
99 |
+
AVD_ENABLE 0
|
100 |
+
AVOID_ACCEL_MAX 3.000000
|
101 |
+
AVOID_ALT_MIN 0.000000
|
102 |
+
AVOID_ANGLE_MAX 1000
|
103 |
+
AVOID_BACKUP_DZ 0.100000
|
104 |
+
AVOID_BACKUP_SPD 0.750000
|
105 |
+
AVOID_BACKZ_SPD 0.750000
|
106 |
+
AVOID_BEHAVE 0
|
107 |
+
AVOID_DIST_MAX 5.000000
|
108 |
+
AVOID_ENABLE 3
|
109 |
+
AVOID_MARGIN 2.000000
|
110 |
+
BARO1_DEVID 65540
|
111 |
+
BARO1_GND_PRESS 94502.828125
|
112 |
+
BARO1_WCF_ENABLE 0
|
113 |
+
BARO2_DEVID 65796
|
114 |
+
BARO2_GND_PRESS 94504.796875
|
115 |
+
BARO2_WCF_ENABLE 0
|
116 |
+
BARO3_DEVID 0
|
117 |
+
BARO3_GND_PRESS 0.000000
|
118 |
+
BARO3_WCF_ENABLE 0
|
119 |
+
BARO_ALTERR_MAX 2000.000000
|
120 |
+
BARO_ALT_OFFSET 0.000000
|
121 |
+
BARO_EXT_BUS -1
|
122 |
+
BARO_FIELD_ELV 0.000000
|
123 |
+
BARO_FLTR_RNG 0
|
124 |
+
BARO_GND_TEMP 0.000000
|
125 |
+
BARO_OPTIONS 0
|
126 |
+
BARO_PRIMARY 0
|
127 |
+
BARO_PROBE_EXT 0
|
128 |
+
BATT2_MONITOR 0
|
129 |
+
BATT3_MONITOR 0
|
130 |
+
BATT4_MONITOR 0
|
131 |
+
BATT5_MONITOR 0
|
132 |
+
BATT6_MONITOR 0
|
133 |
+
BATT7_MONITOR 0
|
134 |
+
BATT8_MONITOR 0
|
135 |
+
BATT9_MONITOR 0
|
136 |
+
BATT_AMP_OFFSET 0.000000
|
137 |
+
BATT_AMP_PERVLT 17.000000
|
138 |
+
BATT_ARM_MAH 0
|
139 |
+
BATT_ARM_VOLT 0.000000
|
140 |
+
BATT_CAPACITY 3300
|
141 |
+
BATT_CRT_MAH 0.000000
|
142 |
+
BATT_CRT_VOLT 0.000000
|
143 |
+
BATT_CURR_PIN 12
|
144 |
+
BATT_FS_CRT_ACT 0
|
145 |
+
BATT_FS_LOW_ACT 0
|
146 |
+
BATT_FS_VOLTSRC 0
|
147 |
+
BATT_LOW_MAH 0.000000
|
148 |
+
BATT_LOW_TIMER 10
|
149 |
+
BATT_LOW_VOLT 10.500000
|
150 |
+
BATT_MONITOR 4
|
151 |
+
BATT_OPTIONS 0
|
152 |
+
BATT_SERIAL_NUM -1
|
153 |
+
BATT_VLT_OFFSET 0.000000
|
154 |
+
BATT_VOLT_MULT 10.100000
|
155 |
+
BATT_VOLT_PIN 13
|
156 |
+
BCN_TYPE 0
|
157 |
+
BRD_BOOT_DELAY 0
|
158 |
+
BRD_OPTIONS 0
|
159 |
+
BRD_RTC_TYPES 1
|
160 |
+
BRD_RTC_TZ_MIN 0
|
161 |
+
BRD_SAFETYOPTION 3
|
162 |
+
BRD_SAFETY_DEFLT 0
|
163 |
+
BRD_SAFETY_MASK 16368
|
164 |
+
BRD_SD_FENCE 0
|
165 |
+
BRD_SD_MISSION 0
|
166 |
+
BRD_SERIAL_NUM 0
|
167 |
+
BRD_VBUS_MIN 4.300000
|
168 |
+
BRD_VSERVO_MIN 0.000000
|
169 |
+
BTN_ENABLE 0
|
170 |
+
CAM1_TYPE 0
|
171 |
+
CAM2_TYPE 0
|
172 |
+
CAM_AUTO_ONLY 0
|
173 |
+
CAM_MAX_ROLL 0
|
174 |
+
CAN_D1_PROTOCOL 1
|
175 |
+
CAN_D1_PROTOCOL2 0
|
176 |
+
CAN_D2_PROTOCOL 1
|
177 |
+
CAN_D2_PROTOCOL2 0
|
178 |
+
CAN_LOGLEVEL 0
|
179 |
+
CAN_P1_DRIVER 0
|
180 |
+
CAN_P2_DRIVER 0
|
181 |
+
CC_TYPE 0
|
182 |
+
CHUTE_ENABLED 0
|
183 |
+
CIRCLE_OPTIONS 1
|
184 |
+
CIRCLE_RADIUS 1000.000000
|
185 |
+
CIRCLE_RATE 20.000000
|
186 |
+
COMPASS_AUTODEC 1
|
187 |
+
COMPASS_AUTO_ROT 2
|
188 |
+
COMPASS_CAL_FIT 16.000000
|
189 |
+
COMPASS_DEC 0.000000
|
190 |
+
COMPASS_DEV_ID 97539
|
191 |
+
COMPASS_DEV_ID2 131874
|
192 |
+
COMPASS_DEV_ID3 263178
|
193 |
+
COMPASS_DEV_ID4 97283
|
194 |
+
COMPASS_DEV_ID5 97795
|
195 |
+
COMPASS_DEV_ID6 98051
|
196 |
+
COMPASS_DEV_ID7 0
|
197 |
+
COMPASS_DEV_ID8 0
|
198 |
+
COMPASS_DIA2_X 1.000000
|
199 |
+
COMPASS_DIA2_Y 1.000000
|
200 |
+
COMPASS_DIA2_Z 1.000000
|
201 |
+
COMPASS_DIA3_X 1.000000
|
202 |
+
COMPASS_DIA3_Y 1.000000
|
203 |
+
COMPASS_DIA3_Z 1.000000
|
204 |
+
COMPASS_DIA_X 1.000000
|
205 |
+
COMPASS_DIA_Y 1.000000
|
206 |
+
COMPASS_DIA_Z 1.000000
|
207 |
+
COMPASS_DISBLMSK 0
|
208 |
+
COMPASS_ENABLE 1
|
209 |
+
COMPASS_EXTERN2 0
|
210 |
+
COMPASS_EXTERN3 0
|
211 |
+
COMPASS_EXTERNAL 1
|
212 |
+
COMPASS_FLTR_RNG 0
|
213 |
+
COMPASS_LEARN 0
|
214 |
+
COMPASS_MOT2_X 0.000000
|
215 |
+
COMPASS_MOT2_Y 0.000000
|
216 |
+
COMPASS_MOT2_Z 0.000000
|
217 |
+
COMPASS_MOT3_X 0.000000
|
218 |
+
COMPASS_MOT3_Y 0.000000
|
219 |
+
COMPASS_MOT3_Z 0.000000
|
220 |
+
COMPASS_MOTCT 0
|
221 |
+
COMPASS_MOT_X 0.000000
|
222 |
+
COMPASS_MOT_Y 0.000000
|
223 |
+
COMPASS_MOT_Z 0.000000
|
224 |
+
COMPASS_ODI2_X 0.000000
|
225 |
+
COMPASS_ODI2_Y 0.000000
|
226 |
+
COMPASS_ODI2_Z 0.000000
|
227 |
+
COMPASS_ODI3_X 0.000000
|
228 |
+
COMPASS_ODI3_Y 0.000000
|
229 |
+
COMPASS_ODI3_Z 0.000000
|
230 |
+
COMPASS_ODI_X 0.000000
|
231 |
+
COMPASS_ODI_Y 0.000000
|
232 |
+
COMPASS_ODI_Z 0.000000
|
233 |
+
COMPASS_OFFS_MAX 1800
|
234 |
+
COMPASS_OFS2_X 5.000000
|
235 |
+
COMPASS_OFS2_Y 13.000000
|
236 |
+
COMPASS_OFS2_Z -18.000000
|
237 |
+
COMPASS_OFS3_X 5.000000
|
238 |
+
COMPASS_OFS3_Y 13.000000
|
239 |
+
COMPASS_OFS3_Z -18.000000
|
240 |
+
COMPASS_OFS_X 5.000000
|
241 |
+
COMPASS_OFS_Y 13.000000
|
242 |
+
COMPASS_OFS_Z -18.000000
|
243 |
+
COMPASS_OPTIONS 0
|
244 |
+
COMPASS_ORIENT 0
|
245 |
+
COMPASS_ORIENT2 0
|
246 |
+
COMPASS_ORIENT3 0
|
247 |
+
COMPASS_PMOT_EN 0
|
248 |
+
COMPASS_PRIO1_ID 97539
|
249 |
+
COMPASS_PRIO2_ID 131874
|
250 |
+
COMPASS_PRIO3_ID 263178
|
251 |
+
COMPASS_SCALE 1.000000
|
252 |
+
COMPASS_SCALE2 1.000000
|
253 |
+
COMPASS_SCALE3 1.000000
|
254 |
+
COMPASS_USE 1
|
255 |
+
COMPASS_USE2 1
|
256 |
+
COMPASS_USE3 1
|
257 |
+
CUST_ROT_ENABLE 0
|
258 |
+
DEV_OPTIONS 0
|
259 |
+
DID_ENABLE 0
|
260 |
+
DISARM_DELAY 10
|
261 |
+
EAHRS_TYPE 0
|
262 |
+
EFI_TYPE 0
|
263 |
+
EK2_ENABLE 0
|
264 |
+
EK3_ABIAS_P_NSE 0.020000
|
265 |
+
EK3_ACC_BIAS_LIM 1.000000
|
266 |
+
EK3_ACC_P_NSE 0.350000
|
267 |
+
EK3_AFFINITY 0
|
268 |
+
EK3_ALT_M_NSE 2.000000
|
269 |
+
EK3_BCN_DELAY 50
|
270 |
+
EK3_BCN_I_GTE 500
|
271 |
+
EK3_BCN_M_NSE 1.000000
|
272 |
+
EK3_BETA_MASK 0
|
273 |
+
EK3_CHECK_SCALE 100
|
274 |
+
EK3_DRAG_BCOEF_X 0.000000
|
275 |
+
EK3_DRAG_BCOEF_Y 0.000000
|
276 |
+
EK3_DRAG_MCOEF 0.000000
|
277 |
+
EK3_DRAG_M_NSE 0.500000
|
278 |
+
EK3_EAS_I_GATE 400
|
279 |
+
EK3_EAS_M_NSE 1.400000
|
280 |
+
EK3_ENABLE 1
|
281 |
+
EK3_ERR_THRESH 0.200000
|
282 |
+
EK3_FLOW_DELAY 10
|
283 |
+
EK3_FLOW_I_GATE 300
|
284 |
+
EK3_FLOW_M_NSE 0.250000
|
285 |
+
EK3_FLOW_USE 1
|
286 |
+
EK3_GBIAS_P_NSE 0.001000
|
287 |
+
EK3_GLITCH_RAD 25
|
288 |
+
EK3_GND_EFF_DZ 4.000000
|
289 |
+
EK3_GPS_CHECK 31
|
290 |
+
EK3_GPS_VACC_MAX 0.000000
|
291 |
+
EK3_GSF_RST_MAX 2
|
292 |
+
EK3_GSF_RUN_MASK 3
|
293 |
+
EK3_GSF_USE_MASK 3
|
294 |
+
EK3_GYRO_P_NSE 0.015000
|
295 |
+
EK3_HGT_DELAY 60
|
296 |
+
EK3_HGT_I_GATE 500
|
297 |
+
EK3_HRT_FILT 2.000000
|
298 |
+
EK3_IMU_MASK 3
|
299 |
+
EK3_LOG_LEVEL 0
|
300 |
+
EK3_MAGB_P_NSE 0.000100
|
301 |
+
EK3_MAGE_P_NSE 0.001000
|
302 |
+
EK3_MAG_CAL 3
|
303 |
+
EK3_MAG_EF_LIM 50
|
304 |
+
EK3_MAG_I_GATE 300
|
305 |
+
EK3_MAG_MASK 0
|
306 |
+
EK3_MAG_M_NSE 0.050000
|
307 |
+
EK3_MAX_FLOW 2.500000
|
308 |
+
EK3_NOAID_M_NSE 10.000000
|
309 |
+
EK3_OGNM_TEST_SF 2.000000
|
310 |
+
EK3_OGN_HGT_MASK 0
|
311 |
+
EK3_OPTIONS 0
|
312 |
+
EK3_POSNE_M_NSE 0.500000
|
313 |
+
EK3_POS_I_GATE 500
|
314 |
+
EK3_PRIMARY 0
|
315 |
+
EK3_RNG_I_GATE 500
|
316 |
+
EK3_RNG_M_NSE 0.500000
|
317 |
+
EK3_RNG_USE_HGT -1
|
318 |
+
EK3_RNG_USE_SPD 2.000000
|
319 |
+
EK3_SRC1_POSXY 3
|
320 |
+
EK3_SRC1_POSZ 1
|
321 |
+
EK3_SRC1_VELXY 3
|
322 |
+
EK3_SRC1_VELZ 3
|
323 |
+
EK3_SRC1_YAW 1
|
324 |
+
EK3_SRC2_POSXY 0
|
325 |
+
EK3_SRC2_POSZ 1
|
326 |
+
EK3_SRC2_VELXY 0
|
327 |
+
EK3_SRC2_VELZ 0
|
328 |
+
EK3_SRC2_YAW 0
|
329 |
+
EK3_SRC3_POSXY 0
|
330 |
+
EK3_SRC3_POSZ 1
|
331 |
+
EK3_SRC3_VELXY 0
|
332 |
+
EK3_SRC3_VELZ 0
|
333 |
+
EK3_SRC3_YAW 0
|
334 |
+
EK3_SRC_OPTIONS 1
|
335 |
+
EK3_TAU_OUTPUT 25
|
336 |
+
EK3_TERR_GRAD 0.100000
|
337 |
+
EK3_VELD_M_NSE 0.500000
|
338 |
+
EK3_VELNE_M_NSE 0.300000
|
339 |
+
EK3_VEL_I_GATE 500
|
340 |
+
EK3_VIS_VERR_MAX 0.900000
|
341 |
+
EK3_VIS_VERR_MIN 0.100000
|
342 |
+
EK3_WENC_VERR 0.100000
|
343 |
+
EK3_WIND_PSCALE 1.000000
|
344 |
+
EK3_WIND_P_NSE 0.200000
|
345 |
+
EK3_YAW_I_GATE 300
|
346 |
+
EK3_YAW_M_NSE 0.500000
|
347 |
+
ESC_CALIBRATION 0
|
348 |
+
ESC_TLM_MAV_OFS 0
|
349 |
+
FENCE_ACTION 1
|
350 |
+
FENCE_ALT_MAX 100.000000
|
351 |
+
FENCE_ALT_MIN -10.000000
|
352 |
+
FENCE_AUTOENABLE 0
|
353 |
+
FENCE_ENABLE 0
|
354 |
+
FENCE_MARGIN 2.000000
|
355 |
+
FENCE_NTF_FREQ 1.000000
|
356 |
+
FENCE_OPTIONS 0
|
357 |
+
FENCE_RADIUS 150.000000
|
358 |
+
FENCE_TOTAL 0
|
359 |
+
FENCE_TYPE 7
|
360 |
+
FFT_ENABLE 0
|
361 |
+
FHLD_BRAKE_RATE 8
|
362 |
+
FHLD_FILT_HZ 5.000000
|
363 |
+
FHLD_FLOW_MAX 0.600000
|
364 |
+
FHLD_QUAL_MIN 10
|
365 |
+
FHLD_XY_FILT_HZ 5.000000
|
366 |
+
FHLD_XY_I 0.300000
|
367 |
+
FHLD_XY_IMAX 3000.000000
|
368 |
+
FHLD_XY_P 0.200000
|
369 |
+
FILT1_TYPE 0
|
370 |
+
FILT2_TYPE 0
|
371 |
+
FILT3_TYPE 0
|
372 |
+
FILT4_TYPE 0
|
373 |
+
FILT5_TYPE 0
|
374 |
+
FILT6_TYPE 0
|
375 |
+
FILT7_TYPE 0
|
376 |
+
FILT8_TYPE 0
|
377 |
+
FLIGHT_OPTIONS 0
|
378 |
+
FLOW_TYPE 0
|
379 |
+
FLTMODE1 7
|
380 |
+
FLTMODE2 9
|
381 |
+
FLTMODE3 6
|
382 |
+
FLTMODE4 3
|
383 |
+
FLTMODE5 5
|
384 |
+
FLTMODE6 0
|
385 |
+
FLTMODE_CH 5
|
386 |
+
FLTMODE_GCSBLOCK 0
|
387 |
+
FOLL_ENABLE 0
|
388 |
+
FORMAT_VERSION 120
|
389 |
+
FRAME_CLASS 1
|
390 |
+
FRAME_TYPE 0
|
391 |
+
FRSKY_DNLINK1_ID 20
|
392 |
+
FRSKY_DNLINK2_ID 7
|
393 |
+
FRSKY_DNLINK_ID 27
|
394 |
+
FRSKY_OPTIONS 0
|
395 |
+
FRSKY_UPLINK_ID 13
|
396 |
+
FSTRATE_DIV 1
|
397 |
+
FSTRATE_ENABLE 0
|
398 |
+
FS_CRASH_CHECK 1
|
399 |
+
FS_DR_ENABLE 2
|
400 |
+
FS_DR_TIMEOUT 30
|
401 |
+
FS_EKF_ACTION 1
|
402 |
+
FS_EKF_FILT 5.000000
|
403 |
+
FS_EKF_THRESH 0.800000
|
404 |
+
FS_GCS_ENABLE 0
|
405 |
+
FS_GCS_TIMEOUT 5.000000
|
406 |
+
FS_OPTIONS 16
|
407 |
+
FS_THR_ENABLE 1
|
408 |
+
FS_THR_VALUE 975
|
409 |
+
FS_VIBE_ENABLE 1
|
410 |
+
GCS_PID_MASK 0
|
411 |
+
GEN_TYPE 0
|
412 |
+
GND_EFFECT_COMP 1
|
413 |
+
GPS1_CAN_NODEID 0
|
414 |
+
GPS1_CAN_OVRIDE 0
|
415 |
+
GPS1_COM_PORT 1
|
416 |
+
GPS1_DELAY_MS 0
|
417 |
+
GPS1_GNSS_MODE 0
|
418 |
+
GPS1_MB_TYPE 0
|
419 |
+
GPS1_POS_X 0.000000
|
420 |
+
GPS1_POS_Y 0.000000
|
421 |
+
GPS1_POS_Z 0.000000
|
422 |
+
GPS1_RATE_MS 200
|
423 |
+
GPS1_TYPE 1
|
424 |
+
GPS2_TYPE 0
|
425 |
+
GPS_AUTO_CONFIG 1
|
426 |
+
GPS_AUTO_SWITCH 1
|
427 |
+
GPS_BLEND_MASK 5
|
428 |
+
GPS_DRV_OPTIONS 0
|
429 |
+
GPS_HDOP_GOOD 140
|
430 |
+
GPS_INJECT_TO 127
|
431 |
+
GPS_MIN_ELEV -100
|
432 |
+
GPS_NAVFILTER 8
|
433 |
+
GPS_PRIMARY 0
|
434 |
+
GPS_RAW_DATA 0
|
435 |
+
GPS_SAVE_CFG 2
|
436 |
+
GPS_SBAS_MODE 2
|
437 |
+
GPS_SBP_LOGMASK -256
|
438 |
+
GRIP_ENABLE 0
|
439 |
+
GUID_OPTIONS 0
|
440 |
+
GUID_TIMEOUT 3.000000
|
441 |
+
INITIAL_MODE 0
|
442 |
+
INS_ACC1_CALTEMP -300.000000
|
443 |
+
INS_ACC2OFFS_X 0.001000
|
444 |
+
INS_ACC2OFFS_Y 0.001000
|
445 |
+
INS_ACC2OFFS_Z 0.001000
|
446 |
+
INS_ACC2SCAL_X 1.001000
|
447 |
+
INS_ACC2SCAL_Y 1.001000
|
448 |
+
INS_ACC2SCAL_Z 1.001000
|
449 |
+
INS_ACC2_CALTEMP -300.000000
|
450 |
+
INS_ACC2_ID 0
|
451 |
+
INS_ACC3OFFS_X 0.000000
|
452 |
+
INS_ACC3OFFS_Y 0.000000
|
453 |
+
INS_ACC3OFFS_Z 0.000000
|
454 |
+
INS_ACC3SCAL_X 1.000000
|
455 |
+
INS_ACC3SCAL_Y 1.000000
|
456 |
+
INS_ACC3SCAL_Z 1.000000
|
457 |
+
INS_ACC3_CALTEMP -300.000000
|
458 |
+
INS_ACC3_ID 0
|
459 |
+
INS_ACCEL_FILTER 20
|
460 |
+
INS_ACCOFFS_X 0.001000
|
461 |
+
INS_ACCOFFS_Y 0.001000
|
462 |
+
INS_ACCOFFS_Z 0.001000
|
463 |
+
INS_ACCSCAL_X 1.001000
|
464 |
+
INS_ACCSCAL_Y 1.001000
|
465 |
+
INS_ACCSCAL_Z 1.001000
|
466 |
+
INS_ACC_BODYFIX 2
|
467 |
+
INS_ACC_ID 0
|
468 |
+
INS_ENABLE_MASK 127
|
469 |
+
INS_FAST_SAMPLE 1
|
470 |
+
INS_GYR1_CALTEMP -300.000000
|
471 |
+
INS_GYR2OFFS_X 0.000000
|
472 |
+
INS_GYR2OFFS_Y 0.000000
|
473 |
+
INS_GYR2OFFS_Z 0.000000
|
474 |
+
INS_GYR2_CALTEMP -300.000000
|
475 |
+
INS_GYR2_ID 0
|
476 |
+
INS_GYR3OFFS_X 0.000000
|
477 |
+
INS_GYR3OFFS_Y 0.000000
|
478 |
+
INS_GYR3OFFS_Z 0.000000
|
479 |
+
INS_GYR3_CALTEMP -300.000000
|
480 |
+
INS_GYR3_ID 0
|
481 |
+
INS_GYROFFS_X 0.000000
|
482 |
+
INS_GYROFFS_Y 0.000000
|
483 |
+
INS_GYROFFS_Z 0.000000
|
484 |
+
INS_GYRO_FILTER 20
|
485 |
+
INS_GYRO_RATE 0
|
486 |
+
INS_GYR_CAL 1
|
487 |
+
INS_GYR_ID 0
|
488 |
+
INS_HNTC2_ENABLE 0
|
489 |
+
INS_HNTCH_ENABLE 0
|
490 |
+
INS_LOG_BAT_CNT 1024
|
491 |
+
INS_LOG_BAT_LGCT 32
|
492 |
+
INS_LOG_BAT_LGIN 20
|
493 |
+
INS_LOG_BAT_MASK 0
|
494 |
+
INS_LOG_BAT_OPT 0
|
495 |
+
INS_POS1_X 0.000000
|
496 |
+
INS_POS1_Y 0.000000
|
497 |
+
INS_POS1_Z 0.000000
|
498 |
+
INS_POS2_X 0.000000
|
499 |
+
INS_POS2_Y 0.000000
|
500 |
+
INS_POS2_Z 0.000000
|
501 |
+
INS_POS3_X 0.000000
|
502 |
+
INS_POS3_Y 0.000000
|
503 |
+
INS_POS3_Z 0.000000
|
504 |
+
INS_RAW_LOG_OPT 0
|
505 |
+
INS_STILL_THRESH 2.500000
|
506 |
+
INS_TCAL1_ENABLE 0
|
507 |
+
INS_TCAL2_ENABLE 0
|
508 |
+
INS_TCAL3_ENABLE 0
|
509 |
+
INS_TCAL_OPTIONS 0
|
510 |
+
INS_TRIM_OPTION 1
|
511 |
+
INS_USE 1
|
512 |
+
INS_USE2 1
|
513 |
+
INS_USE3 1
|
514 |
+
KDE_NPOLE 14
|
515 |
+
LAND_ALT_LOW 1000
|
516 |
+
LAND_REPOSITION 1
|
517 |
+
LAND_SPEED 50
|
518 |
+
LAND_SPEED_HIGH 0
|
519 |
+
LGR_ENABLE 0
|
520 |
+
LOG_BACKEND_TYPE 1
|
521 |
+
LOG_BITMASK 180222
|
522 |
+
LOG_BLK_RATEMAX 0.000000
|
523 |
+
LOG_DARM_RATEMAX 0.000000
|
524 |
+
LOG_DISARMED 0
|
525 |
+
LOG_FILE_BUFSIZE 200
|
526 |
+
LOG_FILE_DSRMROT 0
|
527 |
+
LOG_FILE_MB_FREE 500
|
528 |
+
LOG_FILE_RATEMAX 0.000000
|
529 |
+
LOG_FILE_TIMEOUT 5
|
530 |
+
LOG_MAV_BUFSIZE 8
|
531 |
+
LOG_MAV_RATEMAX 0.000000
|
532 |
+
LOG_MAX_FILES 500
|
533 |
+
LOG_REPLAY 0
|
534 |
+
LOIT_ACC_MAX 500.000000
|
535 |
+
LOIT_ANG_MAX 0.000000
|
536 |
+
LOIT_BRK_ACCEL 250.000000
|
537 |
+
LOIT_BRK_DELAY 1.000000
|
538 |
+
LOIT_BRK_JERK 500.000000
|
539 |
+
LOIT_SPEED 1250.000000
|
540 |
+
MAV1_ADSB 0
|
541 |
+
MAV1_EXTRA1 0
|
542 |
+
MAV1_EXTRA2 0
|
543 |
+
MAV1_EXTRA3 0
|
544 |
+
MAV1_EXT_STAT 0
|
545 |
+
MAV1_PARAMS 0
|
546 |
+
MAV1_POSITION 0
|
547 |
+
MAV1_RAW_CTRL 0
|
548 |
+
MAV1_RAW_SENS 0
|
549 |
+
MAV1_RC_CHAN 0
|
550 |
+
MAV2_ADSB 0
|
551 |
+
MAV2_EXTRA1 0
|
552 |
+
MAV2_EXTRA2 0
|
553 |
+
MAV2_EXTRA3 0
|
554 |
+
MAV2_EXT_STAT 0
|
555 |
+
MAV2_PARAMS 0
|
556 |
+
MAV2_POSITION 0
|
557 |
+
MAV2_RAW_CTRL 0
|
558 |
+
MAV2_RAW_SENS 0
|
559 |
+
MAV2_RC_CHAN 0
|
560 |
+
MAV3_ADSB 0
|
561 |
+
MAV3_EXTRA1 0
|
562 |
+
MAV3_EXTRA2 0
|
563 |
+
MAV3_EXTRA3 0
|
564 |
+
MAV3_EXT_STAT 0
|
565 |
+
MAV3_PARAMS 0
|
566 |
+
MAV3_POSITION 0
|
567 |
+
MAV3_RAW_CTRL 0
|
568 |
+
MAV3_RAW_SENS 0
|
569 |
+
MAV3_RC_CHAN 0
|
570 |
+
MAV_GCS_SYSID 255
|
571 |
+
MAV_OPTIONS 0
|
572 |
+
MAV_SYSID 1
|
573 |
+
MAV_TELEM_DELAY 0
|
574 |
+
MIS_OPTIONS 0
|
575 |
+
MIS_RESTART 0
|
576 |
+
MIS_TOTAL 0
|
577 |
+
MNT1_TYPE 0
|
578 |
+
MNT2_TYPE 0
|
579 |
+
MOT_BAT_CURR_MAX 0.000000
|
580 |
+
MOT_BAT_CURR_TC 5.000000
|
581 |
+
MOT_BAT_IDX 0
|
582 |
+
MOT_BAT_VOLT_MAX 12.800000
|
583 |
+
MOT_BAT_VOLT_MIN 9.600000
|
584 |
+
MOT_BOOST_SCALE 0.000000
|
585 |
+
MOT_HOVER_LEARN 2
|
586 |
+
MOT_OPTIONS 0
|
587 |
+
MOT_PWM_MAX 2000
|
588 |
+
MOT_PWM_MIN 1000
|
589 |
+
MOT_PWM_TYPE 0
|
590 |
+
MOT_SAFE_DISARM 0
|
591 |
+
MOT_SAFE_TIME 1.000000
|
592 |
+
MOT_SLEW_DN_TIME 0.000000
|
593 |
+
MOT_SLEW_UP_TIME 0.000000
|
594 |
+
MOT_SPIN_ARM 0.100000
|
595 |
+
MOT_SPIN_MAX 0.950000
|
596 |
+
MOT_SPIN_MIN 0.150000
|
597 |
+
MOT_SPOOL_TIME 0.500000
|
598 |
+
MOT_SPOOL_TIM_DN 0.000000
|
599 |
+
MOT_THST_EXPO 0.650000
|
600 |
+
MOT_THST_HOVER 0.390000
|
601 |
+
MOT_YAW_HEADROOM 200
|
602 |
+
MSP_OPTIONS 0
|
603 |
+
MSP_OSD_NCELLS 0
|
604 |
+
NET_ENABLE 0
|
605 |
+
NET_P1_TYPE 0
|
606 |
+
NET_P2_TYPE 0
|
607 |
+
NET_P3_TYPE 0
|
608 |
+
NET_P4_TYPE 0
|
609 |
+
NMEA_MSG_EN 3
|
610 |
+
NMEA_RATE_MS 100
|
611 |
+
NTF_BUZZ_ON_LVL 1
|
612 |
+
NTF_BUZZ_PIN -1
|
613 |
+
NTF_BUZZ_TYPES 5
|
614 |
+
NTF_BUZZ_VOLUME 100
|
615 |
+
NTF_DISPLAY_TYPE 0
|
616 |
+
NTF_LED_BRIGHT 3
|
617 |
+
NTF_LED_LEN 1
|
618 |
+
NTF_LED_OVERRIDE 0
|
619 |
+
NTF_LED_TYPES 123079
|
620 |
+
OA_TYPE 0
|
621 |
+
OSD_TYPE 0
|
622 |
+
PHLD_BRAKE_ANGLE 3000
|
623 |
+
PHLD_BRAKE_RATE 8
|
624 |
+
PILOT_ACCEL_Z 250
|
625 |
+
PILOT_SPEED_DN 0
|
626 |
+
PILOT_SPEED_UP 250
|
627 |
+
PILOT_THR_BHV 0
|
628 |
+
PILOT_THR_FILT 0.000000
|
629 |
+
PILOT_TKOFF_ALT 0.000000
|
630 |
+
PILOT_Y_EXPO 0.000000
|
631 |
+
PILOT_Y_RATE 202.500000
|
632 |
+
PILOT_Y_RATE_TC 0.000000
|
633 |
+
PLDP_DELAY 0.000000
|
634 |
+
PLDP_RNG_MAX 0.000000
|
635 |
+
PLDP_SPEED_DN 0.000000
|
636 |
+
PLDP_THRESH 0.900000
|
637 |
+
PLND_ENABLED 0
|
638 |
+
PRX1_TYPE 0
|
639 |
+
PRX2_TYPE 0
|
640 |
+
PRX3_TYPE 0
|
641 |
+
PRX4_TYPE 0
|
642 |
+
PRX5_TYPE 0
|
643 |
+
PRX_ALT_MIN 1.000000
|
644 |
+
PRX_FILT 0.250000
|
645 |
+
PRX_IGN_GND 0
|
646 |
+
PRX_LOG_RAW 0
|
647 |
+
PSC_ACCZ_D 0.000000
|
648 |
+
PSC_ACCZ_D_FF 0.000000
|
649 |
+
PSC_ACCZ_FF 0.000000
|
650 |
+
PSC_ACCZ_FLTD 0.000000
|
651 |
+
PSC_ACCZ_FLTE 20.000000
|
652 |
+
PSC_ACCZ_FLTT 0.000000
|
653 |
+
PSC_ACCZ_I 1.000000
|
654 |
+
PSC_ACCZ_IMAX 800.000000
|
655 |
+
PSC_ACCZ_NEF 0
|
656 |
+
PSC_ACCZ_NTF 0
|
657 |
+
PSC_ACCZ_P 0.500000
|
658 |
+
PSC_ACCZ_PDMX 0.000000
|
659 |
+
PSC_ACCZ_SMAX 0.000000
|
660 |
+
PSC_ANGLE_MAX 0.000000
|
661 |
+
PSC_JERK_XY 5.000000
|
662 |
+
PSC_JERK_Z 5.000000
|
663 |
+
PSC_POSXY_P 1.000000
|
664 |
+
PSC_POSZ_P 1.000000
|
665 |
+
PSC_VELXY_D 0.500000
|
666 |
+
PSC_VELXY_FF 0.000000
|
667 |
+
PSC_VELXY_FLTD 5.000000
|
668 |
+
PSC_VELXY_FLTE 5.000000
|
669 |
+
PSC_VELXY_I 1.000000
|
670 |
+
PSC_VELXY_IMAX 1000.000000
|
671 |
+
PSC_VELXY_P 2.000000
|
672 |
+
PSC_VELZ_D 0.000000
|
673 |
+
PSC_VELZ_FF 0.000000
|
674 |
+
PSC_VELZ_FLTD 5.000000
|
675 |
+
PSC_VELZ_FLTE 5.000000
|
676 |
+
PSC_VELZ_I 0.000000
|
677 |
+
PSC_VELZ_IMAX 1000.000000
|
678 |
+
PSC_VELZ_P 5.000000
|
679 |
+
RALLY_INCL_HOME 1
|
680 |
+
RALLY_LIMIT_KM 0.300000
|
681 |
+
RALLY_TOTAL 0
|
682 |
+
RC10_DZ 0
|
683 |
+
RC10_MAX 1900
|
684 |
+
RC10_MIN 1100
|
685 |
+
RC10_OPTION 0
|
686 |
+
RC10_REVERSED 0
|
687 |
+
RC10_TRIM 1500
|
688 |
+
RC11_DZ 0
|
689 |
+
RC11_MAX 1900
|
690 |
+
RC11_MIN 1100
|
691 |
+
RC11_OPTION 0
|
692 |
+
RC11_REVERSED 0
|
693 |
+
RC11_TRIM 1500
|
694 |
+
RC12_DZ 0
|
695 |
+
RC12_MAX 1900
|
696 |
+
RC12_MIN 1100
|
697 |
+
RC12_OPTION 0
|
698 |
+
RC12_REVERSED 0
|
699 |
+
RC12_TRIM 1500
|
700 |
+
RC13_DZ 0
|
701 |
+
RC13_MAX 1900
|
702 |
+
RC13_MIN 1100
|
703 |
+
RC13_OPTION 0
|
704 |
+
RC13_REVERSED 0
|
705 |
+
RC13_TRIM 1500
|
706 |
+
RC14_DZ 0
|
707 |
+
RC14_MAX 1900
|
708 |
+
RC14_MIN 1100
|
709 |
+
RC14_OPTION 0
|
710 |
+
RC14_REVERSED 0
|
711 |
+
RC14_TRIM 1500
|
712 |
+
RC15_DZ 0
|
713 |
+
RC15_MAX 1900
|
714 |
+
RC15_MIN 1100
|
715 |
+
RC15_OPTION 0
|
716 |
+
RC15_REVERSED 0
|
717 |
+
RC15_TRIM 1500
|
718 |
+
RC16_DZ 0
|
719 |
+
RC16_MAX 1900
|
720 |
+
RC16_MIN 1100
|
721 |
+
RC16_OPTION 0
|
722 |
+
RC16_REVERSED 0
|
723 |
+
RC16_TRIM 1500
|
724 |
+
RC1_DZ 20
|
725 |
+
RC1_MAX 2000
|
726 |
+
RC1_MIN 1000
|
727 |
+
RC1_OPTION 0
|
728 |
+
RC1_REVERSED 0
|
729 |
+
RC1_TRIM 1500
|
730 |
+
RC2_DZ 20
|
731 |
+
RC2_MAX 2000
|
732 |
+
RC2_MIN 1000
|
733 |
+
RC2_OPTION 0
|
734 |
+
RC2_REVERSED 0
|
735 |
+
RC2_TRIM 1500
|
736 |
+
RC3_DZ 30
|
737 |
+
RC3_MAX 2000
|
738 |
+
RC3_MIN 1000
|
739 |
+
RC3_OPTION 0
|
740 |
+
RC3_REVERSED 0
|
741 |
+
RC3_TRIM 1500
|
742 |
+
RC4_DZ 20
|
743 |
+
RC4_MAX 2000
|
744 |
+
RC4_MIN 1000
|
745 |
+
RC4_OPTION 0
|
746 |
+
RC4_REVERSED 0
|
747 |
+
RC4_TRIM 1500
|
748 |
+
RC5_DZ 0
|
749 |
+
RC5_MAX 2000
|
750 |
+
RC5_MIN 1000
|
751 |
+
RC5_OPTION 0
|
752 |
+
RC5_REVERSED 0
|
753 |
+
RC5_TRIM 1500
|
754 |
+
RC6_DZ 0
|
755 |
+
RC6_MAX 2000
|
756 |
+
RC6_MIN 1000
|
757 |
+
RC6_OPTION 0
|
758 |
+
RC6_REVERSED 0
|
759 |
+
RC6_TRIM 1500
|
760 |
+
RC7_DZ 0
|
761 |
+
RC7_MAX 2000
|
762 |
+
RC7_MIN 1000
|
763 |
+
RC7_OPTION 7
|
764 |
+
RC7_REVERSED 0
|
765 |
+
RC7_TRIM 1500
|
766 |
+
RC8_DZ 0
|
767 |
+
RC8_MAX 2000
|
768 |
+
RC8_MIN 1000
|
769 |
+
RC8_OPTION 0
|
770 |
+
RC8_REVERSED 0
|
771 |
+
RC8_TRIM 1500
|
772 |
+
RC9_DZ 0
|
773 |
+
RC9_MAX 1900
|
774 |
+
RC9_MIN 1100
|
775 |
+
RC9_OPTION 0
|
776 |
+
RC9_REVERSED 0
|
777 |
+
RC9_TRIM 1500
|
778 |
+
RCMAP_PITCH 2
|
779 |
+
RCMAP_ROLL 1
|
780 |
+
RCMAP_THROTTLE 3
|
781 |
+
RCMAP_YAW 4
|
782 |
+
RC_FS_TIMEOUT 1.000000
|
783 |
+
RC_OPTIONS 32
|
784 |
+
RC_OVERRIDE_TIME 3.000000
|
785 |
+
RC_PROTOCOLS 1
|
786 |
+
RC_SPEED 490
|
787 |
+
RELAY1_DEFAULT 0
|
788 |
+
RELAY1_FUNCTION 1
|
789 |
+
RELAY1_INVERTED 0
|
790 |
+
RELAY1_PIN 13
|
791 |
+
RELAY2_FUNCTION 0
|
792 |
+
RELAY3_FUNCTION 0
|
793 |
+
RELAY4_FUNCTION 0
|
794 |
+
RELAY5_FUNCTION 0
|
795 |
+
RELAY6_FUNCTION 0
|
796 |
+
RNGFND1_TYPE 0
|
797 |
+
RNGFND2_TYPE 0
|
798 |
+
RNGFND3_TYPE 0
|
799 |
+
RNGFND4_TYPE 0
|
800 |
+
RNGFND5_TYPE 0
|
801 |
+
RNGFND6_TYPE 0
|
802 |
+
RNGFND7_TYPE 0
|
803 |
+
RNGFND8_TYPE 0
|
804 |
+
RNGFND9_TYPE 0
|
805 |
+
RNGFNDA_TYPE 0
|
806 |
+
RNGFND_FILT 0.500000
|
807 |
+
RPM1_TYPE 0
|
808 |
+
RPM2_TYPE 0
|
809 |
+
RSSI_TYPE 0
|
810 |
+
RTL_ALT 1500
|
811 |
+
RTL_ALT_FINAL 0
|
812 |
+
RTL_ALT_TYPE 0
|
813 |
+
RTL_CLIMB_MIN 0
|
814 |
+
RTL_CONE_SLOPE 3.000000
|
815 |
+
RTL_LOIT_TIME 5000
|
816 |
+
RTL_OPTIONS 0
|
817 |
+
RTL_SPEED 0
|
818 |
+
SCHED_DEBUG 0
|
819 |
+
SCHED_LOOP_RATE 400
|
820 |
+
SCHED_OPTIONS 0
|
821 |
+
SCR_ENABLE 0
|
822 |
+
SERIAL0_BAUD 115
|
823 |
+
SERIAL0_PROTOCOL 2
|
824 |
+
SERIAL1_BAUD 57
|
825 |
+
SERIAL1_OPTIONS 0
|
826 |
+
SERIAL1_PROTOCOL 2
|
827 |
+
SERIAL2_BAUD 57
|
828 |
+
SERIAL2_OPTIONS 0
|
829 |
+
SERIAL2_PROTOCOL 2
|
830 |
+
SERIAL3_BAUD 230
|
831 |
+
SERIAL3_OPTIONS 0
|
832 |
+
SERIAL3_PROTOCOL 5
|
833 |
+
SERIAL4_BAUD 230
|
834 |
+
SERIAL4_OPTIONS 0
|
835 |
+
SERIAL4_PROTOCOL 5
|
836 |
+
SERIAL5_BAUD 57
|
837 |
+
SERIAL5_OPTIONS 0
|
838 |
+
SERIAL5_PROTOCOL -1
|
839 |
+
SERIAL6_BAUD 57
|
840 |
+
SERIAL6_OPTIONS 0
|
841 |
+
SERIAL6_PROTOCOL -1
|
842 |
+
SERIAL7_BAUD 57
|
843 |
+
SERIAL7_OPTIONS 0
|
844 |
+
SERIAL7_PROTOCOL -1
|
845 |
+
SERIAL_PASS1 0
|
846 |
+
SERIAL_PASS2 -1
|
847 |
+
SERIAL_PASSTIMO 15
|
848 |
+
SERVO10_FUNCTION 0
|
849 |
+
SERVO10_MAX 1900
|
850 |
+
SERVO10_MIN 1100
|
851 |
+
SERVO10_REVERSED 0
|
852 |
+
SERVO10_TRIM 1500
|
853 |
+
SERVO11_FUNCTION 0
|
854 |
+
SERVO11_MAX 1900
|
855 |
+
SERVO11_MIN 1100
|
856 |
+
SERVO11_REVERSED 0
|
857 |
+
SERVO11_TRIM 1500
|
858 |
+
SERVO12_FUNCTION 0
|
859 |
+
SERVO12_MAX 1900
|
860 |
+
SERVO12_MIN 1100
|
861 |
+
SERVO12_REVERSED 0
|
862 |
+
SERVO12_TRIM 1500
|
863 |
+
SERVO13_FUNCTION 0
|
864 |
+
SERVO13_MAX 1900
|
865 |
+
SERVO13_MIN 1100
|
866 |
+
SERVO13_REVERSED 0
|
867 |
+
SERVO13_TRIM 1500
|
868 |
+
SERVO14_FUNCTION 0
|
869 |
+
SERVO14_MAX 1900
|
870 |
+
SERVO14_MIN 1100
|
871 |
+
SERVO14_REVERSED 0
|
872 |
+
SERVO14_TRIM 1500
|
873 |
+
SERVO15_FUNCTION 0
|
874 |
+
SERVO15_MAX 1900
|
875 |
+
SERVO15_MIN 1100
|
876 |
+
SERVO15_REVERSED 0
|
877 |
+
SERVO15_TRIM 1500
|
878 |
+
SERVO16_FUNCTION 0
|
879 |
+
SERVO16_MAX 1900
|
880 |
+
SERVO16_MIN 1100
|
881 |
+
SERVO16_REVERSED 0
|
882 |
+
SERVO16_TRIM 1500
|
883 |
+
SERVO1_FUNCTION 33
|
884 |
+
SERVO1_MAX 1900
|
885 |
+
SERVO1_MIN 1100
|
886 |
+
SERVO1_REVERSED 0
|
887 |
+
SERVO1_TRIM 1500
|
888 |
+
SERVO2_FUNCTION 34
|
889 |
+
SERVO2_MAX 1900
|
890 |
+
SERVO2_MIN 1100
|
891 |
+
SERVO2_REVERSED 0
|
892 |
+
SERVO2_TRIM 1500
|
893 |
+
SERVO3_FUNCTION 35
|
894 |
+
SERVO3_MAX 1900
|
895 |
+
SERVO3_MIN 1100
|
896 |
+
SERVO3_REVERSED 0
|
897 |
+
SERVO3_TRIM 1500
|
898 |
+
SERVO4_FUNCTION 36
|
899 |
+
SERVO4_MAX 1900
|
900 |
+
SERVO4_MIN 1100
|
901 |
+
SERVO4_REVERSED 0
|
902 |
+
SERVO4_TRIM 1500
|
903 |
+
SERVO5_FUNCTION 0
|
904 |
+
SERVO5_MAX 1900
|
905 |
+
SERVO5_MIN 1100
|
906 |
+
SERVO5_REVERSED 0
|
907 |
+
SERVO5_TRIM 1500
|
908 |
+
SERVO6_FUNCTION 0
|
909 |
+
SERVO6_MAX 1900
|
910 |
+
SERVO6_MIN 1100
|
911 |
+
SERVO6_REVERSED 0
|
912 |
+
SERVO6_TRIM 1500
|
913 |
+
SERVO7_FUNCTION 0
|
914 |
+
SERVO7_MAX 1900
|
915 |
+
SERVO7_MIN 1100
|
916 |
+
SERVO7_REVERSED 0
|
917 |
+
SERVO7_TRIM 1500
|
918 |
+
SERVO8_FUNCTION 0
|
919 |
+
SERVO8_MAX 1900
|
920 |
+
SERVO8_MIN 1100
|
921 |
+
SERVO8_REVERSED 0
|
922 |
+
SERVO8_TRIM 1500
|
923 |
+
SERVO9_FUNCTION 0
|
924 |
+
SERVO9_MAX 1900
|
925 |
+
SERVO9_MIN 1100
|
926 |
+
SERVO9_REVERSED 0
|
927 |
+
SERVO9_TRIM 1500
|
928 |
+
SERVO_32_ENABLE 0
|
929 |
+
SERVO_DSHOT_ESC 0
|
930 |
+
SERVO_DSHOT_RATE 0
|
931 |
+
SERVO_FTW_MASK 0
|
932 |
+
SERVO_FTW_POLES 14
|
933 |
+
SERVO_FTW_RVMASK 0
|
934 |
+
SERVO_GPIO_MASK 0
|
935 |
+
SERVO_RATE 50
|
936 |
+
SERVO_RC_FS_MSK 0
|
937 |
+
SERVO_ROB_POSMAX 4095
|
938 |
+
SERVO_ROB_POSMIN 0
|
939 |
+
SERVO_SBUS_RATE 50
|
940 |
+
SERVO_VOLZ_MASK 0
|
941 |
+
SERVO_VOLZ_RANGE 200
|
942 |
+
SID_AXIS 0
|
943 |
+
SIMPLE 0
|
944 |
+
SIM_ACC1_BIAS_X 0.000000
|
945 |
+
SIM_ACC1_BIAS_Y 0.000000
|
946 |
+
SIM_ACC1_BIAS_Z 0.000000
|
947 |
+
SIM_ACC1_RND 0.000000
|
948 |
+
SIM_ACC1_SCAL_X 0.000000
|
949 |
+
SIM_ACC1_SCAL_Y 0.000000
|
950 |
+
SIM_ACC1_SCAL_Z 0.000000
|
951 |
+
SIM_ACC2_BIAS_X 0.000000
|
952 |
+
SIM_ACC2_BIAS_Y 0.000000
|
953 |
+
SIM_ACC2_BIAS_Z 0.000000
|
954 |
+
SIM_ACC2_RND 0.000000
|
955 |
+
SIM_ACC2_SCAL_X 0.000000
|
956 |
+
SIM_ACC2_SCAL_Y 0.000000
|
957 |
+
SIM_ACC2_SCAL_Z 0.000000
|
958 |
+
SIM_ACC3_BIAS_X 0.000000
|
959 |
+
SIM_ACC3_BIAS_Y 0.000000
|
960 |
+
SIM_ACC3_BIAS_Z 0.000000
|
961 |
+
SIM_ACC3_RND 0.000000
|
962 |
+
SIM_ACC3_SCAL_X 0.000000
|
963 |
+
SIM_ACC3_SCAL_Y 0.000000
|
964 |
+
SIM_ACC3_SCAL_Z 0.000000
|
965 |
+
SIM_ACCEL1_FAIL 0.000000
|
966 |
+
SIM_ACCEL2_FAIL 0.000000
|
967 |
+
SIM_ACCEL3_FAIL 0.000000
|
968 |
+
SIM_ACC_FAIL_MSK 0
|
969 |
+
SIM_ACC_FILE_RW 0
|
970 |
+
SIM_ACC_TRIM_X 0.000000
|
971 |
+
SIM_ACC_TRIM_Y 0.000000
|
972 |
+
SIM_ACC_TRIM_Z 0.000000
|
973 |
+
SIM_ADSB_ALT 1000.000000
|
974 |
+
SIM_ADSB_COUNT -1
|
975 |
+
SIM_ADSB_RADIUS 10000.000000
|
976 |
+
SIM_ADSB_TX 0
|
977 |
+
SIM_ADSB_TYPES 1
|
978 |
+
SIM_ARSPD2_FAIL 0.000000
|
979 |
+
SIM_ARSPD2_FAILP 0.000000
|
980 |
+
SIM_ARSPD2_OFS 2013.000000
|
981 |
+
SIM_ARSPD2_PITOT 0.000000
|
982 |
+
SIM_ARSPD2_RATIO 1.990000
|
983 |
+
SIM_ARSPD2_RND 2.000000
|
984 |
+
SIM_ARSPD2_SIGN 0
|
985 |
+
SIM_ARSPD_FAIL 0.000000
|
986 |
+
SIM_ARSPD_FAILP 0.000000
|
987 |
+
SIM_ARSPD_OFS 2013.000000
|
988 |
+
SIM_ARSPD_PITOT 0.000000
|
989 |
+
SIM_ARSPD_RATIO 1.990000
|
990 |
+
SIM_ARSPD_RND 2.000000
|
991 |
+
SIM_ARSPD_SIGN 0
|
992 |
+
SIM_BAR2_DELAY 0
|
993 |
+
SIM_BAR2_DISABLE 0
|
994 |
+
SIM_BAR2_DRIFT 0.000000
|
995 |
+
SIM_BAR2_FREEZE 0
|
996 |
+
SIM_BAR2_GLITCH 0.000000
|
997 |
+
SIM_BAR2_RND 0.200000
|
998 |
+
SIM_BAR2_WCF_BAK 0.000000
|
999 |
+
SIM_BAR2_WCF_DN 0.000000
|
1000 |
+
SIM_BAR2_WCF_FWD 0.000000
|
1001 |
+
SIM_BAR2_WCF_LFT 0.000000
|
1002 |
+
SIM_BAR2_WCF_RGT 0.000000
|
1003 |
+
SIM_BAR2_WCF_UP 0.000000
|
1004 |
+
SIM_BAR3_DELAY 0
|
1005 |
+
SIM_BAR3_DISABLE 0
|
1006 |
+
SIM_BAR3_DRIFT 0.000000
|
1007 |
+
SIM_BAR3_FREEZE 0
|
1008 |
+
SIM_BAR3_GLITCH 0.000000
|
1009 |
+
SIM_BAR3_RND 0.200000
|
1010 |
+
SIM_BAR3_WCF_BAK 0.000000
|
1011 |
+
SIM_BAR3_WCF_DN 0.000000
|
1012 |
+
SIM_BAR3_WCF_FWD 0.000000
|
1013 |
+
SIM_BAR3_WCF_LFT 0.000000
|
1014 |
+
SIM_BAR3_WCF_RGT 0.000000
|
1015 |
+
SIM_BAR3_WCF_UP 0.000000
|
1016 |
+
SIM_BARO_COUNT 2
|
1017 |
+
SIM_BARO_DELAY 0
|
1018 |
+
SIM_BARO_DISABLE 0
|
1019 |
+
SIM_BARO_DRIFT 0.000000
|
1020 |
+
SIM_BARO_FREEZE 0
|
1021 |
+
SIM_BARO_GLITCH 0.000000
|
1022 |
+
SIM_BARO_RND 0.000000
|
1023 |
+
SIM_BARO_WCF_BAK 0.000000
|
1024 |
+
SIM_BARO_WCF_DN 0.000000
|
1025 |
+
SIM_BARO_WCF_FWD 0.000000
|
1026 |
+
SIM_BARO_WCF_LFT 0.000000
|
1027 |
+
SIM_BARO_WCF_RGT 0.000000
|
1028 |
+
SIM_BARO_WCF_UP 0.000000
|
1029 |
+
SIM_BATT_CAP_AH 0.000000
|
1030 |
+
SIM_BATT_VOLTAGE 12.600000
|
1031 |
+
SIM_BAUDLIMIT_EN 0
|
1032 |
+
SIM_CAN_SRV_MSK 0
|
1033 |
+
SIM_CAN_TYPE1 1
|
1034 |
+
SIM_CAN_TYPE2 1
|
1035 |
+
SIM_CLAMP_CH 0
|
1036 |
+
SIM_DRIFT_SPEED 0.050000
|
1037 |
+
SIM_DRIFT_TIME 5.000000
|
1038 |
+
SIM_EFI_TYPE 0
|
1039 |
+
SIM_ENGINE_FAIL 0
|
1040 |
+
SIM_ENGINE_MUL 0.000000
|
1041 |
+
SIM_ESC_ARM_RPM 0.000000
|
1042 |
+
SIM_ESC_TELEM 1
|
1043 |
+
SIM_FLOAT_EXCEPT 1
|
1044 |
+
SIM_FLOW_DELAY 0
|
1045 |
+
SIM_FLOW_ENABLE 0
|
1046 |
+
SIM_FLOW_POS_X 0.000000
|
1047 |
+
SIM_FLOW_POS_Y 0.000000
|
1048 |
+
SIM_FLOW_POS_Z 0.000000
|
1049 |
+
SIM_FLOW_RATE 10
|
1050 |
+
SIM_FLOW_RND 0.050000
|
1051 |
+
SIM_FTOWESC_ENA 0
|
1052 |
+
SIM_FTOWESC_POW 4095
|
1053 |
+
SIM_GND_BEHAV -1
|
1054 |
+
SIM_GPS1_ACC 0.300000
|
1055 |
+
SIM_GPS1_ALT_OFS 0
|
1056 |
+
SIM_GPS1_BYTELOS 0.000000
|
1057 |
+
SIM_GPS1_DRFTALT 0.000000
|
1058 |
+
SIM_GPS1_ENABLE 1
|
1059 |
+
SIM_GPS1_GLTCH_X 0.000000
|
1060 |
+
SIM_GPS1_GLTCH_Y 0.000000
|
1061 |
+
SIM_GPS1_GLTCH_Z 0.000000
|
1062 |
+
SIM_GPS1_HDG 0
|
1063 |
+
SIM_GPS1_HZ 5
|
1064 |
+
SIM_GPS1_JAM 0
|
1065 |
+
SIM_GPS1_LAG_MS 100
|
1066 |
+
SIM_GPS1_LCKTIME 0
|
1067 |
+
SIM_GPS1_NOISE 0.000000
|
1068 |
+
SIM_GPS1_NUMSATS 10
|
1069 |
+
SIM_GPS1_POS_X 0.000000
|
1070 |
+
SIM_GPS1_POS_Y 0.000000
|
1071 |
+
SIM_GPS1_POS_Z 0.000000
|
1072 |
+
SIM_GPS1_TYPE 1
|
1073 |
+
SIM_GPS1_VERR_X 0.000000
|
1074 |
+
SIM_GPS1_VERR_Y 0.000000
|
1075 |
+
SIM_GPS1_VERR_Z 0.000000
|
1076 |
+
SIM_GPS2_ENABLE 0
|
1077 |
+
SIM_GPS3_ENABLE 0
|
1078 |
+
SIM_GPS4_ENABLE 0
|
1079 |
+
SIM_GPS_LOG_NUM 0
|
1080 |
+
SIM_GRPE_ENABLE 0
|
1081 |
+
SIM_GRPE_PIN -1
|
1082 |
+
SIM_GRPS_ENABLE 0
|
1083 |
+
SIM_GRPS_GRAB 2000
|
1084 |
+
SIM_GRPS_PIN -1
|
1085 |
+
SIM_GRPS_RELEASE 1000
|
1086 |
+
SIM_GRPS_REVERSE 0
|
1087 |
+
SIM_GYR1_BIAS_X 0.000000
|
1088 |
+
SIM_GYR1_BIAS_Y 0.000000
|
1089 |
+
SIM_GYR1_BIAS_Z 0.000000
|
1090 |
+
SIM_GYR1_RND 0.000000
|
1091 |
+
SIM_GYR1_SCALE_X 0.000000
|
1092 |
+
SIM_GYR1_SCALE_Y 0.000000
|
1093 |
+
SIM_GYR1_SCALE_Z 0.000000
|
1094 |
+
SIM_GYR2_BIAS_X 0.000000
|
1095 |
+
SIM_GYR2_BIAS_Y 0.000000
|
1096 |
+
SIM_GYR2_BIAS_Z 0.000000
|
1097 |
+
SIM_GYR2_RND 0.000000
|
1098 |
+
SIM_GYR2_SCALE_X 0.000000
|
1099 |
+
SIM_GYR2_SCALE_Y 0.000000
|
1100 |
+
SIM_GYR2_SCALE_Z 0.000000
|
1101 |
+
SIM_GYR3_BIAS_X 0.000000
|
1102 |
+
SIM_GYR3_BIAS_Y 0.000000
|
1103 |
+
SIM_GYR3_BIAS_Z 0.000000
|
1104 |
+
SIM_GYR3_RND 0.000000
|
1105 |
+
SIM_GYR3_SCALE_X 0.000000
|
1106 |
+
SIM_GYR3_SCALE_Y 0.000000
|
1107 |
+
SIM_GYR3_SCALE_Z 0.000000
|
1108 |
+
SIM_GYR_FAIL_MSK 0
|
1109 |
+
SIM_GYR_FILE_RW 0
|
1110 |
+
SIM_IE24_ENABLE 0
|
1111 |
+
SIM_IE24_ERROR 0
|
1112 |
+
SIM_IE24_STATE -1
|
1113 |
+
SIM_IMUT1_ENABLE 0
|
1114 |
+
SIM_IMUT2_ENABLE 0
|
1115 |
+
SIM_IMUT3_ENABLE 0
|
1116 |
+
SIM_IMUT_END 45.000000
|
1117 |
+
SIM_IMUT_FIXED 0.000000
|
1118 |
+
SIM_IMUT_START 25.000000
|
1119 |
+
SIM_IMUT_TCONST 300.000000
|
1120 |
+
SIM_IMU_COUNT 2
|
1121 |
+
SIM_IMU_ORIENT 0
|
1122 |
+
SIM_IMU_POS_X 0.000000
|
1123 |
+
SIM_IMU_POS_Y 0.000000
|
1124 |
+
SIM_IMU_POS_Z 0.000000
|
1125 |
+
SIM_INIT_ALT_OFS 0.000000
|
1126 |
+
SIM_INIT_LAT_OFS 0.000000
|
1127 |
+
SIM_INIT_LON_OFS 0.000000
|
1128 |
+
SIM_INS_THR_MIN 0.100000
|
1129 |
+
SIM_JSON_MASTER 0
|
1130 |
+
SIM_LED_LAYOUT 0
|
1131 |
+
SIM_LOOP_DELAY 0
|
1132 |
+
SIM_MAG1_DEVID 97539
|
1133 |
+
SIM_MAG1_DIA_X 0.000000
|
1134 |
+
SIM_MAG1_DIA_Y 0.000000
|
1135 |
+
SIM_MAG1_DIA_Z 0.000000
|
1136 |
+
SIM_MAG1_FAIL 0
|
1137 |
+
SIM_MAG1_ODI_X 0.000000
|
1138 |
+
SIM_MAG1_ODI_Y 0.000000
|
1139 |
+
SIM_MAG1_ODI_Z 0.000000
|
1140 |
+
SIM_MAG1_OFS_X 5.000000
|
1141 |
+
SIM_MAG1_OFS_Y 13.000000
|
1142 |
+
SIM_MAG1_OFS_Z -18.000000
|
1143 |
+
SIM_MAG1_ORIENT 0
|
1144 |
+
SIM_MAG1_SCALING 1.000000
|
1145 |
+
SIM_MAG2_DEVID 131874
|
1146 |
+
SIM_MAG2_DIA_X 0.000000
|
1147 |
+
SIM_MAG2_DIA_Y 0.000000
|
1148 |
+
SIM_MAG2_DIA_Z 0.000000
|
1149 |
+
SIM_MAG2_FAIL 0
|
1150 |
+
SIM_MAG2_ODI_X 0.000000
|
1151 |
+
SIM_MAG2_ODI_Y 0.000000
|
1152 |
+
SIM_MAG2_ODI_Z 0.000000
|
1153 |
+
SIM_MAG2_OFS_X 5.000000
|
1154 |
+
SIM_MAG2_OFS_Y 13.000000
|
1155 |
+
SIM_MAG2_OFS_Z -18.000000
|
1156 |
+
SIM_MAG2_ORIENT 0
|
1157 |
+
SIM_MAG2_SCALING 1.000000
|
1158 |
+
SIM_MAG3_DEVID 263178
|
1159 |
+
SIM_MAG3_DIA_X 0.000000
|
1160 |
+
SIM_MAG3_DIA_Y 0.000000
|
1161 |
+
SIM_MAG3_DIA_Z 0.000000
|
1162 |
+
SIM_MAG3_FAIL 0
|
1163 |
+
SIM_MAG3_ODI_X 0.000000
|
1164 |
+
SIM_MAG3_ODI_Y 0.000000
|
1165 |
+
SIM_MAG3_ODI_Z 0.000000
|
1166 |
+
SIM_MAG3_OFS_X 5.000000
|
1167 |
+
SIM_MAG3_OFS_Y 13.000000
|
1168 |
+
SIM_MAG3_OFS_Z -18.000000
|
1169 |
+
SIM_MAG3_ORIENT 0
|
1170 |
+
SIM_MAG3_SCALING 1.000000
|
1171 |
+
SIM_MAG4_DEVID 97283
|
1172 |
+
SIM_MAG5_DEVID 97795
|
1173 |
+
SIM_MAG6_DEVID 98051
|
1174 |
+
SIM_MAG7_DEVID 0
|
1175 |
+
SIM_MAG8_DEVID 0
|
1176 |
+
SIM_MAG_ALY_HGT 1.000000
|
1177 |
+
SIM_MAG_ALY_X 0.000000
|
1178 |
+
SIM_MAG_ALY_Y 0.000000
|
1179 |
+
SIM_MAG_ALY_Z 0.000000
|
1180 |
+
SIM_MAG_DELAY 0
|
1181 |
+
SIM_MAG_MOT_X 0.000000
|
1182 |
+
SIM_MAG_MOT_Y 0.000000
|
1183 |
+
SIM_MAG_MOT_Z 0.000000
|
1184 |
+
SIM_MAG_RND 0.000000
|
1185 |
+
SIM_MAG_SAVE_IDS 1
|
1186 |
+
SIM_ODOM_ENABLE 0
|
1187 |
+
SIM_OH_MASK 0
|
1188 |
+
SIM_OH_RELAY_MSK -1
|
1189 |
+
SIM_OPOS_ALT 584.000000
|
1190 |
+
SIM_OPOS_HDG 353.000000
|
1191 |
+
SIM_OPOS_LAT -35.363262
|
1192 |
+
SIM_OPOS_LNG 149.165237
|
1193 |
+
SIM_PARA_ENABLE 0
|
1194 |
+
SIM_PARA_PIN -1
|
1195 |
+
SIM_PIN_MASK 0
|
1196 |
+
SIM_PLD_ALT_LMT 15.000000
|
1197 |
+
SIM_PLD_DIST_LMT 10.000000
|
1198 |
+
SIM_PLD_ENABLE 0
|
1199 |
+
SIM_PLD_HEIGHT 0.000000
|
1200 |
+
SIM_PLD_LAT 0.000000
|
1201 |
+
SIM_PLD_LON 0.000000
|
1202 |
+
SIM_PLD_OPTIONS 0
|
1203 |
+
SIM_PLD_ORIENT 24
|
1204 |
+
SIM_PLD_RATE 100
|
1205 |
+
SIM_PLD_SHIP 0
|
1206 |
+
SIM_PLD_TYPE 0
|
1207 |
+
SIM_PLD_YAW 0
|
1208 |
+
SIM_RATE_HZ 1200
|
1209 |
+
SIM_RC_CHANCOUNT 16
|
1210 |
+
SIM_RC_FAIL 0
|
1211 |
+
SIM_RICH_CTRL -1
|
1212 |
+
SIM_RICH_ENABLE 0
|
1213 |
+
SIM_SERVO_DELAY 0.000000
|
1214 |
+
SIM_SERVO_FILTER 0.000000
|
1215 |
+
SIM_SERVO_SPEED 0.140000
|
1216 |
+
SIM_SHIP_DSIZE 10.000000
|
1217 |
+
SIM_SHIP_ENABLE 0
|
1218 |
+
SIM_SHIP_OFS_X 0.000000
|
1219 |
+
SIM_SHIP_OFS_Y 0.000000
|
1220 |
+
SIM_SHIP_OFS_Z 0.000000
|
1221 |
+
SIM_SHIP_PSIZE 1000.000000
|
1222 |
+
SIM_SHIP_SPEED 3.000000
|
1223 |
+
SIM_SHIP_SYSID 17
|
1224 |
+
SIM_SHOVE_TIME 0
|
1225 |
+
SIM_SHOVE_X 0.000000
|
1226 |
+
SIM_SHOVE_Y 0.000000
|
1227 |
+
SIM_SHOVE_Z 0.000000
|
1228 |
+
SIM_SLUP_ENABLE 0
|
1229 |
+
SIM_SONAR_GLITCH 0.000000
|
1230 |
+
SIM_SONAR_POS_X 0.000000
|
1231 |
+
SIM_SONAR_POS_Y 0.000000
|
1232 |
+
SIM_SONAR_POS_Z 0.000000
|
1233 |
+
SIM_SONAR_RND 0.000000
|
1234 |
+
SIM_SONAR_ROT 25
|
1235 |
+
SIM_SONAR_SCALE 12.121200
|
1236 |
+
SIM_SPEEDUP 1.000000
|
1237 |
+
SIM_SPR_ENABLE 0
|
1238 |
+
SIM_SPR_PUMP -1
|
1239 |
+
SIM_SPR_SPIN -1
|
1240 |
+
SIM_TA_ENABLE 1
|
1241 |
+
SIM_TEMP_BFACTOR 0.000000
|
1242 |
+
SIM_TEMP_BRD_OFF 20.000000
|
1243 |
+
SIM_TEMP_START 25.000000
|
1244 |
+
SIM_TEMP_TCONST 30.000000
|
1245 |
+
SIM_TERRAIN 1
|
1246 |
+
SIM_TETH_ENABLE 0
|
1247 |
+
SIM_THML_SCENARI 0
|
1248 |
+
SIM_TIDE_DIR 0.000000
|
1249 |
+
SIM_TIDE_SPEED 0.000000
|
1250 |
+
SIM_TIME_JITTER 0
|
1251 |
+
SIM_TWIST_TIME 0
|
1252 |
+
SIM_TWIST_X 0.000000
|
1253 |
+
SIM_TWIST_Y 0.000000
|
1254 |
+
SIM_TWIST_Z 0.000000
|
1255 |
+
SIM_UART_LOSS 0.000000
|
1256 |
+
SIM_VIB_FREQ_X 0.000000
|
1257 |
+
SIM_VIB_FREQ_Y 0.000000
|
1258 |
+
SIM_VIB_FREQ_Z 0.000000
|
1259 |
+
SIM_VIB_MOT_HMNC 1
|
1260 |
+
SIM_VIB_MOT_MASK 0
|
1261 |
+
SIM_VIB_MOT_MAX 0.000000
|
1262 |
+
SIM_VIB_MOT_MULT 1.000000
|
1263 |
+
SIM_VICON_FAIL 0
|
1264 |
+
SIM_VICON_GLIT_X 0.000000
|
1265 |
+
SIM_VICON_GLIT_Y 0.000000
|
1266 |
+
SIM_VICON_GLIT_Z 0.000000
|
1267 |
+
SIM_VICON_POS_X 0.000000
|
1268 |
+
SIM_VICON_POS_Y 0.000000
|
1269 |
+
SIM_VICON_POS_Z 0.000000
|
1270 |
+
SIM_VICON_P_SD 0.000000
|
1271 |
+
SIM_VICON_RATE 50
|
1272 |
+
SIM_VICON_TMASK 3
|
1273 |
+
SIM_VICON_VGLI_X 0.000000
|
1274 |
+
SIM_VICON_VGLI_Y 0.000000
|
1275 |
+
SIM_VICON_VGLI_Z 0.000000
|
1276 |
+
SIM_VICON_V_SD 0.000000
|
1277 |
+
SIM_VICON_YAW 0
|
1278 |
+
SIM_VICON_YAWERR 0
|
1279 |
+
SIM_VOLZ_ENA 0
|
1280 |
+
SIM_VOLZ_FMASK 0
|
1281 |
+
SIM_VOLZ_MASK 11
|
1282 |
+
SIM_WAVE_AMP 0.500000
|
1283 |
+
SIM_WAVE_DIR 0.000000
|
1284 |
+
SIM_WAVE_ENABLE 0
|
1285 |
+
SIM_WAVE_LENGTH 10.000000
|
1286 |
+
SIM_WAVE_SPEED 0.500000
|
1287 |
+
SIM_WIND_DIR 180.000000
|
1288 |
+
SIM_WIND_DIR_Z 0.000000
|
1289 |
+
SIM_WIND_SPD 0.000000
|
1290 |
+
SIM_WIND_T 0
|
1291 |
+
SIM_WIND_TC 5.000000
|
1292 |
+
SIM_WIND_TURB 0.000000
|
1293 |
+
SIM_WIND_T_ALT 60.000000
|
1294 |
+
SIM_WIND_T_COEF 0.010000
|
1295 |
+
SIM_WOW_PIN -1
|
1296 |
+
SPRAY_ENABLE 0
|
1297 |
+
SRTL_ACCURACY 2.000000
|
1298 |
+
SRTL_OPTIONS 0
|
1299 |
+
SRTL_POINTS 300
|
1300 |
+
STAT_BOOTCNT 1
|
1301 |
+
STAT_FLTTIME 0
|
1302 |
+
STAT_RESET 1
|
1303 |
+
STAT_RUNTIME 0
|
1304 |
+
SUPER_SIMPLE 0
|
1305 |
+
SURFTRAK_MODE 1
|
1306 |
+
SURFTRAK_TC 1.000000
|
1307 |
+
TCAL_ENABLED 0
|
1308 |
+
TEMP1_TYPE 0
|
1309 |
+
TEMP2_TYPE 0
|
1310 |
+
TEMP3_TYPE 0
|
1311 |
+
TEMP_LOG 0
|
1312 |
+
TERRAIN_CACHE_SZ 12
|
1313 |
+
TERRAIN_ENABLE 1
|
1314 |
+
TERRAIN_MARGIN 0.050000
|
1315 |
+
TERRAIN_OFS_MAX 30.000000
|
1316 |
+
TERRAIN_OPTIONS 0
|
1317 |
+
TERRAIN_SPACING 100
|
1318 |
+
THROW_ALT_ACSND 3.000000
|
1319 |
+
THROW_ALT_DCSND 1.000000
|
1320 |
+
THROW_ALT_MAX 0
|
1321 |
+
THROW_ALT_MIN 0
|
1322 |
+
THROW_MOT_START 0
|
1323 |
+
THROW_NEXTMODE 18
|
1324 |
+
THROW_TYPE 0
|
1325 |
+
THR_DZ 100
|
1326 |
+
TKOFF_RPM_MAX 0
|
1327 |
+
TKOFF_RPM_MIN 0
|
1328 |
+
TKOFF_SLEW_TIME 2.000000
|
1329 |
+
TKOFF_THR_MAX 0.900000
|
1330 |
+
TUNE 0
|
1331 |
+
TUNE_MAX 0.000000
|
1332 |
+
TUNE_MIN 0.000000
|
1333 |
+
VISO_TYPE 0
|
1334 |
+
VTX_ENABLE 0
|
1335 |
+
WINCH_TYPE 0
|
1336 |
+
WPNAV_ACCEL 250.000000
|
1337 |
+
WPNAV_ACCEL_C 0.000000
|
1338 |
+
WPNAV_ACCEL_Z 100.000000
|
1339 |
+
WPNAV_JERK 1.000000
|
1340 |
+
WPNAV_RADIUS 200.000000
|
1341 |
+
WPNAV_RFND_USE 1
|
1342 |
+
WPNAV_SPEED 1000.000000
|
1343 |
+
WPNAV_SPEED_DN 150.000000
|
1344 |
+
WPNAV_SPEED_UP 250.000000
|
1345 |
+
WPNAV_TER_MARGIN 10.000000
|
1346 |
+
WP_NAVALT_MIN 0.000000
|
1347 |
+
WP_YAW_BEHAVIOR 2
|
1348 |
+
WVANE_ENABLE 0
|
1349 |
+
ZIGZ_AUTO_ENABLE 0
|
{drone → misc}/drone_chat.py
RENAMED
File without changes
|
{drone → misc}/hf_model.py
RENAMED
File without changes
|
requirements.txt
CHANGED
@@ -1,11 +1,15 @@
|
|
1 |
-
streamlit
|
2 |
pandas
|
3 |
numpy
|
4 |
matplotlib
|
5 |
seaborn
|
6 |
-
smolagents
|
7 |
python-dotenv
|
8 |
-
transformers
|
9 |
-
huggingface-hub
|
10 |
dronekit
|
11 |
pymavlink
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
pandas
|
2 |
numpy
|
3 |
matplotlib
|
4 |
seaborn
|
|
|
5 |
python-dotenv
|
|
|
|
|
6 |
dronekit
|
7 |
pymavlink
|
8 |
+
litellm
|
9 |
+
ollama
|
10 |
+
rich
|
11 |
+
click
|
12 |
+
pydantic
|
13 |
+
pydantic-settings
|
14 |
+
typer
|
15 |
+
prompt-toolkit
|
setup.py
CHANGED
@@ -1,16 +1,38 @@
|
|
1 |
from setuptools import setup, find_packages
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
setup(
|
4 |
name="deepdrone",
|
5 |
-
version="
|
6 |
-
description="DeepDrone - AI-powered drone control
|
|
|
|
|
7 |
author="DeepDrone Team",
|
8 |
packages=find_packages(),
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
"
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
],
|
16 |
)
|
|
|
1 |
from setuptools import setup, find_packages
|
2 |
|
3 |
+
with open("README.md", "r", encoding="utf-8") as fh:
|
4 |
+
long_description = fh.read()
|
5 |
+
|
6 |
+
with open("requirements.txt", "r", encoding="utf-8") as fh:
|
7 |
+
requirements = [line.strip() for line in fh.readlines() if line.strip() and not line.startswith("#")]
|
8 |
+
|
9 |
setup(
|
10 |
name="deepdrone",
|
11 |
+
version="1.0.0",
|
12 |
+
description="DeepDrone - AI-powered drone control terminal application",
|
13 |
+
long_description=long_description,
|
14 |
+
long_description_content_type="text/markdown",
|
15 |
author="DeepDrone Team",
|
16 |
packages=find_packages(),
|
17 |
+
include_package_data=True,
|
18 |
+
install_requires=requirements,
|
19 |
+
entry_points={
|
20 |
+
"console_scripts": [
|
21 |
+
"deepdrone=main:main",
|
22 |
+
],
|
23 |
+
},
|
24 |
+
python_requires=">=3.8",
|
25 |
+
classifiers=[
|
26 |
+
"Development Status :: 4 - Beta",
|
27 |
+
"Intended Audience :: Developers",
|
28 |
+
"License :: OSI Approved :: MIT License",
|
29 |
+
"Operating System :: OS Independent",
|
30 |
+
"Programming Language :: Python :: 3",
|
31 |
+
"Programming Language :: Python :: 3.8",
|
32 |
+
"Programming Language :: Python :: 3.9",
|
33 |
+
"Programming Language :: Python :: 3.10",
|
34 |
+
"Programming Language :: Python :: 3.11",
|
35 |
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
36 |
+
"Topic :: System :: Hardware :: Hardware Drivers",
|
37 |
],
|
38 |
)
|
simulate_drone.py
ADDED
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
Drone SITL (Software In The Loop) Simulator
|
4 |
+
Starts a virtual drone for testing DeepDrone commands.
|
5 |
+
"""
|
6 |
+
|
7 |
+
import subprocess
|
8 |
+
import sys
|
9 |
+
import time
|
10 |
+
import socket
|
11 |
+
import threading
|
12 |
+
from pathlib import Path
|
13 |
+
|
14 |
+
def check_mavproxy_installed():
|
15 |
+
"""Check if MAVProxy is installed."""
|
16 |
+
try:
|
17 |
+
result = subprocess.run(['mavproxy.py', '--help'],
|
18 |
+
capture_output=True, text=True, timeout=5)
|
19 |
+
return result.returncode == 0
|
20 |
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
21 |
+
return False
|
22 |
+
|
23 |
+
def check_ardupilot_installed():
|
24 |
+
"""Check if ArduPilot SITL is available."""
|
25 |
+
try:
|
26 |
+
result = subprocess.run(['sim_vehicle.py', '--help'],
|
27 |
+
capture_output=True, text=True, timeout=5)
|
28 |
+
return result.returncode == 0
|
29 |
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
30 |
+
return False
|
31 |
+
|
32 |
+
def find_available_port(start_port=14550):
|
33 |
+
"""Find an available UDP port starting from start_port."""
|
34 |
+
for port in range(start_port, start_port + 100):
|
35 |
+
try:
|
36 |
+
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
37 |
+
s.bind(('127.0.0.1', port))
|
38 |
+
return port
|
39 |
+
except OSError:
|
40 |
+
continue
|
41 |
+
return None
|
42 |
+
|
43 |
+
def start_simple_sitl(port=14550):
|
44 |
+
"""Start a simple SITL simulation using ArduPilot."""
|
45 |
+
print(f"🚁 Starting ArduPilot SITL on port {port}...")
|
46 |
+
|
47 |
+
try:
|
48 |
+
# Try to start ArduPilot SITL
|
49 |
+
cmd = [
|
50 |
+
'sim_vehicle.py',
|
51 |
+
'-v', 'ArduCopter',
|
52 |
+
'--out', f'udp:127.0.0.1:{port}',
|
53 |
+
'--map',
|
54 |
+
'--console'
|
55 |
+
]
|
56 |
+
|
57 |
+
process = subprocess.Popen(
|
58 |
+
cmd,
|
59 |
+
stdout=subprocess.PIPE,
|
60 |
+
stderr=subprocess.PIPE,
|
61 |
+
text=True
|
62 |
+
)
|
63 |
+
|
64 |
+
return process, port
|
65 |
+
|
66 |
+
except FileNotFoundError:
|
67 |
+
print("❌ ArduPilot SITL not found. Please install ArduPilot.")
|
68 |
+
return None, None
|
69 |
+
|
70 |
+
def start_mavproxy_sitl(port=14550):
|
71 |
+
"""Start SITL using MAVProxy."""
|
72 |
+
print(f"🚁 Starting MAVProxy SITL on port {port}...")
|
73 |
+
|
74 |
+
try:
|
75 |
+
cmd = [
|
76 |
+
'mavproxy.py',
|
77 |
+
'--master', 'tcp:127.0.0.1:5760',
|
78 |
+
'--out', f'udp:127.0.0.1:{port}',
|
79 |
+
'--aircraft', 'test'
|
80 |
+
]
|
81 |
+
|
82 |
+
process = subprocess.Popen(
|
83 |
+
cmd,
|
84 |
+
stdout=subprocess.PIPE,
|
85 |
+
stderr=subprocess.PIPE,
|
86 |
+
text=True
|
87 |
+
)
|
88 |
+
|
89 |
+
return process, port
|
90 |
+
|
91 |
+
except FileNotFoundError:
|
92 |
+
print("❌ MAVProxy not found.")
|
93 |
+
return None, None
|
94 |
+
|
95 |
+
def create_basic_simulator(port=14550):
|
96 |
+
"""Create a very basic drone simulator for testing."""
|
97 |
+
print(f"🚁 Starting basic drone simulator on port {port}...")
|
98 |
+
print("⚠️ This is a minimal simulator for testing purposes only.")
|
99 |
+
|
100 |
+
# Create a simple UDP server that responds to basic MAVLink messages
|
101 |
+
import socket
|
102 |
+
import struct
|
103 |
+
|
104 |
+
def simulator_thread():
|
105 |
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
106 |
+
sock.bind(('127.0.0.1', port))
|
107 |
+
sock.settimeout(1.0)
|
108 |
+
|
109 |
+
print(f"✅ Basic simulator listening on 127.0.0.1:{port}")
|
110 |
+
|
111 |
+
while True:
|
112 |
+
try:
|
113 |
+
data, addr = sock.recvfrom(1024)
|
114 |
+
# Echo back a simple response
|
115 |
+
sock.sendto(data, addr)
|
116 |
+
except socket.timeout:
|
117 |
+
continue
|
118 |
+
except KeyboardInterrupt:
|
119 |
+
break
|
120 |
+
|
121 |
+
sock.close()
|
122 |
+
|
123 |
+
thread = threading.Thread(target=simulator_thread, daemon=True)
|
124 |
+
thread.start()
|
125 |
+
|
126 |
+
return thread, port
|
127 |
+
|
128 |
+
def print_connection_info(port):
|
129 |
+
"""Print connection information."""
|
130 |
+
connection_string = f"udp:127.0.0.1:{port}"
|
131 |
+
|
132 |
+
print("\n" + "="*60)
|
133 |
+
print("🚁 DRONE SIMULATOR STARTED")
|
134 |
+
print("="*60)
|
135 |
+
print(f"📡 Connection String: {connection_string}")
|
136 |
+
print(f"🌐 IP Address: 127.0.0.1")
|
137 |
+
print(f"🔌 Port: {port}")
|
138 |
+
print("="*60)
|
139 |
+
print("\n💡 To connect DeepDrone:")
|
140 |
+
print(f" 1. Run: python main.py")
|
141 |
+
print(f" 2. Choose your AI provider")
|
142 |
+
print(f" 3. In chat, say: 'Connect to {connection_string}'")
|
143 |
+
print("\n🎯 Example commands once connected:")
|
144 |
+
print(" • 'Take off to 30 meters'")
|
145 |
+
print(" • 'Fly in a square pattern'")
|
146 |
+
print(" • 'Show battery status'")
|
147 |
+
print(" • 'Return home and land'")
|
148 |
+
print("\n⚠️ Press Ctrl+C to stop the simulator")
|
149 |
+
print("="*60)
|
150 |
+
|
151 |
+
def main():
|
152 |
+
"""Main function to start the drone simulator."""
|
153 |
+
print("🚁 DeepDrone Simulator Starting...")
|
154 |
+
print("Checking for available drone simulation software...\n")
|
155 |
+
|
156 |
+
# Find available port
|
157 |
+
port = find_available_port()
|
158 |
+
if not port:
|
159 |
+
print("❌ No available ports found. Please check your network configuration.")
|
160 |
+
return
|
161 |
+
|
162 |
+
simulator_process = None
|
163 |
+
simulator_thread = None
|
164 |
+
|
165 |
+
try:
|
166 |
+
# Try ArduPilot SITL first
|
167 |
+
if check_ardupilot_installed():
|
168 |
+
print("✅ ArduPilot SITL found. Starting professional simulation...")
|
169 |
+
simulator_process, port = start_simple_sitl(port)
|
170 |
+
|
171 |
+
if simulator_process:
|
172 |
+
print_connection_info(port)
|
173 |
+
simulator_process.wait()
|
174 |
+
|
175 |
+
elif check_mavproxy_installed():
|
176 |
+
print("✅ MAVProxy found. Starting MAVProxy simulation...")
|
177 |
+
simulator_process, port = start_mavproxy_sitl(port)
|
178 |
+
|
179 |
+
if simulator_process:
|
180 |
+
print_connection_info(port)
|
181 |
+
simulator_process.wait()
|
182 |
+
|
183 |
+
else:
|
184 |
+
print("⚠️ No professional drone simulation software found.")
|
185 |
+
print("Installing ArduPilot SITL is recommended for full simulation.")
|
186 |
+
print("Falling back to basic simulator for testing...")
|
187 |
+
|
188 |
+
simulator_thread, port = create_basic_simulator(port)
|
189 |
+
print_connection_info(port)
|
190 |
+
|
191 |
+
# Keep the basic simulator running
|
192 |
+
try:
|
193 |
+
while True:
|
194 |
+
time.sleep(1)
|
195 |
+
except KeyboardInterrupt:
|
196 |
+
pass
|
197 |
+
|
198 |
+
except KeyboardInterrupt:
|
199 |
+
print("\n🛑 Stopping drone simulator...")
|
200 |
+
|
201 |
+
finally:
|
202 |
+
if simulator_process:
|
203 |
+
simulator_process.terminate()
|
204 |
+
try:
|
205 |
+
simulator_process.wait(timeout=5)
|
206 |
+
except subprocess.TimeoutExpired:
|
207 |
+
simulator_process.kill()
|
208 |
+
|
209 |
+
print("✅ Drone simulator stopped.")
|
210 |
+
|
211 |
+
def install_instructions():
|
212 |
+
"""Print installation instructions for drone simulation software."""
|
213 |
+
print("\n📋 To install professional drone simulation:")
|
214 |
+
print("\n🔧 ArduPilot SITL (Recommended):")
|
215 |
+
print(" git clone https://github.com/ArduPilot/ardupilot.git")
|
216 |
+
print(" cd ardupilot")
|
217 |
+
print(" git submodule update --init --recursive")
|
218 |
+
print(" ./Tools/environment_install/install-prereqs-ubuntu.sh -y")
|
219 |
+
print(" . ~/.profile")
|
220 |
+
print(" ./waf configure --board sitl")
|
221 |
+
print(" ./waf copter")
|
222 |
+
print(" echo 'export PATH=$PATH:$HOME/ardupilot/Tools/autotest' >> ~/.bashrc")
|
223 |
+
print(" source ~/.bashrc")
|
224 |
+
|
225 |
+
print("\n🔧 MAVProxy (Alternative):")
|
226 |
+
print(" pip install MAVProxy")
|
227 |
+
|
228 |
+
print("\n💡 For now, you can use the basic simulator for testing.")
|
229 |
+
|
230 |
+
if __name__ == "__main__":
|
231 |
+
if len(sys.argv) > 1 and sys.argv[1] == "--install-help":
|
232 |
+
install_instructions()
|
233 |
+
else:
|
234 |
+
main()
|
terrain/S36E149.DAT
ADDED
File without changes
|
test_ollama_features.py
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
Test script to demonstrate the improved Ollama features.
|
4 |
+
"""
|
5 |
+
|
6 |
+
from rich.console import Console
|
7 |
+
from rich.panel import Panel
|
8 |
+
|
9 |
+
console = Console()
|
10 |
+
|
11 |
+
def demo_ollama_features():
|
12 |
+
"""Demo the new Ollama features."""
|
13 |
+
|
14 |
+
console.print(Panel(
|
15 |
+
"[bold green]🚀 Enhanced Ollama Integration Demo[/bold green]\n\n"
|
16 |
+
"[dim]This shows the improved Ollama handling in DeepDrone[/dim]",
|
17 |
+
border_style="bright_green",
|
18 |
+
padding=(1, 2)
|
19 |
+
))
|
20 |
+
|
21 |
+
console.print("\n[bold cyan]✨ New Ollama Features:[/bold cyan]\n")
|
22 |
+
|
23 |
+
features = [
|
24 |
+
"🔍 **Auto-Detection**: Automatically finds your local Ollama models",
|
25 |
+
"📥 **Smart Installation**: Offers to install missing models automatically",
|
26 |
+
"🎯 **Popular Suggestions**: Shows recommended models if none are installed",
|
27 |
+
"⚡ **No API Key**: Skips API key entry for local models",
|
28 |
+
"🔧 **Connection Help**: Clear instructions if Ollama isn't running",
|
29 |
+
"📊 **Status Command**: Use '/ollama' in chat to check model status"
|
30 |
+
]
|
31 |
+
|
32 |
+
for feature in features:
|
33 |
+
console.print(f" {feature}")
|
34 |
+
|
35 |
+
console.print("\n[bold yellow]🎯 Ollama Workflow:[/bold yellow]\n")
|
36 |
+
|
37 |
+
workflow = [
|
38 |
+
"[bold]1. Select Provider:[/bold] Choose 'Ollama' from the provider list",
|
39 |
+
"[bold]2. Model Detection:[/bold] Shows your local models automatically",
|
40 |
+
"[bold]3. Install Missing:[/bold] Offers to download models you don't have",
|
41 |
+
"[bold]4. No API Key:[/bold] Automatically skips API key entry",
|
42 |
+
"[bold]5. Connection Test:[/bold] Verifies Ollama is working",
|
43 |
+
"[bold]6. Chat Ready:[/bold] Start controlling drones with local AI!"
|
44 |
+
]
|
45 |
+
|
46 |
+
for step in workflow:
|
47 |
+
console.print(f" {step}")
|
48 |
+
|
49 |
+
console.print("\n[bold green]💡 Example Experience:[/bold green]\n")
|
50 |
+
|
51 |
+
example = """[cyan]Provider Selection:[/cyan] 6. Ollama
|
52 |
+
[cyan]Model Detection:[/cyan] ✅ Found: llama3.1:latest, codestral:latest
|
53 |
+
[cyan]User Choice:[/cyan] "phi3" (not installed locally)
|
54 |
+
[cyan]Smart Install:[/cyan] "Would you like to install phi3? [Y/n]"
|
55 |
+
[cyan]Download:[/cyan] 📥 Installing phi3... ✅ Success!
|
56 |
+
[cyan]API Key:[/cyan] ⏭️ Skipped (local model)
|
57 |
+
[cyan]Test:[/cyan] ✅ Connection successful!
|
58 |
+
[cyan]Chat Ready:[/cyan] 🚁 DeepDrone ready with local AI!"""
|
59 |
+
|
60 |
+
console.print(Panel(
|
61 |
+
example,
|
62 |
+
title="[bold]Ollama Setup Flow[/bold]",
|
63 |
+
border_style="blue"
|
64 |
+
))
|
65 |
+
|
66 |
+
console.print("\n[bold magenta]🔧 Chat Commands:[/bold magenta]\n")
|
67 |
+
|
68 |
+
commands = [
|
69 |
+
"[cyan]/ollama[/cyan] - Show Ollama status and available models",
|
70 |
+
"[cyan]/status[/cyan] - General system status including AI model",
|
71 |
+
"[cyan]/help[/cyan] - All available commands"
|
72 |
+
]
|
73 |
+
|
74 |
+
for cmd in commands:
|
75 |
+
console.print(f" {cmd}")
|
76 |
+
|
77 |
+
console.print("\n[bold red]⚠️ Troubleshooting:[/bold red]\n")
|
78 |
+
|
79 |
+
issues = [
|
80 |
+
"[bold]No models found:[/bold] Run 'ollama pull llama3.1'",
|
81 |
+
"[bold]Connection refused:[/bold] Run 'ollama serve' in terminal",
|
82 |
+
"[bold]Model not found:[/bold] DeepDrone will offer to install it",
|
83 |
+
"[bold]Ollama not installed:[/bold] Download from https://ollama.com"
|
84 |
+
]
|
85 |
+
|
86 |
+
for issue in issues:
|
87 |
+
console.print(f" • {issue}")
|
88 |
+
|
89 |
+
console.print(f"\n[bold green]🎉 Ready to use local AI for drone control![/bold green]")
|
90 |
+
|
91 |
+
if __name__ == "__main__":
|
92 |
+
demo_ollama_features()
|
tests/__init__.py
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
Test modules for the DeepDrone project.
|
3 |
-
|
4 |
-
This package contains test scripts to verify the functionality of:
|
5 |
-
- Drone connection
|
6 |
-
- Mission planning and execution
|
7 |
-
- Agent interactions
|
8 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_agent_mission.py
DELETED
@@ -1,179 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
Test the ability of the DroneAssistant to plan and execute missions
|
4 |
-
based on natural language requests.
|
5 |
-
|
6 |
-
This script simulates a user asking the agent to create a mission plan and execute it.
|
7 |
-
"""
|
8 |
-
|
9 |
-
import os
|
10 |
-
import time
|
11 |
-
import json
|
12 |
-
import sys
|
13 |
-
from drone.drone_chat import DroneAssistant, generate_mission_plan
|
14 |
-
from drone.drone_control import connect_drone, disconnect_drone
|
15 |
-
from drone.hf_model import HfApiModel, Message
|
16 |
-
from drone import compatibility_fix # Import for Python 3.10+ compatibility
|
17 |
-
|
18 |
-
# Import the simplified model that works with smolagents
|
19 |
-
from tests.test_simple_agent import SimplePlaceholderModel, final_answer
|
20 |
-
|
21 |
-
# Test cases - different natural language requests for missions
|
22 |
-
TEST_CASES = [
|
23 |
-
"Create a mission plan for a survey mission that takes 20 minutes and execute it on the simulator.",
|
24 |
-
"I need to inspect a building. Can you make a plan for a 15-minute inspection mission and fly it?",
|
25 |
-
"Plan a delivery mission to these coordinates and execute it: 37.7749, -122.4194. It should take about 10 minutes.",
|
26 |
-
"Plan and execute a simple square pattern flight around my current position.",
|
27 |
-
]
|
28 |
-
|
29 |
-
def setup_drone_agent():
|
30 |
-
"""Create and initialize a DroneAssistant for testing."""
|
31 |
-
print("Setting up drone agent...")
|
32 |
-
|
33 |
-
# Check if HF_TOKEN is set in environment variables
|
34 |
-
hf_token = os.environ.get("HF_TOKEN", "")
|
35 |
-
if not hf_token:
|
36 |
-
print("WARNING: No Hugging Face API token found. Using a placeholder model.")
|
37 |
-
# Use our simplified placeholder model instead of the previous complex one
|
38 |
-
model = SimplePlaceholderModel()
|
39 |
-
else:
|
40 |
-
# Use the real model if API token is available
|
41 |
-
model = HfApiModel(
|
42 |
-
max_tokens=2096,
|
43 |
-
temperature=0.7,
|
44 |
-
model_id='Qwen/Qwen2.5-Coder-32B-Instruct'
|
45 |
-
)
|
46 |
-
|
47 |
-
# Create the drone assistant with the required tools
|
48 |
-
drone_agent = DroneAssistant(
|
49 |
-
tools=[generate_mission_plan],
|
50 |
-
model=model
|
51 |
-
)
|
52 |
-
|
53 |
-
return drone_agent
|
54 |
-
|
55 |
-
def execute_mission_from_plan(mission_plan):
|
56 |
-
"""Execute a mission based on the provided plan using DroneKit."""
|
57 |
-
print("\nExecuting mission from plan...")
|
58 |
-
|
59 |
-
# Parse the mission plan
|
60 |
-
try:
|
61 |
-
if isinstance(mission_plan, str):
|
62 |
-
plan = eval(mission_plan) # Convert string representation to dict
|
63 |
-
else:
|
64 |
-
plan = mission_plan
|
65 |
-
|
66 |
-
print(f"Mission type: {plan.get('mission_type')}")
|
67 |
-
print(f"Duration: {plan.get('duration_minutes')} minutes")
|
68 |
-
print(f"Flight pattern: {plan.get('flight_pattern')}")
|
69 |
-
print(f"Recommended altitude: {plan.get('recommended_altitude')}")
|
70 |
-
|
71 |
-
# Connect to the simulator
|
72 |
-
print("\nConnecting to simulator...")
|
73 |
-
connected = connect_drone('udp:127.0.0.1:14550')
|
74 |
-
|
75 |
-
if not connected:
|
76 |
-
print("Failed to connect to the simulator. Make sure it's running.")
|
77 |
-
return False
|
78 |
-
|
79 |
-
print("Connected to simulator!")
|
80 |
-
print("Simulating mission execution...")
|
81 |
-
|
82 |
-
# Here we would normally execute the actual mission
|
83 |
-
# For testing purposes, we'll just simulate the execution
|
84 |
-
for i in range(5):
|
85 |
-
print(f"Mission progress: {i*20}%")
|
86 |
-
time.sleep(1)
|
87 |
-
|
88 |
-
print("Mission completed successfully!")
|
89 |
-
disconnect_drone()
|
90 |
-
return True
|
91 |
-
|
92 |
-
except Exception as e:
|
93 |
-
print(f"Error executing mission: {str(e)}")
|
94 |
-
return False
|
95 |
-
|
96 |
-
def run_test_case(drone_agent, test_case):
|
97 |
-
"""Run a single test case with the drone agent."""
|
98 |
-
print(f"\n----- TESTING CASE: '{test_case}' -----")
|
99 |
-
|
100 |
-
# Simulate a user asking the agent
|
101 |
-
print(f"\nUSER: {test_case}")
|
102 |
-
|
103 |
-
# Get the agent's response
|
104 |
-
response = drone_agent.chat(test_case)
|
105 |
-
print(f"\nAGENT: {response}")
|
106 |
-
|
107 |
-
# Check if the response contains or implies a mission plan
|
108 |
-
mission_keywords = ["mission plan", "flight plan", "waypoints", "coordinates"]
|
109 |
-
has_mission_plan = any(keyword in response.lower() for keyword in mission_keywords)
|
110 |
-
|
111 |
-
if not has_mission_plan:
|
112 |
-
print("FAIL: Agent did not create a mission plan.")
|
113 |
-
return False
|
114 |
-
|
115 |
-
# Extract the mission plan and execute it
|
116 |
-
# For a real implementation, we would need to parse the response more carefully
|
117 |
-
# Here we'll just call the generate_mission_plan function directly
|
118 |
-
|
119 |
-
if "survey" in test_case.lower():
|
120 |
-
mission_type = "survey"
|
121 |
-
elif "inspect" in test_case.lower():
|
122 |
-
mission_type = "inspection"
|
123 |
-
elif "delivery" in test_case.lower():
|
124 |
-
mission_type = "delivery"
|
125 |
-
else:
|
126 |
-
mission_type = "custom"
|
127 |
-
|
128 |
-
if "minute" in test_case.lower():
|
129 |
-
# Try to extract the duration
|
130 |
-
import re
|
131 |
-
duration_match = re.search(r'(\d+)\s*minute', test_case)
|
132 |
-
duration = int(duration_match.group(1)) if duration_match else 15
|
133 |
-
else:
|
134 |
-
duration = 15
|
135 |
-
|
136 |
-
print(f"\nGenerating mission plan for {mission_type} mission with duration {duration} minutes...")
|
137 |
-
mission_plan = generate_mission_plan(mission_type=mission_type, duration_minutes=duration)
|
138 |
-
print(f"\nGenerated plan: {mission_plan}")
|
139 |
-
|
140 |
-
# Execute the mission plan
|
141 |
-
result = execute_mission_from_plan(mission_plan)
|
142 |
-
|
143 |
-
if result:
|
144 |
-
print("PASS: Successfully executed the mission plan.")
|
145 |
-
return True
|
146 |
-
else:
|
147 |
-
print("FAIL: Could not execute the mission plan.")
|
148 |
-
return False
|
149 |
-
|
150 |
-
def run_all_tests():
|
151 |
-
"""Run all test cases and report results."""
|
152 |
-
drone_agent = setup_drone_agent()
|
153 |
-
|
154 |
-
results = []
|
155 |
-
for test_case in TEST_CASES:
|
156 |
-
result = run_test_case(drone_agent, test_case)
|
157 |
-
results.append(result)
|
158 |
-
|
159 |
-
# Print summary
|
160 |
-
print("\n----- TEST SUMMARY -----")
|
161 |
-
for i, (test, result) in enumerate(zip(TEST_CASES, results)):
|
162 |
-
status = "PASS" if result else "FAIL"
|
163 |
-
print(f"Test {i+1}: {status} - '{test[:40]}...'")
|
164 |
-
|
165 |
-
success_rate = sum(results) / len(results) * 100
|
166 |
-
print(f"\nOverall Success Rate: {success_rate:.1f}%")
|
167 |
-
|
168 |
-
return all(results)
|
169 |
-
|
170 |
-
if __name__ == "__main__":
|
171 |
-
print("Testing DroneAssistant's ability to plan and execute missions...")
|
172 |
-
success = run_all_tests()
|
173 |
-
|
174 |
-
if success:
|
175 |
-
print("\nAll tests passed! The agent can successfully plan and execute missions.")
|
176 |
-
exit(0)
|
177 |
-
else:
|
178 |
-
print("\nSome tests failed. The agent needs improvement.")
|
179 |
-
exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_connection.py
DELETED
@@ -1,33 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
Basic connection test for DroneKit to ArduPilot SITL
|
4 |
-
"""
|
5 |
-
|
6 |
-
import time
|
7 |
-
import sys
|
8 |
-
from dronekit import connect, APIException
|
9 |
-
|
10 |
-
# Connect to the Vehicle using a different port to avoid conflicts
|
11 |
-
print("Connecting to vehicle on udp:127.0.0.1:14550...")
|
12 |
-
try:
|
13 |
-
vehicle = connect('udp:127.0.0.1:14550', wait_ready=True, timeout=60)
|
14 |
-
except APIException as e:
|
15 |
-
print(f"Connection failed: {e}")
|
16 |
-
sys.exit(1)
|
17 |
-
except Exception as e:
|
18 |
-
print(f"Error: {e}")
|
19 |
-
sys.exit(1)
|
20 |
-
|
21 |
-
# Get some vehicle attributes (state)
|
22 |
-
print("Connection successful!")
|
23 |
-
print("Get some vehicle attribute values:")
|
24 |
-
print(f" GPS: {vehicle.gps_0}")
|
25 |
-
print(f" Battery: {vehicle.battery}")
|
26 |
-
print(f" Last Heartbeat: {vehicle.last_heartbeat}")
|
27 |
-
print(f" Is Armable?: {vehicle.is_armable}")
|
28 |
-
print(f" System status: {vehicle.system_status.state}")
|
29 |
-
print(f" Mode: {vehicle.mode.name}")
|
30 |
-
|
31 |
-
# Close vehicle object
|
32 |
-
vehicle.close()
|
33 |
-
print("Test complete.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_mission.py
DELETED
@@ -1,136 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
DroneKit-Python Mission Test Script
|
4 |
-
|
5 |
-
This script demonstrates how to connect to the ArduPilot SITL simulator,
|
6 |
-
create a simple mission (square pattern), and execute it.
|
7 |
-
"""
|
8 |
-
|
9 |
-
import time
|
10 |
-
import math
|
11 |
-
import sys
|
12 |
-
import argparse
|
13 |
-
from drone import compatibility_fix # Import the compatibility fix for Python 3.10+
|
14 |
-
from drone.drone_control import DroneController
|
15 |
-
from dronekit import connect, VehicleMode, LocationGlobalRelative
|
16 |
-
|
17 |
-
def get_args():
|
18 |
-
"""Parse command line arguments."""
|
19 |
-
parser = argparse.ArgumentParser(description='Test mission script for DeepDrone')
|
20 |
-
parser.add_argument('--connect',
|
21 |
-
help="Vehicle connection target string. If not specified, SITL automatically started.",
|
22 |
-
default='udp:127.0.0.1:14550')
|
23 |
-
return parser.parse_args()
|
24 |
-
|
25 |
-
def main():
|
26 |
-
# Parse connection string
|
27 |
-
args = get_args()
|
28 |
-
connection_string = args.connect
|
29 |
-
|
30 |
-
print(f"Connecting to vehicle on: {connection_string}")
|
31 |
-
|
32 |
-
try:
|
33 |
-
# Connect to the Vehicle
|
34 |
-
print("Connecting to vehicle on %s" % connection_string)
|
35 |
-
vehicle = connect(connection_string, wait_ready=True, timeout=60)
|
36 |
-
|
37 |
-
# Get some vehicle attributes (state)
|
38 |
-
print("Get some vehicle attribute values:")
|
39 |
-
print(" GPS: %s" % vehicle.gps_0)
|
40 |
-
print(" Battery: %s" % vehicle.battery)
|
41 |
-
print(" Last Heartbeat: %s" % vehicle.last_heartbeat)
|
42 |
-
print(" Is Armable?: %s" % vehicle.is_armable)
|
43 |
-
print(" System status: %s" % vehicle.system_status.state)
|
44 |
-
print(" Mode: %s" % vehicle.mode.name)
|
45 |
-
|
46 |
-
# Define the home position (current position where script is run)
|
47 |
-
home_position = vehicle.location.global_relative_frame
|
48 |
-
print("Home position: %s" % home_position)
|
49 |
-
|
50 |
-
# Define a square mission
|
51 |
-
offset = 0.0001 # Approximately 11 meters at the equator
|
52 |
-
|
53 |
-
# Create a mission with a square pattern
|
54 |
-
print("Creating mission waypoints...")
|
55 |
-
waypoints = [
|
56 |
-
# North
|
57 |
-
LocationGlobalRelative(home_position.lat + offset, home_position.lon, 20),
|
58 |
-
# Northeast
|
59 |
-
LocationGlobalRelative(home_position.lat + offset, home_position.lon + offset, 20),
|
60 |
-
# East
|
61 |
-
LocationGlobalRelative(home_position.lat, home_position.lon + offset, 20),
|
62 |
-
# Return to Launch
|
63 |
-
LocationGlobalRelative(home_position.lat, home_position.lon, 20),
|
64 |
-
]
|
65 |
-
|
66 |
-
# Arm the vehicle
|
67 |
-
print("Arming motors")
|
68 |
-
vehicle.mode = VehicleMode("GUIDED")
|
69 |
-
vehicle.armed = True
|
70 |
-
|
71 |
-
# Wait until armed
|
72 |
-
while not vehicle.armed:
|
73 |
-
print("Waiting for arming...")
|
74 |
-
time.sleep(1)
|
75 |
-
|
76 |
-
print("Taking off to 20 meters")
|
77 |
-
vehicle.simple_takeoff(20) # Take off to 20m
|
78 |
-
|
79 |
-
# Wait until the vehicle reaches a safe height
|
80 |
-
while True:
|
81 |
-
print("Altitude: %s" % vehicle.location.global_relative_frame.alt)
|
82 |
-
# Break and return when we reach target altitude or close to it
|
83 |
-
if vehicle.location.global_relative_frame.alt >= 19:
|
84 |
-
print("Reached target altitude")
|
85 |
-
break
|
86 |
-
time.sleep(1)
|
87 |
-
|
88 |
-
# Fly through the waypoints
|
89 |
-
for i, waypoint in enumerate(waypoints):
|
90 |
-
print(f"Flying to waypoint {i+1}")
|
91 |
-
vehicle.simple_goto(waypoint)
|
92 |
-
|
93 |
-
# Start timer for waypoint timeout
|
94 |
-
start_time = time.time()
|
95 |
-
|
96 |
-
# Wait until we reach the waypoint
|
97 |
-
while True:
|
98 |
-
# Calculate distance to waypoint
|
99 |
-
current = vehicle.location.global_relative_frame
|
100 |
-
distance_to_waypoint = math.sqrt(
|
101 |
-
(waypoint.lat - current.lat)**2 +
|
102 |
-
(waypoint.lon - current.lon)**2) * 1.113195e5
|
103 |
-
|
104 |
-
print(f"Distance to waypoint: {distance_to_waypoint:.2f} meters")
|
105 |
-
|
106 |
-
# Break if we're within 3 meters of the waypoint
|
107 |
-
if distance_to_waypoint < 3:
|
108 |
-
print(f"Reached waypoint {i+1}")
|
109 |
-
break
|
110 |
-
|
111 |
-
# Break if we've been flying to this waypoint for more than 60 seconds
|
112 |
-
if time.time() - start_time > 60:
|
113 |
-
print(f"Timed out reaching waypoint {i+1}, continuing to next waypoint")
|
114 |
-
break
|
115 |
-
|
116 |
-
time.sleep(2)
|
117 |
-
|
118 |
-
# Return to home
|
119 |
-
print("Returning to home")
|
120 |
-
vehicle.mode = VehicleMode("RTL")
|
121 |
-
|
122 |
-
# Wait for the vehicle to land
|
123 |
-
while vehicle.armed:
|
124 |
-
print("Waiting for landing and disarm...")
|
125 |
-
time.sleep(2)
|
126 |
-
|
127 |
-
print("Mission complete!")
|
128 |
-
|
129 |
-
# Close vehicle object
|
130 |
-
vehicle.close()
|
131 |
-
|
132 |
-
except Exception as e:
|
133 |
-
print(f"Error: {str(e)}")
|
134 |
-
|
135 |
-
if __name__ == "__main__":
|
136 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_mission_planning.py
DELETED
@@ -1,131 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
Simplified test script for mission planning and execution.
|
4 |
-
This script tests the mission planning and execution functionality directly,
|
5 |
-
without using the DroneAssistant or smolagents.
|
6 |
-
"""
|
7 |
-
|
8 |
-
import time
|
9 |
-
import math
|
10 |
-
import sys
|
11 |
-
import json
|
12 |
-
from drone.drone_chat import generate_mission_plan
|
13 |
-
from drone.drone_control import connect_drone, disconnect_drone, takeoff, land, return_home
|
14 |
-
from drone import compatibility_fix # Import for Python 3.10+ compatibility
|
15 |
-
|
16 |
-
def test_mission_planning():
|
17 |
-
"""Test the mission planning functionality."""
|
18 |
-
print("\n----- TESTING MISSION PLANNING -----")
|
19 |
-
|
20 |
-
# Test different mission types
|
21 |
-
mission_types = ["survey", "inspection", "delivery", "custom"]
|
22 |
-
durations = [10, 15, 20, 5]
|
23 |
-
|
24 |
-
for mission_type, duration in zip(mission_types, durations):
|
25 |
-
print(f"\nGenerating {mission_type} mission plan for {duration} minutes...")
|
26 |
-
|
27 |
-
mission_plan = generate_mission_plan(mission_type=mission_type, duration_minutes=duration)
|
28 |
-
|
29 |
-
# Parse the mission plan
|
30 |
-
if isinstance(mission_plan, str):
|
31 |
-
try:
|
32 |
-
plan = eval(mission_plan) # Convert string representation to dict
|
33 |
-
print("Successfully parsed mission plan:")
|
34 |
-
print(f" Mission type: {plan.get('mission_type')}")
|
35 |
-
print(f" Duration: {plan.get('duration_minutes')} minutes")
|
36 |
-
print(f" Flight pattern: {plan.get('flight_pattern')}")
|
37 |
-
print(f" Recommended altitude: {plan.get('recommended_altitude')} meters")
|
38 |
-
print(f" Waypoint count: {len(plan.get('waypoints', []))}")
|
39 |
-
print(" Mission description:", plan.get('description', '')[:50] + "...")
|
40 |
-
except Exception as e:
|
41 |
-
print(f"Error parsing mission plan: {str(e)}")
|
42 |
-
print("Raw mission plan:", mission_plan)
|
43 |
-
else:
|
44 |
-
print("Mission plan is not a string:", type(mission_plan))
|
45 |
-
print(mission_plan)
|
46 |
-
|
47 |
-
print("\nMission planning test completed.")
|
48 |
-
return True
|
49 |
-
|
50 |
-
def test_mission_execution(mission_type="survey", duration=10):
|
51 |
-
"""Test the mission execution functionality using the simulator."""
|
52 |
-
print("\n----- TESTING MISSION EXECUTION -----")
|
53 |
-
|
54 |
-
# Generate a mission plan
|
55 |
-
print(f"Generating {mission_type} mission plan for {duration} minutes...")
|
56 |
-
mission_plan = generate_mission_plan(mission_type=mission_type, duration_minutes=duration)
|
57 |
-
|
58 |
-
# Parse the mission plan
|
59 |
-
try:
|
60 |
-
if isinstance(mission_plan, str):
|
61 |
-
plan = eval(mission_plan) # Convert string representation to dict
|
62 |
-
else:
|
63 |
-
plan = mission_plan
|
64 |
-
|
65 |
-
print("Mission plan details:")
|
66 |
-
print(f" Mission type: {plan.get('mission_type')}")
|
67 |
-
print(f" Duration: {plan.get('duration_minutes')} minutes")
|
68 |
-
print(f" Flight pattern: {plan.get('flight_pattern')}")
|
69 |
-
print(f" Recommended altitude: {plan.get('recommended_altitude')} meters")
|
70 |
-
print(f" Waypoint count: {len(plan.get('waypoints', []))}")
|
71 |
-
|
72 |
-
# Connect to the simulator
|
73 |
-
print("\nConnecting to simulator...")
|
74 |
-
success = connect_drone('udp:127.0.0.1:14550')
|
75 |
-
|
76 |
-
if not success:
|
77 |
-
print("Failed to connect to the simulator. Make sure it's running.")
|
78 |
-
return False
|
79 |
-
|
80 |
-
print("Connected to simulator!")
|
81 |
-
|
82 |
-
# Execute the mission
|
83 |
-
# For the test, we'll just print the waypoints instead of actually flying
|
84 |
-
print("\nSimulating mission execution...")
|
85 |
-
print(f"Taking off to {plan.get('recommended_altitude')} meters...")
|
86 |
-
time.sleep(2)
|
87 |
-
|
88 |
-
print("Flying mission waypoints:")
|
89 |
-
waypoints = plan.get('waypoints', [])
|
90 |
-
for i, waypoint in enumerate(waypoints):
|
91 |
-
print(f" Waypoint {i+1}: {waypoint}")
|
92 |
-
time.sleep(1)
|
93 |
-
|
94 |
-
print("Returning to home...")
|
95 |
-
time.sleep(2)
|
96 |
-
|
97 |
-
print("Landing...")
|
98 |
-
time.sleep(2)
|
99 |
-
|
100 |
-
print("Mission completed successfully!")
|
101 |
-
disconnect_drone()
|
102 |
-
return True
|
103 |
-
|
104 |
-
except Exception as e:
|
105 |
-
print(f"Error executing mission: {str(e)}")
|
106 |
-
return False
|
107 |
-
|
108 |
-
def main():
|
109 |
-
"""Run all tests."""
|
110 |
-
print("Testing mission planning and execution...")
|
111 |
-
|
112 |
-
# Test mission planning
|
113 |
-
planning_success = test_mission_planning()
|
114 |
-
|
115 |
-
# Test mission execution
|
116 |
-
execution_success = test_mission_execution("survey", 10)
|
117 |
-
|
118 |
-
# Print test summary
|
119 |
-
print("\n----- TEST SUMMARY -----")
|
120 |
-
print(f"Mission Planning Test: {'PASS' if planning_success else 'FAIL'}")
|
121 |
-
print(f"Mission Execution Test: {'PASS' if execution_success else 'FAIL'}")
|
122 |
-
|
123 |
-
if planning_success and execution_success:
|
124 |
-
print("\nAll tests passed! The mission planning and execution are working properly.")
|
125 |
-
return 0
|
126 |
-
else:
|
127 |
-
print("\nSome tests failed. Check the output above for details.")
|
128 |
-
return 1
|
129 |
-
|
130 |
-
if __name__ == "__main__":
|
131 |
-
sys.exit(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_prompt_examples.md
DELETED
@@ -1,120 +0,0 @@
|
|
1 |
-
# DeepDrone Test Prompts
|
2 |
-
|
3 |
-
This document contains example prompts that can be used to test the DeepDrone agent's ability to understand natural language mission requests, plan missions, and execute them.
|
4 |
-
|
5 |
-
## Running the Tests
|
6 |
-
|
7 |
-
1. Start the ArduPilot SITL simulator:
|
8 |
-
```bash
|
9 |
-
cd ~/ardupilot && ./Tools/autotest/sim_vehicle.py -v ArduCopter --console --map
|
10 |
-
```
|
11 |
-
|
12 |
-
2. In a new terminal, run the DeepDrone application:
|
13 |
-
```bash
|
14 |
-
cd ~/deepdrone && streamlit run main.py
|
15 |
-
```
|
16 |
-
|
17 |
-
3. Use one of the example prompts below in the chat interface to test the agent's capabilities.
|
18 |
-
|
19 |
-
## Example Prompts for Testing
|
20 |
-
|
21 |
-
### Basic Mission Planning
|
22 |
-
|
23 |
-
#### Example 1: Square Pattern Mission
|
24 |
-
```
|
25 |
-
Plan and execute a simple square pattern flight around my current position with sides of 50 meters at an altitude of 20 meters.
|
26 |
-
```
|
27 |
-
|
28 |
-
Expected behavior:
|
29 |
-
- Agent should generate a square-shaped mission plan
|
30 |
-
- Mission should include 4 waypoints forming a square
|
31 |
-
- Altitude should be set to 20 meters
|
32 |
-
- Agent will connect to the simulator and execute the mission
|
33 |
-
|
34 |
-
#### Example 2: Survey Mission
|
35 |
-
```
|
36 |
-
I need to create a survey mission for a 100x100 meter area. It should take about 15 minutes and cover the area systematically with a camera.
|
37 |
-
```
|
38 |
-
|
39 |
-
Expected behavior:
|
40 |
-
- Agent should generate a survey mission plan with grid pattern
|
41 |
-
- Mission plan should specify recommended altitude for survey (40-60 meters)
|
42 |
-
- Plan should include information about camera settings and overlap
|
43 |
-
- Agent will offer to execute the mission on the simulator
|
44 |
-
|
45 |
-
#### Example 3: Inspection Mission
|
46 |
-
```
|
47 |
-
Create an inspection mission for a tower structure. The mission should orbit around a central point at varying distances and capture detailed images.
|
48 |
-
```
|
49 |
-
|
50 |
-
Expected behavior:
|
51 |
-
- Agent should generate an inspection mission plan with orbital pattern
|
52 |
-
- Mission should recommend lower altitude (5-20 meters)
|
53 |
-
- Plan should include waypoints at different heights and distances
|
54 |
-
- Agent will offer to execute the mission on the simulator
|
55 |
-
|
56 |
-
### Specific Execution Instructions
|
57 |
-
|
58 |
-
#### Example 4: Delivery Mission with Specific Coordinates
|
59 |
-
```
|
60 |
-
Plan a delivery mission to these coordinates: 37.7749, -122.4194. Make sure to maintain at least 30 meters altitude en route and avoid populated areas.
|
61 |
-
```
|
62 |
-
|
63 |
-
Expected behavior:
|
64 |
-
- Agent should generate a delivery mission plan with the specified coordinates
|
65 |
-
- Mission should set 30+ meters as the flight altitude
|
66 |
-
- Plan should mention safety considerations
|
67 |
-
- Agent will offer to execute the mission on the simulator
|
68 |
-
|
69 |
-
#### Example 5: Custom Mission with Multiple Waypoints
|
70 |
-
```
|
71 |
-
I need a custom mission with the following waypoints:
|
72 |
-
1. Take off to 15 meters
|
73 |
-
2. Fly to 50 meters north of home position
|
74 |
-
3. Then 50 meters east
|
75 |
-
4. Then 50 meters south
|
76 |
-
5. Return to home and land
|
77 |
-
```
|
78 |
-
|
79 |
-
Expected behavior:
|
80 |
-
- Agent should generate a custom mission plan with the specified waypoints
|
81 |
-
- Plan should include exact coordinates for each waypoint
|
82 |
-
- Agent will offer to execute the mission on the simulator
|
83 |
-
|
84 |
-
## Troubleshooting
|
85 |
-
|
86 |
-
If the agent doesn't respond correctly to any of these prompts, check the following:
|
87 |
-
|
88 |
-
1. Make sure the SITL simulator is running properly
|
89 |
-
2. Verify that you have set the HF_TOKEN environment variable for the Hugging Face API
|
90 |
-
3. Check that the DroneKit connection to the simulator is working
|
91 |
-
4. Look for any error messages in the terminal
|
92 |
-
|
93 |
-
## Expected Response Format
|
94 |
-
|
95 |
-
A typical response from the agent should include:
|
96 |
-
|
97 |
-
1. Acknowledgment of the mission request
|
98 |
-
2. A detailed mission plan including:
|
99 |
-
- Mission type
|
100 |
-
- Duration
|
101 |
-
- Flight pattern
|
102 |
-
- Altitude recommendations
|
103 |
-
- Waypoint information
|
104 |
-
3. Options to execute the mission or modify the plan
|
105 |
-
|
106 |
-
Example:
|
107 |
-
```
|
108 |
-
I'll create a survey mission plan for your 100x100 meter area.
|
109 |
-
|
110 |
-
Mission Plan:
|
111 |
-
- Type: Survey
|
112 |
-
- Duration: 15 minutes
|
113 |
-
- Flight pattern: Grid with 70% overlap
|
114 |
-
- Recommended altitude: 50 meters
|
115 |
-
- Camera settings: 4K resolution, 1 shot every 2 seconds
|
116 |
-
|
117 |
-
The plan includes 12 waypoints in a grid pattern to ensure complete coverage of the area.
|
118 |
-
|
119 |
-
Would you like me to execute this mission on the simulator?
|
120 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_simple_agent.py
DELETED
@@ -1,75 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
Simplified test for the DroneAssistant with proper smolagents format support
|
4 |
-
"""
|
5 |
-
|
6 |
-
import os
|
7 |
-
import sys
|
8 |
-
from drone.drone_chat import DroneAssistant, generate_mission_plan
|
9 |
-
from drone.hf_model import Message
|
10 |
-
|
11 |
-
def final_answer(answer):
|
12 |
-
"""Final answer function for smolagents"""
|
13 |
-
print(f"FINAL ANSWER: {answer}")
|
14 |
-
return answer
|
15 |
-
|
16 |
-
class SimplePlaceholderModel:
|
17 |
-
"""A very simple placeholder model that always returns a properly formatted response"""
|
18 |
-
|
19 |
-
def __call__(self, messages):
|
20 |
-
"""Called when used as a function"""
|
21 |
-
return self.generate(messages)
|
22 |
-
|
23 |
-
def generate(self, messages, **kwargs):
|
24 |
-
"""Generate a response with proper smolagents formatting"""
|
25 |
-
mission_type = "survey"
|
26 |
-
duration = 15
|
27 |
-
|
28 |
-
# Format the response in the way smolagents expects with proper code formatting
|
29 |
-
response = f"""Thought: I will create a {mission_type} mission plan for {duration} minutes and execute it on the simulator.
|
30 |
-
Code:
|
31 |
-
```py
|
32 |
-
mission_plan = generate_mission_plan(mission_type="{mission_type}", duration_minutes={duration})
|
33 |
-
print(f"Generated mission plan: {{mission_plan}}")
|
34 |
-
final_answer(f"I've created a {mission_type} mission plan that will take approximately {duration} minutes to execute. The plan includes waypoints for a square pattern around your current position.")
|
35 |
-
```<end_code>"""
|
36 |
-
|
37 |
-
return Message(response)
|
38 |
-
|
39 |
-
def test_simple_agent():
|
40 |
-
"""Test the drone assistant with a simple model"""
|
41 |
-
print("\n=== Testing Simple Agent ===")
|
42 |
-
|
43 |
-
# Create placeholder model
|
44 |
-
model = SimplePlaceholderModel()
|
45 |
-
|
46 |
-
# Create drone assistant
|
47 |
-
assistant = DroneAssistant(
|
48 |
-
tools=[generate_mission_plan],
|
49 |
-
model=model
|
50 |
-
)
|
51 |
-
|
52 |
-
# Test a simple mission planning request
|
53 |
-
user_message = "Plan a simple square pattern mission around my current position."
|
54 |
-
|
55 |
-
print(f"\nUser: {user_message}")
|
56 |
-
|
57 |
-
try:
|
58 |
-
response = assistant.chat(user_message)
|
59 |
-
print(f"\nAgent response: {response}")
|
60 |
-
return True
|
61 |
-
except Exception as e:
|
62 |
-
print(f"\nError: {str(e)}")
|
63 |
-
print(f"Error type: {type(e)}")
|
64 |
-
import traceback
|
65 |
-
traceback.print_exc()
|
66 |
-
return False
|
67 |
-
|
68 |
-
if __name__ == "__main__":
|
69 |
-
success = test_simple_agent()
|
70 |
-
if success:
|
71 |
-
print("\nTest passed!")
|
72 |
-
sys.exit(0)
|
73 |
-
else:
|
74 |
-
print("\nTest failed!")
|
75 |
-
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|