update power estimation and upload 2 models of yolo

This commit is contained in:
gcw_4spBpAfv
2026-05-08 23:41:42 +08:00
parent 5e7db5e271
commit a090579db9
12 changed files with 2061 additions and 159 deletions

124
power.py
View File

@@ -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)),
)