NOTE · Autonomous Systems

Crazyflie 与 Loco Positioning:公开复现实验环境

基于官方资料整理 Crazyflie、Crazyradio 与 Loco Positioning 的可复现安装、验证和安全排障流程。

这是一份面向公开复现的实验环境笔记,保留官方工具链、历史配置、通用原则和安全排障流程。普通设备编号、内网地址和场地参数可作为已标注的历史样本;密码、令牌、私钥以及未公开且未闭环的控制与任务代码不在本文范围内。

先区分当前工具链与历史环境

用途建议环境状态
当前 Python 客户端与脚本Python 3.10+、当前版 cfclient / cflibBitcraze 当前文档支持
新的 ROS 项目ROS 2 与 Crazyswarm2Crazyswarm2 仍在开发和使用
复现原始 CrazyswarmUbuntu 20.04、Python 3.7、ROS Noetic历史环境,只用于复现

原始 Crazyswarm 仓库已经明确标注为“不再积极维护”,并建议新项目使用 Crazyswarm2。ROS Noetic 也已于 2025 年 5 月 31 日结束官方支持。因此,历史实验应锁定仓库提交、子模块和固件版本,不要把当前 cflib 直接覆盖到旧工作空间中。

安装当前 Bitcraze Python 工具

Bitcraze 当前文档要求 Python 3.10 或更高版本,并推荐使用虚拟环境:

python3 --version
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install cflib cfclient
cfclient

只有需要修改上游源码时才使用 editable install;普通使用直接安装 PyPI 发布版更容易复现。不要用 sudo pip 把 Python 包写入系统环境。

如果确实要调试 cflib 源码,官方文档给出的开发安装方式是:

git clone https://github.com/bitcraze/crazyflie-lib-python.git
cd crazyflie-lib-python
python -m pip install -e .

这时应同时记录仓库 commit 和 Python 环境;-e 表示解释器直接使用工作树中的代码,不适合当作没有版本记录的长期系统安装。

Linux USB 权限

Crazyradio 与 Crazyflie USB 连接应通过 udev 规则授权,而不是以 root 身份启动客户端。按照 Bitcraze 当前文档,将以下内容保存为 /etc/udev/rules.d/99-bitcraze.rules

# Crazyradio (normal operation)
SUBSYSTEM=="usb", ATTRS{idVendor}=="1915", ATTRS{idProduct}=="7777", MODE="0664", GROUP="plugdev"
# Bootloader
SUBSYSTEM=="usb", ATTRS{idVendor}=="1915", ATTRS{idProduct}=="0101", MODE="0664", GROUP="plugdev"
# Crazyflie over USB
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", MODE="0664", GROUP="plugdev"

确保当前用户属于 plugdev 组,重新加载规则后退出并重新登录:

sudo usermod -a -G plugdev "$USER"
sudo udevadm control --reload-rules
sudo udevadm trigger

如果系统还没有 plugdev 组,应先由管理员创建。不要再叠加第二套把设备授权给 users 或设为全员可写的规则;重复规则会让权限来源难以追踪。

规则不生效时,按以下顺序检查,而不是直接改成 MODE="0666"

lsusb
id
udevadm info --attribute-walk --name=/dev/bus/usb/BUS/DEVICE

其中 BUS/DEVICE 来自本机本次 lsusb 输出,只用于诊断,不应写死在脚本里。重新登录前,新增的用户组通常不会出现在当前会话中。

固件更新的安全边界

固件只从 Bitcraze 官方 release 页面获取,并通过 cfclient 的 bootloader 功能更新。刷写前至少完成以下检查:

  1. 确认固件目标与机体型号一致。
  2. 取下桨叶,保持电池电量稳定,并记录当前可工作的固件版本。
  3. 先更新一台机体并完成连接、传感器和日志验证,再批量处理。
  4. 不在 crazyflie_server、其他客户端或控制脚本占用无线链路时刷写。
  5. 更新后重新读取固件版本和参数,不把“能够连接”等同于“可以安全起飞”。

旧版 cfclient 的恢复刷写界面路径曾记录为 Connect → bootloader → Cold boot (recovery):机体从断电状态开始,按住电源键上电约三秒,待蓝色 M2 LED 闪烁后执行 cold boot,再选择官方 release 的 firmware-cf2*.zipProgram。这是历史界面提示;按钮名称和按键时序可能已改变。操作时必须取下桨叶,并以当前 官方 release 与当前客户端说明为准。

Loco Positioning 的公开配置原则

Loco Positioning System 使用 UWB 锚点提供绝对位置。常见模式的边界是:

  • TWR:标签主动与锚点测距,适合单机调试;官方给出的实际 3D 系统通常使用约 6 个锚点增加冗余。
  • TDoA2:最多 8 个锚点,可支持多个被动监听标签,但对锚点包围形成的有效空间更敏感。
  • TDoA3:支持更多锚点和多个标签,当前多机布置通常优先考虑这一模式。

可靠配置不依赖某一张示例布局图,而依赖以下可验证条件:

  1. 所有锚点运行兼容固件,且 ID 唯一。
  2. 所有锚点位置使用同一坐标系和米制单位;原点、轴向和高度定义有书面记录。
  3. 锚点围绕有效飞行体积布置,避免贴近大面积金属、墙面或其他强反射物。
  4. 在客户端中逐个确认锚点在线,并回读写入的位置。
  5. 先手持机体缓慢沿 +x+y+z 移动,确认估计方向、尺度和连续性正确。
  6. 扩大系统时一次只加入少量锚点,每一步都重新检查覆盖与异常值。

模式切换和锚点位置写入步骤会随固件与客户端版本变化,应以 Bitcraze 的 TDoA3 设置指南无线模式切换指南为准。

旧版官方教程还给过一组便于初次布置的经验值:anchor 尽量均匀分布在飞行区四周,相邻间距约 2 m 以上,天线与墙壁、天花板及大块金属至少留出约 15 cm。这些数值是旧硬件/旧文档中的起点,不是射频验收标准;场地变大、遮挡增多或硬件 revision 改变后,仍应通过在线率、残差和手持轨迹重新验证。

从空系统开始的配置顺序

下面给出仍有复用价值的操作顺序。后面的旧版客户端截图用于识别流程;按钮位置仍以当前版本为准:

  1. 更新一台 Crazyflie:安装 Loco deck,刷写与硬件匹配的官方 release,确认客户端能识别 deck。
  2. 逐个更新 LPS Node:节点通过 USB 进入 DFU/bootloader 后刷写官方节点固件;Windows 首次识别 bootloader 时按 Bitcraze 的 Zadig 指南安装驱动。
  3. 逐个配置节点:把节点设为 anchor,分配唯一 ID,并在实物外壳上贴同样的标签。一次只连接一个节点,避免把配置写错对象。
  4. 建立坐标表:选择统一原点与轴向,以米记录每个 anchor 的 (x, y, z);测量表、客户端输入值和实验配置应来自同一份清单。
  5. 写入后回读:不要把“点击了写入”当作成功。重新读取所有位置,逐项与测量表比较。
  6. 检查在线状态:把带 Loco deck 的 Crazyflie 放在有效空间内,确认预期 anchor 全部持续在线,而不是偶尔闪现。
  7. 手持验证:靠近各 anchor 核对显示的 ID,再沿三个正轴移动,确认方向、尺度和连续性。
  8. 最后切换模式:先在当前模式完成上述检查,再统一切到 TDoA2 或 TDoA3;任何混合模式都应视为不可用状态。

旧版参考布置常用 6 个 anchor 调试 TWR、8 个 anchor 调试 TDoA2。它们是便于起步的示例,不是“达到数量就一定可定位”的保证。TDoA2 的有效区域通常应位于 anchor 几何包围范围内;TDoA3 可扩展到更多 anchor,官方仍建议逐步扩展并在每一步测试。

旧版 LPS 工具与客户端界面参考

以下图片记录历史界面,不含凭据或真实姓名。它们适合帮助识别流程,但驱动、按钮和界面布局可能随版本变化。

节点进入 DFU 模式时,旧版图示要求连接 USB 的同时按住节点上的 DFU 按钮:

LPS Node 进入 DFU 模式

在旧版 LPS configuration tool 中选择官方 lps-node-firmware.dfu,执行更新,再按节点 reset:

选择 LPS Node 固件

执行 LPS Node 固件更新

LPS Node 更新后复位

普通 USB 连接下,旧版工具可以设置唯一 ID 和 anchor/TWR 模式;每个节点配置完都应贴实体标签:

旧版工具中的节点 ID 与模式配置

旧版文档使用的 8-anchor 与 6-anchor 示例布置如下。图中的坐标原点和尺寸仅属于示例场地:

8 个 anchor 的参考布局

6 个 anchor 的参考布局

旧版 LPS Node 图示列出 USB、圆孔电源和螺丝端子三种供电接口。实际电压、电流和极性必须按手中硬件 revision 的规格核对:

旧版 LPS Node 的供电接口

当时的硬件说明写的是:Micro-USB、圆孔插座和螺丝端子可以作为供电入口,螺丝端子线径上限约 0.5 mm²,输入为 5–12 V,电源至少能提供 150 mA。多个入口在旧版板卡上可同时连接,但这组规格不能跨 revision 直接套用;接线前必须核对板卡丝印、极性和当前官方硬件文档。

当时的配置把 Crazyflie 无线数据率设为 2 Mbit/s,以减少与 UWB 的干扰;是否适用仍应按当前固件和射频环境验证:

旧版客户端中的 2 Mbit/s 无线设置

启用 Loco Positioning 标签页后,先观察 anchor 状态,不要在红色或间歇状态下继续写位置或起飞:

旧版客户端中的 Loco Positioning 标签页

旧版客户端中的 anchor 在线状态

旧版位置配置流程依次是打开配置、从 anchor 读取、核对坐标、写回并等待回读变绿:

打开 anchor 位置配置

从 anchor 读取历史坐标

编辑历史 anchor 坐标

写回并回读 anchor 坐标

最后用三维视图逐个做 anchor identification,再切回 position estimation 手持移动验证:

逐个识别 anchor ID

手持验证位置估计

模式切换为什么必须回读状态

官方无线切换流程由客户端经 Crazyflie/Loco deck 向 anchor 发送配置消息。该链路没有可靠的逐节点确认回传,因此按钮操作完成不等于所有节点都已切换。切换时应:

  1. 先确认 Crazyflie 与所有 anchor 视线良好且状态稳定;
  2. 在客户端选择系统当前模式,再发送目标模式;
  3. 等待节点重启,检查所有状态是否重新变绿;
  4. 若出现混合模式,停止定位和飞行,必要时逐个通过 USB 修正。

从 TDoA 切回 TWR 时,Bitcraze 文档特别要求 TDoA 系统的主 anchor 仍可工作。无线方式无法恢复时,逐个 USB 配置比反复广播切换更可控。

旧版客户端中的切换过程如下:先强制 Crazyflie 按当前 TWR 状态观察 anchor,发出 TDoA 切换后状态短暂变红,最后回到 Auto 并确认目标模式及全部节点恢复绿色:

切换前选择 TWR

anchor 切换模式时的状态

切换后恢复 Auto 并确认 TDoA3

只读连接与日志验证

在发送任何飞行指令前,可以先用官方 cflib API 验证无线链路和日志目录。下面的示例只读取电池电压;URI 必须通过环境变量提供,避免把实际频道和地址提交到仓库。

import os

import cflib.crtp
from cflib.crazyflie import Crazyflie
from cflib.crazyflie.log import LogConfig
from cflib.crazyflie.syncCrazyflie import SyncCrazyflie
from cflib.crazyflie.syncLogger import SyncLogger


uri = os.environ["CFLIB_URI"]

cflib.crtp.init_drivers()
log_config = LogConfig(name="Battery", period_in_ms=1000)
log_config.add_variable("pm.vbat", "float")

with SyncCrazyflie(uri, cf=Crazyflie(rw_cache="./cache")) as scf:
    with SyncLogger(scf, log_config) as logger:
        timestamp, data, _ = next(iter(logger))
        print(timestamp, data["pm.vbat"])

运行前把 CFLIB_URI 设置成当前设备的 URI,但不要把该值写进公开文章、截图或版本库。

例如只在当前终端中提供:

export CFLIB_URI='YOUR_CRAZYFLIE_URI'
python connect_log_param.py

SyncCrazyflie 把底层异步连接封装为上下文管理器,离开 with 块时会断开连接;SyncLogger 则等待日志数据。首次测试只读取一个低频变量,是为了把无线连接、日志配置和控制命令分开验证。若连接失败,依次检查供电、Crazyradio USB 权限、URI、是否有 cfclient 或其他进程占用无线电,以及固件与客户端是否兼容。

旧教程使用过 radio://0/80/2M/E7E7E7E7E7 作为演示 URI。它是 Bitcraze 示例格式,不是凭据;字段依次涉及本机 radio 索引、频道、数据率和地址。真实机群可以沿用公开地址约定,但脚本应优先通过 CFLIB_URI 或部署配置读取,避免把示例误当成每台机体的固定地址。

最小连接示例

下面这个教学脚本只连接三秒,随后自动断开。这里用更清楚的 uri_helper 写法实现同一行为;它不会解锁电机,也不发送控制量:

import time

import cflib.crtp
from cflib.crazyflie import Crazyflie
from cflib.crazyflie.syncCrazyflie import SyncCrazyflie
from cflib.utils import uri_helper


uri = uri_helper.uri_from_env(
    default="radio://0/80/2M/E7E7E7E7E7"
)


def simple_connect():
    print("connected")
    time.sleep(3)
    print("disconnecting")


if __name__ == "__main__":
    cflib.crtp.init_drivers()
    with SyncCrazyflie(uri, cf=Crazyflie(rw_cache="./cache")):
        simple_connect()
python3 connect_log_param.py

若脚本不能连接,先关闭正在占用 Crazyradio 的 cfclient/server,再核对机体电源、URI、USB 权限和固件兼容性。with 块退出后连接会关闭,因此这个例子适合单独验证链路。

原始 Crazyswarm 的复现边界

原始 Crazyswarm 文档列出的最后一套主要环境是 Ubuntu 20.04、Python 3.7 与 ROS Noetic。这一组合现在应视为封存的历史实验环境:

  • 在隔离主机、容器或明确版本的系统镜像中复现。
  • 记录仓库 commit、所有 submodule commit 和固件版本。
  • 先运行仓库测试和模拟,再连接硬件。
  • 不从非官方教程拼接依赖,也不把新版本 Python 包强行装入旧工作空间。
  • 新开发优先评估 Crazyswarm2,不在 Crazyswarm1 上继续累积未经维护的补丁。

Crazyswarm1 的归档复现骨架

Ubuntu 20.04、Python 3.7、ROS Noetic 的组合只适合复现历史结果。与其执行一串没有版本号的安装命令,更可靠的方式是先从实验记录中找回确切 commit:

git clone https://github.com/USC-ACTLab/crazyswarm.git
cd crazyswarm
git checkout RECORDED_COMMIT
git submodule update --init --recursive
./build.sh

RECORDED_COMMIT 必须替换为当时实际使用的提交;如果找不到,就应明确标注“未能完全复现旧环境”。构建后先在同一 shell 中运行仓库测试和模拟:

source ros_ws/devel/setup.bash
cd ros_ws/src/crazyswarm/scripts
python3 -m pytest
python3 hello_world.py --sim

模拟通过只证明 Python/ROS 工作空间的基本链路可运行,不证明无线、定位或真实机体安全。

旧版依赖与 crazyflie_ros 安装记录

封存实验环境使用 Ubuntu 20.04、Python 3.7 和 ROS Noetic。下面只用于重建该环境;ROS Noetic 已结束支持,新机器应优先参考官方 ROS 归档文档。ROS Noetic 安装笔记是第三方历史参考,不替代官方文档。

历史命令中的 crazyflie_ros 有两处路径空格和构建目录错误,修正后的流程是:

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
git clone https://github.com/whoenig/crazyflie_ros.git
cd crazyflie_ros
git submodule update --init --recursive

cd ~/catkin_ws
catkin_make
source devel/setup.bash
rosrun crazyflie_tools scan

这仍不是“当前一键安装”:应锁定 crazyflie_ros commit,并让其 ROS/Python 依赖与归档系统一致。本文不提供 killall -9 Gazebo 快捷别名,因为它会无条件强杀同名进程,与 Crazyflie 安装本身也无关。

原始 Crazyswarm 的历史依赖清单如下。变量写法来自上游旧教程;在隔离环境中执行前仍要核对锁定提交的 README:

export CSW_PYTHON=python3

# 仅当锁定的旧脚本硬编码调用 `python` 时需要
sudo apt install -y python-is-python3

sudo apt install -y swig lib${CSW_PYTHON}-dev ${CSW_PYTHON}-pip
${CSW_PYTHON} -m pip install pytest numpy PyYAML scipy

# 模拟可视化按需选择
${CSW_PYTHON} -m pip install vispy matplotlib

# 需要录制或转码时再安装
sudo apt install -y ffmpeg
${CSW_PYTHON} -m pip install ffmpeg-python

cfclient 的源码安装入口适合需要复现旧客户端代码的隔离环境;普通使用仍优先采用前文的发布包:

git clone https://github.com/bitcraze/crazyflie-clients-python.git
cd crazyflie-clients-python
git checkout RECORDED_CFCLIENT_COMMIT
python3 -m pip install -e .

Crazyradio firmware 仓库rosrun crazyflie_tools scan -v 可以用于查找固件与回读版本;当时示例报告 99.55。历史命令会把 Crazyswarm prebuilt/ 中的二进制直接刷入 radio,但没有锁定二进制来源提交和校验值,因此本文不提供直接刷写命令。当前硬件只应使用与型号匹配、来源和校验均可确认的官方镜像。

历史配置文件中最重要的字段

原始 Crazyswarm 把通信、外部跟踪和机体参数分散在多个配置文件中。下面同时保留字段含义、占位模板和旧版示例;真实部署前必须逐项替换:

  • hover_swarm.launch:动作捕捉类型、动作捕捉主机占位符、对象跟踪模式;
  • crazyflies.yaml:机体 ID、无线频道、初始位置和机体类型;
  • crazyflieTypes.yaml:电压门限、marker arrangement、动力学限制和固件参数;
  • allCrazyflies.yaml:供 chooser 选择的完整机体清单。

示意配置如下,它故意使用不可直接飞行的占位符:

crazyflies:
  - id: CF_ID_A
    channel: RADIO_CHANNEL_A
    initialPosition: [X_A, Y_A, Z_A]
    type: default

历史 Crazyswarm 使用由机体编号派生的唯一无线地址,惯例是 0xE7E7E7E7<X>:例如 CF1 为 0xE7E7E7E701,CF10 为 0xE7E7E7E70A。修改频道或地址后,机体需要重启才能让新设置生效。

一种大规模配置建议是为机群分配多个无线电或频道,并按 8090100 循环分配。上游文档中的“每个无线电约 15 台”只能作为旧版经验容量;日志变量、控制频率、包大小和干扰都会改变实际上限,必须从小规模逐步压测。曾有示例称 49 台只需 3 个频道,这与“约 15 台/无线电”的前提不一致;即使只按该经验上限计算也至少需要 4 个容量单元,实际数量仍应由链路压测决定。

旧仓库也提供过 ./pc_permissions.sh 自动配置 Crazyradio 权限。它只作为锁定 checkout 内的历史入口;运行任何提权脚本前先阅读内容和 diff。当前系统优先使用前文列出的官方 udev 规则,不与脚本生成的第二套规则叠加。

旧版上游文档给出的机体清单示例如下;这些 ID、频道和位置不是当前场地的秘密,也不是可直接套用的部署配置:

crazyflies:
  - id: 1
    channel: 100
    initialPosition: [1.5, 1.5, 0.0]
    type: default
  - id: 2
    channel: 110
    initialPosition: [1.5, 1.0, 0.0]
    type: medium

唯一 marker arrangement 通常忽略 initialPosition 的匹配作用,但解析器仍可能要求该字段;重复 arrangement 依赖相对准确的初始位置建立 ID 映射;单 marker 可以使用较粗初值,但偏航观测能力更弱。具体语义以锁定 commit 的配置文档为准。

历史 Nokov 环境曾在 hover_swarm.launch 中使用以下字段。10.1.1.198 是当时的私网示例地址,不是当前部署保证:

motion_capture_type: "nokov"
motion_capture_host_name: "10.1.1.198"

对象跟踪模式的关键字段是:

# 每台机体使用唯一刚体,由动捕软件直接区分
object_tracking_type: "motionCapture"

# 重复 arrangement 或单 marker,使用原始点集跟踪
# object_tracking_type: "libobjecttracker"

二者只能选择一种。使用 libobjecttracker 时,应在动捕软件中关闭对这些 marker 的刚体占用;部分软件会从原始点云中移走已经分配给刚体的点,导致 Crazyswarm 看不到完整输入。

下面的 crazyflieTypes.yaml 示例保留 marker 和动力学字段,方便理解结构;数值来自历史示例,不代表当前机体标定结果。batteryVoltateCritical 的拼写来自旧版配置,不能擅自按英语拼写修改,必须查看对应 checkout 的解析代码:

crazyflieTypes:
  default:
    bigQuad: False
    batteryVoltageWarning: 3.8
    batteryVoltateCritical: 3.7
    markerConfiguration: 0
    dynamicsConfiguration: 0
  medium:
    bigQuad: True
    batteryVoltageWarning: 7.6
    batteryVoltateCritical: 7.4
    markerConfiguration: 1
    dynamicsConfiguration: 0

numMarkerConfigurations: 2
markerConfigurations:
  "0":
    numPoints: 4
    offset: [0.0, -0.01, -0.04]
    points:
      "0": [0.0177184, 0.0139654, 0.0557585]
      "1": [-0.0262914, 0.0509139, 0.0402475]
      "2": [-0.0328889, -0.02757, 0.0390601]
      "3": [0.0431307, -0.0331216, 0.0388839]
  "1":
    numPoints: 4
    offset: [0.0, 0.0, -0.03]
    points:
      "0": [-0.00896228, -0.000716753, 0.0716129]
      "1": [-0.0156318, 0.0997402, 0.0508162]
      "2": [0.0461693, -0.0881012, 0.0380672]
      "3": [-0.0789959, -0.0269793, 0.0461144]

numDynamicsConfigurations: 1
dynamicsConfigurations:
  "0":
    maxXVelocity: 2.0
    maxYVelocity: 2.0
    maxZVelocity: 3.0
    maxPitchRate: 20.0
    maxRollRate: 20.0
    maxYawRate: 10.0
    maxRoll: 1.4
    maxPitch: 1.4
    maxFitnessScore: 0.001

三种动作捕捉 marker 模式

旧版配置文档区分三种模式:

模式身份来源主要风险
唯一 marker arrangement动捕软件直接输出唯一刚体arrangement 太相似时可能误识别或切换身份
重复 marker arrangementCrazyswarm 从原始点集逐帧匹配依赖正确初始位置,长时间遮挡后可能无法恢复身份
单 marker每帧做分配,偏航主要依赖机载估计静止时无法由一个点观测偏航,不能与完整刚体跟踪等价

无论使用哪种模式,都必须把“配置中的机体 ID—无线地址—动捕刚体名”做成显式一对一表。普通 marker 坐标和场地初始位置可以公开,但必须标明硬件、坐标系、单位和适用 commit,不能让读者误以为是通用标定值。

旧文档中的标准多 marker 与单 marker 外观参考如下。照片只展示硬件布置,不代表可以直接复制其中的 marker 坐标:

标准 Crazyflie 的多 marker arrangement

单 marker arrangement 示例

重复 arrangement 的 marker 坐标可按旧流程测量:把目标机体放在动捕坐标系原点,机头指向 +x,确保当前 shell 已加载工作空间,再读取 marker 点并回填 YAML:

source ros_ws/devel/setup.bash
roslaunch crazyswarm mocap_helper.launch

回填前要记录单位、机体质心/marker frame 的定义和偏移;单 marker 的 points 表示 marker 相对机体质心的位置,不能拿多 marker 的绝对动捕坐标代替。

chooser 与首次测试

chooser.py 可以从 allCrazyflies.yaml 选择活动子集,并读取电池/固件或执行维护操作。它会改写活动的 crazyflies.yaml,因此运行前应备份配置并查看差异。任何刷写、重启或关机操作都应在 crazyflie_server 未占用无线电时进行。

历史启动命令是:

source ros_ws/devel/setup.bash
cd ros_ws/src/crazyswarm/scripts
python chooser.py

界面中的 Clear 取消全部选择,Fill 选择全部;曾有说明把 Fill 也写成“取消选择”,这里已纠正。选择变化会把 allCrazyflies.yaml 中的条目写入活动 crazyflies.yamlBatteryVersion 是只读检查;Power offReboot、STM/NRF flash 会改变设备状态,必须在 server 停止、桨叶取下且固件来源已核验时使用。

旧版 Crazyflie chooser 界面

首次实机验证不应从多机 hello_world.py 开始。先确认模拟、配置解析和 RViz 位姿,再按后文的无桨、手持、单机、多机顺序推进。

用于复现实验环境的 server 与示例脚本命令如下。它们是归档入口,不是跳过安全检查的授权:

source ros_ws/devel/setup.bash
roslaunch crazyswarm hover_swarm.launch

# 另开同样 source 过工作空间的终端
cd ros_ws/src/crazyswarm/scripts
python hello_world.py

只有在 RViz 中所有活动机体位姿新鲜、身份正确,并完成无桨和单机边界检查后,才允许进入实机示例;配置多台机体时,旧脚本可能只任选一台,不能把这个行为当成机群级验收。

历史实物实验记录(未复核)

这一节说明有解释价值的接口、方法和现象,但不提供整段未闭环控制程序,也不把它们包装成推荐方案。

ROS 动捕接口与数据形状

旧程序 4v1-3.py 为每个索引 i 订阅两类话题:

/vrpn_client_node/U_Tracker{i}/pose   geometry_msgs/PoseStamped
/vrpn_client_node/U_Tracker{i}/twist  geometry_msgs/TwistStamped

回调把三维位置和线速度分别存入 current_positions[i]current_velocities[i];任务层再取前两维形成平面位置/速度数组,并把速度裁剪到 [-target_v_max, target_v_max]。原实现只等待“每个位置槽非空”,没有同时检查消息时间戳、新鲜度、坐标系和速度是否到齐;复用这个接口时必须补上这些门限。

掉高缓解现象

当时实验曾用一个简单的垂直速度开关控制:高度高于 0.5 m 时给 -0.05 m/s,低于 0.5 m 时给 +0.05 m/s。实验说明同时写道期望高度是 1 m,因此目标描述与控制阈值并不一致。下面的曲线只作为历史现象,而不是控制效果证明:

Crazyflie 掉高缓解实验曲线

开关控制还缺少死区、滤波、饱和/加速度限制、定位新鲜度与失效保护,不能直接用于当前实机。

CF ID 与动捕 tracker ID 的匹配

身份关联的思路分为两级:

  1. 初始时刻计算 tracker 位置与预期初始位置的欧氏距离矩阵,再为每个预期位置选择最近 tracker;
  2. 运行中以“上一帧位置—当前观测位置”的距离作为成本,调用 scipy.optimize.linear_sum_assignment 做一对一分配。

第一种独立最近邻可能把同一个 tracker 分给多台机体;第二种虽然满足一对一分配,但原实现没有速度预测、距离门控、遮挡/漏检处理、时间戳检查和身份切换迟滞。它适合解释当时的调试路径,不足以证明实时身份关联已经闭环。

固定动作周期的未解决现象

当时的程序尝试每 0.5 s 重新计算一次动作,同时在更高频主循环中持续重发当前速度指令,以避免长时间无新指令。实验中采用该结构后仍出现“飞机猝死”,原因当时未知。这个现象说明“持续调用发送函数”本身不能排除无线超时、调度抖动、定位陈旧、电池或固件保护及控制异常;没有日志和 failsafe 闭环前,不应把它作为可直接运行的控制代码。

分级安全验证

实机问题应按层隔离,不应直接在多机飞行中试错。

1. 无桨静态检查

  • 机架、电机、桨叶与电池无损伤、无松动和鼓包。
  • 客户端能稳定连接,固件和扩展板被正确识别。
  • 电池、姿态与定位日志持续更新,没有长时间停顿或跳变。

2. 手持定位检查

  • 在无桨状态移动机体,验证位置轴向、单位和延迟。
  • 遮挡一个定位源,确认系统的失效表现可观察且能触发停止条件。
  • 检查时间戳持续前进;“数值仍在变化”不能代替新鲜度检查。

3. 单机低风险飞行

  • 使用桨叶保护、明确安全区和可立即触发的停机手段。
  • 先短时低高度悬停,再逐步增加时长和运动范围。
  • 一旦出现姿态发散、定位陈旧、链路质量下降或电压越界,立即结束试验并回到上一层排查。

4. 多机扩展

  • 一次只增加少量机体。
  • 每台机体使用显式且唯一的配置映射;不要依赖数组顺序或运行时最近邻猜测身份。
  • 起飞前确认所有活动机体均有新鲜定位、可用链路和一致坐标系。

常见问题的排查顺序

现象首先检查不应直接做的事
Crazyradio 不可见USB 枚举、udev 规则、用户组、线缆与端口用 root 长期运行客户端
能看到无线电但找不到机体电源、频道/地址配置、其他进程占用、固件兼容性反复批量刷写全部机体
某些锚点离线电源、唯一 ID、视线、模式和固件一致性直接用缺失锚点的定位起飞
位置漂移或跳变坐标、锚点几何、反射、遮挡、时间戳和估计器状态用额外控制增益掩盖定位故障
多机启动失败配置清单、逐机通信、无线信道负载跳过单机验证继续扩大规模

官方资料