STM32电池管理系统(BMS):电量统计原理与实现
一、引言
电池管理系统(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 数据结构定义
// 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 核心算法实现
// 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) { // 电流小于50mAfloat 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();// 库仑计法更新SOCBMS_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. 更新SOCBMS_ReadSensors();float ocv_soc = VoltageToSOC(bms_data.voltage);BMS_SetInitialSOC(ocv_soc);
}
4.3 ADC配置代码
// 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 主程序示例
// 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();// 初始化BMSBMS_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 温度补偿
// 温度补偿系数(简化模型)
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 库仑效率校正
#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 容量衰减估算
// 基于充电循环次数估算容量衰减
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
- 充电循环次数
- 校准参数
- 历史容量数据
// 使用内部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 电池安全标准