update power estimation and upload 2 models of yolo
This commit is contained in:
124
power.py
124
power.py
@@ -141,24 +141,114 @@ def is_charging(threshold_ma=10.0):
|
||||
|
||||
|
||||
def voltage_to_percent(voltage):
|
||||
"""根据电压估算电池百分比(查表插值)"""
|
||||
"""
|
||||
根据电压估算电池百分比(高密度查表插值 + 滤波)。
|
||||
|
||||
- 电压先做 5 点移动平均(抑制瞬时抖动)
|
||||
- SOC 再做一阶低通(抑制“跳电量”)
|
||||
|
||||
注意:
|
||||
- 该方法仍是“开路电压→SOC”的近似;负载较大/瞬时大电流时电压会下沉,SOC 会偏低。
|
||||
- 滤波会带来滞后:电量变化会更平滑,但更新更慢。
|
||||
"""
|
||||
if voltage is None:
|
||||
return 0
|
||||
points = [
|
||||
(4.20, 100), (4.10, 95), (4.05, 85), (4.00, 75), (3.95, 65),
|
||||
(3.90, 55), (3.85, 45), (3.80, 35), (3.75, 25), (3.70, 15),
|
||||
(3.65, 5), (3.60, 0)
|
||||
]
|
||||
if voltage >= points[0][0]:
|
||||
return 100
|
||||
if voltage <= points[-1][0]:
|
||||
try:
|
||||
v = float(voltage)
|
||||
except Exception:
|
||||
return 0
|
||||
for i in range(len(points) - 1):
|
||||
v1, p1 = points[i]
|
||||
v2, p2 = points[i + 1]
|
||||
if voltage >= v2:
|
||||
ratio = (voltage - v1) / (v2 - v1)
|
||||
percent = p1 + (p2 - p1) * ratio
|
||||
return max(0, min(100, int(round(percent))))
|
||||
return 0
|
||||
if v <= 0:
|
||||
return 0
|
||||
return int(round(_BATTERY_MONITOR.get_soc(v)))
|
||||
|
||||
|
||||
class BatteryMonitor:
|
||||
"""
|
||||
电压→SOC 估算器(查表 + 线性插值 + 双重滤波)。
|
||||
|
||||
说明:
|
||||
- 表为单节锂电“静态电压”近似曲线;不同电池/温度/老化会有偏差。
|
||||
- 这里不区分充电/放电曲线(滞后),主要用于“显示电量/粗略判断”。
|
||||
"""
|
||||
|
||||
def __init__(self, avg_window: int = 5, alpha: float = 0.2):
|
||||
# 电压-SOC对照表(电压从高到低)
|
||||
self.voltages = [
|
||||
4.20, 4.15, 4.10, 4.05, 4.00,
|
||||
3.95, 3.90, 3.88, 3.85, 3.82,
|
||||
3.80, 3.78, 3.75, 3.72, 3.70,
|
||||
3.65, 3.60, 3.55, 3.50, 3.45,
|
||||
3.40, 3.35, 3.30, 3.20, 2.50,
|
||||
]
|
||||
self.socs = [
|
||||
100, 98, 95, 90, 85,
|
||||
80, 75, 72, 68, 64,
|
||||
60, 56, 52, 48, 44,
|
||||
38, 32, 26, 20, 14,
|
||||
10, 6, 3, 1, 0,
|
||||
]
|
||||
|
||||
self.avg_window = max(1, int(avg_window))
|
||||
self.alpha = float(alpha) if alpha is not None else 0.2
|
||||
if not (0.0 < self.alpha <= 1.0):
|
||||
self.alpha = 0.2
|
||||
|
||||
self.voltage_history = []
|
||||
self.last_soc = 50.0
|
||||
|
||||
def _voltage_to_soc_raw(self, voltage: float) -> float:
|
||||
# 越界
|
||||
if voltage >= self.voltages[0]:
|
||||
return 100.0
|
||||
if voltage <= self.voltages[-1]:
|
||||
return 0.0
|
||||
|
||||
# 表是降序,二分查找
|
||||
left, right = 0, len(self.voltages) - 1
|
||||
while left <= right:
|
||||
mid = (left + right) // 2
|
||||
vm = self.voltages[mid]
|
||||
if vm == voltage:
|
||||
return float(self.socs[mid])
|
||||
elif vm < voltage:
|
||||
right = mid - 1
|
||||
else:
|
||||
left = mid + 1
|
||||
|
||||
# 线性插值:right 在高电压侧,left 在低电压侧(降序表)
|
||||
# 例:voltages = [4.2,4.15,...],则 v_high=voltages[right] >= voltage >= voltages[left]=v_low
|
||||
v_high, v_low = float(self.voltages[right]), float(self.voltages[left])
|
||||
soc_high, soc_low = float(self.socs[right]), float(self.socs[left])
|
||||
if abs(v_high - v_low) < 1e-9:
|
||||
return soc_low
|
||||
soc = soc_low + (voltage - v_low) * (soc_high - soc_low) / (v_high - v_low)
|
||||
return soc
|
||||
|
||||
def get_soc(self, raw_voltage: float) -> float:
|
||||
# 1) 电压滤波(移动平均)
|
||||
self.voltage_history.append(float(raw_voltage))
|
||||
if len(self.voltage_history) > self.avg_window:
|
||||
self.voltage_history.pop(0)
|
||||
voltage = sum(self.voltage_history) / float(len(self.voltage_history))
|
||||
|
||||
# 2) 查表插值
|
||||
raw_soc = self._voltage_to_soc_raw(voltage)
|
||||
|
||||
# 3) SOC 低通滤波
|
||||
a = self.alpha
|
||||
self.last_soc = a * raw_soc + (1.0 - a) * float(self.last_soc)
|
||||
|
||||
# clip
|
||||
if self.last_soc < 0.0:
|
||||
self.last_soc = 0.0
|
||||
if self.last_soc > 100.0:
|
||||
self.last_soc = 100.0
|
||||
return float(self.last_soc)
|
||||
|
||||
|
||||
# 模块级单例:保留历史,实现平滑(进程重启会重置)
|
||||
_BATTERY_MONITOR = BatteryMonitor(
|
||||
avg_window=int(getattr(config, "BATTERY_SOC_AVG_WINDOW", 5)),
|
||||
alpha=float(getattr(config, "BATTERY_SOC_LPF_ALPHA", 0.2)),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user