一、引言
电池管理系统(Battery Management System, BMS)是电动汽车、便携式设备、储能系统等应用中的核心组件。准确的电量统计(State of Charge, SOC)是BMS的关键功能之一,它直接影响用户体验和电池寿命。本文将详细介绍基于STM32微控制器的电池电量统计原理,并提供完整的实现代码。
二、电池电量统计原理
2.1 SOC的定义
SOC(State of Charge)表示电池当前剩余电量占总容量的百分比,范围为0-100%。准确估算SOC是BMS设计的难点之一。
2.2 常用的SOC估算方法
2.2.1 开路电压法(OCV法)
原理:利用电池静置时的开路电压与SOC的对应关系来估算电量。
优点:
- 实现简单
- 不需要复杂计算
缺点:
- 需要电池长时间静置
- 精度受温度影响大
- 不适合动态应用场景
2.2.2 库仑计法(安时积分法)
原理:通过对充放电电流进行时间积分来计算电量变化。
公式:
SOC(t) = SOC(t0) - (1/Qn) * ∫I(t)dt
其中:
- SOC(t):当前电量百分比
- Qn:电池额定容量
- I(t):充放电电流(放电为正,充电为负)
优点:
- 实时性好
- 适合动态场景
缺点:
- 存在累积误差
- 初始SOC需要准确校准
- 需要精确的电流传感器
2.2.3 综合算法
实际应用中,通常结合多种方法,如:
- 库仑计法 + 开路电压校准
- 卡尔曼滤波算法
- 神经网络估算
本文重点介绍基于库仑计法的实现。
三、硬件设计方案
3.1 系统架构
[电池包] → [电流传感器] → [ADC采集] → [STM32处理] → [显示/通信]
↓
[电压采集]
↓
[温度传感器]
3.2 关键硬件选型
- 微控制器:STM32F103C8T6(或更高性能型号)
- 电流传感器 :
- 霍尔电流传感器(如ACS712)
- 分流电阻 + 差分放大器
- 专用电量计芯片(如INA226)
- 电压采集:电阻分压 + ADC
- 温度传感器:NTC热敏电阻或DS18B20
3.3 ADC配置要点
- 采样频率:1-10Hz(根据应用场景)
- 分辨率:12位ADC
- 参考电压:3.3V或使用外部精密基准
- 多通道采集:电压、电流、温度
四、软件实现
4.1 数据结构定义
c
// bms.h
#ifndef __BMS_H
#define __BMS_H
#include "stm32f10x.h"
// 电池参数配置
#define BATTERY_CAPACITY 3000 // 电池容量(mAh)
#define BATTERY_NOMINAL_VOLTAGE 3.7 // 标称电压(V)
#define BATTERY_MAX_VOLTAGE 4.2 // 最大电压(V)
#define BATTERY_MIN_VOLTAGE 3.0 // 最小电压(V)
#define CELLS_IN_SERIES 1 // 串联节数
// 采样参数
#define SAMPLE_PERIOD_MS 1000 // 采样周期(ms)
#define SAMPLE_PERIOD_SEC (SAMPLE_PERIOD_MS/1000.0f)
// ADC参数
#define ADC_RESOLUTION 4096.0f // 12位ADC
#define ADC_VREF 3.3f // 参考电压
// 电压分压比(根据实际电路调整)
#define VOLTAGE_DIVIDER_RATIO 2.0f
// 电流传感器参数(以ACS712-5A为例)
#define CURRENT_SENSOR_SENSITIVITY 0.185f // V/A
#define CURRENT_SENSOR_OFFSET 2.5f // 零电流输出电压
// BMS状态枚举
typedef enum {
BMS_STATE_IDLE = 0,
BMS_STATE_CHARGING,
BMS_STATE_DISCHARGING,
BMS_STATE_FULL,
BMS_STATE_EMPTY,
BMS_STATE_ERROR
} BMS_State_t;
// BMS数据结构
typedef struct {
float voltage; // 当前电压(V)
float current; // 当前电流(A)正为放电,负为充电
float temperature; // 当前温度(℃)
float soc; // 剩余电量(%)
float capacity_remaining; // 剩余容量(mAh)
uint32_t charge_cycles; // 充电循环次数
BMS_State_t state; // 当前状态
uint8_t error_code; // 错误代码
} BMS_Data_t;
// 函数声明
void BMS_Init(void);
void BMS_Update(void);
void BMS_SetInitialSOC(float soc);
float BMS_GetSOC(void);
float BMS_GetVoltage(void);
float BMS_GetCurrent(void);
BMS_State_t BMS_GetState(void);
void BMS_Calibrate(void);
#endif
4.2 核心算法实现
c
// bms.c
#include "bms.h"
#include <math.h>
// 全局变量
static BMS_Data_t bms_data;
static uint32_t last_update_time = 0;
// OCV-SOC查找表(锂电池典型曲线)
typedef struct {
float voltage;
float soc;
} OCV_Point_t;
static const OCV_Point_t ocv_table[] = {
{4.20f, 100.0f},
{4.15f, 95.0f},
{4.11f, 90.0f},
{4.08f, 85.0f},
{4.02f, 80.0f},
{3.98f, 75.0f},
{3.95f, 70.0f},
{3.91f, 65.0f},
{3.87f, 60.0f},
{3.85f, 55.0f},
{3.84f, 50.0f},
{3.82f, 45.0f},
{3.80f, 40.0f},
{3.79f, 35.0f},
{3.77f, 30.0f},
{3.74f, 25.0f},
{3.68f, 20.0f},
{3.62f, 15.0f},
{3.55f, 10.0f},
{3.46f, 5.0f},
{3.00f, 0.0f}
};
#define OCV_TABLE_SIZE (sizeof(ocv_table)/sizeof(OCV_Point_t))
/**
* @brief 线性插值函数
*/
static float LinearInterpolate(float x, float x0, float y0, float x1, float y1) {
if (x1 == x0) return y0;
return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
}
/**
* @brief 根据电压估算SOC(OCV法)
*/
static float VoltageToSOC(float voltage) {
// 边界处理
if (voltage >= ocv_table[0].voltage) return 100.0f;
if (voltage <= ocv_table[OCV_TABLE_SIZE-1].voltage) return 0.0f;
// 查找表插值
for (uint8_t i = 0; i < OCV_TABLE_SIZE - 1; i++) {
if (voltage >= ocv_table[i+1].voltage && voltage <= ocv_table[i].voltage) {
return LinearInterpolate(voltage,
ocv_table[i+1].voltage, ocv_table[i+1].soc,
ocv_table[i].voltage, ocv_table[i].soc);
}
}
return 50.0f; // 默认值
}
/**
* @brief 读取ADC值并转换为物理量
*/
static void BMS_ReadSensors(void) {
uint16_t adc_voltage, adc_current, adc_temp;
// 读取ADC值(这里需要根据实际硬件实现)
adc_voltage = ADC_GetValue(ADC_CHANNEL_VOLTAGE);
adc_current = ADC_GetValue(ADC_CHANNEL_CURRENT);
adc_temp = ADC_GetValue(ADC_CHANNEL_TEMP);
// 转换电压
float adc_volt = (adc_voltage / ADC_RESOLUTION) * ADC_VREF;
bms_data.voltage = adc_volt * VOLTAGE_DIVIDER_RATIO;
// 转换电流(ACS712传感器)
adc_volt = (adc_current / ADC_RESOLUTION) * ADC_VREF;
bms_data.current = (adc_volt - CURRENT_SENSOR_OFFSET) / CURRENT_SENSOR_SENSITIVITY;
// 转换温度(NTC热敏电阻,需要根据实际参数计算)
// 这里简化处理
bms_data.temperature = 25.0f; // 实际应用需要实现温度转换算法
}
/**
* @brief 库仑计算法更新SOC
*/
static void BMS_UpdateSOC_Coulomb(float delta_time_sec) {
// 计算电量变化(mAh)
// 电流为正表示放电,为负表示充电
float delta_capacity = (bms_data.current * 1000.0f) * (delta_time_sec / 3600.0f);
// 更新剩余容量
bms_data.capacity_remaining -= delta_capacity;
// 边界限制
if (bms_data.capacity_remaining > BATTERY_CAPACITY) {
bms_data.capacity_remaining = BATTERY_CAPACITY;
}
if (bms_data.capacity_remaining < 0) {
bms_data.capacity_remaining = 0;
}
// 计算SOC百分比
bms_data.soc = (bms_data.capacity_remaining / BATTERY_CAPACITY) * 100.0f;
}
/**
* @brief SOC校准(使用OCV法)
*/
static void BMS_CalibrateSOC(void) {
// 当电流很小且电压稳定时,使用OCV法校准
if (fabs(bms_data.current) < 0.05f) { // 电流小于50mA
float ocv_soc = VoltageToSOC(bms_data.voltage);
// 融合校准(可选:使用卡尔曼滤波等更复杂算法)
// 这里采用简单的加权平均
bms_data.soc = bms_data.soc * 0.9f + ocv_soc * 0.1f;
bms_data.capacity_remaining = (bms_data.soc / 100.0f) * BATTERY_CAPACITY;
}
}
/**
* @brief 更新BMS状态
*/
static void BMS_UpdateState(void) {
// 根据电流方向判断充放电状态
if (bms_data.current > 0.1f) {
bms_data.state = BMS_STATE_DISCHARGING;
} else if (bms_data.current < -0.1f) {
bms_data.state = BMS_STATE_CHARGING;
} else {
bms_data.state = BMS_STATE_IDLE;
}
// 判断满电和空电
if (bms_data.soc >= 99.0f && bms_data.voltage >= BATTERY_MAX_VOLTAGE - 0.05f) {
bms_data.state = BMS_STATE_FULL;
} else if (bms_data.soc <= 1.0f || bms_data.voltage <= BATTERY_MIN_VOLTAGE) {
bms_data.state = BMS_STATE_EMPTY;
}
// 错误检测
if (bms_data.voltage > BATTERY_MAX_VOLTAGE + 0.2f ||
bms_data.voltage < BATTERY_MIN_VOLTAGE - 0.2f ||
bms_data.temperature > 60.0f ||
bms_data.temperature < -20.0f) {
bms_data.state = BMS_STATE_ERROR;
bms_data.error_code = 1; // 定义具体错误代码
}
}
/**
* @brief BMS初始化
*/
void BMS_Init(void) {
// 初始化硬件(ADC、定时器等)
// ... 硬件初始化代码 ...
// 初始化数据结构
bms_data.voltage = 0.0f;
bms_data.current = 0.0f;
bms_data.temperature = 25.0f;
bms_data.soc = 50.0f; // 初始SOC,实际应用需要从EEPROM读取
bms_data.capacity_remaining = BATTERY_CAPACITY * 0.5f;
bms_data.charge_cycles = 0;
bms_data.state = BMS_STATE_IDLE;
bms_data.error_code = 0;
last_update_time = HAL_GetTick(); // 或使用其他时间函数
}
/**
* @brief BMS主循环更新函数(定时调用)
*/
void BMS_Update(void) {
uint32_t current_time = HAL_GetTick();
float delta_time = (current_time - last_update_time) / 1000.0f; // 转换为秒
if (delta_time < SAMPLE_PERIOD_SEC) {
return; // 未到采样周期
}
// 读取传感器数据
BMS_ReadSensors();
// 库仑计法更新SOC
BMS_UpdateSOC_Coulomb(delta_time);
// SOC校准
BMS_CalibrateSOC();
// 更新状态
BMS_UpdateState();
last_update_time = current_time;
}
/**
* @brief 设置初始SOC
*/
void BMS_SetInitialSOC(float soc) {
if (soc < 0.0f) soc = 0.0f;
if (soc > 100.0f) soc = 100.0f;
bms_data.soc = soc;
bms_data.capacity_remaining = (soc / 100.0f) * BATTERY_CAPACITY;
}
/**
* @brief 获取当前SOC
*/
float BMS_GetSOC(void) {
return bms_data.soc;
}
/**
* @brief 获取当前电压
*/
float BMS_GetVoltage(void) {
return bms_data.voltage;
}
/**
* @brief 获取当前电流
*/
float BMS_GetCurrent(void) {
return bms_data.current;
}
/**
* @brief 获取当前状态
*/
BMS_State_t BMS_GetState(void) {
return bms_data.state;
}
/**
* @brief BMS校准函数
*/
void BMS_Calibrate(void) {
// 执行校准流程
// 1. 确保电池静置
// 2. 读取OCV
// 3. 更新SOC
BMS_ReadSensors();
float ocv_soc = VoltageToSOC(bms_data.voltage);
BMS_SetInitialSOC(ocv_soc);
}
4.3 ADC配置代码
c
// adc.c - STM32 HAL库版本
#include "stm32f1xx_hal.h"
ADC_HandleTypeDef hadc1;
void ADC_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
// ADC1配置
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 3;
HAL_ADC_Init(&hadc1);
// 配置电压通道
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 配置电流通道
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_2;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 配置温度通道
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = ADC_REGULAR_RANK_3;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
uint16_t ADC_GetValue(uint8_t channel) {
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = channel;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
uint16_t value = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
return value;
}
4.4 主程序示例
c
// main.c
#include "stm32f1xx_hal.h"
#include "bms.h"
#include <stdio.h>
// UART用于调试输出
extern UART_HandleTypeDef huart1;
void SystemClock_Config(void);
void Error_Handler(void);
int main(void) {
HAL_Init();
SystemClock_Config();
// 初始化外设
ADC_Init();
// UART_Init();
// TIM_Init();
// 初始化BMS
BMS_Init();
// 从EEPROM读取上次保存的SOC(可选)
// float saved_soc = EEPROM_ReadSOC();
// BMS_SetInitialSOC(saved_soc);
printf("BMS System Initialized\r\n");
while (1) {
// 更新BMS数据
BMS_Update();
// 获取并显示数据
float soc = BMS_GetSOC();
float voltage = BMS_GetVoltage();
float current = BMS_GetCurrent();
BMS_State_t state = BMS_GetState();
// 串口输出(每秒一次)
static uint32_t last_print = 0;
if (HAL_GetTick() - last_print > 1000) {
printf("SOC: %.1f%% | Voltage: %.2fV | Current: %.2fA | State: %d\r\n",
soc, voltage, current, state);
last_print = HAL_GetTick();
}
// 保存SOC到EEPROM(可选,定期或在关机前)
// if (需要保存) {
// EEPROM_SaveSOC(soc);
// }
HAL_Delay(10);
}
}
五、算法优化与精度提升
5.1 误差来源分析
- 电流测量误差:传感器精度、ADC精度、温度漂移
- 容量衰减:电池老化导致实际容量下降
- 温度影响:低温时可用容量降低
- 库仑效率:充放电效率不为100%
- 自放电:长期静置的电量损失
5.2 改进措施
5.2.1 温度补偿
c
// 温度补偿系数(简化模型)
static float GetTemperatureCompensation(float temperature) {
if (temperature >= 20.0f) {
return 1.0f;
} else if (temperature >= 0.0f) {
return 0.8f + (temperature / 20.0f) * 0.2f;
} else {
return 0.6f + (temperature / 20.0f) * 0.2f;
}
}
5.2.2 库仑效率校正
c
#define CHARGE_EFFICIENCY 0.95f // 充电效率
#define DISCHARGE_EFFICIENCY 1.0f // 放电效率
// 在计算delta_capacity时应用效率
if (bms_data.current < 0) { // 充电
delta_capacity = delta_capacity * CHARGE_EFFICIENCY;
}
5.2.3 容量衰减估算
c
// 基于充电循环次数估算容量衰减
static float GetCapacityFading(uint32_t cycles) {
// 简化模型:每500次循环衰减5%
float fading = 1.0f - (cycles / 500) * 0.05f;
if (fading < 0.7f) fading = 0.7f; // 最多衰减30%
return fading;
}
5.3 数据持久化
使用STM32内部Flash或外部EEPROM存储:
- 当前SOC
- 充电循环次数
- 校准参数
- 历史容量数据
c
// 使用内部Flash保存数据(需实现Flash读写函数)
void BMS_SaveData(void) {
Flash_Erase(DATA_ADDRESS);
Flash_Write(DATA_ADDRESS, &bms_data.soc, sizeof(float));
Flash_Write(DATA_ADDRESS + 4, &bms_data.charge_cycles, sizeof(uint32_t));
}
void BMS_LoadData(void) {
Flash_Read(DATA_ADDRESS, &bms_data.soc, sizeof(float));
Flash_Read(DATA_ADDRESS + 4, &bms_data.charge_cycles, sizeof(uint32_t));
}
六、测试与调试
6.1 测试方法
- 静态测试:使用标准电压源验证电压测量精度
- 动态测试:实际充放电测试,对比容量计算值与实际值
- 长期测试:连续运行数天,观察SOC漂移情况
6.2 调试技巧
- 使用串口输出实时数据,绘制电压、电流、SOC曲线
- 记录完整充放电周期的数据,分析误差规律
- 使用示波器观察ADC采样波形,排除干扰
- 对比OCV法和库仑计法的结果差异
七、实际应用注意事项
7.1 安全保护
- 过充保护:电压超过4.2V时停止充电
- 过放保护:电压低于3.0V时切断负载
- 过流保护:电流超过额定值时报警
- 温度保护:温度异常时停止充放电
7.2 电池配置
不同类型电池(锂离子、磷酸铁锂、三元锂等)的特性不同,需要:
- 修改OCV-SOC查找表
- 调整电压范围参数
- 调整温度补偿系数
7.3 多节串并联
对于多节电池,需要:
- 单体电压监测
- 均衡管理
- SOC取平均值或最低值
八、总结
本文详细介绍了基于STM32的电池管理系统中电量统计的实现方法,重点讲解了库仑计法的原理和代码实现。在实际应用中,需要根据具体的电池类型、应用场景和精度要求,选择合适的算法和优化策略。
关键要点:
- 库仑计法是最常用的动态SOC估算方法
- 需要结合OCV法进行定期校准
- 温度补偿和效率校正可提高精度
- 数据持久化确保系统断电后的连续性
- 安全保护是BMS的首要任务
通过本文的原理讲解和代码实现,读者可以快速搭建一个基本的BMS系统,并在此基础上进行功能扩展和性能优化。
参考资料
- STM32参考手册
- 锂电池数据手册
- 《电池管理系统设计与应用》
- IEC 62619 电池安全标准