news 2026/9/9 11:09:11

太阳能追光系统实战:基于STM32与Arduino的光伏板自动追踪设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
太阳能追光系统实战:基于STM32与Arduino的光伏板自动追踪设计

简介:针对STM32与Arduino联合实现的太阳能追光系统,这份资料提供了完整的工程源码与硬件配置,适合嵌入式学习者、电子设计竞赛参赛者以及新能源应用开发者。项目以自供能为特色,涵盖光照检测、角度追踪算法、步进电机驱动和电源管理,可帮助读者掌握双控制器协同开发方法。压缩包共1011个文件,约23.96MB,以C代码文件(561个)和头文件(245个)为主,还有编译链接配置、汇编启动文件、CubeMX配置以及少量文档,清晰呈现工程结构。已有1813人学习,资料中既包含STM32主控逻辑与Arduino辅助控制实现,也涉及DSP数学库等移植内容,便于对照原理图、代码和调试文件复现整套追光系统。

1. 项目概述与核心思路

1.1 这个追光系统到底在解决什么问题

太阳能追光系统的本质,是想办法让光伏板始终正对太阳。固定安装的太阳能板在阳光斜射时,接收到的等效光照强度会按入射角余弦衰减,早上和傍晚差距尤其明显。实测下来,一套简单的单轴追光系统能把日发电量提升20%到40%,双轴系统在高纬度或者季节变化明显的地区收益更大。

这个项目的应用场景很典型:课程设计、毕业设计、智能农业设备里的独立供电单元升级,或者纯粹想玩玩单片机的人给自己阳台上的小光伏板做个自动支架。不管哪种情况,核心需求都是同一个——用光敏传感器感知光源方向,靠电机驱动机械结构去追踪太阳。

我选择用STM32和Arduino双平台来聊这个项目,是因为两条路线代表了两种完全不同的学习路径。Arduino屏蔽了大量底层寄存器操作,你可以用十几行代码就看到系统转起来,适合快速验证方案和入门理解;STM32则要求你理解ADC配置、定时器PWM生成、GPIO复用这些底层机制,踩坑更多,但做完之后你对单片机的理解会上一个台阶。

1.2 Arduino和STM32怎么选

选型这事,别听别人吹,得看你的目标和手里有什么工具。

对比维度Arduino(以UNO/Nano为例)STM32(以F103C8T6为例)
核心芯片ATmega328P,8位AVRCortex-M3,32位ARM
ADC精度10位,0~102312位,0~4095
工作电压/逻辑电平5V3.3V
PWM输出6路,8位精度多路16位定时器PWM
上手难度低,库函数封装完善高,需要配置底层寄存器或通过CubeMX生成初始化代码
价格十几到几十元几元到十几元
适合场景快速原型、学习、小负载多路传感器采集、精确控制、边缘计算迭代

如果你之前没接触过嵌入式,别犹豫,先用Arduino把整个追光流程跑通。Arduino自带Servo库,一行命令就能控制舵机转动,光敏电阻分压电路连接也很直观。等你理解了“传感器读数→比较差值→调整执行器”这条闭环控制链路,再切到STM32补齐底层知识,会顺畅很多。

反过来,如果你是在做毕业设计或者需要同时控制多个传感器(比如风速、温度、光照采集加双轴电机),STM32的硬件资源会更宽裕。F103C8T6的千兆赫级主频、多个ADC通道、多路定时器,可以让你把追光逻辑和远程监控逻辑通过FreeRTOS分任务管理,不必像Arduino那样在一个loop里挤时间片。

2. 硬件设计与选型

2.1 传感器方案对比:从光敏电阻到四象限传感器

市面上做光照方向检测的方案有好几种,针对追光这个词,可选路径包括:

  • 光敏电阻(LDR)+固定电阻分压。优点是便宜、电路简单、调试直观,缺点是温漂明显、响应慢、同一批次个体差异大。
  • 光电池(小面积太阳能板)直接做传感器。输出信号就是电压,稍微放大后接入ADC即可,因为太阳能板本身就是工作对象,“以板测光”的思路很自然。
  • 光电二极管/光电三极管。响应速度快,线性度比LDR好,但需要接运放做电流转电压,电路复杂度高一些。
  • 四象限光敏传感器(QPD)。用四块独立光敏面,加上隔光罩,通过四个象限的差值判断光源方位和俯仰角,精度高但价格也高。

对绝大多数学习项目和小型应用来说,光敏电阻+阴影遮挡法是最划算的选择。

阴影遮挡法的原理特别容易理解:拿一块长条形的不透光隔板,隔板左右两侧各放一个光敏传感器。当光源正对隔板顶面时,两个传感器接收到的光线强度一致;当光源偏向某一侧时,隔板会挡住那一侧的部分光线,对应传感器的读数下降,通过比较左右两侧读数的差值,就能推算出光源偏转方向和偏转程度。

我实际做下来,隔板高度建议在25mm到35mm之间。隔板太低,角度分辨率不够;太高,光线稍微偏一点传感器就完全进入阴影区,导致控制系统在零点附近震荡。尺寸这个东西没有绝对标准,装好后对着太阳实测一下,用透明胶带暂时固定隔板,把角度偏差从30度扫到30度,记录两侧ADC数值曲线,曲线越陡说明灵敏度越高,但也要确保在扫描终点没有提前进入饱和区。

2.2 双轴还是单轴:先想清楚再动手

单轴追光系统只控制一个方位角(水平旋转),安装简单,一个舵机就能带起来。如果你的光伏板主要在低纬度地区或者只是做个演示,单轴完全够用。太阳能板朝正南方向后,单轴追踪太阳的东升西落,一天下来发电量提升已经很可观。

双轴系统在单轴基础上增加了俯仰控制(仰角追踪),可以跟随太阳高度角的变化。因为不同纬度和季节的太阳高度角差异很大,双轴收益在春秋两季尤其明显。但代价是结构复杂程度翻倍:需要两个舵机或步进电机、一套十字万向节支架、可能还要考虑重心平衡。

我做第一版追光系统时贪求全,直接上了双轴,结果头两周的时间都花在机械结构和调试上。后来拆掉一个舵机,先用单轴跑通闭环逻辑,又把双轴加回来,反而很快完成了。这也是我这次特别想分享的经验——先用单轴验证算法,再扩展机械结构。双轴的传感器布局可以沿用单轴思路,做一个十字形隔板架,四个方位各放一个光敏电阻,水平差值控制方位舵机,垂直差值控制俯仰舵机。

2.3 舵机怎么选:不是越贵就越好

市面上9g舵机(如SG90)、金属齿大扭矩舵机(如MG996R)是追光项目最常用的执行器。选型时关注三个参数:

第一是扭矩。太阳能板越大,风阻和惯性力矩越大,小舵机撑不住。单片小光伏板用SG90就够了,桌面型太阳能板支架也勉强能用;如果你做的是一块40cm乘40cm以上的板子,或者放在室外风大的地方,至少选MG996R,并且做好支架重心平衡,不给舵机额外增加静态负载。

第二是工作电压。SG90标称4.8V到6V,MG996R也是如此。Arduino的5V引脚可以直接带一路SG90,但MG996R瞬间电流可能到1A以上,直接从单片机供电口取电会把主控拉复位。正确做法是舵机独立供电,电源负极和单片机共地即可。

第三是PWM频率和脉宽范围。标准舵机的控制信号是50Hz(20ms周期)、0.5ms到2.5ms的脉宽对应0到180度。Arduino的Servo库会自动处理这些参数。STM32上如果你自己用定时器生成PWM,就要把计数器周期设置为20ms,再根据你想达到的角度范围调整脉宽上下限,这里很容易犯的错误是默认用了1ms到2ms的标准,导致舵机只能在90度到180度之间转动。

3. 电路接线与参数计算

3.1 光敏电阻分压电路计算

光敏电阻和固定电阻串联,中间抽头接到单片机的ADC引脚,这个电路是追光系统的感知入口。选固定电阻阻值时,考虑的是让ADC的输入电压范围在整个光照变化区间内都处于合适区间。

以常用的LDR(亮阻约800Ω到1.2kΩ,暗阻约50kΩ到300kΩ)为例,电源接5V,固定电阻R固定取10kΩ。可以用欧姆定律算一下ADC输入电压摆幅:

光照充足时,LDR阻值约1kΩ:Vout = 5 × 10000 / (1000 + 10000) ≈ 4.55V

光照微弱时,LDR阻值约100kΩ:Vout = 5 × 10000 / (100000 + 10000) ≈ 0.45V

这样ADC输入范围几乎覆盖了整个0到5V区间,10位AD转换分辨率足够。如果你在测试中发现读数长期偏高或偏低,把固定电阻往LDR阻值相近的方向调整。

多说一个细节:STM32的ADC输入电压范围是0到VREF,F103系列VREF一般接3.3V,不能直接把5V传感器的输出怼进去,要用电阻分压或者确认光敏分压电路输出不超过3.3V。如果沿用刚才的分压参数,在STM32系统中把光敏电阻串联电路的供电接到3.3V即可。

3.2 完整接线总览

这里给出一套Arduino UNO + SG90舵机 + 两个光敏电阻的最小系统接线表,照抄即可跑通第一版:

组件引脚连接目标
光敏电阻左LDR一端A0
光敏电阻左LDR另一端5V
10kΩ固定电阻左一端A0
10kΩ固定电阻左另一端GND
光敏电阻右LDR一端A1
光敏电阻右LDR另一端5V
10kΩ固定电阻右一端A1
10kΩ固定电阻右另一端GND
舵机信号线9号引脚
舵机电源线外部5V电源正极
舵机地线外部电源GND
外部电源GNDArduino GND(共地)

注意这里的共地非常关键。舵机电源和单片机电源如果各自独立不共地,PWM信号参考电压和舵机电源参考电压不一致,会导致舵机位置漂移甚至不动作。在面包板上接线时,用一根黑色杜邦线把两个电源的负极连起来,能避免很多诡异问题。

3.3 电源选型和功耗预算

整套系统的功耗大头在舵机,不在单片机。SG90正常转动电流在100mA到250mA之间,堵转时会飙升到500mA以上。Arduino UNO如果通过USB供电,调节器输出电流能力有限,不足以稳定驱动舵机。我的做法是:舵机电源用一节18650锂电池加降压模块调到5V,或者直接用一个手机充电宝转USB 5V供电,充电宝的电流输出能力通常在1A以上,带单个SG90绰绰有余。STM32的3.3V电源用AMS1117稳压芯片从5V降压,或者直接用带3.3V输出的FTDI模块供电。

整机功耗算下来大概在0.5W到1.5W之间,白天依靠一个小型太阳能板搭配锂电池就能自维持。如果你考虑做成独立离网系统,可以按这个功耗来配电池容量和太阳能板功率。光是这个系统,用一块3W的太阳能板和2000mAh锂电池,连续阴天也能撑两到三天待机加半天的追光运行。

4. 软件实现与代码逻辑

4.1 Arduino版:最快跑通闭环控制

Arduino版本的代码核心逻辑极其简单:循环比较两个光敏电阻的ADC读数,差值超过预设阈值时,让舵机朝光线强的方向步进;差值在阈值内时,保持当前角度,防止舵机在零点附近来回抖动。

#include <Servo.h> #define LDR_LEFT A0 #define LDR_RIGHT A1 #define SERVO_PIN 9 #define DEADZONE 25 #define STEP_SIZE 2 Servo myservo; int angle = 90; int readAverage(int pin, int times) { long sum = 0; for (int i = 0; i < times; i++) { sum += analogRead(pin); delay(5); } return sum / times; } void setup() { myservo.attach(SERVO_PIN); myservo.write(angle); Serial.begin(9600); delay(500); } void loop() { int left = readAverage(LDR_LEFT, 5); int right = readAverage(LDR_RIGHT, 5); int diff = left - right; if (diff > DEADZONE) { angle = min(angle + STEP_SIZE, 180); } else if (diff < -DEADZONE) { angle = max(angle - STEP_SIZE, 0); } myservo.write(angle); Serial.print("L:"); Serial.print(left); Serial.print(" R:"); Serial.print(right); Serial.print(" ANG:"); Serial.println(angle); delay(50); }

这里有几个我调试了很久才想明白的细节。

采样为什么要做多次平均?因为环境光本身就有轻微波动,加上ADC引脚的噪声,单次采样可能偏离真实值几个到十几个LSB。我做了5次采样平均,每两次采样之间留5ms延时,效果稳定很多。你还可以试试去掉最大值和最小值后对中间值取平均,也就是中值平均滤波,应对偶发干扰效果更好。

DEADZONE值为什么设成25?这个值要结合你的分压电路参数来定。如果固定电阻太大或太小,同样的光线变化反馈到ADC上的差值大小差异会非常大。我刚开始把死区设太小时,舵机在正午的强光下不停小幅抖动,角度变化肉眼可见;设太大又会出现明显追踪滞后。25这个数值在我的电路配置下表现比较均衡,你可以用串口监视器观察两个通道的稳定读数,然后根据波动幅度调整这个参数。

STEP_SIZE设成2度每次循环,配合50ms延时,舵机从90度转到接近极限位置大概需要几秒钟。这个响应速度对追光场景完全够用,同时平稳的运动方式也保护了舵机齿轮,避免频繁大角度动作造成磨损。

4.2 STM32版本:从CubeMX到Keil的完整流程

STM32版本的开发路线和Arduino完全不一样。我推荐用STM32CubeMX生成初始化代码,IDE选Keil MDK或者VSCode加EIDE插件都行。

CubeMX里需要配置的核心外设:

  • RCC:HSE外部高速晶振,配置为Crystal/Ceramic Resonator
  • SYS:Debug选项选Serial Wire,否则烧录一次后第二次连接可能报找不到目标芯片
  • ADC1:开启两个通道。比如IN0和IN1,采样时间拉长到239.5周期,能显著降低输入阻抗带来的采样误差
  • TIM1:设置PWM输出通道1,输出比较频率50Hz,占空比初值对应90度角
  • 系统时钟:从默认的8MHz倍频到64MHz或72MHz,看你的芯片频率上限

在main.c的主循环里写控制逻辑:

uint16_t ldr_left, ldr_right; int diff; int angle = 900; // 对应90度,这里假设脉宽步进值以1/20000周期计 while (1) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 10); ldr_left = HAL_ADC_GetValue(&hadc1); HAL_ADC_Start(&hadc2); HAL_ADC_PollForConversion(&hadc2, 10); ldr_right = HAL_ADC_GetValue(&hadc2); diff = (int)ldr_left - (int)ldr_right; if (diff > 80) { if (angle < 1800) angle += 20; } else if (diff < -80) { if (angle > 0) angle -= 20; } __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, angle); HAL_Delay(50); }

关于角度和占空比的关系,需要解释一下。20ms周期的脉冲,0到180度对应0.5ms到2.5ms脉宽,也就是占空比0.025到0.125。当你使用16位定时器,计数器计数范围从0到9999(对应20ms周期)时,映射关系就是250比1度。如果你使用ARR=19999,那么就是500比1度。上面代码里的angle=900,其实是我用一个简化映射模拟出来的示例,实际你需要按定时器配置修改这个比例。

这块儿非常容易翻车。我在做STM32版本时,前面几次配置PWM都出现舵机没有反应,后来用逻辑分析仪看信号才发现,脉宽根本没有对应到合适的电平持续时间。最靠谱的排查方法就是写一个测试程序,固定把angle设成100,然后用示波器或者逻辑分析仪看舵机信号脚的输出,确认高电平时间在0.5ms到2.5ms的合理区间,再接舵机测试。

4.3 控制逻辑进阶:不只有“差值比较”

前面写的代码全是位置式差值比较。对于追光系统,还可以做增量式控制或者PID控制。增量式控制很直观:每次只根据上一轮差值和这一轮差值的变化量来调整,避免连续向同一个方向大步进。这个和算法上的区别,效果上类似给电机运动加了一个缓冲,能明显减少超调。

如果做过PID,你会发现追光系统用P环节就够了。I环节用在光照稳定但传感器存在系统偏差的场景里,D环节可以抑制天气突变引起的抖动。但是追光系统本身响应很慢,环境光非线性和传感器自身差异带来的误差,被死区机制抵消了一部分,没必要把控制问题复杂化。我见过一个项目里写了完整的三环PID,最后效果和简单差值比较差不多,还白白增加了调试成本。

5. 常见问题与排查技巧实录

5.1 舵机抖动、不动、乱动

这个是我被问得最多的问题,也是所有舵机控制项目的通用难点。

先说舵机完全不动。先测信号线有没有电平输出,再看电源线电压是否正常。曾经有一次我把舵机信号线插反,折腾了半小时;还有一次是PWM通道的时钟没有使能,输出引脚一直是低电平,自然没反应。建议按信号线、电源线、地线、PWM配置四步挨个排查,不要跳着查。

再说舵机乱动或者抖动。多半是电源不够导致的。特别是STM32开发板,板载稳压器输出能力有限,舵机启动瞬间把电压拉低,单片机复位,表现出来就是舵机一顿一顿地抽搐。解决办法只有独立供电加共地。另外,舵机信号线走线不要和电机电源线绑在一起,如果万不得已要靠近,也可以在信号线对地加一个1kΩ下拉电阻,降低干扰影响。

5.2 环境光干扰导致追踪错误

室外的自然光环境和室内差异很大。在室内测试时,头顶的LED灯光会在不同区域产生明显的明暗差,导致追光系统被照明灯“带跑”而不是去追太阳。在室外,云层飘过、地面反光、阴影边界,都会造成某一个传感器的读数瞬间异常。

处理环境光干扰,我的经验是三层滤波:

  1. 软件上做多次采样取中值或平均,去掉瞬时毛刺。
  2. 控制上加大死区,让系统对小扰动不敏感。
  3. 硬件上用遮光罩只保证前方和侧前方的一定张角内自然光进入传感器,抑制后方和下方杂散光的影响。

如果你发现系统偶尔会“发疯”地转到一个错误方向,大概率是某一侧传感器被一阵强反光闪了一下。应对思路是限制单次连续步进次数:记录舵机连续朝同一方向动作的累计步数,如果超过一个合理值(比如从90度往同一方向移动超过60度),就要判断一下对应方向的传感器读数是否真的持续偏高,而不是偶尔一次。

5.3 ADC读数不稳定

Arduino的10位ADC在引脚悬空时会出现明显的随机波动,接上传感器后有改善但也会有小幅跳变。要把“读数稳”落在实处,两个办法:

一是把采样周期拉长,用多个采样点进行一次计算,这个方法我在上面的代码里已经体现了。二是在ADC引脚和GND之间并联一个0.1μF滤波电容,可以把高频噪声滤掉不少。实测下来,加了电容后同样条件下读数波动从上下20缩到上下3左右。

STM32上还要注意ADC采样时间的配置。采样时间短,源阻抗高时,采样电容来不及充满,导致读数偏低且不稳定。CubeMX里把采样时间设置成239.5周期,是消除这个问题的常规操作。

5.4 常见问题速查表

现象可能原因解决方法
舵机完全不动信号线没接触好或PWM未配置用示波器/逻辑分析仪测信号输出
舵机抖动/复位供电不足独立电源供电,保证足够电流,务必共地
角度一直偏向某一侧两个传感器静态读数不一致用软件校准:初始化时记录差值的基准量
正午阳光强烈时读数饱和分压电阻选配不合理增大固定电阻阻值,重新计算分压
追光速度过慢STEP_SIZE太小或循环延时太长适当增大步进角和减小延时
云层遮挡后乱转环境光变化过快增大死区,限制单次连续步进次数
夜晚还在持续转动未设置无光照待机状态加一个全局光强阈值判断,低于阈值不执行追光

关于传感器的静态校准,多说一句。就算你用同一个批次的光敏电阻,装到左右两个位置后,由于焊接、导线长度、安装角度的微小差异,两个通道在相同光照下的ADC读数也可能差十几个数。正确做法是上电后,用遮光罩盖住整体,读取一段基础偏差值存在全局变量里,后续每次比较都用当前读数减去这个基础偏差。

5.5 夜间待机和快速回归

最后再加一个小技巧。追光系统本身是日间运行设备,但晚上一直在loop里空转不仅浪费电,还会让舵机在低光环境下反复调整到极限角度。我习惯在程序里加一个环境光总强度判断。

把左右两个传感器的ADC读数相加,当总和低于某个设定阈值时,说明环境已经变暗,此时可以把舵机缓缓转回初始位置(比如90度),然后进入低功耗等待状态。第二天早上光照恢复后再重启追踪。这个逻辑用Arduino实现非常直接,只需要在loop开头加一个条件判断即可。

我第一版完全不考虑晚间状态,舵机在晚上疯狂抖动,第二天早上一看,舵机齿轮已经磨损得有明显虚位了。加了这个简易待机逻辑之后,问题彻底消失,而且整机待机电流能降到只有1mA上下,对电池系统的压力小很多。

这个项目最让人上瘾的地方在于,它把传感器信号处理、执行器驱动、机械结构设计、逻辑闭环完整串了一遍,做完一遍以后,你再去看那些更复杂的自动化设备,思路基本都是一样的:感知、决策、执行。下一步如果想升级,可以把光敏传感器方案替换为太阳位置算法加对时模块,做成能自动校准的系统,或者接入ESP8266上报发电数据,这些都是在目前这套硬件基础上的自然扩展。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 11:08:13

JMeter压测实战指南:线程组选型、参数化与性能分析全流程

JMeter这个工具我前前后后用了差不多六年&#xff0c;从最开始只会对着百度一顿乱搜&#xff0c;到后来给公司搭了一整套压测环境&#xff0c;中间踩过的坑说多不多说少不少。今天就把整个JMeter做压力测试的完整思路捋一遍&#xff0c;从安装配置到脚本设计&#xff0c;从线程…

作者头像 李华
网站建设 2026/9/9 11:07:52

基于CVaR的微网动态定价与调度策略及Matlab实现

做微网优化的人&#xff0c;应该都遇到过这种纠结&#xff1a;光伏出力飘忽不定&#xff0c;批发市场电价上蹿下跳&#xff0c;靠期望值做出来的调度方案看着利润挺高&#xff0c;但一遇到极端场景就“翻车”&#xff0c;不是购电成本爆表就是被考核罚款。这两年“基于条件风险…

作者头像 李华
网站建设 2026/9/9 11:06:47

STM32红外遥控器实战:NEC协议解码与发射完整指南

简介&#xff1a;STM32 红外遥控器程序是一份面向嵌入式初学者及课程设计/毕设学生的完整工程源码包。项目以 STM32 为控制核心&#xff0c;涵盖红外通信、PWM 脉冲编解码、GPIO 与定时器中断等硬件接口处理&#xff0c;并给出基于 NEC、RC5 等标准的信号解码与校验实现&#x…

作者头像 李华
网站建设 2026/9/9 11:03:36

Abaqus二次开发全解析:从UMAT到Python脚本的实战指南

简介&#xff1a;面向需要进行ABAQUS二次开发的工程师与研究人员&#xff0c;这份完整代码资料包以Python语言为主线&#xff0c;系统覆盖API调用、自动化建模、自定义材料与载荷等开发场景&#xff0c;既能用于功能验证&#xff0c;也能作为二次开发模板。包内共343个文件&…

作者头像 李华
网站建设 2026/9/9 11:03:31

从BSP到系统架构师:思维方式与能力模型的关键跃迁

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/9 11:01:20

opencode开源AI编程助手实战指南:从安装配置到Skills扩展

最近 opencode 这个开源 AI 编程助手在开发者圈子里讨论度一下子起来了。经常能看到“opencode 安装”“opencode 使用教程”“opencode 免费模型”这些热搜词挂在首页&#xff0c;还有人问它到底是哪家公司的、和 Claude Code、Codex 有什么区别。简单说&#xff0c;opencode …

作者头像 李华