2024 年 7 月 26 日,我司(广州燧创信息技术有限公司)派我赴北京参与了由 TDengine 官方举办的用户大会,在创始人陶建辉的精彩演讲中,他给观众们展示了时序数据对于人工智能的巨大价值,并且发布了用于时序数据预测的人工智能分析工具 TDgpt。这次大会令我眼前一亮,作为我司的人工智能部门负责人,我早已带领团队把 TDengine 和人工智能结合在一起了,今天把这些经验分享出来供大家参考。
作者的话
内容摘要
- 该电芯容量预测系统主要利用机器学习为电池生产厂商提供服务。
- 该电芯容量预测系统采用了 TDengine 和自研算法代替了传统数据库 + 矢量数据库。
- 在 TDengine 的协助下,该电池容量预测系统可帮助用户节省约超过 6000 万 RMB 的硬件、场地、人力等成本以及大量的时间成本。
在锂离子电池生产工艺中,电池的容量(Capacity)数据,是出厂前评价单体锂离子电池质量好坏的一个重要指标。厂家会将锂离子电池根据不同的容量等级进行分档,相同档位的电芯会配组成为电池包(Pack),以获取更好的稳定性和寿命。
目前常用的获得锂离子电池容量数据的方法是:通过充放电设备,先对锂离子电池进行完全充电,然后进行多组阶梯式的电流放电,即让电池完全放电,最终将每个放电工步的总容量叠加,作为电池的出厂容量。该过程称为分容(Grading)。 该方法的缺点是,整个充放电过程极度耗时耗能,以标准容量为 3200 mAH 的 18650 电池举例,整个充放电过程会持续 4~5 小时,其中每个充放电阶段都会消耗非常多的电能。此外,在分容之前,还有一个必需的化成(Formation)工序。锂离子电池在灌输电解液并封口后,其物理结构基本组装完成。为使电极充分被电解液浸润从而全面活化,需要经过一个以多个恒压充电——放电循环为主要步骤的激活过程,最终使电池具有放电能力,这一电化学过程即为化成工序。化成完成后,一般电池会经过至少一日的常温静置/高温老化时间,待其化学性质趋于稳定,再通过分容检测容量,这一过程会耗费相当大的成本:
- 静置和分容均耗费了很多时间。
- 分容时的多个充放电步骤非常耗电。
- 大量分容设备体积巨大,不仅占地面积大,也为堆垛机和物流线等实施增加困难。
但实际上,截至化成(Formation)完成的时候,电池本身的理化性质已基本确定,且会反映在(化成期间的)充放电曲线内,并与前中段线生产的数据记录高度相关。
由此,电芯容量预测系统 kun 应运而生,在 kun 的助力下,绝大多数(70%-90%)的电池容量直接通过 kun 便可以计算预测获得,它具备如下优势:
- 分容设备被大量取缔,建厂时用地、资金等成本被大幅压缩。
- 省略分容充放电步骤,节约大量电能。
- 节省了静置和分容时间,变相大大提高了产能。
- 整线体系简化,降低了分容设备故障导致的停产概率和设备维修期间造成的各种费用。
经济收益
目前,该项目于 2022 年 11 月上线,已经正常生产近两年时间,平均每月出货率为产能的 85%,前期调试和工艺磨合周期为 2 个月。
在我们与客户、TDengine 团队两个月的共同努力下,产线落实了工艺和制程一致性的规范,确保容量预测介入的生产整体在较稳定的环境下进行,降低了 NG 率的同时提高了预测精度。
每天容量预测系统为产线完成大量电芯的容量预测结果。其平均误差保持在 0.37%,预测判别 NG 率为 5.7%。
目前容量预测系统完全达到客户要求,第一年成功为客户节约了超过 5600 万 RMB 的经济价值,并完成了全局验收,第二年所节约的成本将超过 6000 万 RMB。
TDengine 选型背景
在我们的项目中,TDengine 起到了相当大的作用。
2021 年底,我们的系统(kun)已经基本完成,后端数据库使用了 MariaDB 作为数据持久化的方案。但当我用大量的模拟数据进行交付测试时,遭遇了一个性能瓶颈问题:由于 MariaDB 是单表存储的模型,当数据量达到一定的规模时,按时间查询太慢了。
尽管我们系统的主要功能是利用机器学习提供服务,但车间现场电芯的生产数据(生产时间、条码、经转的设备号和通道号等)均需要记录在案。
这些“流水账”有什么用呢?我们的甲方(电池厂)要对客户(比如电动轿车厂)负责,质量上肯定是要精益求精的。因此,如果某些电芯预测出的容量偏离了正常分布(如果只有系统误差,一般可以看做这类数据符合正态分布),就需要及时回溯上述数据以便快速定位产线上的问题,进而及时纠错,恢复电芯容量的精度。举个例子,某化成设备在某个位点上的夹具温度传感器出了故障,测量的温度不准确,这里涉及到几个问题:
1.哪个设备上的夹具温度偏离了正常分布?
2.具体是该设备上的哪个充放电位点?
3.该位点是从什么时间开始出现故障的?
为保障生产,当故障出现时,工厂都是希望第一时间定位和解决问题的。一般的做法是,快速查询出可能出现问题的时段的数据,再进行分析。
使用 MariaDB,当单表数据量达到一定规模时,查询性能会急剧下降,对于难以索引的时间字段更加如此。如果一定要继续使用 MariaDB 作为数据持久化的方案,则需要在写入时就在应用层代码中建立复杂的分片分区逻辑,以便未来在查询时快速定位到具体的数据。
更进一步地,我们也希望数据库有强大的对新写入数据的即时计算的能力,这样就可以及时上报错误,要比人为发现问题后再去反查更为安全便捷。
带着需求,我开始重新考虑数据库选型的问题。在开源社区搜索时,偶然间发现 TDengine 这个产品,带着好奇心看完了它的主要特性之后,我们认定,TDengine 与 kun 是天作之合:
可以看到,TDengine 的特性与我们的需求是非常匹配的。而且我相信,它解决了许多工业场景的痛点,在这一领域的许多开发者深入了解它之后,都会有“相见恨晚”的感觉。
运行环境
作为一款时序数据库产品,TDengine 对硬件的要求非常友好。得益于它优异的数据压缩比率,我们在生产环境的硬件配置一降再降,完成了此前无法完成的工作——硬件成本缩减了一倍以上,做到了真正意义的“降本增效”。
三台机架式服务器:
TDengine 版本: 3.2.3.0,三副本模式。
自研算法 + TDengine 代替矢量数据库
为了保障预测容量的精度,我们采用了持续机器学习(continuous machine learning)的设计:通过这种机制,我们得以以全自动的方式始终保持模型的行为和最新的生产状况保持一致。
前文提及,为了节约设备和空间成本,厂家往往只会采购原生产所需的分容设备的 10%~30%。但这样小规模的分容设备产生的可用于模型训练的全流程数据非常少,这也导致在前期机器学习无法正常工作。
但是,通过自研的曲线搜索算法,我们成功解决了这个问题,从而保证了甲方的出货效率,具体方案是:经过过滤的全流程数据被收进数据库,当有新电芯待预测容量时,通过曲线搜索算法从中检索出相似的曲线,进而归纳得到预测容量。
相信熟悉矢量数据库(vector database)产品的工程师都会感到非常熟悉:这不是矢量数据库的常见功能吗。
矢量数据库往往原生支持一些基于聚类(比如根据距离)的搜索算法,常见的有 k-NN (k-nearest Neighbor)、HNSW (Hierarchical Navigable Small World)或者 IVF (Inverted File Index)等。
但如果在我们的系统中额外集成一种矢量数据库,不仅运维成本提升了,也为程序员们引入了额外的心智负担。
幸运的是,TDengine 本身提供的聚合函数、流式计算等特性,配合其灵活的自定义函数(User-defined Function, UDF),使得我们有机会在写入全流程数据的同时就高效地进行一系列复杂的预处理,为快速搜索近似曲线提供了可能。
使用矢量数据库还有一个不便之处,就是电芯复杂的元数据(metadata)无法直接与计算用的特征值(features)建立绑定关系。在调研选型阶段,我们就留意到,除了上文提到的优秀特性,TDengine 还提供了标准的 SQL 语法,也有和关系型数据库相似的关联查询用法,使程序员可以以很小的学习代价来记录和利用字段间的关联性,开发更简单;TDengine 的 Rust 连接器还提供了参数绑定这种更高效的写入方式,当客户端有数据发送过来时,我们可以处理后快速写入;后续如果用户需要将计算结果与元数据结合在一起分析(详见生产异常预警小节),一次查询就可以获取所需的全部信息,这样的数据建模极大地提升了运行时效率。
可以看出,TDengine 在一整条数据链路的各个环节都为开发者提供了相当大的便利。
TDengine Rust 连接器锦上添花
熟悉深度学习的朋友们都知道,为了使模型的参数得到尽量利用以提高其泛化性,理应在每个 epoch 中将整个数据集按批次(batch)喂给模型学习。
上文提到,经过预处理的 features 也通过 TDengine 持久化。但数据量达到一定规模后,简单地将整个数据集从数据库中一次取出用于训练是不可取的,会有严重的性能问题甚至引起 OOM(Out Of Memory,内存不足)中断。
由于要将当前 batch 的数据实时从数据库取出加入训练,直观上可能操作起来会有一些技术问题。
所幸,TDengine 的开发团队对社区的支持非常友好。kun 是完全基于 Rust 语言开发的,我们早期在调研时就注意到,TDengine 也为 Rust 实现了连接器,其中有两个亮点:
1.同时支持同步/异步上下文。2.借由 FFI(Foreign Function Interface,外部语言接口)实现了 Rust 与 C 语言的 primitive 数据类型的无缝对接。
一般来讲,由于网络传输及数据查询均需要一定的时间,因此建议在异步上下文中与数据库进行交互。但考虑到训练模型是 CPU 密集型的计算任务,我们将其置于 rayon 开启的线程池中运行。此时如果有数据需要与数据库交互,理论上可以通过消息通道机制将消息传出实时写入数据库,但也有些场景不要求并发(比如一次性地查询某些数据),此时连接器支持同步上下文就显得尤为方便。
高效的数据可视化
电芯分档的“金标准”是容量值。因此,生产监督人员往往非常关注预测得到的容量值的分布。随着产量的累积,单个批次可能在一段时间内生产的电芯数目非常多,如果按照传统做法从数据库查出全部的数据,再在应用层汇总计算,如此大量的数据传输会使前端经历相当久的延迟才能绘制出统计图表,破坏了用户体验。
好消息是,通过 TDengine 的窗口切分查询和聚合函数可以很方便实现这种直方图汇总,在数据库层直接完成计算后返回结果:
let capacity_histogram_data: Vec<CapacityIntervalHistogram> = taos
.query(format!(
"SELECT histogram(capacity,'linear_bin','{}',0) as capacity_rang
FROM inference.`{}` WHERE ts > (NOW - {}d) ;",
serde_json::json!({
"start": min_capacity as i32,
"width": (max_capacity as i32 / total_capacity_intervals),
"count": total_capacity_intervals,
"infinity": false
}),
table_name,
ts_n_days
))
.await?
.deserialize()
.try_collect()
.await?;
在前端就可以直接利用返回值绘图了:
生产异常预警
现代电池厂多采用全自动产线,但受制于设备和软件等因素,产线往往缺乏即时的反馈能力。因此,为了保障出货速度和质量,厂家的生产监督人员(比如工艺员和技术员等)有一项重要而繁琐的工作:以一定间隔(比如 4~6 小时)收集数据并制作报表分析,以便及时发现异常数据,再定位到异常的工序甚至设备,进而纠错或修理。
这样的工作方式主要有两个弊端:
1.间隔太久,生产异常难以第一时间被发现和处理。2.工作内容重复、工作量太大。
那么有没有一种全自动的方式来纠错和预警呢?当然有。我们可以部署一个计划任务,每隔一段时间就查询所有的关键数据(比如上柜电压、某时刻的夹具温度等)的数值特征(比如均值相对于总体的偏离程度等),再在应用层进行分析。同样地,这种做法有和上文类似的问题,数据库发送回查询结果时大量的数据传输和应用层做计算时的高 CPU 负载可能使生产环境苦不堪言。这样的性能开销也意味着这类型的任务无法经常执行。
在工业 4.0 的概念中,其中一部分目标就是建立具有自适应性的智能制造工厂。为向这一惊艳的概念致敬,我们计划在下个版本中通过 TDengine 的流式计算特性来实时对上述关键数据进行分析。
由于这些数值特征在数据写入时就已经以极小的代价计算完毕,需要汇总时按需查询、呈现结果就变得异常容易和高效。这样的做法将计算的负载均匀分摊在程序运行的整个生命周期中,是极佳的“削峰填谷”思想的实践。
以上,便是我们使用 TDengine 前前后后的一切,特此整理分享出来。(耐心读到最后的读者,有可能会对 kun 这个单词感到一丝熟悉——没错,这个系统的负责人/这篇文章的核心作者正是一个 xiao hei zi,如果你想结识“志同道合”的他,欢迎留言)
关于广州燧创信息技术有限公司
自 2016 年起,燧创就站在工业自动化的前沿。通过与跨行业精英的紧密合作,将互联网的灵活性、工业控制的精确性和信息化的深度知识融为一体。伴随多个海内外大型项目陆续落地,燧创信息更加坚定了为全球客户带来高质量自动化软件服务的承诺(https://sctmes.com/)