【嵌入式DIY实例-Arduino篇】-最大功率点跟踪 (MPPT) 太阳能充电控制器
最大功率点跟踪 (MPPT) 太阳能充电控制器
文章目录
- 最大输出功率追踪系统 (MPPT) 太阳能电源系统
-
- 功能说明
-
- 设备作业
-
- 太阳能电源系统功能解析
-
- 设备电路规划
-
- 控制逻辑编程
1、应用介绍
在这个应用中, 我们将利用Arduino并融合主动与被动类型的电子设备, 制造一个专属于我们的MPPT太阳能充放电控制器. MPPT代表最大功率点跟踪控制器(Maximum Power Point Tracking Controller). 大多数太阳能电池板输出的电压明显高于所需的12伏, 但实际情况具体分析. 一般而言, 12伏电源适配器会输出约16至18伏特电压, 而仅需约14.6伏特即可为大部分12伏电源供电. 大部分多余的能量并未被有效利用. 使用MPPT充放电技术, 我们可以将多余的电压转换为电流, 因此这一技术有助于提升系统的效率
本文旨在探讨太阳能充电与MPPT充ergy技术之间的关系
2、硬件准备
本次应用使用到的硬件配件如下:
| 序号 | 配件名称 | 数量 |
|---|---|---|
| 1 | 20x4 LCD Display | 1 |
| 2 | ACS712 电流传感器 | 1 |
| 3 | IR2104 半桥驱动器 | 1 |
| 4 | 场效应管IRFZ44 | 4 |
| 5 | 三极管 2N2222A | 1 |
| 6 | 二极管 P6KE36CA | 1 |
| 7 | 二极管1N4148 | 2 |
| 8 | 按钮开关 | 2 |
| 9 | 电阻100K | 1 |
| 10 | 电阻20K | 2 |
| 11 | 电阻470K | 1 |
| 12 | 电阻10K | 3 |
| 13 | 电阻1K | 1 |
| 14 | 电阻220Ω | 6 |
| 15 | 电容器220uF | 1 |
| 16 | 电容器10uF | 2 |
| 17 | 电容器 0.1uF | 6 |
| 18 | 电感33uH | 1 |
| 19 | 红色LED | 1 |
| 20 | 绿色 LED | 1 |
| 21 | 蓝色 LED | 1 |
| 22 | 太阳能电池板 (12/24/36V) | 1 |
| 23 | 铅酸蓄电池 12V | 1 |
| 24 | 接线端子 | 3 |
| 25 | 母头 | 2套 |
| 26 | 直流插孔 | 1 |
| 27 | 二极管1N4007 | 1 |
| 28 | Arduino Nano | 1 |
元器件如下图所示:

3、太阳能充电控制器介绍
太阳光能充电控制器属于一类电子装置,并且负责控制从太阳能电池板输送电流至相应的储能单元。

所有安装有电池的太阳能组件都必须配备充能控制器。该装置的主要职责是调节并控制从太阳发电系统传输到存储设备中的电力,并通过提高能量储存效率来延长设备寿命。
充电控制器分为三种类型:
注

开关控制器 是非常简单的设备。
脉冲宽度调制 controller 的充电速度略高于开关型电力电子 controller. 在充入过程中随著电源储存量的增长而逐步下降. 在达到满电量的时候, 该 controller 会切换至浮充模式, 主要维持微电流输入至电源以避免其反向放电. PWM 控制器通过采用简单的开关控制策略来提升电源续航时间.
**最大功率点跟踪控制器 (MPPT)**是一种电子设备,在太阳辐射下工作的太阳能电池板能够高效地储存电能,并被用来调节这些太阳能电池板的充电过程以确保最佳充电效率。该装置的最大功能在于最大化储存于太阳能电池板中的能量并与其匹配以保证最高的能量转换效率。

与之前提到的两种旧类型相比,Maximum PowerPoint 跟踪控制器在先进程度和效率上都更为卓越。这些 controller 具备高度智能化,并能够有效地将 excess voltage 转换为通常会被 PWM controller 未利用的额外 current。
大多数太阳能电池板产生的电压高出12V电池充电所需的电压许多倍,在具备这种配置的情况下,则会达到24或48伏特。12V充电板实际上能产生16至18伏特的电压(具体情况而定),但其输出通常维持在约14.6伏特以满足大多数12V电池组的需求。因此,在这种情况下MPPT控制器能够将多余的电压转换成更多电流从而加快对电池组的充能速度整体效率也有明显提升
MPPT 太阳能充电控制器的优缺点:
MPPT 控制器能够将多余的电压转化为额外的电流,并因此加快充电速度并提升效率;此外,在处理太阳能电池板上的高电压配置方面具有显著优势;即通过串联更多太阳能电池板来提高输入电压从而使得运行更小规格的电线或者在电源转换的过程中传输更远的距离而不会造成能量损失;Pulse-Width Modulation (PWM) controller.
建议您在并网时考虑添加一些电池作为备用电源。MPPT 是目前最有效的解决方案。其效率通常在 94% 到 99%之间波动,并且相较于同类 PWM 控制器,在能量转换效率上表现更为突出。
MPPT控制器在寒冷或多云等离网式太阳能电池板系统中的应用极为关键,在这些地区中尤为重要。这种控制器能够最大限度地提取每一份可用的太阳能能量。然而,在弱光环境下该装置存在主要缺点之一:它表现尚可(即效果不错),但由于其难以准确捕捉到最大功率的最佳点位置而显得不够理想。幸运的是,在这种情况下这些条件的时间跨度较短,并且它的性能足以弥补剩余时间的需求
4、硬件设计
目前我们采用Arduino技术进行MPPT太阳能充电控制器项目的开发与制造。该项目的目标是通过开发相应的电路和软件来确保系统的高效性和稳定性。
基于MPPT算法原理进行能量转换
太阳能电池板会受到多种因素的影响而产生不同的电压值;具体来说,这些因素包括日照量、连接负载以及温度等物理参数.该项目包含多个环节,其中涉及大量的复杂计算分析.
硬件接线图如下:

受一天中日照量的影响,在太阳能电池板上其输出电压随之发生变化。因为输出电压的变化会导致输出电流的变化,在给定特定电压水平时(通常绘制在IV曲线上),能够测量到对应的电流值(即安培)。这些数据关系通常通过绘制在IV曲线上进行表示

在本图表中,蓝曲线显示当电压达到30伏特时,输出电流约为6.2安培;绿曲线显示当电压达到35伏特时,输出电流为5安培。
我们知道,功率 =电压*电流
在上图所示的电路中,在一个点上电压乘以对应的电流会产生最大功率。
我们项目中采用的太阳能电池板具体参数设置如上图所示。

电路的工作原理及注意事项
对于50瓦太阳能电池板和12伏铅酸电池负载而言,在设计降压转换器方面我们有需求。案例中的降压转换器采用了电感、电容以及MOSFET进行设计。在该案例中采取了较高的开关频率以期达到较好的性能表现,并在MOSFET中实现了较低的开关损耗以提高效率

基于这些限制,在选择了50KHz的频率后
注:改写说明:
- 将"考虑到"改为"基于"
将"选择"改为"采用了"
将"达到这个频率"改为"以实现这一频率"
将"使用了..."改为采用...
将"因为很容易获得..."改为因其易于获取...
将"...值有足够的余量..."改为值具备足够的裕度...
将"...我们需要一个..."改为为了驱动...
将"...最适合这种应用..."改为IR2104半桥驱动芯片最为适合该应用

经太阳能电池板电压输入的降压转换器主要由同步MOSFET开关Q4、Q5以及储能元件包括电感器L1、电容器C4和C9组成。此电感器可滤波开关电流并与C4协同作用以平滑输出电压其中的C3与R4构成滤波电路以抑制开关端点处由感应电流引发的电磁振荡
引入 MOSFET Q3 实现对电路中电池电量的 night-time sequestration, which effectively prevents the energy from being discharged back to the solar panel during off-peak hours. When a voltage is applied through terminal D2, this enables the operation of transistor Q4. The resistor R1's connection to the gate of transistor Q1 ensures stable operation, thus when its output is inhibited by R1, it results in the closure of Q4 gate circuitry.

该类芯片采用半桥MOS管栅极驱动架构,并通过Arduino Pin D6上的PWM脉冲控制高边和低边MOS管的导通状态。其具备对PWM占空比进行实时监控的功能,并可防止占空比达到100%或长期处于开启状态以保护电荷泵电路。同时支持通过Arduino D5引脚设置引脚复位信号。其中,D4和C6是上拉网络的一部分,并为其栅极施加高电平驱动电压。
有两个独立的分压电路组(编号分别为R1、R2以及R7、R8)分别用于检测太阳能电池板的工作状态及电源系统的运行情况。其输出端口将所得电压信号通过数据线传输至Arduino模拟端口A0与A2

二极管 D3 可以为转换器提供更高的效率保障。D1 和 D5 这两个 TVS 二极管分别负责对太阳能电池板和负载电路进行过压保护。MOSFET Q2 则承担着调节负载状态的关键功能。其中晶体管 Q1、电阻 R5 以及 R6 组成了完整的电路单元。
该系统通过电流传感器ACS712检测太阳能电池板产生的电流,并将其传输至Arduino模拟引脚A1用于电路控制。由3个LED由微控制器的数字引脚控制并用于指示充电进度。该系统通过驱动背光显示屏实时监控电量状态。当用户按下开关时系统将在15秒后自动关闭以保护设备。
硬件组装

在项目团队的研究中,我们采用了24伏特的太阳能电池组件作为核心组件.这种高效能组件拥有较大的表面积,并能最大限度地吸收太阳辐射.该组件通过其输出端子与电路系统中的电源模块相连接.为了给Arduino Nano开发板及其他电路组件提供稳定的电源供应,可以选择5伏至9伏直流稳压电源适配器来实现稳定供电.

5、驱动代码实现
驱动代码将使用到如下驱动库:
代码包含测量太阳能电池板电压、电流、功率等参数以及电池电压、充电器状态等信息,并且能够记录所有这些数据。20×4 LCD 显示屏将这些参数的实时状态实时显示出来。
#include "TimerOne.h"
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#define ENABLE_DATALOGGER 0
#define LOAD_ALGORITHM 0
#define SOL_AMPS_CHAN 1
#define SOL_VOLTS_CHAN 0
#define BAT_VOLTS_CHAN 2
#define AVG_NUM 8
#define SOL_AMPS_SCALE 0.026393581
#define SOL_VOLTS_SCALE 0.029296875
#define BAT_VOLTS_SCALE 0.029296875
#define PWM_PIN 6
#define PWM_ENABLE_PIN 5
#define PWM_FULL 1023
#define PWM_MAX 100
#define PWM_MIN 60
#define PWM_START 90
#define PWM_INC 1
#define TRUE 1
#define FALSE 0
#define ON TRUE
#define OFF FALSE
#define TURN_ON_MOSFETS digitalWrite(PWM_ENABLE_PIN, HIGH)
#define TURN_OFF_MOSFETS digitalWrite(PWM_ENABLE_PIN, LOW)
#define ONE_SECOND 50000
#define LOW_SOL_WATTS 5.00
#define MIN_SOL_WATTS 1.00
#define MIN_BAT_VOLTS 11.00
#define MAX_BAT_VOLTS 14.10
#define BATT_FLOAT 13.60
#define HIGH_BAT_VOLTS 13.00
#define LVD 11.5
#define OFF_NUM 9
#define LED_YELLOW 12
#define LED_GREEN 11
#define LED_RED 10
#define LOAD_PIN 4
#define BACK_LIGHT_PIN 3
byte battery_icons[6][8] =
{{
0b01110,
0b11011,
0b10001,
0b10001,
0b10001,
0b10001,
0b10001,
0b11111,
},
{
0b01110,
0b11011,
0b10001,
0b10001,
0b10001,
0b10001,
0b11111,
0b11111,
},
{
0b01110,
0b11011,
0b10001,
0b10001,
0b10001,
0b11111,
0b11111,
0b11111,
},
{
0b01110,
0b11011,
0b10001,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
},
{
0b01110,
0b11011,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
},
{
0b01110,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
}
};
#define SOLAR_ICON 6
byte solar_icon[8] =
{
0b11111,
0b10101,
0b11111,
0b10101,
0b11111,
0b10101,
0b11111,
0b00000
};
#define PWM_ICON 7
byte _PWM_icon[8] =
{
0b11101,
0b10101,
0b10101,
0b10101,
0b10101,
0b10101,
0b10101,
0b10111,
};
byte backslash_char[8] =
{
0b10000,
0b10000,
0b01000,
0b01000,
0b00100,
0b00100,
0b00010,
0b00010,
};
float sol_amps;
float sol_volts;
float bat_volts;
float sol_watts;
float old_sol_watts = 0;
unsigned int seconds = 0;
unsigned int prev_seconds = 0;
unsigned int interrupt_counter = 0;
unsigned long time = 0;
int delta = PWM_INC;
int pwm = 0;
int back_light_pin_State = 0;
boolean load_status = false;
enum charger_mode {off, on, bulk, bat_float} charger_state;
LiquidCrystal_I2C lcd(0x27, 20, 4);
void setup()
{
pinMode(PWM_ENABLE_PIN, OUTPUT);
TURN_OFF_MOSFETS;
charger_state = off;
lcd.init();
lcd.backlight();
for (int batchar = 0; batchar < 6; ++batchar)
{
lcd.createChar(batchar, battery_icons[batchar]);
}
lcd.createChar(PWM_ICON, _PWM_icon);
lcd.createChar(SOLAR_ICON, solar_icon);
lcd.createChar('\ ', backslash_char);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
Timer1.initialize(20);
Timer1.pwm(PWM_PIN, 0);
Timer1.attachInterrupt(callback);
Serial.begin(9600);
pwm = PWM_START;
pinMode(BACK_LIGHT_PIN, INPUT);
pinMode(LOAD_PIN, OUTPUT);
digitalWrite(LOAD_PIN, LOW);
digitalWrite(BACK_LIGHT_PIN, LOW);
lcd.setCursor(0, 0);
lcd.print("SOL");
lcd.setCursor(4, 0);
lcd.write(SOLAR_ICON);
lcd.setCursor(8, 0);
lcd.print("BAT");
}
void loop()
{
read_data();
run_charger();
print_data();
load_control();
led_output();
lcd_display();
}
int read_adc(int channel)
{
int sum = 0;
int temp;
int i;
for (i = 0; i < AVG_NUM; i++)
{
temp = analogRead(channel);
sum += temp;
delayMicroseconds(50);
}
return (sum / AVG_NUM);
}
void read_data(void)
{
sol_amps = (read_adc(SOL_AMPS_CHAN) * SOL_AMPS_SCALE - 13.51);
sol_volts = read_adc(SOL_VOLTS_CHAN) * SOL_VOLTS_SCALE;
bat_volts = read_adc(BAT_VOLTS_CHAN) * BAT_VOLTS_SCALE;
sol_watts = sol_amps * sol_volts ;
}
void callback()
{
if (interrupt_counter++ > ONE_SECOND)
{
interrupt_counter = 0;
seconds++;
}
}
void set_pwm_duty(void)
{
if (pwm > PWM_MAX) {
pwm = PWM_MAX;
}
else if (pwm < PWM_MIN) {
pwm = PWM_MIN;
}
if (pwm < PWM_MAX) {
Timer1.pwm(PWM_PIN, (PWM_FULL * (long)pwm / 100), 20);
}
else if (pwm == PWM_MAX) {
Timer1.pwm(PWM_PIN, (PWM_FULL - 1), 20);
}
}
void run_charger(void)
{
static int off_count = OFF_NUM;
switch (charger_state)
{
case on:
if (sol_watts < MIN_SOL_WATTS)
{
charger_state = off;
off_count = OFF_NUM;
TURN_OFF_MOSFETS;
}
else if (bat_volts > (BATT_FLOAT - 0.1))
{
charger_state = bat_float;
}
else if (sol_watts < LOW_SOL_WATTS) {
pwm = PWM_MAX;
set_pwm_duty();
}
else {
pwm = ((bat_volts * 10) / (sol_volts / 10)) + 5;
charger_state = bulk;
}
break;
case bulk:
if (sol_watts < MIN_SOL_WATTS)
{
charger_state = off;
off_count = OFF_NUM;
TURN_OFF_MOSFETS;
}
else if (bat_volts > BATT_FLOAT)
{
charger_state = bat_float;
}
else if (sol_watts < LOW_SOL_WATTS)
{
charger_state = on;
TURN_ON_MOSFETS;
}
else {
if (old_sol_watts >= sol_watts)
{
delta = -delta;
}
pwm += delta;
old_sol_watts = sol_watts;
set_pwm_duty();
}
break;
case bat_float:
if (sol_watts < MIN_SOL_WATTS)
{
charger_state = off;
off_count = OFF_NUM;
TURN_OFF_MOSFETS;
set_pwm_duty();
}
else if (bat_volts > BATT_FLOAT)
{
TURN_OFF_MOSFETS;
pwm = PWM_MAX;
set_pwm_duty();
}
else if (bat_volts < BATT_FLOAT)
{
pwm = PWM_MAX;
set_pwm_duty();
TURN_ON_MOSFETS;
if (bat_volts < (BATT_FLOAT - 0.1))
{
charger_state = bulk;
}
}
break;
case off:
TURN_OFF_MOSFETS;
if (off_count > 0)
{
off_count--;
}
else if ((bat_volts > BATT_FLOAT) && (sol_volts > bat_volts)) {
charger_state = bat_float;
TURN_ON_MOSFETS;
}
else if ((bat_volts > MIN_BAT_VOLTS) && (bat_volts < BATT_FLOAT) && (sol_volts > bat_volts)) {
charger_state = bulk;
TURN_ON_MOSFETS;
}
break;
default:
TURN_OFF_MOSFETS;
break;
}
}
void load_control()
{
#if LOAD_ALGORITHM == 0
load_on(sol_watts < MIN_SOL_WATTS && bat_volts > LVD);
#else
load_on(sol_watts > MIN_SOL_WATTS && bat_volts > BATT_FLOAT);
#endif
}
void load_on(boolean new_status)
{
if (load_status != new_status)
{
load_status = new_status;
digitalWrite(LOAD_PIN, new_status ? HIGH : LOW);
}
}
void print_data(void)
{
Serial.print(seconds, DEC);
Serial.print(" ");
Serial.print("Charging = ");
if (charger_state == on) Serial.print("on ");
else if (charger_state == off) Serial.print("off ");
else if (charger_state == bulk) Serial.print("bulk ");
else if (charger_state == bat_float) Serial.print("float");
Serial.print(" ");
Serial.print("pwm = ");
if (charger_state == off)
Serial.print(0, DEC);
else
Serial.print(pwm, DEC);
Serial.print(" ");
Serial.print("Current (panel) = ");
Serial.print(sol_amps);
Serial.print(" ");
Serial.print("Voltage (panel) = ");
Serial.print(sol_volts);
Serial.print(" ");
Serial.print("Power (panel) = ");
Serial.print(sol_volts);
Serial.print(" ");
Serial.print("Battery Voltage = ");
Serial.print(bat_volts);
Serial.print(" ");
Serial.print("\n\r");
}
void light_led(char pin)
{
static char last_lit;
if (last_lit == pin)
return;
if (last_lit != 0)
digitalWrite(last_lit, LOW);
digitalWrite(pin, HIGH);
last_lit = pin;
}
void led_output(void)
{
static char last_lit;
if (bat_volts > 14.1 )
light_led(LED_YELLOW);
else if (bat_volts > 11.9)
light_led(LED_GREEN);
else
light_led(LED_RED);
}
void lcd_display()
{
static bool current_backlight_state = -1;
back_light_pin_State = digitalRead(BACK_LIGHT_PIN);
if (current_backlight_state != back_light_pin_State) {
current_backlight_state = back_light_pin_State;
if (back_light_pin_State == HIGH)
lcd.backlight();
else
lcd.noBacklight();
}
if (back_light_pin_State == HIGH)
{
time = millis();
}
lcd.setCursor(0, 1);
lcd.print(sol_volts);
lcd.print("V ");
lcd.setCursor(0, 2);
lcd.print(sol_amps);
lcd.print("A");
lcd.setCursor(0, 3);
lcd.print(sol_watts);
lcd.print("W ");
lcd.setCursor(8, 1);
lcd.print(bat_volts);
lcd.setCursor(8, 2);
if (charger_state == on)
lcd.print("on ");
else if (charger_state == off)
lcd.print("off ");
else if (charger_state == bulk)
lcd.print("bulk ");
else if (charger_state == bat_float)
{
lcd.print(" ");
lcd.setCursor(8, 2);
lcd.print("float");
}
int pct = 100.0 * (bat_volts - 11.3) / (12.7 - 11.3);
if (pct < 0)
pct = 0;
else if (pct > 100)
pct = 100;
lcd.setCursor(12, 0);
lcd.print((char)(pct * 5 / 100));
lcd.setCursor(8, 3);
pct = pct - (pct % 10);
lcd.print(pct);
lcd.print("% ");
lcd.setCursor(15, 0);
lcd.print("PWM");
lcd.setCursor(19, 0);
lcd.write(PWM_ICON);
lcd.setCursor(15, 1);
lcd.print(" ");
lcd.setCursor(15, 1);
if ( charger_state == off)
lcd.print(0);
else
lcd.print(pwm);
lcd.print("% ");
lcd.setCursor(15, 2);
lcd.print("Load");
lcd.setCursor(15, 3);
if (load_status)
{
lcd.print("On ");
}
else
{
lcd.print("Off ");
}
spinner();
backLight_timer();
}
void backLight_timer()
{
if ((millis() - time) <= 15000)
lcd.backlight();
else
lcd.noBacklight();
}
void spinner(void)
{
static int cspinner;
static char spinner_chars[] = { '*', '*', '*', ' ', ' '};
cspinner++;
lcd.print(spinner_chars[cspinner % sizeof(spinner_chars)]);
}
