简介:这份资源是Simple Bgc开源项目的完整源码,面向从事三轴增稳云台开发、嵌入式控制学习以及STM32工程实践的开发者。该项目源自俄版云台方案,早期基于Arduino平台,后续版本转向STM32控制器,可用于研究云台姿态解算、电机驱动与闭环稳定控制等核心逻辑。压缩包共200个文件,约580KB,以C语言源文件(89个)和头文件(95个)为主体,另含汇编启动文件、Keil工程文件(uvproj、uvopt)、批处理脚本及少量配置说明文件,构成一套可直接编译的嵌入式工程结构。目前已有3433人学习下载,说明其在云台控制领域具有较高的参考价值。读者可从中获取完整的STM32外设驱动代码(如定时器、Flash、RCC、ADC等模块)、云台控制算法实现以及工程组织方式,适合作为二次开发或课程设计的底层参考,帮助理解从传感器采集到电机输出的完整控制链路。
1. 三轴增稳云台为什么值得从源码啃起
很多人第一次接触三轴增稳云台,是从一段抖动的航拍画面开始的:明明电机转得挺稳,镜头却像得了帕金森。Simple BGC 这类开源固件之所以被反复翻出来研究,核心就在于它把「姿态解算 + 无刷电机闭环 + 云台机械补偿」这条链路完整摊开在源码里,而不是封装成一个黑匣子。基于 STM32 的三轴增稳云台开源代码,本质上是一套跑在 Cortex-M 内核上的实时控制系统:MPU6050 之类的 IMU 出原始角速度与加速度,经过互补滤波或卡尔曼滤波融合出姿态角,再通过 PID 输出三路 PWM 去驱动三个无刷电机的电调,最终让相机在载体晃动时保持指向稳定。它适合谁?适合已经会点 STM32、想搞懂闭环控制怎么落地的人,也适合做机器人、车载云台、手持稳定器的团队拿来做二次开发底座。这一章先把「它到底在控制什么」讲清楚,后面才好动手。
2. 从源码结构看三轴增稳云台的控制链路
拿到一份基于 STM32 的三轴增稳云台开源代码,别急着编译烧录,先花二十分钟把目录结构读一遍,比盲目改参数省下大量返工。典型工程会分成驱动层、算法层、应用层三块,理解这个分层,后面调 PID 才知道该动哪个文件。
2.1 工程目录与模块职责划分
常见做法是下面这种布局,不同作者命名略有差异,但职责基本一致:
| 目录/文件 | 职责 | 改动频率 |
|---|---|---|
bsp/或hardware/ | GPIO、定时器、I2C、串口底层驱动 | 低 |
imu/ | IMU 初始化、原始数据读取、零偏校准 | 中 |
filter/ | 互补滤波、卡尔曼滤波姿态解算 | 中 |
pid/ | 三轴 PID 计算与输出限幅 | 高 |
motor/ | PWM 输出、电调驱动、方向映射 | 中 |
main.c | 主循环调度、模式切换、保护逻辑 | 中 |
读代码的顺序建议是:先看main.c的主循环周期,再看 IMU 采样频率,最后看 PID 在哪一步被调用。因为云台是硬实时系统,主循环周期直接决定控制带宽,周期不稳,后面参数怎么调都是玄学。
2.2 姿态解算:互补滤波与卡尔曼滤波怎么选
姿态解算是整条链路的地基。加速度计长期准但短期噪声大,陀螺仪短期准但会漂移,所以必须融合。互补滤波实现简单、算力占用低,适合 STM32F1/F4 这类主频不高的芯片;卡尔曼滤波精度更好,但矩阵运算吃 CPU,F1 上跑三轴容易吃力。
互补滤波的核心一行大概是这样:
// angle: 融合后角度, gyro: 陀螺仪角速度(deg/s), acc: 加速度计解算角度 // dt: 采样周期(s), alpha: 互补系数, 一般取 0.96~0.99 angle = alpha * (angle + gyro * dt) + (1.0f - alpha) * acc;逻辑说明:前半段用陀螺仪积分预测角度,后半段用加速度计观测值修正漂移。alpha越大越信任陀螺仪,动态响应好但会漂;越小越信任加速度计,静态稳但抖动大。参数说明:dt必须和实际采样周期一致,如果你在 1kHz 定时器里调用却填了 0.01,角度会直接算错,这是新手最常见的翻车点。
卡尔曼滤波则要维护状态协方差矩阵,代码量翻几倍。我的建议是:先用互补滤波把云台跑稳,确认机械和电机没问题,再考虑换卡尔曼。一上来就啃卡尔曼,很容易在参数整定阶段迷失。
2.3 三轴 PID 与电机输出映射
姿态角算出来后,目标角度减去当前角度得到误差,送进 PID:
// 单轴 PID 简化实现 float pid_calc(PID_t *pid, float target, float measure, float dt) { float err = target - measure; pid->integral += err * dt; // 积分项累加 if (pid->integral > pid->i_max) // 积分限幅, 防饱和 pid->integral = pid->i_max; else if (pid->integral < -pid->i_max) pid->integral = -pid->i_max; float derivative = (err - pid->last_err) / dt; // 微分项 pid->last_err = err; return pid->kp * err + pid->ki * pid->integral + pid->kd * derivative; }逻辑说明:比例项决定响应力度,积分项消除静态误差,微分项抑制超调。参数说明:i_max是积分限幅,没有它,云台被外力顶住时积分会疯狂累加,松手瞬间猛甩,这就是典型的积分饱和。三轴各自独立一套 PID,但 roll 和 yaw 的耦合往往需要额外补偿,源码里通常有交叉耦合项,别忽略。
电机输出映射要注意方向:PID 算出的是力矩需求,映射到 PWM 时,不同电机安装方向不同,符号必须对应。映射错了,云台会直接自激振荡,越调越抖。
3. 在 STM32 上把云台跑起来的最小步骤
这一章是能照着抄的部分。假设你手上有一块 STM32F405 开发板、一个六轴 IMU、三个无刷电机加电调,目标是让云台先「能稳住」,不追求性能。
3.1 硬件连接与定时器 PWM 配置
先确认三路 PWM 分别接哪个定时器通道。以常见做法为例,用 TIM1 的 CH1/CH2/CH3 输出三路 50Hz PWM 给电调:
// TIM1 PWM 初始化(节选), 50Hz, 脉宽 1000~2000us void pwm_init(void) { // 预分频与自动重装值按 84MHz 主频计算 // 50Hz -> 周期 20ms, 计数频率 1MHz -> ARR = 20000-1 TIM1->PSC = 84 - 1; // 84MHz / 84 = 1MHz TIM1->ARR = 20000 - 1; // 1MHz / 20000 = 50Hz TIM1->CCR1 = 1500; // 中位脉宽 1.5ms, 电调解锁用 TIM1->CCR2 = 1500; TIM1->CCR3 = 1500; // 配置 CH1~CH3 为 PWM 模式并使能输出(略) }逻辑说明:电调识别的是脉宽而非占空比,1ms 对应最小、2ms 对应最大、1.5ms 是中位。参数说明:PSC和ARR必须按你实际主频重算,主频填错,输出频率就偏,电调可能直接不响应。上电先给三路 1.5ms 中位,等电调完成自检再进控制循环,这一步顺序错了电机不会转。
3.2 IMU 零偏校准与采样周期设定
IMU 上电后必须静止校准零偏,否则陀螺仪漂移会让云台缓慢跑偏:
// 静止采样 500 次求均值作为零偏 void imu_calibrate(void) { int32_t sum[3] = {0}; for (int i = 0; i < 500; i++) { imu_read_raw(); sum[0] += gyro_raw[0]; sum[1] += gyro_raw[1]; sum[2] += gyro_raw[2]; delay_ms(2); } gyro_bias[0] = sum[0] / 500; gyro_bias[1] = sum[1] / 500; gyro_bias[2] = sum[2] / 500; }逻辑说明:静止时陀螺仪理论输出为零,实际有固定偏置,采样求均值即可估计。参数说明:采样次数 500 次约 1 秒,太少噪声大,太多上电等待久。校准期间云台必须完全静止,手碰一下数据就废了,这是血泪经验。
采样周期建议 1kHz 读 IMU、200Hz 跑姿态解算、200Hz 跑 PID,分层执行能省 CPU。周期用定时器中断保证,别用delay凑。
3.3 上电顺序与首次通电验证
首次通电务必拆桨或卸负载,按这个顺序走:
- 只给主控供电,串口打印 IMU 原始数据,确认三轴数值合理。
- 静止校准零偏,观察姿态角是否稳定在零附近。
- 接电调但不装电机,示波器或逻辑分析仪看三路 PWM 是否为 1.5ms。
- 装电机空载,给一个小目标角度,看电机是否朝正确方向响应。
- 全部正常后再装相机负载,从小角度开始测。
跳过第 3、4 步直接上负载,一旦方向映射错了,云台会瞬间猛甩,轻则烧电调,重则打坏相机。
4. 调参与排错:云台抖动的常见坑
云台调不好,八成不是算法问题,而是参数、机械或时序问题。这一章按「现象 → 原因 → 解决」列几条我踩过的坑。
4.1 高频抖动与低频摆动的区分
现象:云台发出高频嗡嗡声并伴随小幅抖动。原因:PID 的kp或kd过大,系统带宽超过了机械和电机的响应能力。解决:先把kd降到零,单独调kp到临界振荡再回退 30%,最后加kd抑制超调。反过来,如果是低频缓慢摆动,多半是ki太小或机械重心没配平。
4.2 积分饱和导致的松手猛甩
现象:用手顶住云台一段时间,松手瞬间云台猛地弹向一边。原因:积分项在受阻期间持续累加,超出合理范围。解决:给积分加限幅(见 2.3 的i_max),并在误差过大时暂停积分累加。这是最容易被忽略的一条,很多开源代码默认限幅偏大。
4.3 姿态角缓慢漂移
现象:云台静止时角度慢慢往一个方向偏。原因:陀螺仪零偏没校准好,或互补滤波alpha太大导致加速度计修正不足。解决:重新做静止校准,把alpha从 0.98 降到 0.95 试试,同时检查 IMU 是否离电机太近受振动干扰。
4.4 电机发热与异响
现象:电机空载也发烫,或有明显异响。原因:PWM 频率与电调不匹配,或 PID 输出长期处于大幅振荡。解决:确认 PWM 是 50Hz 还是更高频率(部分电调支持 400Hz),并检查 PID 输出是否在限幅内来回跳。电机发热往往是振荡的副产品,别只盯着电机。
4.5 主循环周期不稳
现象:参数明明调好了,运行几分钟后开始抖。原因:主循环里混入了串口打印、Flash 写入等耗时操作,周期被拉长。解决:把打印和存储移出控制循环,用 DMA 或缓冲方式处理。控制循环里只做采样、解算、PID、输出这四件事。
5. 从能跑到好用:进阶技巧与验证方法
把云台跑稳只是起点,真正决定它好不好用的是几个细节。第一是前馈补偿:当载体本身有已知运动趋势时,纯反馈 PID 总是慢半拍,加入角速度前馈能明显提升跟随性,源码里通常预留了前馈系数,调的时候从小往大加。第二是滤波与延迟的权衡:滤波越重,姿态越平滑,但延迟越大,云台响应越迟钝,我的习惯是把滤波截止频率设在控制带宽的 3 到 5 倍,既压噪声又不拖后腿。
验证方法上,别只靠肉眼看。可以用串口把目标角度、实际角度、PID 输出三路数据以固定周期打出来,导入表格画曲线,看超调量和稳态误差。更直接的是给云台一个阶跃目标,观察它多久稳定、有没有过冲。下面这段是数据打点的常用写法:
// 每 5ms 打一帧, 格式: target,actual,output void log_frame(float target, float actual, float output) { static uint32_t cnt = 0; if (++cnt % 5 == 0) { // 200Hz 控制, 40Hz 打点 printf("%.2f,%.2f,%.2f\r\n", target, actual, output); } }逻辑说明:打点频率要低于控制频率,避免占用过多串口带宽。参数说明:%.2f精度够用,太高反而拖慢打印。拿到数据后重点看三件事:稳态时 actual 是否贴着 target、阶跃后有没有等幅振荡、output 是否频繁撞限幅。
最后一个习惯:每次只改一个参数,改完记录现象。云台调参是典型的耦合系统,同时动kp和kd,你永远不知道是哪个起了作用。我早期图快一次改三个参数,结果振荡了整整两天才定位到是kd符号写反。慢就是快,希望帮到你。
本文还有配套的精品资源,点击获取