基于 STM32 的太阳能追光系统设计
摘要
针对固定安装的太阳能板无法随太阳方位变化调整姿态、发电效率受限的问题,本文设计并实现了一种基于 STM32 的太阳能追光系统。系统以 STM32F103C8T6 为控制核心,4 路 BH1750 数字光照传感器按四象限布置于光伏板背板,经 TCA9548A I²C 多路复用器分时采集;主控对四路读数做成对求和差分得到两轴偏差,以 P 控制律驱动 2×MG996R 舵机构成的双轴云台(方位轴 + 俯仰轴)指向光源。系统采用强光门限滞回区分直射光源与环境光,死区与单周期限幅抑制抖动,指数逼近的输出平滑保证舵机运动连续;通过串口非阻塞发送、OLED 分片刷新与本帧自锚定的帧周期补足三项措施,将 50Hz 软件 PWM 的帧周期钉死在 20ms,彻底解决了舵机"爆冲"问题。板载 RAM 环形黑匣子支持偶发故障的现场复盘。测试表明,系统对准精度与动态响应满足设计要求,运动平稳无抖动。
关键词:STM32;太阳能追光;BH1750;双轴云台;P 控制;软件 PWM
1 引言
太阳能发电的转换效率与光照入射角密切相关:当太阳光垂直入射光伏板时输出功率最大,入射角偏离法线时效率显著下降。固定安装的光伏板只在正午前后短暂接近最佳倾角,全天平均利用率低。双轴追光系统通过实时调整光伏板姿态,使其法线始终指向太阳,可显著提升日均发电量。
传统低成本方案多用 4 个光敏电阻分置光伏板四角,经 ADC 采样电压差判断光照方位。这类方案存在三个固有缺陷:光敏电阻一致性差、温漂大,需要逐个标定;ADC 单次采样易受环境光突变与电源噪声干扰,导致执行机构频繁误触发;开关式(bang-bang)控制使电机启停突兀、过冲抖动,长期损耗机械结构。
本文采用数字光照传感器 BH1750 替代光敏电阻(免标定、一致性好、直接输出 lux 物理量),以 I²C 多路复用器扩展四路采集,配合带回差的强光门限、死区限幅 P 控制与指数逼近输出平滑,在 STM32F103C8T6 上实现了运动连续、无爆冲的双轴追光系统。
2 系统总体设计
2.1 系统架构
系统由感知、决策、执行、交互四部分组成:
- 感知:4 路 BH1750 光照传感器按"左上、右上、左下、右下"四象限布置在光伏板背板的载板上,载板中央设十字遮光挡板(高 15
25mm),使相邻传感器对偏离法线的入射光产生稳定的照度差——这是差分定向的物理基础。4 路传感器地址均为 0x23,经 TCA9548A 八路 I²C 复用器的 SC0SC3/SD0~SD3 通道分时访问。 - 决策:STM32F103C8T6(72MHz)每 100ms 读取四路照度,计算两轴偏差并执行 P 控制律。
- 执行:2 只 MG996R 舵机构成标准双轴云台——方位轴(Z)绕竖轴水平旋转,俯仰轴(Y)绕水平轴俯仰,独立设定 0~180°。
- 交互:0.96 寸 SSD1306 OLED 实时显示四路读数与云台状态;4 个板载按键支持手动点动标定;USART1 串口提供在线调参命令。
2.2 元器件选型
| 类别 | 型号 | 关键参数 | 作用 |
|---|---|---|---|
| 主控 | STM32F103C8T6 | 72MHz,64K Flash / 20K RAM | 采集、控制、PWM 生成 |
| 光照传感 | BH1750 ×4 | I²C,1~65535 lux,高分辨率模式 | 四象限照度采集 |
| I²C 复用 | TCA9548A | 8 通道,ADDR=0x70 | 解决 4 路同地址冲突 |
| 舵机 | MG996R ×2 | 0~180°,金属齿轮 | 方位轴 / 俯仰轴驱动 |
| 显示 | SSD1306 OLED 0.96" | 128×64,I²C 0x3C | 读数与状态显示 |
| 按键 | 轻触开关 ×4 | 板载 | 手动点动与组合键 |
2.3 引脚资源分配
| 外设 | 引脚 | 模式 | 说明 |
|---|---|---|---|
| TCA9548A / BH1750 | PB14 / PB15 | 软件 I²C | SCL / SDA |
| OLED | PB8 / PB9 | 软件 I²C | 独立总线,互不干扰 |
| 方位舵机(Z) | PA12 | 推挽输出,软件 50Hz PWM | 上电中位 90° |
| 俯仰舵机(Y) | PA11 | 推挽输出,软件 50Hz PWM | 上电中位 90° |
| 按键 Key1~Key4 | PA3 / PB5 / PB6 / PB7 | 上拉输入 | Z− / Z+ / Y− / Y+ |
| 串口 | USART1 PA9 / PA10 | 115200bps | 调参命令与状态输出 |
引脚对应关系经按键实测校正:Key2 使云台抬头/低头,故 PA11 为俯仰;Key4 使云台水平旋转,故 PA12 为方位——与最初标称相反,代码已按实测修正。
3 硬件设计
3.1 四象限传感器头
传感器头采用"模块载板一体式"结构:TCA9548A 模块与 4 只 BH1750 模块焊死在同一块洞洞板载板上,不靠杜邦线悬空。云台是运动平台,运转振动下悬空跳线是 I²C 掉帧的头号原因,表现为追踪抽搐、读数跳变;焊死载板可根治。载板四角对应四象限物理位置,代码中由 kPosToCh[] 映射表兜住物理位置与硬件通道的对应关系(遮挡实测标定:左上=CH2、右上=CH3、左下=CH0、右下=CH1),PCB 走线差异不影响软件逻辑。
3.2 舵机驱动
MG996R 采用软件 PWM 驱动:50Hz 周期、0.5~2.5ms 脉宽,由 DWT 周期计数器实现微秒级延时。PA11/PA12 推挽输出,软件反相宏 SERVO_PAN_INVERT / SERVO_TILT_INVERT 兼容不同舵盘安装方向,改宏后若闭环朝反方向跑,翻转对应的方向符号宏即可。
3.3 安装与朝向要求
方位轴为 180° 舵机,只能覆盖半个圆,摆位决定系统能否全天追到太阳。核心要求:云台 Z=90°(上电中位)指向正南。此时 Z=0 朝东、Z=180 朝西,太阳东→南→西的全天弧段恰好压满 180° 行程,死区留在太阳永不经过的北天空。俯仰轴安装时令 Y=90° 板面竖直(法线水平),向两端各留 90° 余量,覆盖日出至日落的全高度角。底座需用水平仪校平——底座倾斜会使方位轴不竖直,四路读数整体偏移,闭环无法找到平衡点。
4 软件系统设计
4.1 主循环与帧节拍
主循环采用超循环结构,以 20ms 软件伺服帧为基本节拍:每帧给起点打点(Servo_FrameMark),帧尾由 Servo_Frame 等待到「本帧起点 + 20ms」。只要"本帧工作 + 脉冲输出"小于 20ms,帧周期即严格恒定,与单帧工作量波动无关;偶发超时的帧不等待也不补帧(宁可慢一帧,不连发)。传感器采集与控制决策每 5 帧(100ms)执行一次,其余任务(按键扫描、OLED、黑匣子)逐帧执行。主程序流程如图 4-1 所示。

4.2 光照采集与滤波
BH1750 上电时对 4 个通道各初始化一次连续高分辨率模式,之后每周期直接读数,避免了每次测量 180ms 的阻塞等待——4 路读一遍仅约 3.3ms,支撑 100ms 控制周期。采集与滤波流程如图 4-2 所示。

原始读数先经坏样本剔除再作一阶低通(EMA 系数 1/2):TCA9548A 切换瞬间或手电反光会产生数千 lux 的孤立尖峰,直接喂给 P 控制会导致目标连续满步进——宏观表现就是"爆冲"。单次跳变超过 1500 lux 判为坏样本,沿用上一次滤波值:
/* 一阶低通 + 坏样本剔除 */
int diff = (int)lux[pos] - (int)lux_f[pos];
if (diff > 1500 || diff < -1500) {
dbg_bad_sample++; /* 坏样本: 沿用上次滤波值 */
} else {
lux_f[pos] = (uint16_t)(((uint32_t)lux_f[pos] + lux[pos] + 1u) >> 1);
}
4.3 强光门限与滞回
环境光(阴天 30~100 lux)与直射光源(手电/太阳,300 lux 以上)在量级上可分。系统取四路最大值 peak 作判据:peak ≥ 250 lux 进入追光(TRK),peak < 150 lux(进入门限的 60%)退回保持(HLD,角度原地锁存),滞回区间防止临界照度下状态反复翻转。门限支持串口 @T<lux> 在线调整,退出门限按 60% 联动。
4.4 成对差分 P 控制
四象限读数按行、列两两求和后作差,得到俯仰与方位两个方向的偏差:
/* 原始光差(未乘方向符号) */
raw_tilt = lux[LT] + lux[RT] - lux[LB] - lux[RB]; /* 上排 - 下排 */
raw_pan = lux[LT] + lux[LB] - lux[RT] - lux[RB]; /* 左列 - 右列 */
/* P 控制步进: 死区 20 lux, 单周期限幅 3° (最大 30°/s) */
e_tilt = tilt_dir * raw_tilt;
if (e_tilt > TRACK_DEADBAND_LUX || e_tilt < -TRACK_DEADBAND_LUX) {
int32_t step = e_tilt * TRACK_KP_NUM / (TRACK_KP_DEN * 10);
if (step > TRACK_STEP_LIMIT) step = TRACK_STEP_LIMIT;
if (step < -TRACK_STEP_LIMIT) step = -TRACK_STEP_LIMIT;
Angle_SetTilt((int)tilt_target + (int)step);
}
强光门限判断、成对差分与死区限幅步进的完整流程如图 4-3 所示。

控制参数:比例系数使 100 lux 光差产生 4° 步进,死区 20 lux 抑制噪声抖动,单周期(100ms)限幅 3° 对应最大 30°/s 追赶速度。任一路传感器出错,两轴均保持不动并置 I2C 错误标志。方向符号(照左列 Z 减小、照上排 Y 增大)以手电实测锁定,支持运行时经组合键或串口 @S<z>,<y> 翻转;另提供 @C 命令触发一次方向自学习(每轴试探 ±10° 比较光差改善量),实测该功能在窄视场条件下易误判,默认关闭。
4.5 输出平滑
P 控制只写目标角度,实际输出每 20ms PWM 帧朝目标指数逼近:走剩余距离的 1/4,单帧步进上限 3°,且速度每帧最多变化 1°(加速度限幅)。目标角度以 10Hz 阶跃更新,而舵机在 50Hz 的每一帧都在微调,运动连续不打点。按键点动、串口设定与回中位命令同样走这条平滑路径,保证任何来源的角度指令都不产生机械冲击。输出平滑流程如图 4-4 所示。

4.6 PWM 帧时序保障
软件 PWM 的周期等于主循环一圈的耗时,任何阻塞都会造成丢帧——舵机丢帧后突然追位,即"爆冲"。分段计时(tm_sensor_max_us 等)定位出三个元凶:串口阻塞发送一行 150 字节约 13ms、OLED 整行刷新单字段实测 69.4ms、四路传感器读取 3.3ms。三项修复:
- 串口非阻塞:
UART_Print改环形缓冲 + 发送完成中断,主循环零等待; - OLED 分片刷新:数值变化只标脏,每帧由
OLED_Service()最多写一个字符(约 4.3ms),状态行节流至 200ms; - 本帧自锚定补时:帧尾等到「本帧起点 + 20ms」(见 4.1)。
修复前后对比:主循环 12 秒内超 25ms 的帧由 273 帧降为 0 帧,单帧角度跳变计数恒为 0。串口状态行尾附诊断计数器(fm=最长帧 ovf=超时帧 jmp=跳变 bad=坏样本),故障时先读这组数即可快速归因:jmp>0 是固件问题,ovf 增长是主循环被阻塞,均为 0 则是电源或机械问题。
4.7 RAM 黑匣子
偶发故障(如"只动 Z 轴 Y 却跟着动")难以现场复现。固件将每帧关键状态压缩为 20 字节写入 RAM 环形缓冲(512 条 × 20B = 10KB),内容含时间戳、两轴实际/目标角、四路光照、按键状态、模式位与帧周期;有变化才写,静止时 500ms 一条心跳。事后用 SWD 脚本整体读出并自动归因(按键触发 / 闭环驱动 / 无来源帧)。该机制不占串口带宽、不拉长 PWM 帧,配合诊断计数器构成完整的"可观测性"体系。
4.8 人机交互与串口命令
OLED 以 2×2 布局显示四路物理位置读数(所见即所得),第 4 行显示 Z:xxx Y:xxx 与状态字(TRK 追光 / CAL 自学习 / HLD 保持 / MAN 手动)或 I2C ERR。4 按键短按 2°/步、按住 100ms 连发、1s 后加速至 5°/步;任意按键动作自动切入手动模式,闭环停止抢角度。组合键(按住 1s):Key1+Key4 回自动并复位 90/90,Key1+Key2 翻转 Z 方向,Key3+Key4 翻转 Y 方向。
| 命令 | 功能 | 命令 | 功能 |
|---|---|---|---|
@Z<deg> / @Y<deg> | 单独设定方位/俯仰角 | @T<lux> | 在线调整强光门限 |
@ZY<z>,<y> | 同时设定两轴角度 | @S<z>,<y> | 设定两轴追光方向符号 |
@A | 回自动追光(复位 90/90) | @C | 触发一次方向自学习 |
@B | 清空黑匣子 | — | — |
5 系统测试
| 测试项 | 方法 | 结果 |
|---|---|---|
| 四路采集 | 遮挡单路观察读数与 OLED 对应位置 | 读数响应正确,位置映射无误 |
| 强光门限 | 手电照射 / 移开 | 250 lux 进入 TRK,150 lux 以下回 HLD,无临界抖动 |
| 追光对准 | 手电绕云台缓慢移动 | 云台连续跟随,对准后进入死区静止 |
| 输出平滑 | 目标阶跃 30° | 运动连续无打点,无过冲回摆 |
| 帧周期 | 串口诊断计数器连续观测 10s | fm≤20 ovf=0 jmp=0(修复前 12s 内 273 帧超时) |
| 手动/自动切换 | 按键点动后放手电 | 自动切 MAN,按键释放后手电触发回 TRK |
独立测试固件链接到 0x08000000 复位向量直接运行(区别于主工程的 bootloader+app 分区模型),main() 首行纠正 VTOR 后才开启 SysTick,避免从已擦空的 app 槽取向量导致 HardFault;烧录采用整片擦除 + 写入 + 校验流程,烧录前以 readelf/objdump 静态验证入口地址与向量表内容。
6 结论
本文实现了一种基于 STM32 的双轴太阳能追光系统。系统以数字光照传感器四象限差分替代传统光敏电阻方案,免标定且一致性好;强光门限滞回、死区限幅与指数逼近输出平滑三层机制,将闭环运动的平稳性建立在控制结构而非参数凑试之上;帧节拍自锚定与串口/OLED 分片化,解决了软件 PWM 平台的时序确定性问题;RAM 黑匣子与诊断计数器为偶发故障提供了可复盘的观测手段。系统对准精度与动态响应满足设计要求,运动连续无抖动,可直接应用于小型太阳能板的追光控制,其"传感—差分—P 控制—平滑输出"的框架与帧时序保障方法亦可推广至云台跟踪类场景。
参考文献
[1] 意法半导体. STM32F103xx 参考手册(RM0008)[Z]. 2021. [2] 意法半导体. STM32F1 Cortex-M3 编程手册(PM0056)[Z]. 2018. [3] ROHM Semiconductor. BH1750FVI Ambient Light Sensor IC Datasheet[Z]. 2011. [4] NXP Semiconductors. TCA9548A Low-Voltage 8-Channel I2C Switch Datasheet[Z]. 2015. [5] 胡寿松. 自动控制原理(第七版)[M]. 北京:科学出版社, 2019. [6] 刘火良, 杨森. STM32 库开发实战指南[M]. 北京:机械工业出版社, 2013. [7] Solving sensor integration issues in solar tracking applications. Solar Energy, 2020. [8] ARM Ltd. Cortex-M3 Embedded Development: DWT Cycle Counter Usage Note[Z]. ARM Application Note, 2015.