储能站或车队电池包的运维里,最怕的不是曲线变多,而是曲线已经在变坏,系统还只给出一堆历史记录。电压、电流、温度、SOC、充放电倍率、循环次数每天都在写入,单看某一个点位很难判断电池是不是正在异常衰减。到真正需要处理时,往往已经出现容量下降、单体温差扩大、充电时间变长,甚至某些单体提前进入高风险区间。
TimechoAI 官方文档把电池健康管理列为典型场景之一,关注点包括从充放电时序数据预测健康状态变化和剩余寿命趋势。文档里提到,SOH 和 RUL 不能直接测量,受温度、倍率、放电深度、日历老化等因素影响;TimechoAI 可以基于充放电过程数据和长期运行条件建模退化趋势,用于识别异常退化、高风险单体,并辅助运维和充放电策略优化。这个方向和普通“上传一条曲线看预测”不太一样,它更像把 BMS、时序数据库、特征表和预测服务接在一起。
企业版官方链接:https://timecho.com
时序大模型 TimechoAI:https://ai.timecho.com/
电池健康分析和普通温度、压力预测有一个明显差别:目标变量经常不完整。温度可以每秒采,电压可以每秒采,SOH 却不会每秒出现一次。它可能来自定期容量标定,也可能来自离线算法结果,还可能只在维护窗口里更新。TimechoAI 的使用重点就不能只停在“调一个预测接口”,而要先把高频采样、循环特征、低频健康标签组织到同一套可解释的数据模型里。
另一个差别是风险对象经常藏在局部。电池包整体容量看起来还行,某个模组里的单体已经开始偏离;平均温度还没超过阈值,温差却在慢慢拉大。传统阈值可以处理显著越界,但对“趋势正在变坏”不敏感。时序大模型更适合补这一层:给出未来趋势和异常偏离提示,再由 SQL、规则和人工复核把对象筛出来。
先把电池数据拆成两层
电池健康分析不能只拿一条温度曲线。比较稳的拆法是两层:底层保留高频采样,记录每个单体或模组的电压、电流、温度、SOC;上层按充放电循环聚合特征,形成可用于 SOH/RUL 预测的时间序列。TimechoAI 负责预测和时序分析,数据侧仍然要把循环边界、特征口径、异常样本先整理好。
原始采样表可以这样设计:
CREATE TABLE battery_cell_sample (
sample_time TIMESTAMP NOT NULL,
station_id VARCHAR(64) NOT NULL,
pack_id VARCHAR(64) NOT NULL,
module_id VARCHAR(64) NOT NULL,
cell_id VARCHAR(64) NOT NULL,
voltage_v DOUBLE PRECISION,
current_a DOUBLE PRECISION,
temperature_c DOUBLE PRECISION,
soc_percent DOUBLE PRECISION,
charge_state VARCHAR(16),
PRIMARY KEY (sample_time, station_id, pack_id, module_id, cell_id)
);
这张表解决的是“原始证据”。后面无论预测结果是否合理,都能回到原始采样窗口里查。当某个单体被标为弱单体时,也能看它在同一模组里的电压偏差、温度偏差和电流变化。
循环表单独保存,不和高频采样混在一起:
CREATE TABLE battery_cycle (
cycle_id BIGINT PRIMARY KEY,
station_id VARCHAR(64) NOT NULL,
pack_id VARCHAR(64) NOT NULL,
cycle_no INTEGER NOT NULL,
start_time TIMESTAMP NOT NULL,
end_time TIMESTAMP NOT NULL,
charge_ah DOUBLE PRECISION,
discharge_ah DOUBLE PRECISION,
charge_kwh DOUBLE PRECISION,
discharge_kwh DOUBLE PRECISION,
dod_percent DOUBLE PRECISION,
avg_temp_c DOUBLE PRECISION,
max_temp_c DOUBLE PRECISION,
min_temp_c DOUBLE PRECISION,
soh_percent DOUBLE PRECISION
);
soh_percent 不一定每次都有。很多时候 SOH 是通过容量标定、检修结果或离线计算得到的低频标签,而电压、电流、温度是高频采样。TimechoAI 适合处理的,是把这些长期变化序列组织起来,预测健康趋势和剩余寿命,而不是凭空补一个“准确 SOH”。
这里把原始采样和循环特征拆开,是为了避免两个问题。第一个问题是粒度混乱:秒级采样直接进入寿命预测,窗口会很长,噪声也多;按循环聚合后,健康退化趋势更清楚。第二个问题是责任边界混乱:采样表记录事实,循环表记录经过计算后的特征。后面预测结果出现偏差时,能明确排查是原始采样异常、循环边界切错,还是模型输入窗口选择不合适。
先用 SQL 看衰减有没有明显分层
进入模型前,可以先用 SQL 看电池包之间的分布。比如相同时间范围内,不同 pack 的循环次数、平均温度和放电容量是否已经拉开。
SELECT
station_id,
pack_id,
COUNT(*) AS cycle_count,
ROUND(AVG(discharge_ah)::numeric, 2) AS avg_discharge_ah,
ROUND(AVG(avg_temp_c)::numeric, 2) AS avg_temp_c,
ROUND(MAX(max_temp_c)::numeric, 2) AS max_temp_c,
ROUND(MIN(soh_percent)::numeric, 2) AS min_soh
FROM battery_cycle
WHERE start_time >= TIMESTAMP '2026-01-01 00:00:00'
GROUP BY station_id, pack_id
ORDER BY min_soh ASC NULLS LAST, avg_temp_c DESC;
这条 SQL 不做预测,只找值得关注的对象。低 SOH、高温、容量下降同时出现的 pack,优先进入 TimechoAI 的预测窗口。没有这个筛选,直接把所有电池包一起预测,容易把注意力浪费在健康状态很稳定的对象上。
弱单体可以从同一时刻的离散度开始查:
WITH cell_stat AS (
SELECT
sample_time,
station_id,
pack_id,
module_id,
cell_id,
voltage_v,
temperature_c,
AVG(voltage_v) OVER (
PARTITION BY sample_time, station_id, pack_id, module_id
) AS module_avg_voltage,
AVG(temperature_c) OVER (
PARTITION BY sample_time, station_id, pack_id, module_id
) AS module_avg_temp
FROM battery_cell_sample
WHERE sample_time >= TIMESTAMP '2026-06-01 00:00:00'
AND sample_time < TIMESTAMP '2026-06-08 00:00:00'
)
SELECT
station_id,
pack_id,
module_id,
cell_id,
COUNT(*) AS sample_count,
ROUND(AVG(voltage_v – module_avg_voltage)::numeric, 4) AS avg_voltage_delta,
ROUND(MAX(temperature_c – module_avg_temp)::numeric, 2) AS max_temp_delta
FROM cell_stat
GROUP BY station_id, pack_id, module_id, cell_id
HAVING ABS(AVG(voltage_v – module_avg_voltage)) > 0.015
OR MAX(temperature_c – module_avg_temp) > 3.0
ORDER BY max_temp_delta DESC, ABS(AVG(voltage_v – module_avg_voltage)) DESC;
这类查询适合作为模型前的诊断材料。TimechoAI 的电池健康场景强调异常退化和高风险对象识别,SQL 先把候选对象缩小,后面的预测才更聚焦。
这一步也能避免把 TimechoAI 用成“全量扫库工具”。储能站里 pack 和 cell 数量可能很多,每个对象都高频预测并不划算。先用 SQL 做粗筛,把近期容量下降、温差扩大、电压离散明显的对象挑出来,再调用时序大模型做趋势判断,资源利用更稳定,运维视角也更清楚。
如果要看某个 pack 最近 30 天的容量保持率变化,可以再加一条窗口查询:
SELECT
cycle_no,
cycle_start_time,
ROUND(capacity_retention::numeric, 5) AS capacity_retention,
ROUND(
(capacity_retention – LAG(capacity_retention) OVER (
PARTITION BY station_id, pack_id ORDER BY cycle_no
))::numeric,
5
) AS retention_delta
FROM battery_cycle_feature
WHERE station_id = 'S001'
AND pack_id = 'PACK-07'
AND cycle_start_time >= CURRENT_DATE – INTERVAL '30 day'
ORDER BY cycle_no;
retention_delta 连续为负时,不一定马上代表故障,但说明这个 pack 值得进入预测窗口。TimechoAI 的输出更适合作为“下一步观察对象”的依据,而不是单独替代维护结论。
把循环特征整理成预测序列
电池 SOH/RUL 预测通常不直接拿原始秒级采样进入模型,而是先把每个循环聚合成特征。比如放电容量、平均温度、温差、DOD、循环间隔、容量保持率。这样得到的是按循环编号推进的时间序列,和 TimechoAI 的预测接口更容易对齐。
先做一张循环特征表:
CREATE TABLE battery_cycle_feature (
station_id VARCHAR(64) NOT NULL,
pack_id VARCHAR(64) NOT NULL,
cycle_no INTEGER NOT NULL,
cycle_start_time TIMESTAMP NOT NULL,
discharge_ah DOUBLE PRECISION,
capacity_retention DOUBLE PRECISION,
avg_temp_c DOUBLE PRECISION,
temp_span_c DOUBLE PRECISION,
dod_percent DOUBLE PRECISION,
charge_discharge_gap_h DOUBLE PRECISION,
soh_percent DOUBLE PRECISION,
PRIMARY KEY (station_id, pack_id, cycle_no)
);
再用 SQL 从 battery_cycle 写入特征。额定容量可以放在电池包主数据里,这里用 battery_pack 关联。
CREATE TABLE battery_pack (
station_id VARCHAR(64) NOT NULL,
pack_id VARCHAR(64) NOT NULL,
rated_capacity_ah DOUBLE PRECISION NOT NULL,
online_date DATE,
manufacturer VARCHAR(128),
PRIMARY KEY (station_id, pack_id)
);
INSERT INTO battery_cycle_feature (
station_id,
pack_id,
cycle_no,
cycle_start_time,
discharge_ah,
capacity_retention,
avg_temp_c,
temp_span_c,
dod_percent,
charge_discharge_gap_h,
soh_percent
)
SELECT
c.station_id,
c.pack_id,
c.cycle_no,
c.start_time,
c.discharge_ah,
c.discharge_ah / NULLIF(p.rated_capacity_ah, 0) AS capacity_retention,
c.avg_temp_c,
c.max_temp_c – c.min_temp_c AS temp_span_c,
c.dod_percent,
EXTRACT(EPOCH FROM (c.end_time – c.start_time)) / 3600.0 AS charge_discharge_gap_h,
c.soh_percent
FROM battery_cycle c
JOIN battery_pack p
ON p.station_id = c.station_id
AND p.pack_id = c.pack_id
WHERE c.discharge_ah IS NOT NULL;
这张特征表是后续预测的主输入。capacity_retention 可以作为目标序列,avg_temp_c、temp_span_c、dod_percent、charge_discharge_gap_h 可以作为历史协变量。已有 SOH 标签时,也可以用 SOH 作为目标;没有 SOH 标签时,容量保持率更容易从充放电记录里得到。
特征口径要固定。比如 capacity_retention 是用放电容量除以额定容量,还是用最近一次标定容量做基准,这两个写法的含义不同;temp_span_c 是循环内最大最小温差,还是同一时刻单体最大温差,后续解释也不同。只要口径变了,同一条曲线的历史就不能简单拼在一起预测。
可以把特征版本单独记录下来:
CREATE TABLE battery_feature_version (
version_id VARCHAR(64) PRIMARY KEY,
target_rule TEXT NOT NULL,
covariate_rule TEXT NOT NULL,
created_by VARCHAR(64),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
预测请求里保存 version_id,后面回看同一个 pack 的趋势时,才能确认不同批次结果是否可比。
用 Python SDK 跑 SOH 趋势预测
TimechoAI 官方 SDK 文档写明安装包为 timecho-ai,Python 版本要求为 >=3.10 且 <3.13。预测接口支持 targets、history_covs、future_covs、output_length、time_col 等参数;output_length 范围为 1 到 720。官方文档还列出了 Auto、Timer-3.5、Timer-3.0、Chronos-2、AutoARIMA、Holt-Winters 等可选 model_id。
pip install timecho-ai pandas sqlalchemy psycopg2-binary
读取循环特征:
import os
import pandas as pd
from sqlalchemy import create_engine
from timecho_ai import TimechoAIClient
engine = create_engine(os.environ["BATTERY_DB_URL"])
sql = """
SELECT
cycle_start_time AS time,
capacity_retention,
avg_temp_c,
temp_span_c,
dod_percent,
charge_discharge_gap_h
FROM battery_cycle_feature
WHERE station_id = %(station_id)s
AND pack_id = %(pack_id)s
ORDER BY cycle_no
"""
raw_df = pd.read_sql(
sql,
engine,
params={"station_id": "S001", "pack_id": "PACK-07"},
)
raw_df = raw_df.dropna(subset=["capacity_retention"])
target = raw_df[["time", "capacity_retention"]].tail(512)
history_covs = raw_df[[
"time",
"avg_temp_c",
"temp_span_c",
"dod_percent",
"charge_discharge_gap_h",
]].tail(512)
调用 TimechoAI:
client = TimechoAIClient(api_key=os.environ["TIMECHOAI_API_KEY"])
result = client.forecast(
targets=target,
history_covs=history_covs,
output_length=64,
time_col="time",
model_id="Timer-3.5",
auto_adapt=True,
)
print(result[0])
这段代码没有写成“模型一定能预测寿命”。它做的是更实在的事:把过去 512 个循环的容量保持率和运行条件交给 TimechoAI,预测后续 64 个循环的趋势。真正进入运维系统时,还要结合阈值、检修记录和人工复核。
REST API 适合接进现有服务
如果系统不是 Python 技术栈,TimechoAI 官方文档提供了 REST API:POST https://ai.timecho.com/ai/api/v1/forecast,请求头使用 Authorization: Bearer {API-Key}。REST 方式适合接进 Java、Go、Node 或调度平台。
用 SQL 把目标序列转成 JSON 结构也可以,先查出最近窗口:
SELECT
to_char(cycle_start_time, 'YYYY-MM-DD"T"HH24:MI:SS') AS time,
ROUND(capacity_retention::numeric, 6) AS capacity_retention
FROM battery_cycle_feature
WHERE station_id = 'S001'
AND pack_id = 'PACK-07'
ORDER BY cycle_no DESC
LIMIT 512;
Python 里组装 REST 请求:
import requests
forecast_payload = {
"targets": [
{
"columns": ["time", "capacity_retention"],
"data": target.values.tolist(),
}
],
"history_covs": [
{
"columns": [
"time",
"avg_temp_c",
"temp_span_c",
"dod_percent",
"charge_discharge_gap_h",
],
"data": history_covs.values.tolist(),
}
],
"output_length": [64],
"time_col": ["time"],
}
resp = requests.post(
"https://ai.timecho.com/ai/api/v1/forecast",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['TIMECHOAI_API_KEY']}",
},
json=forecast_payload,
timeout=30,
)
resp.raise_for_status()
forecast_json = resp.json()
官方 REST 文档说明返回结果里包含 code、message 和 data,其中预测结果在 data.results 里。入库时不要只保存整个 JSON,最好拆到结构化表。
CREATE TABLE battery_soh_forecast_run (
run_id BIGSERIAL PRIMARY KEY,
station_id VARCHAR(64) NOT NULL,
pack_id VARCHAR(64) NOT NULL,
target_name VARCHAR(64) NOT NULL,
input_start TIMESTAMP NOT NULL,
input_end TIMESTAMP NOT NULL,
output_length INTEGER NOT NULL,
model_id VARCHAR(64),
request_hash VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE battery_soh_forecast_point (
run_id BIGINT NOT NULL,
forecast_time TIMESTAMP NOT NULL,
forecast_value DOUBLE PRECISION NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (run_id, forecast_time)
);
拆结果:
from datetime import datetime
rows = forecast_json["data"]["results"][0]["data"]
point_df = pd.DataFrame(rows, columns=["forecast_time", "forecast_value"])
point_df["forecast_time"] = pd.to_datetime(point_df["forecast_time"])
point_df["forecast_value"] = point_df["forecast_value"].astype(float)
print(point_df.head())
这一步让 TimechoAI 的输出进入数据库,而不是停在一次接口返回。后面要做 pack 间对比、弱单体排序、寿命趋势回看,都需要结构化结果。
REST 接入还有一个实际好处:服务端语言不受限制。BMS 后台如果是 Java,可以用同样 payload 调用;调度平台如果只会发 HTTP,也能接入。TimechoAI 提供 Web、REST API、Python SDK 多种方式,页面适合试验,SDK 适合数据分析脚本,REST 更适合进已有系统。
为了让请求可复盘,payload 不要只存在日志文本里,可以保存一份哈希和简化参数:
ALTER TABLE battery_soh_forecast_run
ADD COLUMN feature_version VARCHAR(64),
ADD COLUMN api_mode VARCHAR(32),
ADD COLUMN input_points INTEGER;
这些字段不复杂,但后面排查会省很多时间。比如同样是 PACK-07 的预测,一个输入 512 个循环,一个输入 128 个循环;一个使用 Timer-3.5,一个使用 Auto。如果不保存参数,看曲线时很容易误判。
用 SQL 找出需要重点复核的电池包
预测结果入库后,SQL 可以继续发挥作用。比如找未来 64 个循环内容量保持率下降最快的电池包:
WITH forecast_slope AS (
SELECT
r.station_id,
r.pack_id,
r.run_id,
MIN(p.forecast_value) AS min_forecast_value,
MAX(p.forecast_value) AS max_forecast_value,
MAX(p.forecast_value) – MIN(p.forecast_value) AS forecast_span
FROM battery_soh_forecast_run r
JOIN battery_soh_forecast_point p
ON p.run_id = r.run_id
WHERE r.status = 'success'
AND r.created_at >= CURRENT_DATE – INTERVAL '7 day'
GROUP BY r.station_id, r.pack_id, r.run_id
)
SELECT
station_id,
pack_id,
run_id,
ROUND(min_forecast_value::numeric, 4) AS min_capacity_retention,
ROUND(forecast_span::numeric, 4) AS forecast_span
FROM forecast_slope
WHERE min_forecast_value < 0.82
OR forecast_span < –0.03
ORDER BY min_forecast_value ASC, forecast_span ASC;
这里的阈值只是示例,不应该直接复制进生产系统。不同电池类型、不同运营策略、不同温度区间下,容量保持率阈值都要重新设定。TimechoAI 负责给趋势,SQL 负责筛选和解释,最终处置仍然要结合运维规则。
弱单体复核可以把未来趋势和近期温差、电压差放在一起:
WITH recent_cell_risk AS (
SELECT
station_id,
pack_id,
module_id,
cell_id,
MAX(temperature_c) – MIN(temperature_c) AS temp_range,
MAX(voltage_v) – MIN(voltage_v) AS voltage_range,
COUNT(*) AS sample_count
FROM battery_cell_sample
WHERE sample_time >= CURRENT_TIMESTAMP – INTERVAL '24 hour'
GROUP BY station_id, pack_id, module_id, cell_id
)
SELECT
r.station_id,
r.pack_id,
r.module_id,
r.cell_id,
ROUND(r.temp_range::numeric, 2) AS temp_range,
ROUND(r.voltage_range::numeric, 4) AS voltage_range,
f.min_capacity_retention
FROM recent_cell_risk r
JOIN (
SELECT
fr.station_id,
fr.pack_id,
MIN(fp.forecast_value) AS min_capacity_retention
FROM battery_soh_forecast_run fr
JOIN battery_soh_forecast_point fp
ON fp.run_id = fr.run_id
GROUP BY fr.station_id, fr.pack_id
) f
ON f.station_id = r.station_id
AND f.pack_id = r.pack_id
WHERE r.temp_range > 6
OR r.voltage_range > 0.08
ORDER BY f.min_capacity_retention ASC, r.temp_range DESC;
这类查询比单纯“预测 SOH”更接近实际运维。它把预测结果和近期异常表现放在一张结果里,运维人员可以优先看趋势差、温差大、电压离散度高的对象。
错误处理别写成一行日志
TimechoAI 官方 SDK 文档列出了不同异常类型,包括鉴权、参数错误、权限、限流、服务不可用、超时等。预测任务进入调度后,这些错误要分开处理。
from timecho_ai import (
AuthenticationError,
BadRequestError,
RateLimitError,
ServiceUnavailableError,
TimeoutError,
)
def forecast_with_status(client, target, history_covs):
try:
result = client.forecast(
targets=target,
history_covs=history_covs,
output_length=64,
time_col="time",
model_id="Timer-3.5",
)
return {"status": "success", "result": result}
except AuthenticationError:
return {"status": "auth_failed"}
except BadRequestError as exc:
return {"status": "bad_request", "message": str(exc)}
except RateLimitError:
return {"status": "rate_limited"}
except ServiceUnavailableError:
return {"status": "service_unavailable"}
except TimeoutError:
return {"status": "timeout"}
调度表里最好保留状态:
CREATE TABLE battery_forecast_job (
job_id BIGSERIAL PRIMARY KEY,
station_id VARCHAR(64) NOT NULL,
pack_id VARCHAR(64) NOT NULL,
job_type VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
retry_count INTEGER NOT NULL DEFAULT 0,
last_error TEXT,
next_run_at TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
失败状态拆开后,后续处理会简单很多。鉴权失败要检查 API Key,参数错误要回到 payload,限流要调整调度频率,服务临时不可用可以重试。所有失败都写成 failed,等于把排查线索丢掉。
调度频率也要和电池业务节奏匹配。SOH/RUL 不是秒级变化指标,没有必要每分钟调用一次长窗口预测。更合理的方式是:充放电循环结束后触发一次特征更新;每天固定时间对重点 pack 做一次趋势预测;当弱单体 SQL 命中或异常检测分数升高时,再临时提高预测频率。
UPDATE battery_forecast_job
SET next_run_at =
CASE
WHEN status = 'rate_limited' THEN CURRENT_TIMESTAMP + INTERVAL '30 minute'
WHEN status = 'service_unavailable' THEN CURRENT_TIMESTAMP + INTERVAL '10 minute'
WHEN job_type = 'daily_soh' THEN date_trunc('day', CURRENT_TIMESTAMP) + INTERVAL '1 day 02 hour'
ELSE CURRENT_TIMESTAMP + INTERVAL '1 hour'
END,
updated_at = CURRENT_TIMESTAMP
WHERE job_id = :job_id;
这个调度策略不追求复杂。它把错误状态、任务类型和下一次运行时间绑在一起,避免因为短时失败不断重试,也避免健康趋势类任务过度调用。
这条路径适合怎么用
TimechoAI 在电池健康场景里更适合做三件事。第一,围绕容量保持率、SOH、温度、DOD 等序列做趋势预测;第二,结合充放电过程数据识别异常退化和高风险对象;第三,通过 REST API 或 Python SDK 把预测能力接进现有 BMS、储能运维平台或数据分析任务。
它不应该被写成“自动判断电池寿命”的黑箱。更稳的用法,是让 SQL 先完成对象筛选和特征组织,让 TimechoAI 做未来趋势分析,再让数据库保存预测结果和复核状态。这样既能突出时序大模型的预测能力,也保留了工程系统需要的边界。
从使用角度看,TimechoAI 的门槛并不高:SDK 可以直接传 DataFrame,REST API 有明确的 forecast endpoint,协变量也有对应参数。真正需要花时间的,是把电池业务数据整理成连续、可信、可解释的时间序列。这个工作做好后,时序大模型才有稳定发挥空间。
电池健康管理里最值得先落地的不是“大而全平台”,而是一条能跑通的闭环:SQL 选出候选 pack,Python 或 REST 调 TimechoAI 预测容量保持率,预测结果入库,SQL 再把高风险对象排出来。这个闭环跑稳定后,再扩展到更多 pack、更多协变量、更多异常规则。TimechoAI 的优势也更容易被看见:它处理的是时间序列的趋势和偏离,而不是替代数据库、替代 BMS、替代运维判断。
企业版官方链接:https://timecho.com
时序大模型 TimechoAI:https://ai.timecho.com/



