尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32太阳能追光系统:从光敏电阻到PID控制的完整仿真方案

STM32太阳能追光系统:从光敏电阻到PID控制的完整仿真方案 在嵌入式开发领域如何高效利用太阳能一直是个热门课题。很多同学在做课程设计或毕业设计时都会遇到一个经典项目设计一个能自动追踪太阳、并为锂电池充电的系统。想法很美好但实际动手时常常卡在硬件选型、传感器数据处理、舵机控制逻辑和系统仿真验证这几个环节网上资料要么过于零散要么只讲理论没有完整代码。本文将围绕“基于STM32的太阳能追光锂电池充电系统”这一主题为你提供一套从原理到仿真、从代码到调试的完整闭环解决方案。无论你是正在做相关课题的学生还是希望深入了解STM32在新能源领域应用的开发者都能从本文中获得可直接复用的设计思路、核心代码以及Proteus仿真方法。我们将重点拆解光敏电阻阵列的布局与AD采样策略、双轴舵机的PID控制算法、锂电池充电管理电路TP4056的集成以及如何在Proteus中搭建包含这些外设的仿真环境。读完本文你将能独立完成一个功能完整的太阳能追踪器控制系统的设计与仿真验证。1. 系统核心概念与设计背景在深入代码之前我们有必要厘清整个系统要解决的核心问题以及其背后的技术原理。这有助于我们理解每一个模块的设计动机而不仅仅是机械地拼接代码。1.1 什么是太阳能自动追光系统太阳能自动追光系统顾名思义是一个能够使太阳能电池板实时对准太阳以最大化接收太阳辐射能量的装置。其核心目标在于提升光伏发电效率。研究表明与固定角度的光伏板相比采用双轴追踪的系统平均可提升20%-40%的发电量。该系统通常由以下几个部分构成感知单元用于检测太阳光的方向。常见方案有光敏电阻阵列、四象限光电探测器或视觉传感器。本文采用成本低、易于实现的光敏电阻阵列方案。控制单元作为系统的大脑负责处理传感器数据、执行控制算法并驱动执行机构。STM32系列单片机因其丰富的外设多路ADC、定时器、PWM和较高的性价比成为该角色的理想选择。执行机构根据控制单元的指令带动太阳能板进行水平方位角和垂直高度角两个维度的转动。通常使用舵机Servo或步进电机Stepper Motor。舵机控制简单适合仿真和中小型模型。能量管理单元将太阳能电池板产生的电能进行存储和管理。核心包括锂电池充电管理芯片如TP4056和升压/降压电路确保系统能持续稳定工作并为控制单元和舵机供电。1.2 为什么选择STM32F103C8T6在众多单片机中我们选择STM32F103C8T6俗称“蓝桥杯”或“最小系统板”核心芯片作为主控主要基于以下几点考量性能与资源平衡ARM Cortex-M3内核72MHz主频足以流畅运行追光控制算法。具备多达10个通道的12位ADC可同时采样多个光敏电阻拥有多个定时器可轻松产生多路PWM信号驱动舵机。生态与成本STM32的HAL库和标准库资料极其丰富开发工具Keil, STM32CubeIDE成熟降低了开发门槛。该芯片及开发板价格亲民非常适合学习和原型开发。Proteus仿真支持Proteus仿真软件对STM32F103系列有较好的模型支持便于我们在没有实物硬件的情况下完成系统逻辑和基础算法的验证。1.3 系统整体工作流程整个系统的运行遵循一个清晰的闭环控制流程数据采集STM32周期性地通过其ADC模块读取布置在太阳能板四个方向上、下、左、右的光敏电阻的电压值。电压值反比于光照强度光照越强电阻越小分压电压越低。偏差计算将采集到的四路电压值进行处理计算出太阳能板当前朝向与太阳光方向的偏差。通常采用“比较法”例如(左-右) 反映水平方向偏差(上-下) 反映垂直方向偏差。控制决策将计算出的偏差送入控制算法如简单的比例控制或更稳定的PID控制计算出需要调整的角度或对应的PWM脉宽。执行输出STM32通过定时器产生两路PWM信号分别控制水平舵机和垂直舵机转动从而带动太阳能板向减小偏差的方向移动。能量管理太阳能电池板输出的电能经过TP4056充电管理电路为锂电池充电同时锂电池为整个STM32控制系统和舵机供电形成一个自持的能量循环。2. 开发环境与准备工作“工欲善其事必先利其器”。在开始编写代码和进行仿真之前我们需要搭建好相应的软件环境并明确所需的组件。2.1 软件环境清单软件名称推荐版本主要用途Keil uVision5 (MDK-ARM)V5.XX 或更高STM32程序的编写、编译和调试。需安装STM32F1的Device Family Pack。STM32CubeMXV6.X 或更高图形化配置STM32引脚、时钟、外设ADC, TIM等生成初始化代码框架极大提升开发效率。Proteus 8 ProfessionalV8.9 或更高电路设计与仿真。用于绘制系统原理图并仿真STM32程序与虚拟外设光敏电阻、舵机、电压表等的交互。串口调试助手如SSCOM、XCOM用于在实物调试时通过串口打印传感器数据、PWM占空比等信息辅助调试。版本说明软件版本会持续更新本文以当时常见稳定版为例。只要核心功能如HAL库、仿真模型具备版本差异通常不影响主要逻辑。请根据你的实际情况安装并注意Keil和CubeMX的兼容性。2.2 核心硬件组件仿真模型对应在Proteus仿真中我们需要找到以下元件的仿真模型单片机STM32F103C8光敏传感器使用LDR光敏电阻模型配合普通电阻组成分压电路接入ADC。舵机SERVO-MOTOR模型。Proteus中的舵机模型可以通过输入PWM信号来模拟转动。锂电池充电管理TP4056在Proteus中可能没有直接模型我们可以用其功能等效电路来仿真一个电压源模拟太阳能板通过一个理想二极管和限流电阻为电容模拟电池充电并用电压表监测。对于学习控制逻辑可以简化此部分。指示与调试LED、VIRTUAL TERMINAL虚拟串口终端用于显示状态和调试信息。2.3 工程创建与基础配置我们使用STM32CubeMX来快速生成工程骨架。新建项目打开CubeMX选择STM32F103C8Tx。配置时钟在RCC中将HSE设置为Crystal/Ceramic Resonator。在Clock Configuration标签页将系统时钟源设置为HSE并配置PLL使系统时钟SYSCLK达到72MHz。配置调试接口在SYS中将Debug设置为Serial Wire这样可以使用ST-LINK进行下载和调试。配置USART1用于调试输出在Connectivity-USART1中将模式设置为Asynchronous并配置合适的波特率如115200。这将用于在Proteus虚拟终端或实物串口上打印数据。保存并生成代码配置好项目名称、路径和工具链MDK-ARM V5点击GENERATE CODE。至此一个包含HAL库和基础时钟、调试串口初始化的Keil工程就生成了。我们后续的代码将在此基础上添加。3. 核心模块原理与驱动实现接下来我们深入系统最核心的三个模块光照检测、舵机驱动和充电管理并给出具体的代码实现。3.1 光照检测模块光敏电阻阵列与ADC采样我们采用四个光敏电阻分别放置在太阳能板的东、西、南、北四个边缘对应左、右、下、上与固定电阻组成分压电路连接至STM32的四个ADC通道。电路原理VCC-LDR-ADC引脚-固定电阻如10K-GND。光照越强LDR阻值越小ADC引脚测得的电压越低。代码实现使用HAL库 首先在CubeMX中使能ADC1并配置通道0、1、2、3对应PA0, PA1, PA2, PA3为Single-ended模式设置合适的采样时间。// 文件路径Src/adc.c (部分由CubeMX生成) // 文件路径Inc/main.h 中添加 #define LDR_UP_CHANNEL ADC_CHANNEL_0 // PA0 #define LDR_DOWN_CHANNEL ADC_CHANNEL_1 // PA1 #define LDR_LEFT_CHANNEL ADC_CHANNEL_2 // PA2 #define LDR_RIGHT_CHANNEL ADC_CHANNEL_3 // PA3 // 文件路径Src/main.c 用户代码区 // 定义变量存储ADC值 uint32_t adc_value_up 0, adc_value_down 0, adc_value_left 0, adc_value_right 0; int16_t error_horizontal 0, error_vertical 0; // 水平与垂直偏差 // 函数读取指定通道的ADC值取多次平均滤波 uint32_t ADC_Read_Channel(ADC_HandleTypeDef* hadc, uint32_t channel) { ADC_ChannelConfTypeDef sConfig {0}; uint32_t raw_value 0; uint8_t i; // 配置通道 sConfig.Channel channel; sConfig.Rank 1; sConfig.SamplingTime ADC_SAMPLETIME_55CYCLES_5; if (HAL_ADC_ConfigChannel(hadc, sConfig) ! HAL_OK) { Error_Handler(); } // 启动ADC转换读取停止 HAL_ADC_Start(hadc); for(i0; i16; i) { // 采样16次取平均 HAL_ADC_PollForConversion(hadc, 10); raw_value HAL_ADC_GetValue(hadc); } HAL_ADC_Stop(hadc); return (raw_value 4); // 除以16右移4位 } // 在main循环中调用 void Read_LDR_Sensors(void) { adc_value_up ADC_Read_Channel(hadc1, LDR_UP_CHANNEL); adc_value_down ADC_Read_Channel(hadc1, LDR_DOWN_CHANNEL); adc_value_left ADC_Read_Channel(hadc1, LDR_LEFT_CHANNEL); adc_value_right ADC_Read_Channel(hadc1, LDR_RIGHT_CHANNEL); // 计算偏差假设光照强则ADC值小。偏差 暗侧值 - 亮侧值 error_horizontal adc_value_left - adc_value_right; // 左右说明右边更亮需要向右转 error_vertical adc_value_down - adc_value_up; // 下上说明上边更亮需要向上转 // 可以加入死区避免微小偏差引起的抖动 if(abs(error_horizontal) 50) error_horizontal 0; if(abs(error_vertical) 50) error_vertical 0; }3.2 舵机驱动模块PWM信号生成与控制舵机的控制需要一个周期为20ms50Hz脉宽在0.5ms到2.5ms之间的PWM信号对应舵机角度0°到180°。STM32定时器配置 我们使用通用定时器如TIM2和TIM3的两个通道来产生两路PWM。系统时钟72MHz经过分频和自动重装载值设定产生50Hz的PWM波。在CubeMX中配置TIM2和TIM3Clock Source: Internal ClockChannel1/2/3/4: PWM Generation CHxParameter Settings:Prescaler (PSC): 71 72MHz / (711) 1MHzCounter Period (ARR): 19999 1MHz / 20000 50Hz此时计时器计数频率为1MHz每个计数1us。脉宽0.5ms对应计数值5002.5ms对应2500。代码实现// 文件路径Inc/main.h 中添加 #define SERVO_HORIZONTAL_TIM htim2 #define SERVO_HORIZONTAL_CH TIM_CHANNEL_1 #define SERVO_VERTICAL_TIM htim3 #define SERVO_VERTICAL_CH TIM_CHANNEL_1 #define SERVO_PWM_MIN 500 // 0.5ms #define SERVO_PWM_MAX 2500 // 2.5ms #define SERVO_PWM_MID 1500 // 1.5ms (中位) // 文件路径Src/main.c // 启动PWM输出在main函数初始化部分调用 HAL_TIM_PWM_Start(SERVO_HORIZONTAL_TIM, SERVO_HORIZONTAL_CH); HAL_TIM_PWM_Start(SERVO_VERTICAL_TIM, SERVO_VERTICAL_CH); // 设置舵机到中位 __HAL_TIM_SET_COMPARE(SERVO_HORIZONTAL_TIM, SERVO_HORIZONTAL_CH, SERVO_PWM_MID); __HAL_TIM_SET_COMPARE(SERVO_VERTICAL_TIM, SERVO_VERTICAL_CH, SERVO_PWM_MID); // 函数根据偏差更新舵机PWM简单比例控制 void Update_Servo_Position(int16_t error_h, int16_t error_v) { static uint16_t pwm_h SERVO_PWM_MID, pwm_v SERVO_PWM_MID; float Kp 0.1; // 比例系数需要根据实际调试 // 水平舵机控制 pwm_h (int16_t)(error_h * Kp); // 限幅防止超出有效范围 if(pwm_h SERVO_PWM_MIN) pwm_h SERVO_PWM_MIN; if(pwm_h SERVO_PWM_MAX) pwm_h SERVO_PWM_MAX; __HAL_TIM_SET_COMPARE(SERVO_HORIZONTAL_TIM, SERVO_HORIZONTAL_CH, pwm_h); // 垂直舵机控制 pwm_v (int16_t)(error_v * Kp); if(pwm_v SERVO_PWM_MIN) pwm_v SERVO_PWM_MIN; if(pwm_v SERVO_PWM_MAX) pwm_v SERVO_PWM_MAX; __HAL_TIM_SET_COMPARE(SERVO_VERTICAL_TIM, SERVO_VERTICAL_CH, pwm_v); // 可以通过串口打印PWM值进行调试 // printf(H_PWM:%d, V_PWM:%d\r\n, pwm_h, pwm_v); }3.3 能量管理模块TP4056充电电路集成TP4056是一款完整的单节锂电池恒流/恒压线性充电管理芯片。其外围电路非常简单在实物连接中VCC接太阳能板需加防反接二极管。BAT接锂电池正极。GND接公共地。PROG引脚通过一个电阻如1.2K设置充电电流约1000mA。CHRG和STDBY引脚可接LED指示充电状态。在Proteus中的简化仿真 由于Proteus可能没有TP4056精确模型且仿真重点是控制逻辑我们可以简化能量管理部分的仿真用一个可调电压源DC模拟太阳能电池板输出电压例如5V-6V。用一个二极管DIODE模拟防反接。用一个大的电容CAPACITOR如1000uF并联合适的电阻负载来模拟锂电池及其负载。通过观察电容两端电压使用DC VOLTMETER的变化可以定性理解充电过程。STM32系统和舵机的供电可以直接由该“电池电容”或另一个稳压源如LM7805提供。STM32的电压监测可选 可以通过另一个ADC通道分压后监测电池电压实现低电压保护或电量显示。// 文件路径Src/main.c // 假设电池电压通过分压连接到PA4 (ADC1 Channel4) #define BAT_VOLTAGE_CHANNEL ADC_CHANNEL_4 float battery_voltage 0.0; #define VOLTAGE_DIVIDER_RATIO 2.0 // 根据实际分压电阻计算 void Read_Battery_Voltage(void) { uint32_t adc_val ADC_Read_Channel(hadc1, BAT_VOLTAGE_CHANNEL); // ADC参考电压为3.3V12位分辨率 battery_voltage (adc_val * 3.3 / 4095) * VOLTAGE_DIVIDER_RATIO; // printf(Battery: %.2fV\r\n, battery_voltage); if(battery_voltage 3.3) { // 假设锂电池保护电压 // 进入低功耗模式或停止追光仅维持核心功能 // HAL_TIM_PWM_Stop(...); } }4. 系统整合与Proteus仿真实战现在我们将所有模块整合到一个完整的系统中并在Proteus中搭建仿真电路验证我们的控制逻辑。4.1 主程序逻辑设计主程序采用一个简单的超级循环Super Loop结构包含初始化、数据采集、控制计算、输出执行等步骤。// 文件路径Src/main.c (主循环部分) int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM2_Init(); MX_TIM3_Init(); MX_USART1_UART_Init(); // 启动PWM舵机回中 HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 1500); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 1500); HAL_Delay(1000); // 等待系统稳定 while (1) { // 1. 读取传感器数据 Read_LDR_Sensors(); // 2. 读取电池电压可选 Read_Battery_Voltage(); // 3. 根据偏差更新舵机位置 Update_Servo_Position(error_horizontal, error_vertical); // 4. 通过串口发送数据到Proteus虚拟终端用于监控 // 注意在Proteus中需要将STM32的USART_TX引脚连接到COMPIM或VIRTUAL TERMINAL printf(L:%4lu R:%4lu U:%4lu D:%4lu | ErrH:%4d ErrV:%4d\r\n, adc_value_left, adc_value_right, adc_value_up, adc_value_down, error_horizontal, error_vertical); // 5. 控制循环周期例如每100ms执行一次 HAL_Delay(100); } } // 重定向printf到串口需在工程中勾选Use MicroLIB #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF); return ch; }4.2 Proteus仿真电路搭建新建工程打开Proteus新建一个原理图设计。放置元件从库中搜索并放置STM32F103C8。放置4个LDR和4个RES10k电阻搭建分压电路连接到PA0-PA3。放置2个SERVO-MOTOR信号线分别连接到PA0TIM2_CH1和PA6TIM3_CH1。注意Proteus中舵机模型可能需要指定控制引脚。放置DC电压源设为5V、DIODE、CAPACITOR1000uF、RES100Ω模拟负载来模拟充电电路。放置DC VOLTMETER并联在电容两端监测“电池”电压。放置VIRTUAL TERMINAL将其RXD引脚连接到STM32的PA9(USART1_TX)用于接收调试信息。配置其波特率为115200。为STM32放置CRYSTAL8MHz和两个CAP22pF构成晶振电路。放置复位电路RESBUTTONCAP。电源与地为所有元件连接POWER和GROUND。加载程序双击STM32元件在Program File一栏选择由Keil编译生成的.hex文件位于工程目录的Objects或MDK-ARM子文件夹下。4.3 仿真运行与调试点击Proteus左下角的运行按钮。观察虚拟终端会打印出四个光敏电阻的ADC值以及计算出的偏差。你可以通过鼠标拖动Proteus中的“太阳光源”实际上是通过改变原理图中一个全局光照参数或者更简单地直接使用鼠标点击并改变每个LDR上的光照强度数值来模拟太阳移动。例如增大右边LDR的光照值使其电阻变小ADC读数变小观察error_horizontal变为负数同时水平舵机的角度应发生相应变化在Proteus中舵机模型会转动或显示角度值。观察“电池”电容两端的电压表示数理论上它会因为“太阳能板”电压源的充电而缓慢上升。通过仿真你可以直观地验证传感器数据处理、控制算法和舵机响应的正确性而无需等待实物焊接和调试。5. 常见问题与调试技巧在实际开发与仿真中你可能会遇到以下典型问题。5.1 舵机抖动或不转动可能原因1PWM频率或脉宽不对。确保定时器配置为50Hz周期20ms脉宽范围在500-2500之间。使用逻辑分析仪或Proteus中的示波器OSCILLOSCOPE检查PWM输出波形。可能原因2供电不足。舵机启动瞬间电流很大可能导致STM32复位。确保电源能提供足够的电流实物中建议单独供电或使用大容量电容缓冲。可能原因3控制偏差计算过于敏感。检查ADC采样是否稳定是否加入了软件滤波如均值滤波和死区控制。避免因传感器噪声导致PWM值频繁微小变化。5.2 ADC采样值跳动大系统不稳定解决方案硬件滤波在ADC输入引脚对地加一个0.1uF的电容。软件滤波采用多次采样取平均、中值滤波或一阶低通滤波算法。上文代码已使用了16次平均。检查参考电压确保STM32的VREF和VREF-或VDDA和VSSA连接稳定通常VDDA接3.3VVSSA接地。在Proteus中LDR模型的光照参数是理想化的跳动可能不明显。实物中此问题更突出。5.3 Proteus仿真时程序不运行或终端无输出检查1HEX文件路径确认在STM32属性中加载的.hex文件路径正确且是最近一次成功编译生成的。检查2晶振配置Proteus中STM32的时钟源默认可能是内部RC。需要在Edit Component中将Advanced Properties里的Crystal Frequency设置为8MHz并与原理图上的晶振值一致。更可靠的方法是在CubeMX中配置好时钟树并确保生成的代码中SystemClock_Config()函数被正确调用。检查3串口连接确保VIRTUAL TERMINAL的波特率与代码中设置的一致均为115200且RXD连接到了STM32的TX引脚。检查4复位电路Proteus中简单的RC复位电路可能不可靠可以尝试在仿真开始时手动点击一下复位按钮。5.4 锂电池充电异常实物TP4056发热严重检查充电电流设置电阻R_PROG是否正确以及电池是否接反或短路。确保太阳能板输入电压高于电池电压0.1V以上。电池充不满检查太阳能板在当前光照下的实际输出电压和电流是否满足TP4056的输入要求典型4.5V-5.5V。测量BAT引脚电压恒压阶段应达到4.2V。6. 优化与进阶方向一个基础系统完成后可以考虑以下方向进行优化和功能扩展这会让你的项目更具竞争力。6.1 控制算法优化PID控制将简单的比例控制替换为完整的PID比例-积分-微分控制。积分项可以消除静态误差微分项可以抑制超调使追踪更平滑、精确。需要仔细调试Kp,Ki,Kd三个参数。// 简化的增量式PID示例结构 typedef struct { float Kp, Ki, Kd; float error, last_error, integral; } PID_Controller; float PID_Calculate(PID_Controller* pid, float current_error) { float p_out pid-Kp * current_error; pid-integral current_error; float i_out pid-Ki * pid-integral; float d_out pid-Kd * (current_error - pid-last_error); pid-last_error current_error; return p_out i_out d_out; }模糊控制对于光照变化非线性强的场景可以研究模糊控制算法它不依赖于精确的数学模型鲁棒性更强。6.2 增加系统功能光强阈值判断当四个光敏电阻的平均值低于某个阈值时如阴天、夜晚系统停止追踪舵机回归到初始安全位置例如水平朝南仰角为当地纬度以节省电能并减少机械磨损。MPPT最大功率点跟踪这是一个更高级的能量管理课题。通过算法动态调整太阳能板的工作点使其始终输出最大功率。可以使用专用的MPPT芯片或者用STM32的ADC和PWM模拟实现一个简单的扰动观察法。无线通信与监控增加ESP8266或HC-05蓝牙模块将系统状态角度、光强、电池电压发送到手机APP或PC上位机实现远程监控。姿态传感器备份加入MPU6050等姿态传感器在系统启动或长时间阴天后可以根据实时时间和地理位置计算出太阳的理论位置快速将光伏板调整到大致方向再结合光敏传感器进行精调。6.3 工程实践建议电源隔离与保护电机舵机电路与单片机数字电路之间建议使用光电耦合器或独立的电源进行隔离防止电机噪声干扰单片机稳定运行。电源入口处务必加入保险丝和防反接二极管。结构设计考虑舵机的扭矩与太阳能板重量、风阻的匹配。在转轴处使用轴承减少摩擦。户外使用需做好防水防尘处理。代码模块化将ADC驱动、舵机驱动、PID算法、状态机等封装成独立的.c/.h文件提高代码可读性和可移植性。使用RTOS如果功能复杂如同时处理追踪、通信、显示可以考虑引入FreeRTOS等实时操作系统将不同任务拆分到不同线程中提高系统响应能力和可靠性。从光敏电阻的电压读取到偏差计算再到PWM输出驱动舵机最后在Proteus中看到整个系统动态响应光照变化——这个过程完整地串联了嵌入式开发的硬件感知、软件算法和仿真验证三大环节。本文提供的代码和仿真方案可以直接作为你课程设计或项目原型的起点。真正的挑战和乐趣在于调试和优化如何让追踪更平滑快速如何在弱光下保持稳定如何延长电池续航这些问题的探索将让你对嵌入式系统设计有更深的理解。建议在仿真通过后尽快搭建实物进行验证因为真实世界的光照、机械结构和电源噪声会带来新的问题解决它们才是工程能力的体现。
返回列表