如何用Python准确检测电池电压时间序列的循环次数?
电池循环次数统计的替代方法
核心思路
电池充放电循环对应电压上升(充电)+ 电压下降(放电)的完整过程,单纯检测峰值无法覆盖完整循环逻辑,建议结合趋势判断(充放电状态转换)或峰谷配对的方式统计。
方法一:基于充放电状态转换的状态机统计
通过计算电压变化趋势,跟踪充放电状态的切换,每完成一次「充电→放电」的完整过程即计为一个循环(可根据实际循环定义调整判断逻辑)。
import pandas as pd import numpy as np import matplotlib.pyplot as plt # 加载并预处理数据(确保时间序列有序) df = pd.read_csv("your_data.csv") df['datetime'] = pd.to_datetime(df['datetime']) df = df.sort_values('datetime').reset_index(drop=True) # 计算电压差分,过滤噪声后判断趋势 diff_threshold = 0.01 # 根据数据波动调整,过滤微小噪声 df['voltage_diff'] = df['voltage'].diff() df['trend'] = np.where( df['voltage_diff'] > diff_threshold, 'charging', np.where(df['voltage_diff'] < -diff_threshold, 'discharging', 'stable') ) # 状态机跟踪循环次数 current_state = df['trend'].iloc[0] cycle_count = 0 cycle_end_times = [] for idx, row in df.iterrows(): # 当状态从充电转为放电时,视为完成一次完整充放循环 if current_state == 'charging' and row['trend'] == 'discharging': cycle_count += 1 cycle_end_times.append(row['datetime']) current_state = row['trend'] print(f"统计到的电池循环次数:{cycle_count}") # 可视化验证 plt.figure(figsize=(19, 8)) plt.plot(df['datetime'], df['voltage'], label='电池电压') for time in cycle_end_times: plt.axvline(x=time, color='r', linestyle='--', label='循环结束' if time == cycle_end_times[0] else "") plt.xlabel('时间') plt.ylabel('电压') plt.legend() plt.show()
方法二:峰谷配对检测循环
结合峰值(充电最高点)和谷值(放电最低点)的检测,通过时间顺序配对峰谷,每一对有效「谷值→峰值→谷值」或「峰值→谷值→峰值」计为一个循环。
import pandas as pd import matplotlib.pyplot as plt from scipy.signal import find_peaks # 加载并预处理数据 df = pd.read_csv("your_data.csv") df = df.sort_values('datetime').reset_index(drop=True) # 先平滑电压数据,减少噪声干扰 df['voltage_smoothed'] = df['voltage'].rolling(window=50).mean().dropna() # 检测峰值(充电最高点) peaks, _ = find_peaks( df['voltage_smoothed'], prominence=0.5, # 峰与周围的高度差,根据数据调整 height=51 # 峰值最低阈值 ) # 检测谷值(放电最低点,通过反转电压找峰值实现) valleys, _ = find_peaks( -df['voltage_smoothed'], prominence=0.5, height=-48 # 谷值最高阈值(反转后为负,对应原电压最低值) ) # 按时间顺序配对峰谷,统计循环次数 cycle_count = 0 last_valley_idx = None for peak_idx in peaks: # 找到当前峰值之前的最近谷值 preceding_valleys = valleys[valleys < peak_idx] if len(preceding_valleys) > 0 and (last_valley_idx is None or preceding_valleys[-1] > last_valley_idx): cycle_count += 1 last_valley_idx = preceding_valleys[-1] print(f"统计到的电池循环次数:{cycle_count}") # 可视化验证 plt.figure(figsize=(19, 8)) plt.plot(df['voltage_smoothed'], label='平滑后电压') plt.plot(peaks, df['voltage_smoothed'].iloc[peaks], 'x', color='r', label='充电峰值') plt.plot(valleys, df['voltage_smoothed'].iloc[valleys], 'o', color='g', label='放电谷值') plt.xlim(0, 45000) plt.xlabel('数据点索引') plt.ylabel('电压') plt.legend() plt.show()
关键调整提示
- 噪声处理:如果数据波动大,先通过滚动平均(
rolling.mean)或高斯滤波平滑数据,避免误判趋势。 - 阈值参数:
diff_threshold、prominence、height等参数需要根据你的实际电压范围和波动幅度调整,可通过可视化逐步优化。 - 循环定义:根据电池实际使用场景调整循环判断逻辑(比如部分场景下「放电→充电」也可视为一个循环)。
内容的提问来源于stack exchange,提问作者aparna podili
相关产品推荐
相关产品推荐

