DroneMind/docs/DEPLOYMENT_AND_OPERATIONS.md
2026-04-14 09:54:26 +08:00

289 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 部署与运维手册(项目总结)
本文面向 **生产/外场部署**:说明 **voice_drone_assistant** 是什么、与 **云端** / **ROS 伴飞桥** / **PX4** 如何衔接,以及 **推荐启动顺序**、**环境变量**与**常见问题**。
协议细节见 [`llmcon.md`](llmcon.md),通用配置索引见 [`PROJECT_GUIDE.md`](PROJECT_GUIDE.md),伴飞桥行为见 [`FLIGHT_BRIDGE_ROS1.md`](FLIGHT_BRIDGE_ROS1.md)`flight_intent` 字段见 [`FLIGHT_INTENT_SCHEMA_v1.md`](FLIGHT_INTENT_SCHEMA_v1.md)。
---
## 1. 项目总结
### 1.1 定位
**voice_drone_assistant** 是板端 **语音无人机助手**:麦克风 → 降噪/VAD → **SenseVoice STT****唤醒词** → 用户一句指令 → **云端 WebSocket**LLM + TTS**本地 Qwen + Kokoro** → 若服务端返回 **`flight_intent`**,可在本机 **校验后执行**TCP Socket 旧路径,或 **ROS 伴飞桥** 推荐路径)。
### 1.2 推荐数据流(方案一:云 → 语音程序 → ROS 桥)
```mermaid
flowchart LR
subgraph cloud [云端]
WS[WebSocket LLM+TTS]
end
subgraph board [机载 香橙派等]
MIC[麦克风]
MAIN[main.py TakeoffPrintRecognizer]
ROSPUB[子进程 publish JSON]
BRIDGE[flight_bridge ros1_node]
MAV[MAVROS]
end
FCU[PX4 飞控]
MIC --> MAIN
MAIN <-->|WSS pcm_asr_uplink + flight_intent| WS
MAIN -->|ROCKET_FLIGHT_INTENT_ROS_BRIDGE| ROSPUB
ROSPUB -->|std_msgs/String /input| BRIDGE
BRIDGE --> MAV --> FCU
```
- **不**把 ROS 直接暴露给公网:云端只连板子的 **WSS/WS**;飞控由 **本机 MAVROS + 伴飞桥** 执行。
- **TCP Socket**`system.yaml``socket_server`)是另一条试飞控通道,与云端 **无关**;未起 Socket 服务端时仅会重连日志,不影响 ROS 方案。
### 1.3 目录与核心入口(仓库根 = `voice_drone_assistant/`
| 路径 | 说明 |
|------|------|
| `main.py` | 语音助手入口 |
| `with_system_alsa.sh` | 建议包装启动,修正 Conda 与系统 ALSA |
| `voice_drone/main_app.py` | 唤醒、云端/本地 LLM、TTS、`flight_intent` 执行策略 |
| `voice_drone/flight_bridge/ros1_node.py` | ROS1 订阅 `/input`,执行 `flight_intent` |
| `voice_drone/flight_bridge/ros1_mavros_executor.py` | MAVROSoffboard / AUTO.LAND / RTL |
| `voice_drone/tools/publish_flight_intent_ros_once.py` | 单次向 ROS 发布 JSON主程序 ROS 桥会子进程调用) |
| `scripts/run_flight_bridge_with_mavros.sh` | 一键roscore可选+ MAVROS + 伴飞桥 |
| `scripts/run_flight_intent_bridge_ros1.sh` | 仅伴飞桥(须已有 roscore + MAVROS |
| `voice_drone/config/system.yaml` | 音频、STT、TTS、云端、`assistant` 等 |
| `requirements.txt` | Python 依赖;**rospy** 来自 `apt` 的 ROS Noetic见文件内注释 |
---
## 2. 环境与依赖
### 2.1 硬件与系统(典型)
- ARM64 板卡(如 RK3588、ES8388 等音频编解码器、USB/内置麦克风。
- Ubuntu 20.04 + **ROS Noetic**(伴飞桥 / MAVROS 路径);同机运行语音进程与 `ros1_node`
- 飞控串口(如 `/dev/ttyACM0`)与 MAVROS `fcu_url` 一致。
### 2.2 Python
- Python 3.10+(与原仓库一致即可)。
-**`voice_drone_assistant`** 根目录:
```bash
pip install -r requirements.txt
```
### 2.3 ROS / MAVROS伴飞桥方案必选
```bash
sudo apt install ros-noetic-ros-base ros-noetic-mavros ros-noetic-mavros-extras
# 按官方文档执行 mavros 地理库安装(如有)
```
- 语音主程序的 **ROS 桥**子进程会 `source /opt/ros/noetic/setup.bash` 并 **prepend** `PYTHONPATH`**不要**在未 source ROS 的 shell 里把 `PYTHONPATH` 设成「只有工程根」,否则会找不到 `rospy`(参见 `main_app` 中 `_publish_flight_intent_to_ros_bridge`)。
### 2.4 模型与权重
- STT / TTS /可选VAD 放入 `models/`,或 `bash scripts/bundle_for_device.sh` 从原仓库打包。
- 本地 LLMGGUF 默认路径或 `ROCKET_LLM_GGUF`**纯云端对话**时可弱化本地模型,但回退/混合模式仍需。
---
## 3. 部署拓扑
### 3.1 单机一体化(常见)
同一台香橙派上同时运行:
1. **roscore**(若尚无 master由 `run_flight_bridge_with_mavros.sh` 拉起)。
2. **MAVROS**`px4.launch`,串口连 PX4
3. **伴飞桥** `python3 -m voice_drone.flight_bridge.ros1_node`(订阅 **`/input`**)。
4. **语音** `bash with_system_alsa.sh python main.py`。
`ROS_MASTER_URI` / `ROS_HOSTNAME`:一键脚本内默认 `http://127.0.0.1:11311` 与 `127.0.0.1`**新开调试终端** 执行 `rostopic`/`rosservice` 前须自行 `source /opt/ros/noetic/setup.bash` 并 export **同一** `ROS_MASTER_URI`(见下文「常见问题」)。
### 3.2 网络
- 板子能访问 **云端 WebSocket**`ROCKET_CLOUD_WS_URL`)。
- PX4 + 遥控 + 安全开关等按外场规范配置;本文不替代安全检校清单。
---
## 4. 启动顺序(推荐)
### 4.1 终端 A飞控栈 + 伴飞桥
在 **`voice_drone_assistant`** 根目录:
```bash
cd /path/to/voice_drone_assistant
bash scripts/run_flight_bridge_with_mavros.sh /dev/ttyACM0 921600
```
脚本会:
- 设置 `ROS_MASTER_URI`、`ROS_HOSTNAME`(未预设时默认为本机 master
- 如无 master 则启动 **roscore**
- 启动 **MAVROS** 并等待 `/mavros/state` **connected**
- 前台启动伴飞桥,日志中应出现:`flight_intent_bridge 就绪:订阅 /input`。
**仅桥(已有 MAVROS 时)**
```bash
source /opt/ros/noetic/setup.bash
export ROS_MASTER_URI="${ROS_MASTER_URI:-http://127.0.0.1:11311}"
export ROS_HOSTNAME="${ROS_HOSTNAME:-127.0.0.1}"
bash scripts/run_flight_intent_bridge_ros1.sh
```
### 4.2 终端 B语音助手 + 云端 + 执行飞控
```bash
cd /path/to/voice_drone_assistant
export ROCKET_CLOUD_VOICE=1
export ROCKET_CLOUD_WS_URL='ws://<云主机>:8766/v1/voice/session'
export ROCKET_CLOUD_AUTH_TOKEN='<token>'
export ROCKET_CLOUD_DEVICE_ID='drone-001' # 可选
# 云端返回 flight_intent 时是否在机端执行
export ROCKET_CLOUD_EXECUTE_FLIGHT=1
# 走 ROS 伴飞桥(与 Socket/offboard 序列互斥,勿双开重复执行)
export ROCKET_FLIGHT_INTENT_ROS_BRIDGE=1
# 可选ROCKET_FLIGHT_BRIDGE_TOPIC=/input ROCKET_FLIGHT_BRIDGE_WAIT_SUB=2
# 默认关闭本地「起飞演示」口令直起 offboard需要时再设为 1
# export ROCKET_LOCAL_KEYWORD_TAKEOFF=1
bash with_system_alsa.sh python main.py
```
成功时日志类似:`[飞控-ROS桥] 已发布至 /input`;伴飞桥端出现 `执行 flight_intentsteps=...`。
### 4.3 配置写进 YAML可选
- 云端:`system.yaml` → `cloud_voice``enabled`、`server_url`、`auth_token` 等)。
- 本地口令起飞:`assistant.local_keyword_takeoff_enabled`(默认 `false`);环境变量 `ROCKET_LOCAL_KEYWORD_TAKEOFF` **非空时优先生效**。
---
## 5. 环境变量速查(飞控与云端)
| 变量 | 含义 |
|------|------|
| `ROCKET_CLOUD_VOICE` | `1`:对话走云端 WebSocket |
| `ROCKET_CLOUD_WS_URL` | 云端会话地址 |
| `ROCKET_CLOUD_AUTH_TOKEN` | WS 鉴权 |
| `ROCKET_CLOUD_DEVICE_ID` | 设备 ID可选 |
| `ROCKET_CLOUD_EXECUTE_FLIGHT` | `1`:云端 `flight_intent` 在机端执行 |
| `ROCKET_FLIGHT_INTENT_ROS_BRIDGE` | `1`:执行方式为 **发布到 ROS `/input`**,不跑机内 Socket+offboard 序列 |
| `ROCKET_FLIGHT_BRIDGE_TOPIC` | 默认 `/input` |
| `ROCKET_FLIGHT_BRIDGE_SETUP` | 子进程内 source ROS 的命令,默认 `source /opt/ros/noetic/setup.bash` |
| `ROCKET_FLIGHT_BRIDGE_WAIT_SUB` | 发布前等待订阅者的秒数,默认 `2``0` 即尽可能快发 |
| `ROCKET_LOCAL_KEYWORD_TAKEOFF` | 非空时:`1/true/yes` 开启 **`keywords.yaml` takeoff → 本地 offboard** |
| `ROCKET_CLOUD_PX4_CONTEXT_FILE` | 覆盖 `cloud_voice.px4_context_file`,合并进 session.start |
更多调试变量见 **`voice_drone/main_app.py` 文件头注释** 与 [`PROJECT_GUIDE.md`](PROJECT_GUIDE.md) 第 5 节。
---
## 6. 联调与自测
### 6.1 仅测 ROS 链(无语音)
终端已 `source /opt/ros/noetic/setup.bash` 且与 master 一致:
```bash
rostopic pub -1 /input std_msgs/String \
"data: '{\"is_flight_intent\":true,\"version\":1,\"actions\":[{\"type\":\"land\",\"args\":{}}],\"summary\":\"测\"}'"
```
注意:`std_msgs/String` 在命令行里只能写 **`data: '...json...'`**,不能把 JSON 放在消息顶层。
### 6.2 确认话题与 master
```bash
source /opt/ros/noetic/setup.bash
export ROS_MASTER_URI=http://127.0.0.1:11311
rosnode list
rostopic info /input
rosservice list | grep set_mode
```
若 `Unable to communicate with master!`:当前 shell 未连上正在运行的 **roscore**(或未 export 正确 `ROS_MASTER_URI`)。
---
## 7. 常见问题(摘录)
| 现象 | 可能原因 | 处理 |
|------|----------|------|
| `ModuleNotFoundError: rospy` | 子进程未继承 ROS 的 `PYTHONPATH` | 已修复为 `PYTHONPATH=<>:$PYTHONPATH`;确保 `ROCKET_FLIGHT_BRIDGE_SETUP` 能 source Noetic |
| 语音端「已发布」但桥无日志 | 曾用相对 `input`,与全局 `/input` 不一致 | 伴飞桥默认已改为订阅 **`/input`**;重启桥 |
| `set_mode unavailable` / land 失败 | OFFBOARD 断流、MAVROS 异常等 | 伴飞桥降落逻辑已带持续 setpoint + 重连 proxy仍失败则查 `rosservice`、`/mavros/state`、链路 |
| takeoff 超时 | 未进 OFFBOARD、未解锁、定位未就绪 | 查地面站、`/mavros/state`、适当增大 `~takeoff_timeout_sec`ROS 私有参数) |
| ALSA underrun | 播放与采集竞争 | 板端常见;可调缓冲区/设备或 `recognizer.ack_pause_mic_for_playback` |
---
## 8. 安全与运维建议
- 外场前在 **SITL 或系留** 环境验证完整 **`flight_intent`** 序列。
- 云端 token、WS URL 勿提交到公开仓库;用环境变量或本机 **overlay** 配置注入。
- 升级伴飞桥或 MAVROS 后清日志重试一遍 **`/input`** 手发 JSON。
---
## 9. 迁移到另一台香橙派:是否只拷贝 `voice_drone_assistant` 即可?
**结论:目录是「代码 + 配置」的核心载体,但仅靠「整文件夹 scp 过去」通常不够新板必须再装系统级依赖、模型与可选ROS并按现场改配置。**
### 9.1 拷贝目录本身会带上什么
| 已包含 | 说明 |
|--------|------|
| 全部 Python 源码、`voice_drone/config/*.yaml` 默认配置 | 可直接改 YAML / 环境变量适配新环境 |
| `scripts/`、`with_system_alsa.sh`、`docs/` | 启动与说明在包内 |
### 9.2 新板必须单独准备(不随目录自动存在)
| 项 | 说明 |
|----|------|
| **Ubuntu + 音频/ALSA** | 与当前开发板同代或自行适配;录音设备索引可能变化,需重选或设 `ROCKET_INPUT_DEVICE_INDEX` |
| **`pip install -r requirements.txt`** | 每台新 Python 环境执行一次(或整体迁移同一 conda 目录) |
| **`models/`** | STT/TTS/VAD 体积大,**务必**在本机先 `bash scripts/bundle_for_device.sh /path/to/rocket_drone_audio` 或手工拷入,见 `models/README.txt` |
| **`cache/` GGUF** | 纯云端可不强依赖;若需本地 Qwen 回退,拷贝或设 `ROCKET_LLM_GGUF` |
| **ROS Noetic + MAVROS** | **apt** 安装;伴飞桥方案 **必选**`rospy` **不要**指望只靠 pip |
| **云端连通** | 新板 IP/防火墙能访问 `ROCKET_CLOUD_WS_URL`token 用环境变量注入 |
| **`dialout` 等权限** | 访问 `/dev/ttyACM0` 的用户加入 `dialout`,否则 MAVROS 无串口 |
| **`system.yaml` 现场差异** | `socket_server` IP、可选 `tts.output_device`、若麦索引固定可写 `audio.input_device_index` |
### 9.3 推荐迁移流程(简表)
1. 在旧机或 CI**bundle 模型** → 打包整个 `voice_drone_assistant`(含 `models/`,按需含 `cache/`)。
2. 新香橙派:解压到任意路径,安装 **`requirements.txt`**、**ROS+MAVROS**、系统音频工具。
3. 用 **`with_system_alsa.sh python main.py`** 试麦与 STT再按本文 **§4** 双终端起 **桥 + 语音**。
4. 首次外场前做一次 **`rostopic pub /input`** 手发 JSON见 **§6**)。
### 9.4 常见误区
- **只拉 Git、不拷 `models/`**STT/TTS 启动即失败。
- **新板 Noetic 未装却开 `ROCKET_FLIGHT_INTENT_ROS_BRIDGE`**:发布子进程仍可能报错。
- **假设麦克风设备号一定相同**Orange Pi 刷机或换内核后常变,以首次启动日志为准。
---
## 10. 文档索引
| 文档 | 用途 |
|------|------|
| [`README.md`](../README.md) | 仓库简介、模型、`bundle` |
| [`PROJECT_GUIDE.md`](PROJECT_GUIDE.md) | 配置项与日常用法索引 |
| **本文** | 部署拓扑、启动顺序、环境变量、联调、**§9 迁移清单** |
| [`FLIGHT_BRIDGE_ROS1.md`](FLIGHT_BRIDGE_ROS1.md) | 伴飞桥参数、PX4 行为、`rostopic pub` 注意 |
| [`FLIGHT_INTENT_SCHEMA_v1.md`](FLIGHT_INTENT_SCHEMA_v1.md) | JSON 协议 |
| [`llmcon.md`](llmcon.md) | 云端协议 |
| [`CLOUD_VOICE_FLIGHT_CONFIRM_v1.md`](CLOUD_VOICE_FLIGHT_CONFIRM_v1.md) | **飞控口头二次确认**(闲聊不变、确认/取消/超时)云端与机端字段约定 |
---
*文档版本与仓库同步;若行为与代码不一致,以当前 `main_app.py`、`flight_bridge/*.py` 为准。*