2025-09-11 21:54:41 +08:00
2025-08-17 22:41:54 +08:00
2025-08-17 22:41:54 +08:00
2025-09-25 21:57:32 +08:00
2025-09-11 21:54:41 +08:00
2025-10-13 11:54:02 +08:00
2025-10-13 11:54:02 +08:00

无人机自然语言控制项目

本项目构建了一个完整的无人机自然语言控制系统集成了检索增强生成RAG知识库、大型语言模型LLM、FastAPI后端服务和ROS2通信最终实现通过自然语言指令控制无人机执行复杂任务。

项目结构

项目被清晰地划分为几个核心模块:

.
├── 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可视化图像
│   └── 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】用于将自然语言知识摄入向量数据库
│
├── /             # ROS2接口定义 (保持不变)
└── docs/
    └── README.md                 # 本说明文件

核心配置Orin IP 地址

重要提示: 本项目的后端服务和知识库工具需要与在NVIDIA Jetson Orin设备上运行的服务进行通信嵌入模型和LLM推理服务默认的IP地址为localhost,所以使用电脑本地部署的模型服务同样可以,但是需要注意指定模型的端口。

在使用前您必须配置正确的Orin设备IP地址。您可以通过以下两种方式之一进行设置

  1. 设置环境变量 (推荐): 在您的终端中设置一个名为 ORIN_IP 的环境变量。

    export ORIN_IP="192.168.1.100" # 请替换为您的Orin设备的实际IP地址
    

    脚本会优先使用这个环境变量。

  2. 直接修改脚本: 如果您不想设置环境变量,可以打开 tools/ingest.pybackend_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路径下执行以下命令启动模型

    ./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
    

    至此llama.cpp推理框架就完成了无需进一步即可启动后端

    如果使用vllm后端则执行以下命令

    vllm serve Qwen3-4B-AWQ       --host=0.0.0.0       --port=8081       --dtype=auto       --max-num-seqs=1       --max-model-len=16384           --served-model-name "qwen3-4b-awq"   --trust-remote-code    --gpu-memory-utilization=0.75       --uvicorn-log-level=debug 
    

    由于调用vllm时在发送HTTP请求时需要指定模型名称所以在启动后端服务前需要添加环境变量执行以下命令

    export CLASSIFIER_MODEL="qwen3-4b-awq"
    export SIMPLE_MODEL="qwen3-4b-awq"
    export COMPLEX_MODEL="qwen3-4b-awq"
    
  2. Embedding模型部署

    /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
    

指令分类与分流

后端在生成任务前会先对用户指令进行“简单/复杂”分类,并分流到不同提示词与模型:

  • 分类提示词: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_idvisualization_url(单动作可视化)。

环境变量(可选)

支持为“分类/简单/复杂”三类调用分别配置模型与Base URL未设置时回退到默认本地配置

  • CLASSIFIER_MODEL, CLASSIFIER_BASE_URL
  • SIMPLE_MODEL, SIMPLE_BASE_URL
  • COMPLEX_MODEL, COMPLEX_BASE_URL

通用API KeyOPENAI_API_KEY

示例:

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"

测试简单模式

启动服务后,运行内置测试脚本:

cd tools
python test_api.py

示例输入:“简单模式,起飞” 或 “起飞到10米”。返回结果为简单JSONroot):包含 modeactionplan_idvisualization_url


工作流程

整个系统的工作流程分为两个主要阶段:

  1. 知识库构建(一次性设置): 将环境信息、无人机能力等知识加工并存入向量数据库。
  2. 后端服务运行与交互: 启动主服务通过API接收指令、生成并执行任务。

阶段一:环境设置与编译

此阶段为项目准备好运行环境,仅需在初次配置或依赖变更时执行。一个稳定、隔离且兼容的环境是所有后续步骤成功的基础。

1. 创建Conda环境 (关键步骤)

为了从根源上避免本地Python环境与系统ROS 2环境的库版本冲突特别是Python版本和C++标准库),我们必须使用Conda创建一个干净、隔离且版本精确的虚拟环境。

# 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所需的所有库。

# 确保在项目根目录 (drone/) 下执行
pip install -r backend_service/requirements.txt

3. 编译ROS 2接口

为了让后端服务能够像导入普通Python包一样导入我们自定义的Action接口 (drone_interfaces),你需要先使用colcon对其进行编译。

# 确保在项目根目录 (drone/) 下执行
colcon build

成功后,您会看到build/, install/, log/三个新目录。这一步会将.action文件转换为Python和C++代码。


阶段二:数据处理流水线

此阶段为RAG系统准备数据让LLM能够理解任务环境。

1. 准备原始数据

将你的原始数据文件(例如,.world, .json 文件等)放入 tools/map/ 目录中。

2. 数据预处理

运行脚本将原始数据“翻译”成自然语言知识。

# 确保在项目根目录 (drone/) 下并已激活backend环境
cd tools
python build_knowledge_base.py

该脚本会扫描 tools/map/ 目录,并在 tools/knowledge_base/ 目录下生成对应的 _knowledge.ndjson 文件。

3. 数据入库Ingestion

运行脚本将处理好的知识加载到向量数据库中。

# 仍在tools/目录下执行
python ingest.py

该脚本会自动扫描 tools/knowledge_base/ 目录,并将数据存入 tools/vector_store/ 目录中。


阶段三:服务启动与测试

完成前两个阶段后,即可启动并测试后端服务。

1. 启动后端服务

启动服务的关键在于按顺序激活环境先激活ROS 2工作空间再激活Conda环境。

# 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/
# 如果使用vllm后端此时还应当指定使用的模型名称
uvicorn src.main:app --host 0.0.0.0 --port 8000

当您看到日志中出现 Uvicorn running on http://0.0.0.0:8000 时,表示服务已成功启动。

2. 运行API接口测试

我们提供了一个脚本来验证核心的“任务生成”功能。

打开一个新的终端,并执行以下命令:

# 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 foundModuleNotFoundError: 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:
    {
      "user_prompt": "无人机起飞到10米然后前往机库最后降落。"
    }
    
  • Success Response复杂模式:
    {
      "root": { ... },
      "plan_id": "some-unique-id",
      "visualization_url": "/static/py_tree.png"
    }
    
  • Success Response简单模式:
    {
      "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 对象)
    {
      "py_tree": {
        "root": { ... }
      }
    }
    
  • Response:
    {
      "status": "execution_started"
    }
    
D. 接收实时状态

通过WebSocket连接实时接收无人机在执行任务时的状态反馈。

  • Endpoint: WS /ws/status
  • Usage: 使用任意WebSocket客户端连接到 ws://<服务器IP>:8000/ws/status。当任务执行时服务器会主动推送JSON消息例如
    {"node_id": "takeoff_node_1", "status": 0} // 0: RUNNING
    {"node_id": "takeoff_node_1", "status": 1} // 1: SUCCESS
    
Description
No description provided
Readme 133 MiB
Languages
C 34.8%
C++ 23.5%
Makefile 17.1%
CMake 12.7%
Python 9.5%
Other 2.4%