引言:为什么选择 SimpleBGC?
在无人机航拍、工业检测、机器人视觉等场景中,"稳定" 是核心需求 ------ 哪怕设备轻微抖动,也会导致画面模糊、数据偏差。而市面上专业稳像设备(如大疆 Ronin 系列)动辄数千元,且闭源架构无法自定义扩展。
SimpleBGC 的出现打破了这一局面:它以开源架构为核心,硬件设计文件、固件代码完全公开,成本仅为专业设备的 1/5~1/10,同时支持从微型运动相机到工业级负载的全场景适配。无论是电子爱好者 DIY、学生做科创项目,还是中小企业开发定制化设备,SimpleBGC 都是性价比极高的选择。
本文将从 "硬件电路设计→软件代码解析→软件算法分析" 三个维度,带大家彻底搞懂这款开源稳像平台的技术细节,即使是刚接触嵌入式开发的新手,也能跟着步骤理解原理、动手实践。
第一部分:SimpleBGC 硬件电路设计 ------ 开源架构下的模块化方案
SimpleBGC 的硬件设计遵循 "模块化、可扩展" 原则,核心是 "主控 + 传感器 + 电机驱动 + 电源 + 接口" 五大模块,不同型号(Tiny/Regular/Extended Long/OEM)的差异主要体现在模块配置上。本节将拆解每个模块的电路原理、元件选型和开源资源使用方法。
1.1 硬件核心参数概览:不同型号怎么选?
首先用表格明确各型号的关键差异,帮你快速定位适合自己场景的版本:
| 型号 | 主控芯片 | 传感器配置 | 电机驱动方式 | 最大负载 | 核心接口 | 适用场景 | 参考价格(美元) |
|---|---|---|---|---|---|---|---|
| Tiny | STM32F103C8T6 | 单 IMU(MPU6050) | 6 步换向 | 0.5kg | UART、I2C、PWM | 微型无人机、运动相机(GoPro) | 50~80 |
| Regular | STM32F103C8T6 | 单 / 双 IMU(MPU6050) | 6 步换向 | 1.5kg | UART、I2C、PWM、SPI | 中小型相机(索尼 ZV-1) | 80~120 |
| Extended Long | STM32F405RGT6 | 双 IMU(MPU9250)+ 编码器 | FOC(磁场定向控制) | 3kg | UART、I2C、SPI、CAN、USB | 专业航拍(大疆 Mavic 3)、工业检测 | 170~220 |
| OEM | STM32F405RGT6 | 可定制 IMU | 6 步 / FOC 可选 | 2kg | 板载接口(无外壳) | 嵌入式设备(机器人视觉) | 120~150 |
1.2 核心模块电路拆解:从元件到原理图
1.2.1 主控模块:稳像平台的 "大脑"
主控芯片是 SimpleBGC 的核心,负责读取传感器数据、运行姿态解算算法、控制电机转动。目前主流型号采用 STM32 系列,不同芯片的差异主要在算力、存储和接口数量上。
| 元件类别 | 型号选择 | 核心参数 | 功能描述 | 选型理由(开源视角) |
|---|---|---|---|---|
| 主控 MCU | STM32F103C8T6(基础版) | Cortex-M3 内核,72MHz 主频,64KB Flash,20KB RAM | 处理传感器数据、运行 PID 算法、控制电机 | 成本低(约 5 美元)、社区资源丰富、支持 Arduino 开发 |
| STM32F405RGT6(高端版) | Cortex-M4 内核,168MHz 主频,1MB Flash,192KB RAM | 支持多传感器融合(EKF)、FOC 驱动、CAN 通信 | 算力更强,满足工业级精度需求,开源固件已适配 | |
| 晶振 | 8MHz(外部)+32.768kHz(RTC) | 8MHz 用于 MCU 主频,32.768kHz 用于实时时钟 | 保证时钟稳定,避免算法计算偏差 | 通用型号,易采购,开源 PCB 设计已预留封装 |
| 复位电路 | MAX811REUR(复位芯片) | 低电平复位,复位时间 140ms | 防止 MCU 异常死机,确保系统稳定重启 | 性价比高,支持宽电压(2.97V~5.5V),适配电源模块 |
| 调试接口 | SWD(2 线) | 支持 ST-Link 调试、下载固件 | 开发时烧录代码、调试程序 | 简化布线,开源调试工具(OpenOCD)支持 |
主控最小系统原理图解读(通俗版):
- 晶振就像 "时钟",给 MCU 提供稳定的节拍,确保算法每一步计算的时间一致;
- 复位电路像 "重启按钮",当电压不稳或程序卡顿时,自动让 MCU 重新开始运行;
- SWD 接口是 "数据线",连接电脑和 MCU,用来把写好的代码烧进去,还能实时查看程序运行状态。
1.2.2 传感器模块:稳像平台的 "眼睛"
传感器负责感知设备的姿态(角度、角速度、磁场),是稳像的 "数据来源"。SimpleBGC 主要用 IMU(惯性测量单元),高端型号会加编码器提升精度。
| 传感器类型 | 型号 | 测量维度 | 核心参数 | 电路连接方式 | 适用场景 |
|---|---|---|---|---|---|
| 6 轴 IMU(基础) | MPU6050 | 加速度(3 轴)+ 角速度(3 轴) | 加速度精度 ±2g/±4g/±8g/±16g,角速度精度 ±250/±500/±1000/±2000°/s | I2C 接口 | 入门级稳像(Tiny/Regular 版) |
| 9 轴 IMU(高端) | MPU9250 | 加速度 + 角速度 + 磁场(3 轴) | 集成 AK8963 磁力计,磁场精度 ±4800μT | I2C 接口 | 抗干扰场景(工业检测) |
| 磁编码器 | AS5048A | 绝对角度(1 轴) | 14 位分辨率(精度 ±0.022°),支持 SPI 接口 | SPI 接口 | 电机位置校准(Extended Long 版) |
| 温度传感器 | DS18B20 | 温度(-55℃~125℃) | 精度 ±0.5℃(-10℃~85℃) | 1-Wire 接口 | 电机温度保护,避免过热损坏 |
传感器电路设计关键点(新手必看):
- ** decoupling 电容 **:每个传感器的电源引脚旁都要接 0.1μF 陶瓷电容,减少电源噪声对传感器数据的干扰(开源 PCB 已设计,不用自己加);
- I2C 地址冲突:如果用双 IMU(如 Regular 版可选双 MPU6050),需要通过硬件修改 I2C 地址(比如接不同的 ADDR 引脚电平),避免两个传感器数据冲突;
- 屏蔽处理:MPU9250 的磁力计容易受电机磁场干扰,开源设计中会将传感器 PCB 与电机驱动部分隔开,或加金属屏蔽罩(可选配件)。
1.2.3 电机驱动模块:稳像平台的 "肌肉"
电机驱动模块负责将 MCU 的控制信号转化为电机的转动动力,核心是 "驱动芯片 + 功率管"。
| 驱动方案 | 核心芯片 | 支持电机类型 | 最大电流 | 优点 | 缺点 | 适用型号 |
|---|---|---|---|---|---|---|
| 6 步换向(基础) | A4988 | 两相四线步进电机 | 2A | 成本低(约 3 美元)、电路简单 | 转矩脉动大、噪音高 | Tiny/Regular |
| FOC(高端) | DRV8313 | 三相无刷电机 | 10A | 转矩脉动小(<5%)、效率高(>90%) | 电路复杂、需要编码器配合 | Extended Long |
| 功率放大 | IRF540N(MOS 管) | 配合驱动芯片增强功率 | 33A(导通电流) | 支持大负载电机,抗过载能力强 | 需要散热片,体积较大 | Extended Long(重型负载) |
电机驱动电路通俗解释:
- 6 步换向:像 "走路" 一样,分 6 步依次给电机线圈通电,让电机转动,适合轻负载;
- FOC(磁场定向控制):像 "精准操控方向盘",实时调整电流方向和大小,让电机转动更平稳,适合重负载和高精度场景;
- MOS 管:像 "功率放大器",把驱动芯片的小电流放大,带动大电机转动,比如工业检测中 1kg 的相机,就需要 MOS 管增强动力。
1.2.4 电源模块:稳像平台的 "血液"
电源模块负责给所有模块供电,核心是 "电压转换 + 过载保护",不同型号的电源配置差异较大。
| 电源模块部分 | 核心元件 | 输入电压范围 | 输出电压 | 功能描述 | 适用场景 |
|---|---|---|---|---|---|
| 输入滤波 | 1000μF 电解电容 + 0.1μF 陶瓷电容 | 6V~24V(LiPo 电池) | - | 过滤电池供电的波动,避免电压不稳影响传感器 | 所有型号 |
| 电压转换(MCU) | AMS1117-3.3V | 5V~12V | 3.3V(1A 输出) | 给 MCU、传感器供电 | Tiny/Regular |
| 电压转换(高端) | LM1117-5V+TPS73633 | 12V~24V | 5V(2A)+3.3V(1A) | 给 FOC 驱动、编码器、MCU 同时供电 | Extended Long |
| 过载保护 | PTC 自恢复保险丝(1.5A) | - | - | 电流过大时自动断开,保护电路不被烧毁 | 所有型号 |
| 电池管理 | TP4056 | 5V(USB 输入) | 4.2V(锂电池充电) | 支持 USB 充电,适合手持设备场景 | Tiny(便携场景) |
电源模块新手避坑指南:
- 不要混用电压:MCU 和传感器需要 3.3V,电机驱动需要 5V 或 12V,接错电压会直接烧毁元件(开源 PCB 已标注电压,按标注接线即可);
- 选择合适的电池:Tiny 版用 2S LiPo 电池(7.4V),Extended Long 版用 6S LiPo 电池(22.2V),电池容量建议≥2000mAh,避免续航不足;
- 注意散热:电压转换芯片(如 LM1117)在大电流下会发热,需要贴散热片,尤其是 Extended Long 版,长时间运行会超过 60℃。
1.2.5 接口模块:稳像平台的 "手脚"
接口模块负责连接外部设备(如相机、飞控、遥控器),核心是 "通信接口 + 控制接口"。
| 接口类型 | 核心元件 / 协议 | 功能描述 | 连接设备示例 | 适用型号 |
|---|---|---|---|---|
| UART(串口) | MAX3232(电平转换) | 与飞控(如 Pixhawk)、蓝牙模块通信 | 飞控、HC-05 蓝牙模块 | 所有型号 |
| I2C | 板载排针 | 扩展传感器(如额外的温度传感器)、OLED 显示屏 | DS18B20 温度传感器、0.96 寸 OLED | 所有型号 |
| SPI | 板载排针 | 连接编码器、高速传感器 | AS5048A 编码器、SPI Flash | Extended Long/OEM |
| CAN 总线 | MCP2515(CAN 控制器) | 工业级设备通信,支持多设备联动 | 工业相机、PLC 控制器 | Extended Long |
| PWM 控制 | 板载排针 | 控制相机快门、变焦 | 索尼 A7S III 相机(变焦控制) | 所有型号 |
| USB | FT232RL(USB 转串口) | 连接电脑调试、烧录固件 | 电脑(调参软件) | 所有型号 |
接口使用通俗说明:
- UART:最常用的 "串口",比如用蓝牙模块连接手机,通过 APP 控制云台转动;
- CAN 总线:工业场景专用,比如工厂里多个 SimpleBGC 云台联动,同时调整角度,就需要 CAN 总线通信;
- PWM:控制相机的 "遥控器",比如无人机航拍时,通过 PWM 信号让相机自动变焦、拍照。
1.3 开源硬件资源使用指南:从文件到 PCB
SimpleBGC 的硬件设计文件完全开源,你可以直接下载使用,也可以修改后定制自己的版本。以下是详细步骤:
1.3.1 开源资源获取
| 资源类型 | 获取地址 | 包含内容 | 使用工具 |
|---|---|---|---|
| PCB 设计文件 | https://www.basecamelectronics.com/simplebgc32bit/ | KiCad 格式的 PCB 文件、原理图、元件清单(BOM) | KiCad 6.0+(开源 PCB 设计软件) |
| 3D 外壳模型 | Thingiverse:https://www.thingiverse.com/search?q=SimpleBGC | STL 格式的外壳、支架模型 | PrusaSlicer(3D 打印切片软件) |
| 元件采购清单(BOM) | GitHub 仓库的 BOM.csv 文件 | 元件型号、数量、供应商建议(如 Digikey) | Excel/WPS 表格 |
1.3.2 从 PCB 文件到实物的步骤(新手友好)
- 下载 PCB 文件:从 GitHub 克隆仓库,打开 KiCad 中的 "EVO-BGC.kicad_pcb" 文件,查看原理图是否符合需求(比如是否需要增加接口);
- 修改设计(可选):如果需要定制,比如给 Tiny 版增加 CAN 接口,在 KiCad 中添加 MCP2515 芯片和排针,调整 PCB 布局;
- 生成 Gerber 文件:在 KiCad 中导出 Gerber 文件(生产 PCB 需要的文件),包含 Top Layer(顶层)、Bottom Layer(底层)、Silkscreen(丝印)等;
- PCB 打样:将 Gerber 文件上传到 PCB 厂家(如嘉立创、捷配),选择 "双层板""1.6mm 厚度""喷锡工艺",新手建议打样 5 片(约 30 元);
- 采购元件:根据 BOM 表在淘宝、Digikey 采购元件,新手优先选 "国产替代型号"(如 MPU6050 用国产 GY-521 模块,约 5 元);
- 焊接组装:先焊接小元件(电阻、电容、MCU),再焊接大元件(驱动芯片、接口排针),最后焊接电机和传感器;
- 测试:用 ST-Link 连接 SWD 接口,给 PCB 通电(3.3V),查看 MCU 是否正常工作(用 OpenOCD 检测)。
1.3.3 硬件调试常见问题(表格汇总)
| 常见问题 | 可能原因 | 解决方法 | 工具需求 |
|---|---|---|---|
| MCU 无法烧录代码 | 1. SWD 接口接线错误;2. 复位电路故障;3. MCU 损坏 | 1. 检查 SWD 的 SWCLK/SWDIO 引脚是否接对;2. 更换复位芯片 MAX811;3. 重新焊接 MCU | ST-Link、万用表 |
| 传感器无数据输出 | 1. I2C 地址冲突;2. 传感器接线错误;3. 电源未供电 | 1. 修改传感器 ADDR 引脚电平(如 MPU6050 接 GND 改地址);2. 检查 SDA/SCL 引脚;3. 用万用表测传感器电源引脚电压 | 万用表、串口助手(查看数据) |
| 电机不转动 | 1. 驱动芯片未通电;2. 电机接线错误;3. 驱动芯片损坏 | 1. 测驱动芯片 VCC 引脚电压(如 A4988 的 VMOT);2. 交换电机线圈接线;3. 更换驱动芯片 | 万用表、示波器(测驱动信号) |
| 电源模块发热严重 | 1. 输出电流过大;2. 电压转换芯片未贴散热片;3. 短路 | 1. 减少电机负载(如换轻量级相机);2. 贴散热片;3. 检查 PCB 是否有短路(用万用表测通断) | 万用表、红外测温仪 |
第二部分:SimpleBGC 软件代码解析 ------ 开源固件的核心逻辑
SimpleBGC 的软件固件基于 C 语言开发,开源仓库包含 "驱动层 + 算法层 + 应用层" 三层架构,支持 STM32 系列芯片。本节将从代码结构入手,拆解关键模块的代码实现,即使是新手也能看懂核心逻辑。
2.1 软件代码架构概览:三层架构清晰分工
SimpleBGC 的固件代码遵循 "高内聚、低耦合" 原则,分为三层,每层负责不同功能,修改某一层不会影响其他层(比如改算法层的 PID 参数,不用动驱动层代码)。
| 代码层级 | 核心功能 | 包含文件(src 目录下) | 依赖关系 |
|---|---|---|---|
| 驱动层(底层) | 硬件驱动(传感器、电机、接口) | sensor/(传感器驱动)、motor/(电机驱动)、uart/(串口驱动) | 直接操作硬件寄存器,不依赖其他层 |
| 算法层(中层) | 姿态解算、PID 控制、电机驱动算法 | algo/(姿态解算)、pid/(PID 控制)、foc/(FOC 算法) | 依赖驱动层的传感器数据,给应用层提供控制接口 |
| 应用层(顶层) | 业务逻辑(模式切换、调参、通信) | app/(主应用)、config/(配置管理)、comm/(通信协议) | 依赖算法层的控制接口,处理用户需求(如调参) |
代码架构通俗比喻:
- 驱动层:像 "司机的手脚",直接控制硬件(比如传感器读取数据、电机转动);
- 算法层:像 "司机的大脑",根据手脚传来的信息(传感器数据),计算该怎么操作(比如 PID 输出控制信号);
- 应用层:像 "乘客的需求",比如乘客说 "转向",应用层就告诉大脑(算法层),让手脚(驱动层)执行转向动作。
2.2 核心代码模块拆解:从驱动到应用
2.2.1 驱动层代码:硬件的 "直接操控者"
驱动层代码直接操作 STM32 的寄存器,实现 "传感器读取""电机控制""接口通信" 三大功能,核心是 "简洁、高效"(因为要实时响应,不能占用太多算力)。
(1)传感器驱动:MPU6050 数据读取
以最常用的 MPU6050 为例,驱动代码的核心是 "初始化→读取原始数据→数据预处理"。
代码文件 :src/sensor/mpu6050.c核心函数:mpu6050_init ()(初始化)、mpu6050_read_data ()(读取数据)
c
运行
// 1. MPU6050初始化函数(通俗注释版)
bool mpu6050_init(void) {
// 步骤1:给MPU6050发送"唤醒"命令(默认是睡眠模式)
i2c_write_reg(MPU6050_ADDR, PWR_MGMT_1, 0x00); // PWR_MGMT_1是电源管理寄存器,0x00表示唤醒
delay_ms(10); // 等待10ms,让传感器稳定
// 步骤2:配置加速度计量程(±4g)
i2c_write_reg(MPU6050_ADDR, ACCEL_CONFIG, 0x08); // ACCEL_CONFIG是加速度计配置寄存器,0x08对应±4g
// 步骤3:配置陀螺仪量程(±500°/s)
i2c_write_reg(MPU6050_ADDR, GYRO_CONFIG, 0x10); // GYRO_CONFIG是陀螺仪配置寄存器,0x10对应±500°/s
// 步骤4:检查传感器是否正常(读取WHO_AM_I寄存器,正常返回0x68)
uint8_t who_am_i = i2c_read_reg(MPU6050_ADDR, WHO_AM_I);
if (who_am_i != 0x68) {
return false; // 传感器异常,返回错误
}
return true; // 初始化成功
}
// 2. 读取MPU6050原始数据(通俗注释版)
void mpu6050_read_data(imu_data_t *data) {
uint8_t buf[14]; // MPU6050输出14字节数据(加速度3轴×2字节 + 陀螺仪3轴×2字节 + 温度×2字节)
// 步骤1:从MPU6050的0x3B寄存器开始读取14字节数据
i2c_read_regs(MPU6050_ADDR, 0x3B, 14, buf);
// 步骤2:将原始数据(16位)转换为实际值(加速度单位:g,陀螺仪单位:°/s)
// 加速度转换公式:原始值 / 16384 = g(因为±4g量程对应16384 LSB/g)
data->accel_x = (int16_t)(buf[0] << 8 | buf[1]) / 16384.0f;
data->accel_y = (int16_t)(buf[2] << 8 | buf[3]) / 16384.0f;
data->accel_z = (int16_t)(buf[4] << 8 | buf[5]) / 16384.0f;
// 陀螺仪转换公式:原始值 / 65.5 = °/s(因为±500°/s量程对应65.5 LSB/(°/s))
data->gyro_x = (int16_t)(buf[8] << 8 | buf[9]) / 65.5f;
data->gyro_y = (int16_t)(buf[10] << 8 | buf[11]) / 65.5f;
data->gyro_z = (int16_t)(buf[12] << 8 | buf[13]) / 65.5f;
}
代码通俗解释:
- 初始化:告诉传感器 "醒来",并设置测量范围(比如加速度测 ±4g,陀螺仪测 ±500°/s);
- 读取数据:传感器输出的是 16 位原始数据(二进制),需要转换成我们能理解的单位(g、°/s),比如原始值 16384 对应 4g,所以除以 16384 就能得到实际加速度。
(2)电机驱动:A4988 6 步换向控制
以基础版的 A4988 驱动芯片为例,核心是 "按 6 步顺序给电机线圈通电",让电机转动。
代码文件 :src/motor/a4988.c核心函数:a4988_set_step ()(设置电机步数)、a4988_rotate ()(控制电机转动)
c
运行
// 6步换向的通电顺序(A4988的STEP和DIR引脚控制)
static const uint8_t step_sequence[6][2] = {
{1, 0}, // 第1步:A相通电,B相不通电
{1, 1}, // 第2步:A相、B相同时通电
{0, 1}, // 第3步:B相通电,A相不通电
{0, 1}, // 第4步:B相通电,A相不通电(反向)
{1, 0}, // 第5步:A相通电,B相不通电(反向)
{0, 0}, // 第6步:都不通电(复位)
};
// 控制电机转动指定角度(通俗注释版)
void a4988_rotate(float angle) {
// 步骤1:计算需要转动的步数(假设电机步距角1.8°,减速比1:10)
uint32_t steps = angle * 10 / 1.8f; // 总步数 = 目标角度 × 减速比 / 步距角
// 步骤2:设置转动方向(DIR引脚:高电平正转,低电平反转)
if (angle > 0) {
GPIO_SetBits(GPIOA, GPIO_Pin_0); // DIR=1,正转
} else {
GPIO_ResetBits(GPIOA, GPIO_Pin_0); // DIR=0,反转
}
// 步骤3:按6步顺序通电,每步延迟1ms(控制转速)
for (uint32_t i = 0; i < steps; i++) {
uint8_t step_idx = i % 6; // 循环取6步顺序
// 控制A4988的STEP引脚(高电平触发一步)
GPIO_SetBits(GPIOA, GPIO_Pin_1);
delay_us(10); // 保持高电平10μs,确保芯片识别
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
// 控制A、B相线圈通电(通过IO口控制)
GPIO_WriteBit(GPIOB, GPIO_Pin_0, step_sequence[step_idx][0]);
GPIO_WriteBit(GPIOB, GPIO_Pin_1, step_sequence[step_idx][1]);
delay_ms(1); // 延迟1ms,控制转速(延迟越小,转速越快)
}
}
代码通俗解释:
- 步距角:电机转一步的角度,比如 1.8°,转 200 步就是 360°(一圈);
- 6 步顺序:像 "走路" 的 6 个动作,依次给电机的 A、B 相通电,让电机持续转动;
- 转速控制:每步之间的延迟时间决定转速,延迟 1ms 就是每秒 1000 步,延迟 2ms 就是每秒 500 步。
(3)串口驱动:UART 通信(与飞控交互)
串口驱动负责与外部设备(如飞控、电脑)通信,核心是 "发送数据 + 接收数据",用中断方式实现(不占用 CPU 资源)。
代码文件 :src/uart/uart1.c核心函数:uart1_init ()(初始化)、uart1_send_data ()(发送数据)、USART1_IRQHandler ()(接收中断)
c
运行
// 串口接收缓冲区(存储接收到的数据)
uint8_t uart1_rx_buf[256];
uint16_t uart1_rx_len = 0;
// 串口初始化(波特率115200,8位数据位,1位停止位,无校验)
void uart1_init(void) {
// 步骤1:配置GPIO引脚(PA9=TX,PA10=RX)
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置TX引脚(PA9,推挽输出)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出(串口功能)
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置RX引脚(PA10,浮空输入)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入(接收数据)
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 步骤2:配置串口参数(波特率115200)
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 支持发送和接收
USART_Init(USART1, &USART_InitStruct);
// 步骤3:使能接收中断(接收到数据时触发中断)
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // RXNE:接收数据寄存器非空
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 中断优先级1
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
// 步骤4:使能串口
USART_Cmd(USART1, ENABLE);
}
// 发送数据(通俗注释版)
void uart1_send_data(uint8_t *data, uint16_t len) {
for (uint16_t i = 0; i < len; i++) {
// 等待发送缓冲区为空(TXE:发送数据寄存器空)
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, data[i]); // 发送1字节数据
}
// 等待所有数据发送完成(TC:发送完成)
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}
// 接收中断服务函数(接收到数据时自动调用)
void USART1_IRQHandler(void) {
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
// 读取接收到的1字节数据
uint8_t rx_data = USART_ReceiveData(USART1);
// 存储到缓冲区(避免溢出,最多256字节)
if (uart1_rx_len < 255) {
uart1_rx_buf[uart1_rx_len++] = rx_data;
}
// 清除中断标志
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
代码通俗解释:
- 波特率:通信速度,115200 表示每秒传输 115200 字节,飞控和云台通常用这个波特率;
- 中断接收:不用 CPU 一直等数据,接收到数据后会自动触发中断,把数据存到缓冲区,CPU 可以做其他事情;
- 发送数据:先等发送缓冲区为空,再发送数据,避免数据丢失(比如缓冲区满了还发,会覆盖之前的数据)。
2.2.2 算法层代码:稳像的 "核心大脑"
算法层是 SimpleBGC 稳像的关键,核心是 "姿态解算(知道当前角度)+ PID 控制(调整到目标角度)",高端型号还会加 FOC 算法。
(1)姿态解算:互补滤波(基础版)
姿态解算的目的是 "根据传感器数据,计算出设备当前的角度(如俯仰角、横滚角、偏航角)",基础版用互补滤波(计算量小,适合 STM32F1)。
代码文件 :src/algo/complementary_filter.c核心函数:comp_filter_update ()(更新姿态角度)
c
运行
// 全局变量:存储当前姿态角度(单位:°)
float pitch = 0.0f; // 俯仰角(上下转动)
float roll = 0.0f; // 横滚角(左右转动)
float yaw = 0.0f; // 偏航角(水平转动)
// 互补滤波更新姿态角度(通俗注释版)
void comp_filter_update(imu_data_t *imu_data, float dt) {
// 步骤1:从加速度计计算角度(静态稳定,但动态响应慢)
// 俯仰角:由accel_y和accel_z计算(公式:arctan2(accel_y, accel_z) × 180/π)
float pitch_accel = atan2f(imu_data->accel_y, imu_data->accel_z) * 180.0f / PI;
// 横滚角:由accel_x和accel_z计算(公式:arctan2(-accel_x, accel_z) × 180/π)
float roll_accel = atan2f(-imu_data->accel_x, imu_data->accel_z) * 180.0f / PI;
// 步骤2:从陀螺仪计算角度(动态响应快,但有漂移)
// 陀螺仪数据是角速度(°/s),乘以时间dt(s)得到角度变化量
float pitch_gyro = pitch + imu_data->gyro_x * dt;
float roll_gyro = roll + imu_data->gyro_y * dt;
float yaw_gyro = yaw + imu_data->gyro_z * dt;
// 步骤3:互补滤波融合(0.98×陀螺仪角度 + 0.02×加速度计角度,平衡动态和静态)
pitch = 0.98f * pitch_gyro + 0.02f * pitch_accel;
roll = 0.98f * roll_gyro + 0.02f * roll_accel;
// 偏航角:加速度计无法计算(z轴与重力无关),只能用陀螺仪(会漂移,需磁力计修正)
yaw = yaw_gyro;
}
代码通俗解释:
- 加速度计:像 "水平仪",能准确测量静态角度,但运动时会受加速度干扰(比如设备快速转动时,数据不准);
- 陀螺仪:像 "转速表",能准确测量动态角度变化,但静态时会漂移(比如静置时,角度慢慢变化);
- 互补滤波:像 "取长补短",用陀螺仪的动态数据(98% 权重)保证响应快,用加速度计的静态数据(2% 权重)修正漂移,得到稳定的角度。
(2)PID 控制:电机精准控制的核心
PID 控制的目的是 "让设备从当前角度转到目标角度,且无超调、无震荡",核心是 "比例(P)+ 积分(I)+ 微分(D)" 三个参数的配合。
代码文件 :src/pid/pid.c核心结构体与函数:pid_init ()(初始化)、pid_calculate ()(计算输出)
c
运行
// PID参数结构体(存储P、I、D参数和中间变量)
typedef struct {
float kp; // 比例系数
float ki; // 积分系数
float kd; // 微分系数
float target; // 目标值(比如目标角度)
float current; // 当前值(比如当前角度)
float integral; // 积分值(累计误差)
float prev_error; // 上一次误差(用于计算微分)
float output_min; // 输出最小值(防止电机过载)
float output_max; // 输出最大值(防止电机过载)
} pid_t;
// PID初始化(设置参数和范围)
void pid_init(pid_t *pid, float kp, float ki, float kd, float output_min, float output_max) {
pid->kp = kp;
pid->ki = ki;
pid->kd = kd;
pid->output_min = output_min;
pid->output_max = output_max;
// 初始化中间变量为0
pid->target = 0.0f;
pid->current = 0.0f;
pid->integral = 0.0f;
pid->prev_error = 0.0f;
}
// PID计算(输入当前值,输出控制量)
float pid_calculate(pid_t *pid, float current, float dt) {
// 步骤1:计算误差(目标值 - 当前值)
float error = pid->target - current;
// 步骤2:计算比例项(P):误差×比例系数,快速响应误差
float p_out = pid->kp * error;
// 步骤3:计算积分项(I):累计误差×积分系数,消除静态误差
pid->integral += error * dt;
// 积分限幅:防止积分饱和(比如误差一直存在,积分值过大,导致输出超调)
if (pid->integral > pid->output_max / pid->ki) {
pid->integral = pid->output_max / pid->ki;
} else if (pid->integral < pid->output_min / pid->ki) {
pid->integral = pid->output_min / pid->ki;
}
float i_out = pid->ki * pid->integral;
// 步骤4:计算微分项(D):误差变化率×微分系数,抑制超调
float derivative = (error - pid->prev_error) / dt; // 误差变化率(当前误差 - 上一次误差)/ 时间
float d_out = pid->kd * derivative;
// 步骤5:总输出 = P + I + D,然后限幅(防止输出超过电机最大能力)
float output = p_out + i_out + d_out;
if (output > pid->output_max) {
output = pid->output_max;
} else if (output < pid->output_min) {
output = pid->output_min;
}
// 步骤6:保存当前误差,用于下一次计算微分
pid->prev_error = error;
// 更新当前值
pid->current = current;
return output; // 输出控制量(比如电机的PWM占空比)
}
代码通俗解释:
- 比例项(P):像 "油门",误差越大,油门踩得越狠,快速接近目标(比如当前角度离目标 10°,P 值 8,就输出 80 的控制量);
- 积分项(I):像 "补油",如果有静态误差(比如目标 10°,当前 9°,误差 1° 一直存在),积分项会累计误差,慢慢增加输出,直到误差为 0;
- 微分项(D):像 "刹车",如果误差变化太快(比如快速接近目标,可能超调),D 项会根据变化率输出反向控制量,防止超过目标;
- 积分限幅:防止 "积分饱和",比如误差一直很大,积分值越来越大,即使误差变小,输出还是很大,导致电机转过头(超调)。
(3)FOC 算法:高端版电机平稳控制
FOC(磁场定向控制)是高端型号(Extended Long)的核心算法,能让三相无刷电机转动更平稳、效率更高,核心是 "将电流分解为励磁电流和转矩电流,分别控制"。
代码文件 :src/foc/foc.c核心函数:foc_calculate ()(FOC 计算)
c
运行
// FOC算法核心:Clark变换 + Park变换 + PID控制 + 逆Park变换 + SVPWM
float foc_calculate(foc_t *foc, motor_current_t *current, float target_torque, float dt) {
// 步骤1:Clark变换(将三相电流ia、ib、ic转换为两相静止坐标系α、β)
// 公式:iα = ia - 0.5ib - 0.5ic;iβ = (√3/2)ib - (√3/2)ic(假设ic = -ia - ib,简化计算)
float i_alpha = current->ia - 0.5f * current->ib;
float i_beta = (sqrtf(3.0f) / 2.0f) * current->ib;
// 步骤2:Park变换(将α、β转换为两相旋转坐标系d、q,d轴是励磁方向,q轴是转矩方向)
// 公式:id = iα×cosθ + iβ×sinθ;iq = -iα×sinθ + iβ×cosθ(θ是电机转子角度,由编码器获取)
float cos_theta = cosf(foc->theta);
float sin_theta = sinf(foc->theta);
float i_d = i_alpha * cos_theta + i_beta * sin_theta;
float i_q = -i_alpha * sin_theta + i_beta * cos_theta;
// 步骤3:PID控制(q轴电流控制转矩,d轴电流控制励磁,通常d轴目标为0,弱磁控制)
foc->iq_pid.target = target_torque; // q轴目标电流 = 目标转矩(转矩与iq成正比)
foc->id_pid.target = 0.0f; // d轴目标电流 = 0(减少励磁损耗)
float v_q = pid_calculate(&foc->iq_pid, i_q, dt); // q轴电压输出(控制转矩)
float v_d = pid_calculate(&foc->id_pid, i_d, dt); // d轴电压输出(控制励磁)
// 步骤4:逆Park变换(将d、q轴电压转换为α、β轴电压)
float v_alpha = v_d * cos_theta - v_q * sin_theta;
float v_beta = v_d * sin_theta + v_q * cos_theta;
// 步骤5:SVPWM(空间矢量脉宽调制,生成三相PWM信号,控制电机转动)
svpwm_generate(v_alpha, v_beta, &foc->pwm);
return 0.0f;
}
代码通俗解释:
- Clark 变换:像 "将三角形坐标转成直角坐标",把三相电机的电流(ia、ib、ic)转换成两个垂直方向的电流(α、β),方便计算;
- Park 变换:像 "让坐标系跟着电机转子转",把 α、β 电流转换成 d(励磁)和 q(转矩)电流,这样可以分别控制转矩和励磁,效率更高;
- SVPWM:像 "精准控制电机线圈的电压",生成三相 PWM 信号,让电机转子的磁场始终与定子磁场垂直,转矩最大,转动最平稳。
2.2.3 应用层代码:业务逻辑的 "顶层实现"
应用层负责处理用户需求,比如 "调参模式""跟随模式""稳定模式",核心是 "状态机 + 通信协议"。
(1)主函数:程序入口与状态机
代码文件 :src/main.c核心逻辑:初始化所有模块→进入主循环→根据状态执行对应功能。
c
运行
// 全局变量:系统状态(0=初始化,1=稳定模式,2=跟随模式,3=调参模式)
uint8_t system_state = 0;
int main(void) {
// 步骤1:初始化硬件(时钟、GPIO、传感器、电机、串口、PID)
system_clock_init(); // 系统时钟初始化(72MHz for STM32F1)
gpio_init(); // GPIO引脚初始化
uart1_init(); // 串口1初始化(与飞控通信)
uart2_init(); // 串口2初始化(与调参软件通信)
mpu6050_init(); // MPU6050传感器初始化
a4988_init(); // A4988电机驱动初始化
// PID初始化(以俯仰角PID为例,kp=8.0,ki=0.5,kd=0.2,输出范围-100~100)
pid_init(&pitch_pid, 8.0f, 0.5f, 0.2f, -100.0f, 100.0f);
// 其他初始化(如定时器、中断)
timer_init();
nvic_init();
// 步骤2:主循环(死循环,持续运行)
while (1) {
// 读取传感器数据(每1ms读取一次,由定时器中断控制)
if (sensor_data_ready) {
mpu6050_read_data(&imu_data);
sensor_data_ready = false;
}
// 姿态解算(每1ms更新一次角度)
comp_filter_update(&imu_data, 0.001f); // dt=1ms=0.001s
// 根据系统状态执行对应功能
switch (system_state) {
case 0: // 初始化状态:等待传感器校准
if (calibration_finished) {
system_state = 1; // 校准完成,进入稳定模式
}
break;
case 1: // 稳定模式:保持当前角度稳定
// 设置PID目标角度(比如保持0°,即水平)
pitch_pid.target = 0.0f;
roll_pid.target = 0.0f;
yaw_pid.target = 0.0f;
// 计算PID输出(控制量)
float pitch_output = pid_calculate(&pitch_pid, pitch, 0.001f);
float roll_output = pid_calculate(&roll_pid, roll, 0.001f);
float yaw_output = pid_calculate(&yaw_pid, yaw, 0.001f);
// 控制电机转动(将PID输出转换为电机步数)
a4988_rotate(pitch_output);
a4988_rotate(roll_output);
a4988_rotate(yaw_output);
break;
case 2: // 跟随模式:跟随遥控器或飞控的角度指令
// 从串口接收飞控发送的目标角度(比如pitch_target=5°)
if (uart1_rx_len > 0) {
parse_fc_data(uart1_rx_buf, uart1_rx_len, &target_angles);
uart1_rx_len = 0; // 清空缓冲区
}
// 设置PID目标角度为飞控发送的角度
pitch_pid.target = target_angles.pitch;
roll_pid.target = target_angles.roll;
yaw_pid.target = target_angles.yaw;
// 计算PID输出并控制电机
float pitch_out = pid_calculate(&pitch_pid, pitch, 0.001f);
float roll_out = pid_calculate(&roll_pid, roll, 0.001f);
float yaw_out = pid_calculate(&yaw_pid, yaw, 0.001f);
a4988_rotate(pitch_out);
a4988_rotate(roll_out);
a4988_rotate(yaw_out);
break;
case 3: // 调参模式:与电脑调参软件通信,修改PID参数
if (uart2_rx_len > 0) {
parse_config_data(uart2_rx_buf, uart2_rx_len, &pid_params);
// 更新PID参数
pitch_pid.kp = pid_params.pitch_kp;
pitch_pid.ki = pid_params.pitch_ki;
pitch_pid.kd = pid_params.pitch_kd;
roll_pid.kp = pid_params.roll_kp;
roll_pid.ki = pid_params.roll_ki;
roll_pid.kd = pid_params.roll_kd;
yaw_pid.kp = pid_params.yaw_kp;
yaw_pid.ki = pid_params.yaw_ki;
yaw_pid.kd = pid_params.yaw_kd;
uart2_rx_len = 0; // 清空缓冲区
}
break;
}
// 其他功能(如电机温度检测、过载保护)
check_motor_temp();
check_overload();
}
}
代码通俗解释:
- 主循环:像 "永不停歇的工人",持续做三件事:读传感器数据、算姿态角度、根据状态控制电机;
- 状态机:像 "任务切换器",不同状态做不同事(稳定模式保持水平,跟随模式跟着飞控转,调参模式改 PID 参数);
- 数据解析:从串口接收外部指令(如飞控的目标角度、调参软件的 PID 参数),然后更新系统设置。
(2)调参协议:与电脑软件的通信
调参协议负责与官方 GUI 软件(SBGC Configurator)通信,修改 PID 参数、传感器校准参数等,核心是 "数据帧格式 + 校验"。
代码文件 :src/comm/config_protocol.c核心函数:parse_config_data ()(解析调参数据)、send_config_ack ()(发送确认)
c
运行
// 调参数据帧格式(通俗说明):
// 帧头(1字节)+ 命令类型(1字节)+ 数据长度(1字节)+ 数据(N字节)+ 校验和(1字节)
// 例:帧头0xAA + 命令0x01(修改PID)+ 长度0x06 + 数据(pitch_kp,pitch_ki,pitch_kd,roll_kp,roll_ki,roll_kd)+ 校验和
// 解析调参数据(通俗注释版)
void parse_config_data(uint8_t *data, uint16_t len, pid_params_t *params) {
// 步骤1:检查帧头(必须是0xAA)
if (data[0] != 0xAA) {
return; // 帧头错误,丢弃数据
}
// 步骤2:检查数据长度(帧头1 + 命令1 + 长度1 + 数据N + 校验1 = 总长度)
uint8_t cmd = data[1];
uint8_t data_len = data[2];
if (len != 1 + 1 + 1 + data_len + 1) {
return; // 长度错误,丢弃数据
}
// 步骤3:计算校验和(所有字节相加,取低8位)
uint8_t checksum = 0;
for (uint16_t i = 0; i < len - 1; i++) {
checksum += data[i];
}
if (checksum != data[len - 1]) {
return; // 校验错误,丢弃数据
}
// 步骤4:根据命令类型解析数据
switch (cmd) {
case 0x01: // 命令0x01:修改PID参数(数据长度6字节,每个参数1字节,对应0~10的浮点数)
params->pitch_kp = data[3] / 10.0f; // 第3字节是pitch_kp(比如0x08对应0.8?不,实际是8.0,需根据协议调整)
params->pitch_ki = data[4] / 10.0f;
params->pitch_kd = data[5] / 10.0f;
params->roll_kp = data[6] / 10.0f;
params->roll_ki = data[7] / 10.0f;
params->roll_kd = data[8] / 10.0f;
// 发送确认消息
send_config_ack(cmd, 0x00); // 0x00表示成功
break;
case 0x02: // 命令0x02:传感器校准
sensor_calibration(); // 执行传感器校准
send_config_ack(cmd, 0x00);
break;
default: // 未知命令
send_config_ack(cmd, 0x01); // 0x01表示失败
break;
}
}
// 发送确认消息(通俗注释版)
void send_config_ack(uint8_t cmd, uint8_t status) {
uint8_t ack_buf[5];
ack_buf[0] = 0xBB; // 确认帧头(0xBB)
ack_buf[1] = cmd; // 原命令类型
ack_buf[2] = 0x01; // 数据长度(状态1字节)
ack_buf[3] = status; // 状态(0x00成功,0x01失败)
ack_buf[4] = 0xBB + cmd + 0x01 + status; // 校验和
// 发送确认帧到调参软件(串口2)
uart2_send_data(ack_buf, 5);
}
代码通俗解释:
- 数据帧格式:像 "快递包裹",帧头是 "快递单号开头",命令类型是 "包裹类型",数据长度是 "包裹大小",校验和是 "防伪码",确保数据没传错;
- 校验和:像 "验证码",发送方把所有字节相加,接收方再算一次,如果一样,说明数据没丢或没改;
- 确认消息:像 "签收单",接收方收到数据后,告诉发送方 "收到了,没问题",如果有问题,告诉发送方 "重新发"。
2.3 开源代码使用指南:从编译到烧录
SimpleBGC 的开源代码支持多种开发工具(Keil、STM32CubeIDE、Makefile),本节以 STM32CubeIDE(开源免费)为例,教你如何编译、烧录代码。
2.3.1 开发环境准备
| 工具 / 软件 | 下载地址 | 用途 | 安装注意事项 |
|---|---|---|---|
| STM32CubeIDE | https://www.st.com/en/development-tools/stm32cubeide.html | 代码编辑、编译、调试 | 安装时选择对应系统(Windows/macOS/Linux),需要注册 ST 账号(免费) |
| ST-Link V2 | 淘宝 / 京东搜索 "ST-Link V2" | 连接电脑和 MCU,烧录、调试代码 | 选择 "正版" 或 "兼容版",兼容版约 20 元,支持 STM32 全系列 |
| SBGC Configurator | https://www.basecamelectronics.com/sbgc-configurator/ | 调参软件,修改 PID 参数、校准传感器 | 支持 Windows/macOS,安装后需安装 USB 驱动(FT232) |
| Git | https://git-scm.com/downloads | 克隆开源代码仓库 | 安装时选择 "Add Git to PATH",方便命令行使用 |
2.3.2 代码编译与烧录步骤(新手友好)
-
克隆开源代码仓库 :打开 STM32CubeIDE,点击 "File→Import→Git→Projects from Git→Clone URI",输入 GitHub 仓库地址:
https://github.com/BaseCam/EVO-BGC,选择分支(默认 main),克隆到本地。 -
配置项目:
- 克隆完成后,选择对应型号的项目(如 "EVO-BGC-STM32F103" 对应 Regular 版);
- 点击 "Project→Properties→C/C++ Build→Settings",确认编译器是 "ARM GCC",目标芯片是 "STM32F103C8T6"(或对应型号)。
-
编译代码:
- 点击工具栏的 "Build" 按钮(锤子图标),开始编译;
- 编译成功后,在 "Debug" 目录下生成 "EVO-BGC.elf" 和 "EVO-BGC.bin" 文件(bin 文件用于烧录);
- 若编译报错(如 "头文件找不到"),检查 "Include Path" 是否包含所有 src 目录(右键项目→Properties→C/C++ General→Paths and Symbols→Includes→Add,添加 src 下的所有子目录)。
-
连接硬件:
- 用 ST-Link V2 连接电脑和 PCB 的 SWD 接口(SWCLK 接 SWCLK,SWDIO 接 SWDIO,GND 接 GND,3.3V 接 3.3V);
- 给 PCB 通电(用对应的 LiPo 电池,如 Tiny 版用 2S 电池),确保电源指示灯亮(开源 PCB 有电源指示灯)。
-
烧录代码:
- 点击 STM32CubeIDE 的 "Run→Debug Configurations",选择 "STM32 Debugging",新建配置;
- 在 "Debugger" 标签页,选择 "ST-Link V2",点击 "Apply→Debug";
- 代码会自动烧录到 MCU 中,烧录成功后,点击 "Resume" 按钮(绿色三角形),程序开始运行。
-
调参与测试:
- 用 USB 线连接 PCB 的 USB 接口和电脑,打开 SBGC Configurator 软件;
- 软件会自动识别设备,点击 "Connect",进入调参界面;
- 先执行 "Sensor Calibration"(传感器校准),然后调整 PID 参数(基础版推荐:pitch_kp=8.0,pitch_ki=0.5,pitch_kd=0.2);
- 点击 "Test Motors",测试电机是否正常转动,若转动平稳,说明代码烧录成功。
2.3.3 代码调试常见问题(表格汇总)
| 常见问题 | 可能原因 | 解决方法 | 工具需求 |
|---|---|---|---|
| 编译报错 "undefined reference to xxx" | 1. 函数未实现;2. 源文件未添加到项目;3. 链接脚本错误 | 1. 检查是否有该函数的.c 文件;2. 右键项目→Add Files,添加缺失的.c 文件;3. 检查链接脚本(.ld 文件)是否正确 | STM32CubeIDE |
| ST-Link 无法识别 MCU | 1. SWD 接线错误;2. 电源未供电;3. ST-Link 驱动未安装 | 1. 检查 SWCLK/SWDIO/GND/3.3V 引脚是否接对;2. 确认电池已连接,电源指示灯亮;3. 安装 ST-Link 驱动(官网下载) | 设备管理器(Windows)、ST-Link Utility |
| 电机转动异常(抖动、不转) | 1. PID 参数不当;2. 传感器数据错误;3. 电机接线错误 | 1. 恢复默认 PID 参数(SBGC Configurator 有 "Load Default");2. 重新校准传感器;3. 交换电机线圈接线 | SBGC Configurator、万用表 |
| 调参软件无法连接 | 1. USB 驱动未安装;2. 串口端口错误;3. 波特率不匹配 | 1. 安装 FT232 驱动(调参软件官网有链接);2. 在设备管理器查看串口端口(如 COM3),在软件中选择对应端口;3. 确认波特率是 115200(默认) | 设备管理器、SBGC Configurator |
第三部分:SimpleBGC 软件算法分析 ------ 稳像精度的核心保障
SimpleBGC 的稳像效果好坏,关键在算法。本节将深入分析 "姿态解算""PID 控制""电机驱动" 三大核心算法,包括原理、优缺点、调参方法,帮你理解 "为什么这样设计""如何优化精度"。
3.1 姿态解算算法:从传感器数据到角度
姿态解算的核心是 "融合多传感器数据,得到准确的姿态角度",SimpleBGC 支持两种算法:互补滤波(基础版)和卡尔曼滤波(高端版,Extended Long)。
3.1.1 两种姿态解算算法对比
| 算法类型 | 核心原理 | 优点 | 缺点 | 计算量 | 适用型号 | 典型精度(静态) | 典型精度(动态) |
|---|---|---|---|---|---|---|---|
| 互补滤波 | 陀螺仪(动态)+ 加速度计(静态),加权融合 | 计算量小(<1ms)、代码简单、实时性强 | 动态精度依赖权重,权重不当会导致漂移或超调 | 小 | Tiny/Regular | ±0.5° | ±1° |
| 卡尔曼滤波(EKF) | 建立数学模型,预测 + 更新,最小化误差方差 | 动态精度高(<±0.1°)、抗干扰能力强 | 计算量大(>5ms)、代码复杂、需调参 | 大 | Extended Long | ±0.1° | ±0.3° |
3.1.2 互补滤波深度解析(基础版核心)
(1)原理通俗解释
互补滤波的本质是 "用陀螺仪的动态优势弥补加速度计的动态劣势,用加速度计的静态优势弥补陀螺仪的静态劣势",就像 "两个人合作":
- 陀螺仪:擅长 "快速反应",能准确测量设备转动的角速度,但长时间静置会 "失忆"(漂移);
- 加速度计:擅长 "静态定位",能准确测量设备相对于重力的角度,但快速转动时会 "头晕"(受加速度干扰);
- 融合:用陀螺仪的角速度计算角度变化(动态),用加速度计的角度修正陀螺仪的漂移(静态),权重通常是 98%(陀螺仪)+2%(加速度计)。