1、背景
Baseflight是一款开源的飞行控制软件,最初是MultiWii项目的32位分支,旨在为多旋翼、固定翼和直升机等飞行器提供高效的飞行控制解决方案。Baseflight的发展历程可以追溯到MultiWii项目,它在性能和功能上进行了显著的优化和扩展,特别是在移植到更强大的32位处理器(如STM32)后,解决了早期8位单片机的性能瓶颈问题。
1.开源与社区支持
Baseflight是一个完全开源的项目,代码托管在GitHub上(https://github.com/multiwii/baseflight,下载其实几百k,纯c代码的空间都不大),获得了全球众多开发者和爱好者的贡献和支持。
2.兼容多种硬件平台:
Baseflight支持多种流行的飞行控制器硬件,例如NAZE32、SKYLINE32 Mini等,这些硬件具有高性价比和良好的性能表现(http://www.yinyanmodel.com/ch/ProductView.asp?ID=393)。
3.丰富的配置选项:
通过Baseflight Configurator图形用户界面,用户可以方便地调整飞控参数,如PID值、传感器校准等,以满足不同的飞行需求。
所谓调整飞控参数,就是调整源码中类似这种的变量:
float magneticDeclination = 0.0f; ? ? ? // calculated at startup from config
4.命令行CLI(command line)接口:
除了图形界面外,Baseflight还提供了命令行接口(CLI),允许用户直接通过串口进行高级设置和调试操作。
5.家族谱系:
Baseflight是Cleanflight和Betaflight等后续项目的前身。Cleanflight在其基础上增加了更多特性,而Betaflight则进一步提升了性能,成为目前FPV竞速无人机中最常用的固件之一https://oscarliang.com/baseflight-cleanflight-comparison/。
2、代码结构概述
三层架构:驱动-计算-工具
BaseFlight代码通常采用分层架构。最上层是与硬件交互的驱动层,负责采集传感器数据(如加速度计、陀螺仪、磁力计等)并将控制信号输出到电机驱动模块。中间层是核心控制算法层,包含姿态估计、导航和控制算法等。最下层是一些基础的工具函数库。
图1 笔者用source insight阅读baseflight全代码
主要文件的统计和解释
整个项目不过1.6w行,78个文件。最大的文件是cli.c超过一千行(包含大量的交互处理,所以代码多一点),其余文件均小于1k行,符合良好的c语言代码设计规范。数量最多的是drv_开头的驱动层程序。
其次是各种计算和控制文件,比如fw_nav,是前向导航的意思,也就是控制无人机往前飞。比如mixer是混合器,混合器是混合操控命令环境感知等等渠道的信息,形成最终的每个旋翼的电动机的控制命令。
最后是各种辅助文件,包含各种辅助函数,比如utils.c.h,比如printf.c.h。
还有一个不属于上面三类的重要文件makefile,是把baseflight编译成固件.bin的核心文件,一般不要去改。
图2 baseflight的简明项目报告
| 文件名 | 类型 | 大小 | 行数 | 解释 |
| board.h | src | 9197 | - | 包含与硬件板相关的定义和配置信息的头文件 |
| buzzer.c | src | 6377 | 113 | 用于控制蜂鸣器的源代码文件 |
| buzzer.h | src | 1756 | - | 包含与控制蜂鸣器相关的定义和函数声明的头文件 |
| cli.c | src | 48960 | 917 | 用于处理命令行界面的源代码文件 |
| cli.h | src | 158 | - | 包含与命令行界面相关的定义和函数声明的头文件 |
| config.c | src | 12733 | 316 | 用于处理配置信息的源代码文件 |
| drv_adc.c | src | 3462 | 63 | 用于控制ADC(模数转换器)的源代码文件 |
| drv_adc.h | src | 570 | - | 包含与控制ADC相关的定义和函数声明的头文件 |
| drv_adxl345.c | src | 3486 | 60 | 用于控制ADXL345加速度计的源代码文件 |
| drv_adxl345.h | src | 189 | - | 包含与控制ADXL345加速度计相关的定义和函数声明的头文件 |
| drv_ak8975.c | src | 1546 | 30 | 用于控制AK8975磁力计的源代码文件 |
| drv_ak8975.h | src | 122 | - | 包含与控制AK8975磁力计相关的定义和函数声明的头文件 |
| drv_bma280.c | src | 1332 | 27 | 用于控制BMA280加速度计的源代码文件 |
| drv_bma280.h | src | 51 | - | 包含与控制BMA280加速度计相关的定义和函数声明的头文件 |
| drv_bmp085.c | src | 9395 | 149 | 用于控制BMP085气压计的源代码文件 |
| drv_bmp085.h | src | 50 | - | 包含与控制BMP085气压计相关的定义和函数声明的头文件 |
| drv_bmp280.c | src | 7015 | 76 | 用于控制BMP280气压计的源代码文件 |
| drv_bmp280.h | src | 48 | - | 包含与控制BMP280气压计相关的定义和函数声明的头文件 |
| drv_gpio.c | src | 2900 | 62 | 用于控制GPIO(通用输入输出)的源代码文件 |
| drv_gpio.h | src | 1179 | - | 包含与控制GPIO相关的定义和函数声明的头文件 |
| drv_hcsr04.c | src | 4049 | 83 | 用于控制HCSR04超声波传感器的源代码文件 |
| drv_hcsr04.h | src | 185 | - | 包含与控制HCSR04超声波传感器相关的定义和函数声明的头文件 |
| drv_hmc5883l.c | src | 8102 | 93 | 用于控制HMC5883L磁力计的源代码文件 |
| drv_hmc5883l.h | src | 133 | - | 包含与控制HMC5883L磁力计相关的定义和函数声明的头文件 |
| drv_i2c.c | src | 16997 | 289 | 用于控制I2C总线的源代码文件 |
| drv_i2c.h | src | 546 | - | 包含与控制I2C总线相关的定义和函数声明的头文件 |
| drv_i2c_soft.c | src | 4692 | 168 | 用于软件模拟I2C总线的源代码文件 |
| drv_l3g4200d.c | src | 2709 | 48 | 用于控制L3G4200D陀螺仪的源代码文件 |
| drv_l3g4200d.h | src | 68 | - | 包含与控制L3G4200D陀螺仪相关的定义和函数声明的头文件 |
| drv_ledring.c | src | 1214 | 40 | 用于控制LED环的源代码文件 |
| drv_ledring.h | src | 95 | - | 包含与控制LED环相关的定义和函数声明的头文件 |
| drv_mma845x.c | src | 4010 | 41 | 用于控制MMA845X加速度计的源代码文件 |
| drv_mma845x.h | src | 52 | - | 包含与控制MMA845X加速度计相关的定义和函数声明的头文件 |
| drv_mpu.c | src | 18305 | 353 | 用于控制MPU6050/6500/9250 ?IMU(惯性测量单元)的源代码文件 |
| drv_mpu.h | src | 605 | - | 包含与控制MPU6050/6500/9250 ?IMU相关的定义和函数声明的头文件 |
| drv_ms5611.c | src | 5821 | 117 | 用于控制MS5611气压计的源代码文件 |
| drv_ms5611.h | src | 50 | - | 包含与控制MS5611气压计相关的定义和函数声明的头文件 |
| drv_pwm.c | src | 14536 | 237 | 用于控制PWM(脉冲宽度调制)输出的源代码文件 |
| drv_pwm.h | src | 2090 | - | 包含与控制PWM输出相关的定义和函数声明的头文件 |
| drv_serial.c | src | 1019 | 27 | 用于控制串口通信的源代码文件 |
| drv_serial.h | src | 1644 | - | 包含与控制串口通信相关的定义和函数声明的头文件 |
| drv_softserial.c | src | 14155 | 297 | 用于软件模拟串口通信的源代码文件 |
| drv_softserial.h | src | 1619 | - | 包含与软件模拟串口通信相关的定义和函数声明的头文件 |
| drv_spi.c | src | 3091 | 93 | 用于控制SPI(串行外设接口)的源代码文件 |
| drv_spi.h | src | 260 | - | 包含与控制SPI相关的定义和函数声明的头文件 |
| drv_system.c | src | 5970 | 79 | 用于系统级别控制的源代码文件 |
| drv_system.h | src | 564 | - | 包含与系统级别控制相关的定义和函数声明的头文件 |
| drv_timer.c | src | 8105 | 104 | 用于控制定时器的源代码文件 |
| drv_timer.h | src | 856 | - | 包含与控制定时器相关的定义和函数声明的头文件 |
| drv_uart.c | src | 14017 | 329 | 用于控制UART(通用异步收发器)通信的源代码文件 |
| drv_uart.h | src | 1231 | - | 包含与控制UART通信相关的定义和函数声明的头文件 |
| flash.bat | support | 681 | - | 用于执行闪存操作的批处理文件 |
| fw_nav.c | src | 9207 | 176 | 包含导航相关功能的源代码文件 |
| gps.c | src | 57742 | 812 | 包含GPS相关功能的源代码文件 |
| ibus.c | src | 2178 | 49 | 用于处理i-Bus(Flysky接收机的信号协议)的源代码文件 |
| imu.c | src | 15173 | 294 | 包含IMU(惯性测量单元)相关功能的源代码文件 |
| JLinkSettings.ini | - | 573 | - | J-Link调试器的配置文件 |
| main.c | src | 9069 | 202 | 程序的入口点,包含主要的执行逻辑 |
| Makefile | - | 6751 | - | 用于管理项目编译的Makefile文件 |
| mixer.c | src | 24140 | 342 | 用于控制飞行器的混合器的源代码文件 |
| mw.c | src | 43199 | 832 | 主要的飞行控制代码的源代码文件 |
| mw.h | src | 26858 | - | 包含与飞行控制相关的定义和函数声明的头文件 |
| printf.c | src | 6704 | 167 | 实现了格式化输出功能的源代码文件 |
| printf.h | src | 4923 | - | 包含与格式化输出相关的定义和函数声明的头文件 |
| rxmsp.c | src | 533 | 18 | 用于处理R-XSR接收机的源代码文件 |
| sbus.c | src | 3398 | 58 | 用于处理S.Bus(Futaba接收机的信号协议)的源代码文件 |
| sensors.c | src | 17656 | 406 | 用于处理各种传感器的源代码文件 |
| serial.c | src | 37714 | 703 | 用于控制串口通信的源代码文件 |
| spektrum.c | src | 5512 | 116 | 用于处理Spektrum接收机的源代码文件 |
| sumd.c | src | 1950 | 47 | 用于处理SUMD(Graupner接收机的信号协议)的源代码文件 |
| telemetry_common.c | src | 3237 | 90 | 包含通用遥测功能的源代码文件 |
| telemetry_common.h | src | 288 | - | 包含与通用遥测功能相关的定义和函数声明的头文件 |
| telemetry_frsky.c | src | 7648 | 172 | 用于处理FrSky遥测的源代码文件 |
| telemetry_frsky.h | src | 332 | - | 包含与FrSky遥测相关的定义和函数声明的头文件 |
| telemetry_hott.c | src | 9205 | 126 | 用于处理HoTT遥测的源代码文件 |
| telemetry_hott.h | src | 8791 | - | 包含与HoTT遥测相关的定义和函数声明的头文件 |
| utils.c | src | 3944 | 114 | 包含一些常用功能的实用函数的源代码文件 |
| utils.h | src | 211 | - | 包含与实用函数相关的定义和函数声明的头文件 |
表1 全文件列表
里面的main.c,会把主循环拉起来。就像任何持续执行的嵌入式系统,都是靠一个无限循环来保证器件的无限执行的。
?// loopy?while?(1) {?//这里,main启动无限循环? ??loop();?#ifdef?SOFTSERIAL_LOOPBACK? ??if?(loopbackPort1) {? ? ? ??while?(serialTotalBytesWaiting(loopbackPort1)) {? ? ? ? ? ??uint8_t?b =?serialRead(loopbackPort1);? ? ? ? ? ??serialWrite(loopbackPort1, b);? ? ? ? ? ??//serialWrite(core.mainport, 0x01);? ? ? ? ? ??//serialWrite(core.mainport, b);? ? ? ? };? ? }? ??if?(loopbackPort2) {? ? ? ??while?(serialTotalBytesWaiting(loopbackPort2)) {? ? ? ? ? ??serialRead(loopbackPort2);? ? ? ? };? ? }#endif}
区区几行代码,其内容却很丰富:
1. 无限循环 (while (1))
while (1) 创建了一个无限循环,使得代码不断重复执行。后面会说如何终止无限循环。
2. 调用 loop() 函数
这个loop() 就是飞行的主要函数,不在main。c里面,而在mw.c里面。mw的意思就是main work。之所以分开无非是软件工程良好代码规范的需要:不要让单个文件太过庞大。
3. 条件编译 (#ifdef SOFTSERIAL_LOOPBACK)
a. #ifdef SOFTSERIAL_LOOPBACK 是一个预处理器指令,表示只有在定义了 SOFTSERIAL_LOOPBACK 宏的情况下,才会编译下面的代码块。
4. 处理 loopbackPort1
a. 如果 loopbackPort1 存在(即不为 NULL 或假值),则进入一个内部循环。
b. while (serialTotalBytesWaiting(loopbackPort1)):此循环将持续运行,直到 loopbackPort1 上没有任何数据等待读取为止。
c. uint8_t b = serialRead(loopbackPort1);:从 loopbackPort1 读取一个字节的数据并存储在变量 b 中。
d. serialWrite(loopbackPort1, b);:将刚刚读取到的数据再写回到 loopbackPort1,这实际上是在模拟一个回环(loopback)操作。
5. 处理 loopbackPort2
a. 如果 loopbackPort2 存在,则进入另一个内部循环。
b. while (serialTotalBytesWaiting(loopbackPort2)):此循环将持续运行,直到 loopbackPort2 上没有任何数据等待读取为止。
c. serialRead(loopbackPort2);:从 loopbackPort2 读取一个字节的数据,但没有将其存储或重新发送出去,因此这似乎只是简单地丢弃数据。
其中值得注意的几个点:
● 死循环:由于 while (1) 是一个死循环,除非通过外部中断或其他方式终止程序,否则这段代码会一直运行下去。
● 资源消耗:在没有数据可读的情况下,serialTotalBytesWaiting 函数会持续返回 false,导致循环快速结束。然而,如果端口上有大量数据等待读取,这可能会导致 CPU 资源的高占用率。
● 调试和测试:#ifdef SOFTSERIAL_LOOPBACK 指令使得这部分代码可以根据需要启用或禁用。这对于调试和测试不同的功能场景非常有用。
3、如何安装?
假设某一位读者花了很大力气根据自己的设备改写了baseflight的程序,而且这位读者可能就是某个无人机厂商的创始人或者总师,需要把自己的产品(这些改进就是你的产品)变现才能盈利,那么我们怎么把修改后的程序“注入”飞控里面。
Baseflight 主要运行在基于 STM32 系列微控制器 (MCU) 的硬件平台上。STM32 是一种广泛应用于无人机飞行控制器的 32 位 ARM Cortex-M 处理器,具有高性能和丰富的外设接口。具体来说,Baseflight 支持多种常见的飞控板,例如 Naze32、F3、CC3D 等 。
| 步骤 | 描述 | 操作 |
| 1 | 准备工具和环境 | 下载并安装 Betaflight Configurator 或 Baseflight Configurator。 |
| 确保电脑上已安装相应的驱动程序。 | ||
| 2 | 连接飞控板 | 使用 ?USB 线将飞控板连接到电脑,确保连接稳固,飞控板上的电源指示灯亮起。 |
| 3 | 进入引导模式 | 某些飞控板需要按下并按住“Boot”按钮,然后插入 USB 线。 |
| 具体操作请参考飞控板说明书。 | ||
| 4 | 刷写固件 | 1.?打开 Betaflight Configurator 或 Baseflight ?Configurator。 |
| 2.?连接成功后,软件会自动检测到飞控板。 | ||
| 3.?选择最新的 ?Baseflight 固件文件(通常为 .bin 文件格式)。 | ||
| 4.?点击“Flash ?Firmware”按钮开始刷写过程。 | ||
| 5.?刷写完成后,确保所有步骤都正确无误,并等待提示确认固件已成功安装。 | ||
| 5 | 配置和校准 | 通过配置工具对飞控板进行必要的设置和校准。 |
| 包括陀螺仪、加速度计、磁罗盘等传感器校准。 | ||
| PID 参数调整。 |
表2 安装 Baseflight 固件到硬件中
那么固件哪来的?baseflight都是源文件啊。
| 步骤 | 描述 | 操作 |
| 1 | 获取源代码,加入你自己的修改 | 从 Baseflight 的官方 GitHub 仓库获取最新版本的源代码。 |
| 确保使用的是最新的稳定版本或开发分支。 | ||
| 2 | 配置硬件目标 | 根据使用的具体硬件平台(例如 STM32F103C8),配置 Makefile 或其他构建工具来指定目标硬件。 |
| 确保生成的固件与硬件兼容。 | ||
| 3 | 编译固件 | 使用 Makefile 或类似的构建工具来编译源代码。 |
| 通过命令行运行 make 命令启动编译过程。 | ||
| Makefile 会调用编译器、链接器等工具,生成 .bin ?文件。 | ||
| 4 | 生成二进制文件 | 编译完成后,生成的固件将以二进制文件形式保存,通常是 ?.bin 文件格式。这个文件可以直接烧录到 MCU 的闪存中运行。 |
| 5 | 验证固件(避免把硬件烧成砖) | 在烧录之前,建议对生成的 .bin 文件进行验证。 |
| 可以通过检查文件大小、校验和等方式进行初步验证。 | ||
| 一些开发者还会在固件中嵌入版本信息,以便在烧录前进行进一步验证。 | ||
| 6 | 烧录固件 | 使用适当的工具(如 ST-Link、JTAG 等)将生成的 .bin 文件烧录到飞控板的闪存中。 |
| 确保在烧录时选择正确的启动模式(例如,STM32F103C8 ?板有引导模式开关),以便正确加载新固件。 |
表3 baseflight固件生成
特别注意,除非你改变了源工程的文件结构,否则不要改原先的makefile文件。
1972