Betaflight 飞行控制器固件
Betaflight 是一款为多旋翼和固定翼飞行器设计的高性能飞行控制器软件(固件)。它源自Cleanflight,专注于提供顶级的飞行性能、前沿的功能特性以及对广泛硬件平台的支持。
功能特性
- 极致飞行性能:专为竞速和花式飞行优化的核心算法,提供流畅、响应迅速的操控体验。
- 黑匣子日志(Blackbox):记录飞行数据,支持SD卡、Flash和串口等多种存储设备,方便事后分析飞行姿态、PID响应和电机输出。
- 板载设置菜单(CMS/OSD):通过遥控器和图传屏幕即可配置PID、速率、VTX、LED灯带等几乎所有参数,无需连接地面站。
- 全面的GPS支持:支持GPS定位,具备GPS救援模式,在失控时自动返航,并支持GPS圈速计时功能。
- 丰富的调试工具:内置多种调试模式,可实时监控循环时间、电池数据、滤波器状态、PID输出等关键指标。
- 多平台硬件支持:支持STM32F4、F7、H7、G4、APM32、AT32、RP2350等多种MCU,适配各类飞控硬件。
- 灵活的通信协议:支持DShot、ProShot、MultiShot等多种电调协议,并兼容CRSF、SBUS、FPORT、GHST等主流接收机协议。
- 可配置的LED灯带:支持可编程的RGB LED灯带,用于状态指示、视觉报时和竞速效果。
安装指南
系统要求
- 支持的目标飞控板(如MATEK、Holybro、iFlight等品牌的F7/H7系列飞控)
- Windows/Linux/macOS操作系统
- Betaflight Configurator地面站(用于固件烧录和基础配置)
烧录步骤
- 下载并安装 Betaflight Configurator。
- 通过USB将飞控连接到电脑。
- 打开Configurator,选择正确的串口并连接。
- 进入“固件烧录”选项卡,选择您的飞控目标(Target)和固件版本。
- 点击“烧录固件”,等待烧录完成。
# 也可以使用命令行工具通过STM32CubeProgrammer烧录
STM32_Programmer_CLI -c port=USB1 -d betaflight_4.5.0_MATEKF722SE.hex -hardRst
从源码构建
# 克隆仓库
git clone https://github.com/betaflight/betaflight.git
cd betaflight
# 构建特定目标(例如MATEKF722SE)
make MATEKF722SE
使用说明
基础配置
- 连接地面站:通过Betaflight Configurator连接到飞控。
- 校准传感器:在“设置”页面校准加速度计,在“电源”页面校准电压和电流计。
- 配置接收机:在“接收机”页面选择协议(如SBUS、CRSF),并检查通道映射。
- 设置电机和电调:在“电机”页面选择DShot协议,测试电机转向。
- 调整PID和滤波器:使用默认PID通常可获得良好飞行效果,高级用户可进一步微调。
使用板载CMS菜单(通过OSD)
- 进入菜单:解锁后,将油门摇杆推到中间(Yaw Left)+ 俯仰摇杆向上(Pitch Up)。
- 导航:使用摇杆在菜单中移动、选择和退出。
- 快速调整:在“QUICK”菜单中可快速调整限幅、电机输出限制等常用参数。
- PID和速率调参:进入“PID TUNING”和“RATE PROFILE”菜单实时调整飞行手感。
- VTX设置:进入“VTX”菜单修改图传频段、信道和功率(需VTX支持SmartAudio或Tramp协议)。
黑匣子日志分析
// 黑匣子配置示例(通过CLI)
set blackbox_device = SDCARD // 设置存储设备为SD卡
set blackbox_sample_rate = 1/2 // 设置采样率为1/2
set blackbox_mode = NORMAL // 设置记录模式
save
日志文件(.BFL)可使用 Betaflight Blackbox Explorer 打开,分析Gyro轨迹、RC指令和PID响应。
核心代码
主程序入口 - 系统初始化与调度循环
这是飞控的主入口点,负责系统初始化、USB配置、多核支持以及启动主调度器。
// 来自 src/main/main.c
int main(int argc, char * argv[])
{
#ifdef USE_MAIN_ARGS
targetParseArgs(argc, argv);
#else
UNUSED(argc);
UNUSED(argv);
#endif
// 基本系统初始化
systemInit();
#ifdef USE_MULTICORE
// 多核支持的阶段初始化
multicoreExecuteBlocking(initPhase1);
multicoreExecuteBlocking(initPhase2);
#else
initPhase1();
initPhase2();
#endif
#ifdef USE_USB_MSC
// USB大容量存储模式(用于读取SD卡)
if (checkMsc()) {
initMsc();
return 0;
}
#endif
#ifdef USE_VCP
// 初始化USB虚拟串口
usbVcpInit();
#endif
#ifdef USE_MULTICORE
multicoreExecuteBlocking(initPhase3);
#else
initPhase3();
#endif
// 启动主调度器
run();
return 0;
}
// 主调度循环
void FAST_CODE run(void)
{
while (true) {
scheduler(); // 执行所有调度任务(PID循环、传感器读取等)
}
}
黑匣子日志 - 数据编码与写入
黑匣子模块负责将飞行数据(姿态、PID输出、电机值等)编码并写入存储设备。以下代码展示了如何将浮点数编码为字节流。
// 来自 src/main/blackbox/blackbox_encoding.c
int blackboxPrintf(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
const int written = blackboxPrintfv(fmt, va);
va_end(va);
return written;
}
void blackboxWriteUnsignedVB(uint32_t value)
{
// 可变字节编码:每个字节的最高位表示是否还有后续字节
do {
uint8_t b = value & 0x7F;
value >>= 7;
if (value) {
b |= 0x80; // 设置延续标志
}
blackboxWrite(b);
} while (value);
}
void blackboxWriteFloat(float value)
{
// 将浮点数作为32位整数写入
uint32_t *ptr = (uint32_t *)&value;
blackboxWriteU32(*ptr);
}
CMS菜单系统 - 动态配置界面
CMS(板载设置菜单)允许用户通过遥控器和图传屏幕直接修改飞控参数,无需连接地面站。以下代码定义了一个简单的菜单项。
// 来自 src/main/cms/cms_menu_main.c
// 主菜单入口定义
static const OSD_Entry cmsx_menuMainEntries[] =
{
{"--- MAIN MENU ---", OME_Label, NULL, NULL},
{"SETUP & CAL", OME_Submenu, cmsMenuChange_Calibration, &cmsx_menuSetupPopup},
{"POWER & BATTERY", OME_Submenu, cmsMenuChange, &cmsx_menuPower},
{"FAILSAFE", OME_Submenu, cmsMenuChange, &cmsx_menuFailsafe},
{"LED STRIP", OME_Submenu, cmsMenuChange, &cmsx_menuLedstrip},
{"VTX", OME_Funcall, cmsSelectVtx, NULL},
{"MOTORS", OME_Submenu, cmsMenuChange, &cmsx_menuMotors},
{"BLACKBOX", OME_Submenu, cmsMenuChange, &cmsx_menuBlackbox},
{"SAVE & EXIT", OME_Submenu, cmsMenuChange, getSaveExitMenu()},
{"BACK", OME_Back, NULL, NULL},
{NULL, OME_END, NULL, NULL}
};
// 菜单切换函数
const void *cmsMenuChange(displayPort_t *pPort, const void *ptr)
{
pCurrentDisplay = pPort;
return ptr; // 返回要跳转到的菜单指针
}
ADC驱动 - 电池电压和电流采集
ADC模块负责采集电池电压、电流和RSSI等模拟信号,并进行校准和滤波。
// 来自 src/main/drivers/adc.c
uint16_t adcGetValue(adcSource_e source)
{
// 根据不同的ADC源返回对应的采样值
switch (source) {
case ADC_BATTERY:
return adcValues[ADC_BATTERY];
case ADC_CURRENT:
return adcValues[ADC_CURRENT];
case ADC_RSSI:
return adcValues[ADC_RSSI];
default:
return 0;
}
}
// 内部Vref补偿,用于提高电压测量精度
uint16_t adcInternalCompensateVref(uint16_t intVRefAdcValue)
{
// 使用出厂校准值和当前测量值计算实际Vref+
return (uint16_t)((uint32_t)(adcVREFINTCAL * VREFINT_CAL_VREF) / intVRefAdcValue);
}
90F3fWfiJGCEIQVeneEqA3GIOQ5aPW66IMRMuWjhQ7g=