微空科技
文档目录
文章目录

微空 AI 智能无人机教程 5 - AI agent 智能体接入

本章目标

本章在机载电脑(树莓派 5)上部署 OpenClaw 开源 AI 智能体框架,接入云端大语言模型,通过 Telegram 打通自然语言指令到无人机 AI 智能体的完整通信链路。同时编写 MCP 飞控桥接服务,实现通过 Telegram 实时查询无人机状态。

完成本章后,MVD35 应达到以下状态:

  • OpenClaw 框架安装完成并以系统服务运行(开机自启)
  • 云端大语言模型 API 接入并正常调用
  • Telegram Bot 配置完成,可接收自然语言指令
  • MCP 飞控桥接服务部署完成,可通过 Telegram 查询无人机实时状态

前期准备

硬件准备

  • MVD35 智能版(完成前置教程的软件配置与飞行测试)
  • 电脑与良好的网络条件

软件准备

系统架构概览

在序章中我们已经介绍过,AI 智能体运行在机载电脑上,充当一个"聪明的调度员":接收人类的自然语言指令,调用云端大模型进行理解和决策,再将具体动作转化为飞控指令。实现这一架构需要两个核心组件:OpenClaw 框架负责智能体调度与通信,运行在 Node.js 上,支持多种大语言模型,并通过 Telegram Bot 等渠道接收用户指令;MCP Server(Python)作为飞控桥接层,通过 ROS2 和 MAVROS 将飞控数据暴露给 AI 智能体。

自然语言指令(Telegram 消息)
     ↓
  OpenClaw 智能体(Node.js)
     ├── 云端 Gemini API(理解 + 决策)
     ├── Skill(SKILL.md,描述可用能力)
     └── MCP 协议(stdio / JSON-RPC)
          ↓
  drone_mcp_server.py(Python MCP Server)
     └── rclpy → MAVROS → 飞控

安装 OpenClaw

OpenClaw 依赖 Node.js 22 或更高版本。树莓派 Ubuntu 24.04 默认源中的 Node.js 版本过旧,需通过 NVM(Node Version Manager)安装。

安装 NVM

  • 使用 curl 安装 NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

80

  • 安装完成后,重新加载 shell 配置使 NVM 生效
source ~/.bashrc
  • 验证安装
nvm --version

81

✅ 输出 NVM 版本号,如 0.40.1 表示安装成功

安装 Node.js

  • 使用 NVM 安装 Node.js
nvm install 22

82

  • 验证安装
node -v
npm -v

83

✅ 输出 Node.js 版本号,如 v22.23.1;npm 版本号,如 10.9.8 表示安装成功

安装 OpenClaw

  • 使用官方一键安装脚本安装 OpenClaw
curl -fsSL https://openclaw.ai/install.sh | bash

84

  • 安装完成后会直接进入 Setup 流程,使用 exit 指令退出设置(稍后进行配置)

85

  • 验证安装
openclaw --version

86

✅ 输出 OpenClaw x.x.x 表示安装成功

创建 Telegram Bot

Telegram Bot 是 OpenClaw 接收用户指令的通信通道。用户通过 Telegram 向 Bot 发送消息,OpenClaw 将自动接收并处理。

创建 Bot

  1. 注册并登录 Telegram
  2. 搜索 @BotFather 并开始对话
  3. 发送 /newbot
  4. 按提示输入:
    • Bot 名称,如 my mvd35
    • Bot 用户名,必须以 bot 结尾,如 mvd35_bot
  5. BotFather 会回复一个 API Token,保存该 Token,在后续配置 OpenClaw 时使用

87

获取 Telegram 用户 ID

为安全起见,需要设置 OpenClaw 白名单,确保只有授权用户能控制无人机的 AI 智能体。

  • 在 Telegram 搜索 @userinfobot 并开始对话
  • Bot 会回复你的数字用户 ID,记录该 ID,用于后续配置 allowlist 白名单
  • 用户 ID:8295873839

88

OpenClaw 初始化配置

启动 OpenClaw 配置

回到树莓派 SSH 终端,运行 onboard 命令启动配置:

openclaw onboard

OpenClaw(2026.7.1-2 及以上版本)采用 对话式配置,通过内置的 Crestodian 配置助手完成初始化。以下是完整的配置流程。

基础配置

运行 openclaw onboard 后,将进入 Crestodian(OpenClaw 内置的交互式配置助手)对话界面。Crestodian 会自动检测当前环境并列出基础配置方案,输入 yes 确认,即可自动完成 Workspace、Gateway 等基础配置。

89

配置 AI 模型

  • 基础配置完成后,Crestodian 会询问是否配置模型。输入 yes,依次完成以下配置:
  1. Model/auth provider → 选择 Google(如使用 DeepSeek、OpenAI 等其他模型,选择对应 Provider)
  2. Google auth method → 选择 Google Gemini API key
  3. Enter Gemini API key → 输入 API Key
  4. Model configured → 显示默认模型
  5. Default model → 选择 Browse all modelsgoogle/gemini-3-flash-preview(兼顾性能与速度)

90

  • 模型配置完成后,Crestodian 会返回对话界面并显示配置状态:

91

连接 Telegram

  • 在 Crestodian 对话界面中输入:
connect telegram

92

  • Crestodian 会显示通信渠道列表,依次完成以下配置:
  1. Bot Token 存储方式 → 输入 1,将 Token 直接存储在 OpenClaw 配置中
  2. Bot Token → 粘贴前面「创建 Bot」步骤中获取的 Token
  3. allowFrom → 输入前面「获取 Telegram 用户 ID」步骤中记录的数字 ID(用于设置 DM 白名单,仅该用户可与 Bot 交互)

95

allowFrom 白名单是无人机的安全屏障,务必正确配置,防止未授权用户发送控制指令。

配置完成后,Crestodian 会显示:

Done – telegram is configured.

Say restart gateway to apply channel changes, or channels to review.

  • 在 Crestodian 中输入 restart gateway 应用配置,然后输入 yes 确认重启。

96

验证 OpenClaw 服务状态

openclaw gateway status --deep

106

状态显示 Runtime: running ; Connectivity probe: ok,服务即正常运行。

OpenClaw 是用户级 systemd 服务,请使用 openclaw gateway 命令管理,勿用 sudo systemctl

常用管理指令:

openclaw gateway start      # 启动服务
openclaw gateway stop       # 停止服务
openclaw gateway restart    # 重启服务
openclaw logs --follow      # 查看实时日志

验证安装

1. 基本对话测试

  • 从 Telegram BotFather 上进入 my mvd35 会话框,发送消息:

99

你好,你能做什么?

101

✅ Bot 正常回复,说明 OpenClaw + AI 模型 API + Telegram 整条链路已连通。

2. 系统信息访问能力测试

请告诉我这台机器的 hostname 和系统信息(uname -a)

102

✅ OpenClaw 应该能执行 shell 命令并返回树莓派的系统信息。

3. 资源占用检查

  • 在树莓派上查看 OpenClaw 的资源消耗:
htop

103

MCP 飞控桥接服务

OpenClaw 本身是一个通用的 AI 智能体框架,并不直接与飞控通信。为了让 AI 智能体能够获取无人机的状态数据并实施控制,需要编写一个 MCP Server 作为桥接层。

MCP(Model Context Protocol)是 AI 智能体调用外部工具的标准协议。通过 MCP,可以将无人机的状态查询和飞行控制封装为一组 Tool(工具),AI 智能体在需要时自动调用。本教程使用 Python 编写 MCP Server(drone_mcp_server.py),通过 stdio 与 OpenClaw 通信,内部通过 rclpy 和 MAVROS 获取飞控数据。

本节先实现只读工具(状态查询),可通过 Telegram 查询电池、位置、飞行模式等信息,无需起飞即可验证链路。飞行控制工具将在教程 6 中实现。

安装 Python MCP SDK

  • 创建虚拟环境并安装 mcp

Ubuntu 24.04 禁止直接使用 pip 在系统 Python 中安装第三方包,需要创建虚拟环境。

sudo apt install -y python3-venv
python3 -m venv --system-site-packages ~/openclaw_drone/venv
source ~/openclaw_drone/venv/bin/activate
pip install mcp

108

109

  • 验证安装
python3 -c "import mcp; print('MCP SDK 安装成功')"
python3 -c "import rclpy; print('rclpy 可用')"

110

✅ 输出 MCP SDK 安装成功rclpy 可用 表示安装成功

编写 drone_mcp_server.py

  • 在树莓派上创建 MCP Server 文件:
sudo vim ~/openclaw_drone/drone_mcp_server.py
  • 将以下代码写入文件
#!/usr/bin/env python3
"""
MVD35 Drone MCP Server
通过 MCP 协议将无人机状态查询接口暴露给 OpenClaw AI 智能体。
通信方式:stdio (JSON-RPC)

注意:MCP Server 通过 stdio 与 OpenClaw 通信,
      绝对不能在代码中使用 print(),否则会破坏 JSON-RPC 数据流。
      调试信息请输出到 sys.stderr。
"""

import sys
import threading
import math
import rclpy
from rclpy.node import Node
from mcp.server.fastmcp import FastMCP
from geometry_msgs.msg import PoseStamped
from mavros_msgs.msg import State
from sensor_msgs.msg import BatteryState


# ============ ROS 2 节点 ============

class DroneROSNode(Node):
    """持久运行的 ROS 2 节点,订阅无人机状态数据"""

    def __init__(self):
        super().__init__('drone_mcp_bridge')

        # 状态数据(持续更新)
        self.state = State()
        self.pose = PoseStamped()
        self.battery = BatteryState()

        # 连接状态标记
        self.state_received = False
        self.pose_received = False
        self.battery_received = False

        # 订阅 MAVROS 话题
        self.state_sub = self.create_subscription(
            State, '/mavros/state', self._state_cb, 10)
        self.pose_sub = self.create_subscription(
            PoseStamped, '/mavros/local_position/pose', self._pose_cb, 10)
        self.battery_sub = self.create_subscription(
            BatteryState, '/mavros/battery', self._battery_cb, 10)

        self.get_logger().info('DroneROSNode 初始化完成,等待 MAVROS 数据...')

    def _state_cb(self, msg):
        self.state = msg
        self.state_received = True

    def _pose_cb(self, msg):
        self.pose = msg
        self.pose_received = True

    def _battery_cb(self, msg):
        self.battery = msg
        self.battery_received = True

    def quaternion_to_yaw(self, q):
        """将四元数转换为偏航角(度)"""
        siny_cosp = 2.0 * (q.w * q.z + q.x * q.y)
        cosy_cosp = 1.0 - 2.0 * (q.y * q.y + q.z * q.z)
        yaw_rad = math.atan2(siny_cosp, cosy_cosp)
        return math.degrees(yaw_rad)


# ============ MCP Server ============

mcp = FastMCP("MVD35-DroneControl")
drone_node = None  # 全局 ROS 节点引用


@mcp.tool()
def get_drone_status() -> str:
    """获取无人机的综合状态信息,包括连接状态、飞行模式、解锁状态、位置和电池。
    这是了解无人机当前情况的最常用工具。"""
    if drone_node is None:
        return "错误:ROS 2 节点尚未初始化"

    lines = []

    # 飞控连接状态
    if drone_node.state_received:
        s = drone_node.state
        lines.append(f"飞控连接: {'已连接' if s.connected else '未连接'}")
        lines.append(f"飞行模式: {s.mode}")
        lines.append(f"解锁状态: {'已解锁(电机可运转)' if s.armed else '未解锁(安全状态)'}")
    else:
        lines.append("飞控连接: 未收到状态数据(MAVROS 可能未启动)")

    # 位置
    if drone_node.pose_received:
        p = drone_node.pose.pose.position
        yaw = drone_node.quaternion_to_yaw(drone_node.pose.pose.orientation)
        lines.append(f"位置: x={p.x:.2f}m, y={p.y:.2f}m, z={p.z:.2f}m")
        lines.append(f"朝向: {yaw:.1f} 度")
    else:
        lines.append("位置: 暂无数据")

    # 电池
    if drone_node.battery_received:
        b = drone_node.battery
        pct = b.percentage * 100 if b.percentage >= 0 else -1
        lines.append(f"电池: {pct:.0f}% ({b.voltage:.1f}V)")
    else:
        lines.append("电池: 暂无数据")

    return "\n".join(lines)


@mcp.tool()
def get_position() -> str:
    """获取无人机当前的本地坐标位置(米)。
    坐标系:x 为前方,y 为左方,z 为高度。"""
    if drone_node is None or not drone_node.pose_received:
        return "暂无位置数据"
    p = drone_node.pose.pose.position
    yaw = drone_node.quaternion_to_yaw(drone_node.pose.pose.orientation)
    return f"位置: x={p.x:.3f}m, y={p.y:.3f}m, z={p.z:.3f}m, 朝向={yaw:.1f} 度"


@mcp.tool()
def get_battery() -> str:
    """获取无人机的电池状态,包括电压和剩余电量百分比。"""
    if drone_node is None or not drone_node.battery_received:
        return "暂无电池数据"
    b = drone_node.battery
    pct = b.percentage * 100 if b.percentage >= 0 else -1
    return f"电压: {b.voltage:.2f}V, 剩余电量: {pct:.0f}%"


@mcp.tool()
def is_armed() -> str:
    """检查无人机电机是否已解锁。返回解锁状态说明。"""
    if drone_node is None or not drone_node.state_received:
        return "暂无状态数据"
    if drone_node.state.armed:
        return "已解锁:电机可以运转,无人机可能正在飞行中"
    else:
        return "未解锁:电机锁定,无人机处于安全状态"


@mcp.tool()
def get_flight_mode() -> str:
    """获取无人机当前的飞行模式。
    常见模式:STABILIZE(自稳)、ALT_HOLD(定高)、LOITER(定点)、
    GUIDED(引导)、LAND(降落)。"""
    if drone_node is None or not drone_node.state_received:
        return "暂无状态数据"
    return f"当前飞行模式: {drone_node.state.mode}"


@mcp.tool()
def check_connection() -> str:
    """检查各个系统组件的连接状态,包括飞控连接、位置数据和电池数据。"""
    if drone_node is None:
        return "错误:ROS 2 节点尚未初始化"

    lines = []
    lines.append(f"MAVROS 状态话题: {'已连接' if drone_node.state_received else '未收到数据'}")
    lines.append(f"位置数据: {'正常' if drone_node.pose_received else '未收到数据'}")
    lines.append(f"电池数据: {'正常' if drone_node.battery_received else '未收到数据'}")

    if drone_node.state_received:
        lines.append(f"飞控连接: {'已连接' if drone_node.state.connected else '未连接'}")

    return "\n".join(lines)


# ============ 启动逻辑 ============

def run_ros_spin(node):
    """在单独线程中运行 ROS 2 事件循环"""
    rclpy.spin(node)


def main():
    global drone_node

    # 初始化 ROS 2
    rclpy.init()
    drone_node = DroneROSNode()

    # 在后台线程运行 ROS 2 spin(持续接收话题数据)
    ros_thread = threading.Thread(
        target=run_ros_spin, args=(drone_node,), daemon=True)
    ros_thread.start()

    # 调试信息输出到 stderr(不能用 print!)
    sys.stderr.write("[drone_mcp_server] MCP Server 启动中...\n")
    sys.stderr.flush()

    # 启动 MCP Server(阻塞主线程,通过 stdio 与 OpenClaw 通信)
    mcp.run(transport="stdio")


if __name__ == "__main__":
    main()
  • 保存并退出

创建启动脚本

OpenClaw 以 systemd 服务运行,其子进程不会自动加载 ROS 2 环境变量,需要通过 wrapper 脚本加载 ROS 2 环境:

sudo vim ~/openclaw_drone/start_mcp_server.sh
  • 写入以下内容:
#!/bin/bash
# MCP Server 启动脚本,加载 ROS 2 环境和虚拟环境后运行 drone_mcp_server.py
source /opt/ros/jazzy/setup.bash
source ~/ros2_ws/install/setup.bash 2>/dev/null
source ~/openclaw_drone/venv/bin/activate
exec python3 ~/openclaw_drone/drone_mcp_server.py
  • 添加执行权限:
sudo chmod +x ~/openclaw_drone/drone_mcp_server.py
sudo chmod +x ~/openclaw_drone/start_mcp_server.sh

MCP Server 通过 stdio 与 OpenClaw 通信,代码中 禁止使用 print(),否则会破坏 JSON-RPC 数据流。调试信息请使用 sys.stderr.write() 输出。

注册 MCP Server

  • 使用 OpenClaw CLI 命令将 MCP Server 注册到配置中:
openclaw mcp add drone-control --command bash --arg /home/micoair/openclaw_drone/start_mcp_server.sh

112

  • 验证 mcp 加载
openclaw mcp list

113

✅ 在 MCP servers 中能看到刚添加的 drone-control 服务,则成功加载

  • 重启 Gateway 确认配置生效:
openclaw gateway restart
openclaw gateway status

114

创建无人机 Skill

OpenClaw 的 Skill 体系中,Skill 是一个 Markdown 文件SKILL.md),用自然语言描述 AI 智能体在什么场景下应该使用哪些工具、遵循什么规则。

  • 创建 Skill 目录和文件:
mkdir -p ~/.openclaw/skills/mvd35-drone
vim ~/.openclaw/skills/mvd35-drone/SKILL.md
  • 写入以下内容:
---
name: mvd35-drone
description: 查询和监控 MVD35 智能无人机的飞行状态。当用户询问无人机状态、电池、位置、飞行模式等信息时使用此技能。
---

# MVD35 无人机状态查询

你是一架 MVD35 微型智能无人机上的 AI 助手。你可以通过 MCP 工具 `drone-control` 来查询无人机的实时状态。

## 可用查询工具

- `get_drone_status`:获取无人机综合状态(飞行模式、解锁状态、位置、电池),这是最常用的工具
- `get_position`:获取无人机当前坐标位置
- `get_battery`:获取电池电压和剩余电量
- `is_armed`:检查电机是否已解锁
- `get_flight_mode`:获取当前飞行模式
- `check_connection`:检查各系统组件连接状态

## 使用规则

1. 当用户询问无人机的总体情况时,优先使用 `get_drone_status` 获取综合信息
2. 当用户只关心某个具体指标时,使用对应的专用工具
3. 如果工具返回"暂无数据",告诉用户可能是 MAVROS 尚未启动或飞控未连接
4. 用简洁友好的中文回复用户
  • 保存退出后,重启 OpenClaw 使 Skill 生效:
openclaw gateway restart
  • 验证 Skill 是否被识别
openclaw skills list

115

✅ 在 Skills 列表中能看到刚添加的 mvd35-drone skill,则加载成功

联调验证

本节验证整个链路的连通性:Telegram → OpenClaw → MCP Server → MAVROS → 飞控。此步骤无需起飞,只需要无人机通电并连接飞控即可。

启动完整系统

确保以下组件已经在运行:

1. MAVROS

启动 MAVROS 以连接飞控:

ros2 launch mavros apm.launch

119

✅ 等待看到飞控型号、类型等详细信息,确认飞控连接成功。

2. OpenClaw

  • OpenClaw 已作为 systemd 服务在后台运行,确认其状态:
openclaw gateway status
  • 若服务未运行,手动启动:
openclaw gateway start

ℹ️ 本步骤验证无需启动 RealSense 相机和 OpenVINS。仅需 MAVROS 与飞控的连接,即可验证状态查询功能。

通过 Telegram 查询无人机状态

  • 打开 Telegram,找到已创建的 Bot,发送消息:

1:设定身份

你叫mvd35-001,负责本台mvd35智能无人机的管理与控制。

123

2:基础对话

你好,你能做什么?

124

✅ Bot 应回复其能力介绍,并提到无人机相关信息。

3:无人机状态及设备状态查询

无人机现在是什么状态?
检查一下各个系统的连接状态

125

✅ 以上测试全部通过,说明 AI 智能体通信链路已完全打通,可随时通过 Telegram 查询无人机的实时状态。控制及智能任务执行可参考后续教程 6


附录

常见问题排查

现象可能原因解决方案
curl: (60) SSL certificate problem树莓派系统时间不正确sudo timedatectl set-ntp truesudo timedatectl set-timezone Asia/Shanghai
openclaw: command not foundNVM 安装的 Node.js 环境未加载执行 source ~/.bashrc,确认 NVM 配置已写入 .bashrc
安装脚本下载失败网络问题,无法访问 GitHub 或安装源配置代理:export https_proxy=http://代理地址:端口 后重试
Telegram Bot 没有响应服务未启动或网络问题依次检查:openclaw healthopenclaw logs --follow → 确认树莓派能访问 Telegram API
API 调用超时网络状态不稳定检查网络连接
Bot 回复但不查询无人机状态MCP Server 未正确注册执行 openclaw mcp list 确认已注册,路径正确后 openclaw gateway restart
Bot 回复"暂无数据"MAVROS 未启动或飞控未连接确认 MAVROS 已运行并显示 FCU connected,用 ros2 topic list 检查话题是否存在
ModuleNotFoundError: No module named 'mcp'Python MCP SDK 未安装执行 pip install mcp
MCP Server 启动后立即退出ROS 2 环境未正确加载确认 openclaw.jsonenv 中包含了 ROS 2 所需的环境变量

如果进程能正常启动且不报错,说明 ROS 2 和 MCP SDK 的环境配置都是正确的。

参考资源