Flix:ESP32 四旋翼从零制作、调试与源码学习教程

基于 okalachev/flix 当前 master 分支文档与关键源码整理。目标不是“把 README 翻译一遍”,而是给出一条真正可执行的制作、验证、首飞、仿真和二次开发路径。

Arduino / C++ESP32MAVLinkQGroundControlGazebo 11Python / pyflixCascade PID
一句话理解:Flix 是一个教育/研究导向的开源飞控 + 四旋翼参考设计。真正值得学习的地方,是它用非常小的 Arduino 代码把“传感器 → 姿态估计 → 级联控制 → 电机混控 → MAVLink/日志”完整串起来。
安全边界:调电机、刷参数、写 AUTO 脚本时先拆桨;电机测试必须用电池而不是 USB;第一次真实飞行应在空旷区域、低高度、短时间完成。锂电池和高速螺旋桨都可能造成伤害。
01

项目定位:Flix 到底解决什么问题

Flix(flight + X)是一套“从通用元件开始做一台能飞的四旋翼”的开源方案。仓库同时包含飞控固件、硬件/装配文档、Gazebo 仿真、Python 控制库和日志分析工具。

通用元件→ESP32 飞控→Wi‑Fi / ESP‑NOW→MAVLink→QGroundControl / pyflix
能力当前实现学习价值
飞控固件Arduino/C++,主循环约 1000 Hz,代码规模较小适合看懂飞控最小闭环
姿态估计陀螺积分 + 加速度计修正 + 低通滤波理解 IMU、姿态和漂移修正
控制RAW / ACRO / STAB / AUTO;姿态环 → 角速度环理解级联 PID 与电机混控
通信MAVLink,经 Wi‑Fi / ESP‑NOW学习地面站、遥测、参数与控制协议
仿真Gazebo 11 运行原 Arduino 逻辑先仿真再上真机,便于调参与开发
Pythonpyflix:遥测、参数、CLI、AUTO 姿态/角速度控制快速做自动飞行实验
位置控制项目仍标注开发中;pyflix 的 position / velocity 目标暂不可用适合作为后续扩展课题
建议:如果主要目的是“学飞控”,优先走 Gazebo → 源码 → 真机;如果主要目的是“做一台能飞的样机”,优先走预编译固件 → QGroundControl → 真机校准。
02

架构与源码:先把数据流看懂

2.1 仓库主结构

okalachev/flix
├─ flix/ ············ 飞控 Arduino 固件
├─ docs/ ············ 组装、使用、架构、排错、日志文档
├─ gazebo/ ·········· Gazebo 物理模型 + 插件 + Arduino API 模拟
├─ tools/ ············ pyflix、日志下载/转换等工具
├─ Makefile ········· 构建、烧录、监视、仿真、日志命令
└─ README.md ········ BOM、接线、项目入口

2.2 1000 Hz 主循环

flix/flix.ino 的主循环非常直白,几乎就是整套飞控的数据流图:

void loop() {
    readIMU();
    step();
    readRC();
    estimate();
    control();
    sendMotors();
    handleConsole();
    processMavlink();
    readVoltage();
    logData();
    syncParameters();
}
IMU→时间步 dt→RC 输入→姿态估计→控制器→PWM 电机

这也是推荐的源码阅读顺序:先看数据从哪里来,再看它如何被估计、控制和输出。

2.3 姿态估计

  • estimate.ino 先对 gyro 做低通滤波,默认注释约 40 Hz。
  • 每个周期用角速度 × dt 形成旋转向量,再更新四元数姿态。
  • 无人机静止/落地时,用加速度计重力方向修正姿态漂移。
  • 飞行中不直接信任加速度计作为重力方向,而是用很弱的 level correction 抑制长期漂移。
关键理解:这不是复杂 EKF,而是一套为教学和小型飞行器设计的轻量互补思路。优势是代码少、可读;代价是对高动态、外部加速度和高级导航能力有限。

2.4 级联控制与电机混控

control.ino 默认模式为 STAB。姿态控制先把姿态误差变成角速度目标,再由角速度 PID 产生三轴 torque target,最后与总推力一起混到四个电机。

attitudeTarget→Attitude PID→ratesTarget→Rate PID→torqueTarget→Mixer
FL = thrust + roll - pitch + yaw
FR = thrust - roll - pitch - yaw
RL = thrust + roll + pitch - yaw
RR = thrust - roll + pitch + yaw

源码默认参数可作为阅读起点,而不是直接当作“任何机架都通用”的最终调参:roll/pitch rate PID 约 P=.05 I=.2 D=.001,yaw rate P 约 .3;姿态 P 则 roll/pitch 约 6、yaw 约 3。

2.5 其他关键模块

文件职责看什么
imu.ino读取 IMU、校准总线、采样率、偏置与安装方向
rc.inoRC 输入与校准通道映射、控制量归一化
estimate.ino姿态估计gyro 积分、acc 修正、四元数
control.ino控制器STAB/ACRO/AUTO、级联 PID、mixer
motors.inoPWM 输出频率、分辨率、ESC/有刷映射
mavlink.inoMAVLink遥测、地面站、Python 控制入口
parameters.inoNVS 参数ESP32 Preferences 持久化
console.ino串口/MAVLink CLI调试命令与运行状态
03

BOM:先准备哪些硬件和工具

类别建议/仓库当前说明数量
主控ESP32 Mini;也支持 ESP32-S3 / ESP32-C31
IMUGY-91 / MPU-9250/6500、ICM-20948、MPU-6050 等受支持型号1
电机8520 3.7V 有刷电机,注意轴径与桨孔匹配4
螺旋桨55 mm 或 65 mm,A/B 桨按旋向匹配4
MOSFETUMW 100N03A 或兼容型号;仓库特别提醒不要混用不可靠同名厂牌4
电阻10 kΩ,作为下拉/电压测量网络若干
电池1S 3.7V Li‑Po;README 建议足够放电倍率,示例要求 25C 或更高1
机架仓库提供 STL/STEP;建议层高 0.2 mm、线宽 0.4 mm、100% infill1 套
遥控可选:SBUS 遥控接收机;或 Wi‑Fi + 手机/QGC;或 USB 双摇杆1
工具3D 打印机、烙铁、焊锡/助焊剂、螺丝刀、万用表—
买件前先锁定主控 + IMU:这两个选择会影响固件板型、IMU 参数、SPI/I²C 接线和机架安装孔位。不要所有件买完以后才决定。
04

硬件制作:从机架到完整接线

打印并检查机架

从仓库 README 中下载主机架、上盖/ESP32 holder、IMU 垫片 STL/STEP。打印后检查四个电机孔是否紧,IMU 安装面是否平整。

规划电源与 MOSFET 区域

先在机架上摆放 ESP32、4 个 MOSFET、电池线和 IMU,确认不会压到螺旋桨扫掠区。MOSFET 需要下拉电阻,线路尽量短、固定牢靠。

安装四个电机

电机必须非常紧。仓库明确指出,松动和振动会破坏姿态估计并导致飞行不稳。如果孔偏松,用很小的胶带片做紧固,不要让电机在孔内晃动。

按默认机架映射接电机

电机位置旋向桨线色ESP32 默认 GPIO
M0后左逆时针B黑/白GPIO12
M1后右顺时针A蓝/红GPIO13
M2前右逆时针B黑/白GPIO14
M3前左顺时针A蓝/红GPIO15

安装 IMU

使用垫片和 M3×5 螺丝固定 IMU;先保证板子不翘曲、安装方向可确定。后续通过 IMU_ROT_ROLL/PITCH/YAW 参数把传感器坐标系对到机体坐标系。

接 IMU 总线

README 的 ESP32 Mini + SPI 示例为:SCK→GPIO18、MOSI→GPIO23、MISO→GPIO19、CS→GPIO5,供电 3.3V/GND。若用 I²C 或其他主控,以参数配置和板卡能力为准。

固定 ESP32 与上盖

官方简要装配文档使用双面胶把 ESP32 固定到上框,再用 M1.4×5 螺丝固定上框。重点不是“胶带必须相同”,而是主控不能在飞行中移动或接触桨叶。

可选:SBUS 接收机与电压监测

SBUS 示例信号接 GPIO4;电池电压监测要通过分压后进入 ADC1 引脚,不能把 4.2V 电池直接送进 ESP32 ADC。

通电前万用表检查

  • VCC 与 GND 无短路。
  • 电池极性正确。
  • 每个 MOSFET 的 Gate 没有悬空。
  • IMU 供电是 3.3V。
  • 电机线没有裸露互碰。
05

固件烧录:两条路线

路线 A:预编译固件,最快让板子跑起来

  1. 按 ESP32 / ESP32-S3 / ESP32-C3 / Flix2 板型下载仓库文档提供的 merged binary。
  2. 打开 ESP32 Web Flasher,连接板子的串口。
  3. 选择下载的固件。
  4. Flash address 设置为 0。
  5. Program,完成后重启。

适合“先验证硬件/网络/地面站”的第一轮。

路线 B:源码构建,适合学习与二次开发

Arduino IDE

  1. Arduino IDE 2.x。
  2. 安装 ESP32 core 3.3.10。
  3. Library Manager 安装 FlixPeriph 最新版。
  4. 安装 MAVLink 2.0.33。
  5. 打开 flix/flix.ino。
  6. ESP32 Mini 选择 WEMOS D1 MINI ESP32;S3 常用 ESP32S3 Dev Module。
  7. Tools → Core Debug Level → Error。
  8. Build & Upload。

命令行

git clone https://github.com/okalachev/flix.git
cd flix
make
make upload
make upload monitor

Makefile 会安装指定 ESP32 core 和 Arduino 库。S3/C3 可以通过 BOARD 覆盖 FQBN:

make BOARD=esp32:esp32:esp32s3 upload
最小验证技巧:甚至可以先只拿一块“裸 ESP32”刷固件,不接 IMU 和电机。只要 flix Wi‑Fi 出现,Console/QGroundControl 基础链路能工作,就说明构建与网络部分大体正确。
06

首飞前配置:按这个顺序,不要跳步

Step 1
连接 QGroundControl

上电后连接 Wi‑Fi flix,默认密码 flixwifi。启动 QGroundControl,通常会自动看到遥测。

Step 2
进入 Console

USB 串口使用 115200;无线方式:QGroundControl → Analyze Tools → MAVLink Console。先执行 help。

Step 3
配置 IMU 型号与总线

设置 IMU_MODEL、IMU_BUS 与 SPI/I²C 引脚参数,然后 reboot。执行 imu,要求 status: OK,采样率约 1000 Hz。

Step 4
配置 IMU 安装方向

通过 IMU_ROT_ROLL / PITCH / YAW 对齐机体坐标。机体坐标系为 X 向前、Y 向左、Z 向上。

Step 5
校准加速度计

Console 执行 ca,严格按照终端提示做多姿态校准。

Step 6
配置电机输出

非默认引脚设置 MOTOR_PIN_FL/FR/RL/RR。若改用无刷 ESC,还要重新设置 PWM stop/min/max 与频率。

Step 7
拆桨做单电机测试

执行 mfr、mfl、mrl、mrr,逐个核对“位置 + 旋向 + A/B 桨”。此时不要装桨。

Step 8
核对姿态显示

拿起机体做 roll/pitch/yaw,QGroundControl 姿态指示必须与真实方向一致。方向错时先修 IMU orientation,不要用“反向电机”去补。

电机配置时必须拆桨。仓库还特别提醒:不要从 USB 给飞行电机供电做测试,电机测试应使用电池。
07

控制与首飞:先求可控,再求性能

7.1 三种人工控制方式

  • 手机:QGroundControl Virtual Joystick 或 Mavlink Joystick,经 Wi‑Fi。
  • SBUS 遥控:设置 RC_RX_PIN,用 rc 检查接收,再执行 cr 校准。
  • USB 双摇杆:电脑运行 QGroundControl,经 Wi‑Fi 连接无人机,在 Vehicle Setup → Joystick 中校准。

7.2 默认摇杆与解锁

默认左摇杆:Throttle + Yaw;右摇杆:Pitch + Roll。

动作手势源码对应
Arm油门最低 + Yaw 最右throttle < .05 && yaw > .95
Disarm油门最低 + Yaw 最左throttle < .05 && yaw < -.95

7.3 第一趟真实飞行建议

  1. 满电电池,检查 4 个桨均无裂纹。
  2. 先在地面解锁,观察四电机是否平稳、无强振动。
  3. 轻推油门,只做短暂“减重/离地”。
  4. 如果机体明显向固定方向翻,立刻收油,回到电机旋向、桨型、IMU 朝向检查。
  5. 能够稳定离地后,再尝试 0.5–1 m 悬停。
  6. 先保持默认 STAB,不要第一飞就切 ACRO/AUTO。
08

Gazebo 仿真:最值得先跑通的一步

Flix 的模拟器使用 Gazebo 11,并把飞控 Arduino 逻辑放进虚拟物理环境。项目文档推荐 Ubuntu 20.04;近期 macOS 上 Gazebo 11 兼容性不理想。

安装与启动

git clone https://github.com/okalachev/flix.git
cd flix

# 安装 Arduino CLI 后,安装 Gazebo 11 / libgazebo11-dev / SDL2 等依赖
make simulator

模拟器三层结构

gazebo/
├─ models/flix/flix.sdf ··· 机体物理模型
├─ simulator.cpp ········· Gazebo 插件:传感器/输入 ↔ Arduino 飞控
└─ Arduino.h ············· 在插件环境中模拟部分 Arduino API

仿真启动后,可以像真机一样用 QGroundControl 手机虚拟摇杆或 USB 摇杆控制。这样可以在没有螺旋桨风险的情况下熟悉 arm/disarm、参数、控制模式和 pyflix。

推荐实验顺序:Gazebo 稳定手飞 → 读取遥测 → 调一个 PID 参数 → 用 Python 发 AUTO 姿态目标 → 再把相同知识迁移到真机。
09

Python / pyflix:遥测与自动飞行实验

9.1 安装与连接

pip install pyflix
from pyflix import Flix

flix = Flix()   # 等待网络中的 Flix
print(flix.connected)
print(flix.attitude_euler)
print(flix.rates)
print(flix.voltage)

真机要先连接 flix Wi‑Fi;模拟器则让脚本与运行 Gazebo 的主机处于同一网络。

9.2 参数、CLI 与事件

pitch_p = flix.get_param('CTL_ATT_P_P')
flix.set_param('CTL_ATT_P_P', 5)

print(flix.cli('imu'))
print(flix.cli('time'))

flix.on('connected', lambda: print('Connected'))
att = flix.wait('attitude_euler', timeout=3)

9.3 AUTO 模式

pyflix 当前可发姿态目标、角速度目标,也可以直接设置 raw motor outputs。教程建议先只使用“姿态/角速度目标”,不要从 raw motor outputs 开始。

flix.set_mode('AUTO')

# 姿态目标:roll / pitch / yaw + thrust
flix.set_attitude([0.0, 0.0, 0.0], 0.25)

# 停止推力:AUTO 下会自动 disarm
flix.set_attitude([0.0, 0.0, 0.0], 0.0)
只在仿真或拆桨环境先运行。真实机体的悬停油门、PID、质量和电池状态都会影响结果,不能把示例的 thrust 数字当作通用悬停值。

当前文档明确标注 set_position 和 set_velocity 仍在开发、暂不可用。

9.4 与 QGroundControl 同时使用

flix-proxy

然后在 QGroundControl → Comm Links 新建 UDP link,端口 14560,即可让 pyflix 脚本和 QGC 同时工作。

10

日志与排错:飞不稳时怎么定位

10.1 日志

固件默认把飞行日志暂存在 RAM,文档默认容量约 10 秒、100 Hz。Wi‑Fi 连上无人机后:

make log
# CSV 保存在 tools/log/*.csv

make plot
# 用 PlotJuggler 打开最近一次日志

make log plot

也可以把 CSV 转成 ULog 给 FlightPlot,或转 MCAP 给 Foxglove Studio。

10.2 常见故障矩阵

现象优先检查验证方式
编译失败ESP32 core 版本、FlixPeriph/MAVLink 版本、板型对照 3.3.10 / MAVLink 2.0.33
QGC 不连接Wi‑Fi、VPN、Firewall确认连到 flix,临时关闭阻断项
乱码串口波特率115200
IMU 无数据型号、SPI/I²C、引脚、供电imu:status OK / rate≈1000
姿态方向反IMU rotation手动翻滚并看 QGC 姿态仪
起飞即翻电机位置/旋向、A/B 桨、IMU 方向拆桨逐个 mfr/mfl/mrl/mrr
高频抖动机架振动、PID、桨/电机不平衡先机械紧固,再看 rates / motor log
推力不足电池电压/放电能力、电机/桨、接线损耗满电约 4.2V;万用表 + 日志
11

二次开发:怎么改代码最稳妥

11.1 增加一个新子系统

官方架构文档给出的推荐方式:

  1. 新建一个 *.ino。
  2. 定义 setupXxx() 与 loopXxx()。
  3. 需要降频时使用 Rate,不要在飞行主线程里做阻塞延时。
  4. 在 flix.ino 的 setup() / loop() 注册调用。
Rate mySubsystemRate(100);  // 100 Hz

void loopMySubsystem() {
    if (!mySubsystemRate) return;
    // do something lightweight
}

11.2 增加参数

参数系统基于 ESP32 Preferences 存到非易失存储。一般流程:定义全局变量 → 加入 parameters 表 → 需要时绑定参数变化回调。这样 QGroundControl、Console 和 pyflix 都能继续复用统一参数入口。

11.3 不要在飞行主线程 delay()

官方文档明确警告:这是经典 Arduino 单线程设计,飞行时命令里使用阻塞 delay() 可能直接让飞控失去实时性。地面命令需要暂停时使用项目自己的 pause(),以保持 MAVLink 连接处理。

11.4 值得做的二次开发方向

  • 扩展 IMU 或磁力计/气压计支持,并评估更强的姿态/高度估计。
  • 把 AUTO 从姿态/角速度目标推进到位置/速度闭环。
  • 给 pyflix 增加实验脚本、轨迹生成与安全状态机。
  • 为日志增加更多控制误差字段,并自动生成调参图表。
  • 用 Gazebo 建立参数扫描/回归测试,减少真机试错。
12

源码学习路线:按 7 个阶段吃透

Phase 1
跑起来

裸 ESP32 刷固件 → Wi‑Fi → QGC → Console。目标:理解工具链与通信。

Phase 2
仿真飞

Gazebo → arm/disarm → STAB 手飞。目标:建立“参数变化 ↔ 飞行响应”的直觉。

Phase 3
读主循环

flix.ino → util/time → imu → estimate。目标:搞懂 1 kHz 数据链。

Phase 4
读控制器

control.ino → pid.h → motors.ino。自己手算一次姿态误差、rate error 和 mixer。

Phase 5
看通信与参数

mavlink.ino → console.ino → parameters.ino。目标:知道 QGC/pyflix 怎么进飞控。

Phase 6
Python 自动化

pyflix 读遥测 → 参数读取 → AUTO 姿态目标 → 事件等待。

Phase 7
真机 + 日志闭环

装配 → 校准 → 低高度首飞 → 下载日志 → 找误差 → 一次只改一类参数。

最终目标:你应该能够从一条日志解释“为什么抖、为什么漂、为什么某个电机饱和”,并能定位它属于机械、估计、控制、供电还是通信问题。
13

资料与源码入口

← 返回内容索引