This commit is contained in:
2026-02-20 23:04:09 +08:00
parent 43d69d5a99
commit 5d8412bcb6
244 changed files with 9304 additions and 1463 deletions

545
README.md
View File

@@ -1,454 +1,153 @@
# 无人机自然语言控制项目
# 无人机自然语言控制项目 (DronePlanning)
本项目构建了一个完整的无人机自然语言控制系统集成了检索增强生成RAG知识库、大型语言模型LLM、FastAPI后端服务和ROS2通信最终实现通过自然语言指令控制无人机执行复杂任务
本项目提供一个基于 `FastAPI + LLM + RAG` 的无人机任务规划后端。输入自然语言指令,输出严格 JSON 的行为树计划(`py_tree`
## 项目结构
> 当前版本已与 ROS2 解耦。
> 推荐直接使用 `start_all.sh` / `start_all_vllm.sh` 启动。
项目被清晰地划分为几个核心模块:
## 1. 当前代码结构
```
.
├── backend_service/
│ ├── src/ # FastAPI应用核心代码
│ │ ├── __init__.py
│ │ ├── main.py # 应用主入口提供Web API
│ │ ├── py_tree_generator.py # RAG与LLM集成生成py_tree
│ │ ├── prompts/ # LLM 提示词
│ │ │ ├── system_prompt.txt # 复杂模式提示词(行为树与安全监控
├── simple_mode_prompt.txt # 简单模式提示词单一原子动作JSON
│ │ │ └── classifier_prompt.txt # 指令简单/复杂分类提示词
│ │ ├── ...
│ ├── generated_visualizations/ # 存放最新生成的py_tree可视化图像
│ ├── generated_reasoning_content/ # 存放最新推理链Markdown<plan_id>.md
│ └── requirements.txt # 后端服务的Python依赖
├── tools/
│ ├── map/ # 【数据源】存放原始地图文件(如.world, .json)
│ ├── knowledge_base/ # 【处理后】存放build_knowledge_base.py生成的.ndjson文件
│ ├── vector_store/ # 【数据库】存放最终的ChromaDB向量数据库
│ ├── build_knowledge_base.py # 【步骤1】用于将原始数据转换为自然语言知识
│ ├── ingest.py # 【步骤2】用于将自然语言知识摄入向量数据库
│ └── test_llama_server.py # 直接调用本地8081端口llama-server支持 --system / --system-file
├── / # ROS2接口定义 (保持不变)
└── docs/
└── README.md # 本说明文件
```text
backend_service/src/
├── main.py
├── py_tree_generator.py
├── models.py
├── websocket_manager.py
├── pipeline/ # 编排层stage1~stage4
├── llm/ # 模型网关/响应解析/工具运行时
├── retrieval/ # 多知识库检索Location/Pattern/Rules
├── prompting/ # prompt manifest + 动态注入
├── validation/ # schema provider + validator
├── prompts/
└── tools/
```
## 核心配置Orin IP 地址
## 2. 环境准备(完整命令)
**重要提示:** 本项目的后端服务和知识库工具需要与在NVIDIA Jetson Orin设备上运行的服务进行通信嵌入模型和LLM推理服务**默认的IP地址为localhost**,所以使用电脑本地部署的模型服务同样可以,但是需要注意指定模型的端口。
在使用前您必须配置正确的Orin设备IP地址。您可以通过以下两种方式之一进行设置
1. **设置环境变量 (推荐)**:
在您的终端中设置一个名为 `ORIN_IP` 的环境变量。
```bash
export ORIN_IP="192.168.1.100" # 请替换为您的Orin设备的实际IP地址
```
脚本会优先使用这个环境变量。
2. **直接修改脚本**:
如果您不想设置环境变量,可以打开 `tools/ingest.py` 和 `backend_service/src/py_tree_generator.py` 文件,找到 `orin_ip = os.getenv("ORIN_IP", "...")` 这样的行并将默认的IP地址修改为您的Orin设备的实际IP地址。
**在继续后续步骤之前,请务必完成此项配置。**
## 模型端口启动
本项目启动依赖于后端的模型推理服务,即`ORIN_IP`所指向的设备的模型服务端口目前项目使用instruct模型与embedding模型实现流程分别部署在8081端口与8090端口。
1. **推理模型部署**:
在`/llama.cpp/build/bin`路径下执行以下命令启动模型
```bash
./llama-server -m ~/models/gguf/Qwen/Qwen3-8B-GGUF/Qwen3-8B-Q4_K_M.gguf --port 8081 --gpu-layers 36 --host 0.0.0.0 -c 8192
```
2. **Embedding模型部署**
在`/llama.cpp/build/bin`路径下执行以下命令启动模型
```bash
./llama-server -m ~/models/gguf/Qwen/Qwen3-embedding-4B/Qwen3-Embedding-4B-Q4_K_M.gguf --gpu-layers 36 --port 8090 --embeddings --pooling last --host 0.0.0.0
```
---
## 指令分类与分流
后端在生成任务前会先对用户指令进行“简单/复杂”分类,并分流到不同提示词与模型:
- 分类提示词:`backend_service/src/prompts/classifier_prompt.txt`
- 简单模式提示词:`backend_service/src/prompts/simple_mode_prompt.txt`
- 复杂模式提示词:`backend_service/src/prompts/system_prompt.txt`
分类仅输出如下JSON之一`{"mode":"simple"}` 或 `{"mode":"complex"}`。两种模式都会执行检索增强RAG将参考知识拼接到用户指令后再进行推理。
当为简单模式时LLM仅输出
`{"mode":"simple","action":{"name":"<action>","params":{...}}}`。
后端不会再自动封装为复杂行为树将直接返回简单JSON并附加 `plan_id` 与 `visualization_url`(单动作可视化)。
### 环境变量(可选)
支持为“分类/简单/复杂”三类调用分别配置模型与Base URL未设置时回退到默认本地配置
- `CLASSIFIER_MODEL`, `CLASSIFIER_BASE_URL`
- `SIMPLE_MODEL`, `SIMPLE_BASE_URL`
- `COMPLEX_MODEL`, `COMPLEX_BASE_URL`
通用API Key`OPENAI_API_KEY`
推理链捕获相关:
- `ENABLE_REASONING_CAPTURE`:是否允许模型返回含有 <think> 的原文以便捕获推理链;默认 true。
- `REASONING_PREVIEW_LINES`:在后端日志中打印推理链预览的行数;默认 20。
示例:
```bash
export CLASSIFIER_MODEL="qwen2.5-1.8b-instruct"
export SIMPLE_MODEL="qwen2.5-1.8b-instruct"
export COMPLEX_MODEL="qwen2.5-7b-instruct"
export CLASSIFIER_BASE_URL="http://$ORIN_IP:8081/v1"
export SIMPLE_BASE_URL="http://$ORIN_IP:8081/v1"
export COMPLEX_BASE_URL="http://$ORIN_IP:8081/v1"
export OPENAI_API_KEY="sk-no-key-required"
# 推理链捕获(可选)
export ENABLE_REASONING_CAPTURE=true # 默认已为true如需关闭设置为 false
export REASONING_PREVIEW_LINES=30 # 调整日志预览行数
```
### 测试简单模式
启动服务后,运行内置测试脚本:
在项目根目录执行:
```bash
cd tools
python test_api.py
cd /home/huangfukk/DronePlanning
# 1) 创建并激活 venv
python3 -m venv backend_service/venv
source backend_service/venv/bin/activate
# 2) 安装依赖
pip install -r backend_service/requirements.txt
# 3) 可选:设置设备地址(本地默认 localhost
export ORIN_IP="localhost"
```
示例输入:“简单模式,起飞” 或 “起飞到10米”。返回结果为简单JSON无 `root`):包含 `mode`、`action`、`plan_id`、`visualization_url`。
## 3. 启动服务(推荐)
### 直接调用 llama-server绕过后端
当仅需测试本地 8081 端口的推理服务OpenAI 兼容接口)时,可使用内置脚本:
### 3.1 llama.cpp 路线
```bash
cd /home/huangfukk/DronePlanning
./start_all.sh start
```
常用命令:
```bash
./start_all.sh stop
./start_all.sh restart
./start_all.sh vl
./start_all.sh restart-vl
./start_all.sh status
```
### 3.2 vLLM 路线
```bash
cd /home/huangfukk/DronePlanning
./start_all_vllm.sh start
```
常用命令:
```bash
./start_all_vllm.sh stop
./start_all_vllm.sh restart
./start_all_vllm.sh status
```
## 4. 构建与入库RAG
```bash
cd /home/huangfukk/DronePlanning
source backend_service/venv/bin/activate
# 1) 从 map 生成知识文本(可选,已有知识可跳过)
python tools/rag/build_knowledge_base.py
# 2) 入库到 ChromaDBLocation/Pattern/Rules + 兼容集合)
python tools/rag/ingest.py
```
> 运行 `ingest.py` 前,请确保 embedding 服务8090已启动。
## 5. 快速接口验证
```bash
cd /home/huangfukk/DronePlanning
# 健康检查
curl -s http://localhost:8000/docs >/dev/null && echo "fastapi ok"
curl -s http://localhost:8081/v1/models && echo "llm ok"
# 生成计划
curl -s http://localhost:8000/generate_plan \
-H "Content-Type: application/json" \
-d '{"user_prompt":"无人机当前在地面,到广场查找绿色公交车,找到就拍照"}'
```
## 6. 测试命令
```bash
cd /home/huangfukk/DronePlanning
source backend_service/venv/bin/activate
# API 回归
python tools/test_api.py
# 直接测试 8081 的 OpenAI 兼容推理接口
python tools/test_llama_server.py \
--system-file backend_service/src/prompts/system_prompt.txt \
--system-file backend_service/src/prompts/system_prompt_vllm.txt \
--user "起飞到10米然后降落" \
--base-url "http://127.0.0.1:8081/v1" \
--verbose
# 交互式/批量验证
python tools/test_validate/run_tests.py
```
说明:
- 支持 `--system` 或 `--system-file` 自定义提示词文件;`--system-file` 优先。
- 默认解析 OpenAI 风格返回,若包含 `<think>` 推理内容会显示在输出中(具体取决于模型和服务配置)。
## 7. 关键 API
---
- `POST /generate_plan`
- `POST /execute_mission`(当前返回 execution_disabled
- `WS /ws/status`
- `GET /static/py_tree.png`
## 工作流程
整个系统的工作流程分为两个主要阶段:
1. **知识库构建(一次性设置)**: 将环境信息、无人机能力等知识加工并存入向量数据库。
2. **后端服务运行与交互**: 启动主服务通过API接收指令、生成并执行任务。
### 阶段一:环境设置与编译
此阶段为项目准备好运行环境,仅需在初次配置或依赖变更时执行。一个稳定、隔离且兼容的环境是所有后续步骤成功的基础。
#### 1. 创建Conda环境 (关键步骤)
为了从根源上避免本地Python环境与系统ROS 2环境的库版本冲突特别是Python版本和C++标准库),我们**必须**使用Conda创建一个干净、隔离且版本精确的虚拟环境。
## 8. 日志与排障
```bash
# 1. 创建一个使用Python 3.10的新环境。
# --name backend: 指定环境名称。
# python=3.10: 指定Python版本必须与ROS 2 Humble要求的版本一致。
# --channel conda-forge: 使用conda-forge社区源其包通常有更好的兼容性。
# --no-default-packages: 关键不安装Conda默认的包如libgcc避免与系统ROS 2的C++库冲突。
conda create --name backend --channel conda-forge --no-default-packages python=3.10
# 2. 激活新创建的环境
conda activate backend
```
#### 2. 安装所有Python依赖
在激活`backend`环境后,使用`pip`一次性安装所有依赖。`requirements.txt`已包含**运行时**如fastapi, rclpy和**编译时**如empy, catkin-pkg, lark所需的所有库。
```bash
# 确保在项目根目录 (drone/) 下执行
pip install -r backend_service/requirements.txt
```
#### 3. 编译ROS 2接口
为了让后端服务能够像导入普通Python包一样导入我们自定义的Action接口 (`drone_interfaces`),你需要先使用`colcon`对其进行编译。
```bash
# 确保在项目根目录 (drone/) 下执行
colcon build
```
成功后,您会看到`build/`, `install/`, `log/`三个新目录。这一步会将`.action`文件转换为Python和C++代码。
---
### 阶段二:数据处理流水线
此阶段为RAG系统准备数据让LLM能够理解任务环境。
#### 1. 准备原始数据
将你的原始数据文件(例如,`.world`, `.json` 文件等)放入 `tools/map/` 目录中。
#### 2. 数据预处理
运行脚本将原始数据“翻译”成自然语言知识。
```bash
# 确保在项目根目录 (drone/) 下并已激活backend环境
cd tools
python build_knowledge_base.py
```
该脚本会扫描 `tools/map/` 目录,并在 `tools/knowledge_base/` 目录下生成对应的 `_knowledge.ndjson` 文件。
#### 3. 数据入库Ingestion
运行脚本将处理好的知识加载到向量数据库中。
```bash
# 仍在tools/目录下执行
python ingest.py
```
该脚本会自动扫描 `tools/knowledge_base/` 目录,并将数据存入 `tools/vector_store/` 目录中。
---
### 阶段三:服务启动与测试
完成前两个阶段后,即可启动并测试后端服务。
#### 1. 启动所有服务(推荐方式:一键启动脚本)
我们提供了一个一键启动脚本 `start_all.sh`,可以自动启动所有必需的服务:
```bash
# 1. 切换到项目根目录
cd /path/to/your/drone
# 2. 使用一键启动脚本(推荐)
./start_all.sh start
# 或者直接运行start是默认命令
./start_all.sh
```
**脚本功能:**
- 自动启动推理模型服务llama-server端口8081
- 自动启动Embedding模型服务llama-server端口8090
- 自动启动FastAPI后端服务端口8000
- 自动检查端口占用、模型文件、环境配置等
- 自动等待服务就绪
- 统一管理日志文件(保存在 `logs/` 目录)
**环境变量配置(可选):**
在运行脚本前,可以通过环境变量自定义配置:
```bash
# 设置llama-server路径如果不在默认位置
export LLAMA_SERVER_DIR="/path/to/llama.cpp/build/bin"
# 设置模型路径(如果不在默认位置)
export INFERENCE_MODEL="~/models/gguf/Qwen/Qwen3-8B-GGUF/Qwen3-8B-Q4_K_M.gguf"
export EMBEDDING_MODEL="~/models/gguf/Qwen/Qwen3-embedding-4B/Qwen3-Embedding-4B-Q4_K_M.gguf"
# 设置Conda环境名称如果使用不同的环境名
export CONDA_ENV="backend"
# 然后运行脚本
./start_all.sh
```
**脚本命令:**
```bash
./start_all.sh start # 启动所有服务(默认)
./start_all.sh stop # 停止所有服务
./start_all.sh restart # 重启所有服务
./start_all.sh status # 查看服务状态
```
**日志查看:**
所有服务的日志都保存在 `logs/` 目录下:
```bash
# 查看所有日志
cd /home/huangfukk/DronePlanning
tail -f logs/*.log
# 查看特定服务日志
tail -f logs/inference_model.log # 推理模型
tail -f logs/embedding_model.log # Embedding模型
tail -f logs/fastapi.log # FastAPI服务
```
#### 2. 手动启动服务(备选方式)
常见日志:
如果您需要手动控制每个服务的启动,可以按照以下步骤操作:
- `logs/inference_model.log`llama.cpp 推理)
- `logs/vllm_inference_model.log`vLLM 推理)
- `logs/embedding_model.log`
- `logs/fastapi.log`
**启动推理模型服务:**
如果 `tools/rag/ingest.py` 在 Chroma 初始化时报错,优先确认:
```bash
cd /llama.cpp/build/bin
./llama-server -m ~/models/gguf/Qwen/Qwen3-8B-GGUF/Qwen3-8B-Q4_K_M.gguf --port 8081 --gpu-layers 36 --host 0.0.0.0 -c 8192
```
**启动Embedding模型服务**
在另一个终端中:
```bash
cd /llama.cpp/build/bin
./llama-server -m ~/models/gguf/Qwen/Qwen3-embedding-4B/Qwen3-Embedding-4B-Q4_K_M.gguf --gpu-layers 36 --port 8090 --embeddings --pooling last --host 0.0.0.0
```
**启动FastAPI后端服务**
在第三个终端中:
```bash
# 1. 切换到项目根目录
cd /path/to/your/drone
# 2. 激活ROS 2编译环境
# 作用:将我们编译好的`drone_interfaces`包的路径告知系统否则Python会报`ModuleNotFoundError`。
# 注意:此命令必须在每次打开新终端时执行一次。
source install/setup.bash
# 3. 激活Conda Python环境
conda activate backend
# 4. 启动FastAPI服务
cd backend_service/
uvicorn src.main:app --host 0.0.0.0 --port 8000
```
当您看到日志中出现 `Uvicorn running on http://0.0.0.0:8000` 时,表示服务已成功启动。
#### 2. 运行API接口测试
我们提供了一个脚本来验证核心的“任务生成”功能。
**打开一个新的终端**,并执行以下命令:
```bash
# 1. 切换到项目根目录
cd /path/to/your/drone
# 2. 激活Conda环境
conda activate backend
# 3. 运行测试脚本
cd tools/
python test_api.py
```
如果一切正常,您将在终端看到一系列 `PASS` 信息以及从服务器返回的Pytree JSON。
#### 3. API接口使用说明
---
## 故障排除 / 常见问题 (FAQ)
以下是在配置和运行此项目时可能遇到的一些常见问题及其解决方案。
#### **Q1: 启动服务时报错 `ModuleNotFoundError: No module named 'drone_interfaces'`**
- **原因**: 您当前的终端环境没有加载ROS 2工作空间的路径。仅仅激活Conda环境是不够的。
- **解决方案**: 严格遵循“启动后端服务”章节的说明在激活Conda环境**之前**,必须先运行 `source install/setup.bash` 命令。
#### **Q2: `colcon build` 编译失败,提示 `ModuleNotFoundError: No module named 'em'`, `'catkin_pkg'`, 或 `'lark'`**
- **原因**: 您的Python环境中缺少ROS 2编译代码时所必需的依赖包。
- **解决方案**: 我们已将所有已知的编译时依赖(`empy`, `catkin-pkg`, `lark`等)添加到了`requirements.txt`中。请确保您已激活正确的Conda环境然后运行 `pip install -r backend_service/requirements.txt` 来安装它们。
#### **Q3: 启动服务时报错 `ImportError: ... GLIBCXX_... not found` 或 `ModuleNotFoundError: No module named 'rclpy._rclpy_pybind11'`**
- **原因**: 您的Conda环境与系统ROS 2环境存在核心库冲突。最常见的原因是Python版本不匹配例如Conda是Python 3.11而ROS 2 Humble需要3.10或者Conda自带的C++库与系统库冲突。
- **解决方案**: 这是最棘手的环境问题。最可靠的解决方法是彻底删除当前的Conda环境 (`conda env remove --name backend`),然后严格按照本文档「环境设置」章节的说明,用正确的命令 (`conda create --name backend --channel conda-forge --no-default-packages python=3.10`) 重建一个干净、兼容的环境。
#### **Q4: 服务启动时,日志显示正在从网络上下载模型(例如 `all-MiniLM-L6-v2`**
- **原因**: 后端服务在连接向量数据库时没有正确指定使用远程嵌入模型导致ChromaDB退回到默认的、需要下载模型的本地嵌入函数。
- **解决方案**: 此问题在当前代码中**已被修复**。`backend_service/src/py_tree_generator.py`现在会正确地将远程嵌入函数实例传递给ChromaDB。如果您在自己的代码中遇到此问题请检查您的`get_collection`调用。
#### **Q5: 服务启动时,日志停在 `waiting for action server...`无法访问API**
- **原因**: 代码中存在阻塞式的`wait_for_server()`调用它会一直等待直到无人机端的Action服务器上线从而卡住了Web服务的启动流程。
- **解决方案**: 此问题在当前代码中**已被修复**。`backend_service/src/ros2_client.py`现在使用非阻塞的方式初始化,并在发送任务时检查服务器是否可用。
##### **A. 生成任务计划**
接收自然语言指令返回生成的行为树py_treeJSON。
- **Endpoint**: `POST /generate_plan`
- **Request Body**:
```json
{
"user_prompt": "无人机起飞到10米然后前往机库最后降落。"
}
```
- **Success Response复杂模式**:
```json
{
"root": { ... },
"plan_id": "some-unique-id",
"visualization_url": "/static/py_tree.png"
}
```
- **Success Response简单模式**:
```json
{
"mode": "simple",
"action": { "name": "takeoff", "params": { "altitude": 10.0 } },
"plan_id": "some-unique-id",
"visualization_url": "/static/py_tree.png"
}
```
##### **B. 查看任务可视化**
获取最新生成的行为树的可视化图像。
- **Endpoint**: `GET /static/py_tree.png`
- **Usage**: 在浏览器中直接打开 `http://<服务器IP>:8000/static/py_tree.png` 即可查看。每次成功调用 `/generate_plan` 后,该图像都会被更新。
##### **C. 执行任务**
接收一个py_tree JSON下发给无人机执行当前为模拟执行
- **Endpoint**: `POST /execute_mission`
- **Request Body**: (使用 `/generate_plan` 返回的 `root` 对象)
```json
{
"py_tree": {
"root": { ... }
}
}
```
- **Response**:
```json
{
"status": "execution_started"
}
```
##### **D. 接收实时状态**
通过WebSocket连接实时接收无人机在执行任务时的状态反馈。
- **Endpoint**: `WS /ws/status`
- **Usage**: 使用任意WebSocket客户端连接到 `ws://<服务器IP>:8000/ws/status`。当任务执行时服务器会主动推送JSON消息例如
```json
{"node_id": "takeoff_node_1", "status": 0} // 0: RUNNING
{"node_id": "takeoff_node_1", "status": 1} // 1: SUCCESS
```
1. 是否使用项目 venv 运行
2. `tools/rag/vector_store/` 是否损坏(可备份后重建)
3. embedding 服务是否可访问(`http://localhost:8090/v1/embeddings`