AI智能摘要
聚焦智能设备BMS中I2C通信瓶颈,提出结合属性缓存与自适应刷新机制的创新方案,通过缓存属性并动态调整TTL,有效减少I2C访问冲突和通信负担。自适应算法根据属性变化及噪声门限、窗口振幅智能控制刷新频率,提升系统实时性与稳定性,同时引入并发控制与总线熔断机制,确保关键数据高效、安全传输。
此摘要由AI分析文章内容生成,仅供参考。

背景

随着智能设备对电池续航、充电管理和温控的需求不断增加,电池管理系统(BMS)中的 I2C 通信成为了一个核心技术。然而,I2C 作为一种基于总线的通信协议,随着设备数量的增加,容易出现总线竞争、超时、阻塞等问题,尤其在充电管理、电流/电压监控等高频场景中,I2C 总线的竞争会导致性能瓶颈和系统卡顿,最终影响用户体验和系统稳定性。

本文将分析一种通过 属性缓存自适应刷新机制 来优化 I2C 访问的设计方案,结合噪声门限、窗口振幅等自适应算法来提升系统的鲁棒性和实时性。

设计的目标

减少 I2C 访问冲突

I2C 作为共享总线,当多个设备同时请求访问时,会导致总线的争用和锁竞争,尤其是在需要频繁访问的属性(如电流、电压、温度等)上,I2C 请求可能会阻塞其他关键操作,甚至导致超时和死锁。

降低 I2C 请求频率

频繁的 I2C 请求不仅增加了总线的负担,还导致了大量不必要的通信。在传统的方案中,很多属性在短时间内不会发生变化,但仍会频繁请求访问,造成资源浪费。

上层的所有数据请求,均变成读取缓存中的数据,但也仍然兼容实时读取的接口(强实时性)

保持系统稳定性和实时性

在电池管理系统中,某些属性(如电压、电流、温度等)对系统的稳定性至关重要。为确保系统在任何时刻都能及时响应变化,必须保证数据的一致性和实时性。通过自适应刷新机制,可以根据属性的重要性和变化频率动态调整刷新频率,避免频繁更新导致的性能下降。

方案原理

属性缓存机制

传统的 I2C 访问模式中,每次读取设备的状态时,都会直接发起 I2C 请求并读取数据,这可能导致频繁的总线竞争和性能下降。为了解决这个问题,我们引入了 属性缓存 机制:

  • 缓存层:将每个 I2C 属性的值保存在本地缓存中,并且缓存值有一个有效期(TTL)。只有当缓存过期时,才会重新发起 I2C 请求。通过减少重复的 I2C 请求,显著降低了总线的负担。

  • 缓存失效和刷新机制:当属性的缓存过期或收到请求刷新时,会通过后台任务(如 workqueue)异步刷新缓存,而不是在主线程中阻塞等待 I2C 请求完成。

这种机制的优点是能够减少 I2C 请求频率,并确保关键属性能在需要时及时刷新,同时避免了 I2C 总线的竞争和冲突。

自适应刷新机制

为了提高系统的实时性和鲁棒性,我们设计了一套自适应刷新机制,具体原理如下:

按时间敏感度分类

根据属性的变化频率和对系统的影响程度,将所有的 I2C 属性分为 FASTMEDSLOWSTATIC 四类:

  • FAST 类:快速变化的属性,如电流、电压、状态等,需要较高的刷新频率,TTL 范围较小。

  • MED 类:中等变化频率的属性,如温度、SOC 等,TTL 范围较大。

  • SLOW 类:变化较慢的属性,如充电周期、电池健康等,TTL 范围更大。

  • STATIC 类:基本不变化的属性,如设备型号、设计容量等,TTL 非常大,甚至可以忽略刷新。

TTL的动态调整

TTL(Time-To-Live)是属性缓存的有效期,决定了缓存多久后过期。TTL 的值不仅与属性的时间敏感度相关,还会根据 变化频率稳定性 动态调整:

  • 显著变化:当属性的变化大于某个设定的阈值时,TTL 会缩短,强制进行快速刷新。

  • 稳定状态:当属性变化较小时,TTL 会延长,减少不必要的刷新。

  • 噪声抑制:通过 噪声门限窗口振幅,避免因为测量噪声或微小波动导致过度频繁的刷新。

噪声门限与窗口振幅

噪声门限(Noise Threshold)和窗口振幅(Window Amplitude)是自适应刷新机制中的关键算法。它们的作用是过滤掉不重要的噪声变化,只对 显著变化 做出反应。

  • 噪声门限:通过计算属性值的变化幅度,设定一个最小变化阈值,只有当变化大于该阈值时才认为是有效变化。例如,电流传感器可能会因为测量误差或 ADC 抖动导致微小的波动,但这些波动不足以影响系统决策,因此需要设置噪声门限来过滤掉这些微小的变化。

  • 窗口振幅:通过滑动窗口计算一段时间内的 最大变化范围,如果窗口内的变化超过设定的阈值,则认为有显著变化。这能够有效捕捉到 慢变,避免系统错过重要的变化。

并发控制与总线熔断(bus-recovery)

在多属性并发刷新时,可能会导致 I2C 总线资源的竞争。为了避免这种情况,我们采用了 并发控制总线熔断 的机制:

  • 并发控制:通过使用 spinlock 或其他锁机制,确保同一时刻只有一个任务访问同一个属性的缓存或 I2C 总线,避免并发冲突。

  • 总线熔断:当 I2C 总线出现过多错误或延迟时,系统会进行熔断处理,暂停所有的 I2C 访问,直到总线恢复正常。这样可以避免错误的连锁反应,保持系统稳定。

关键算法

自适应刷新算法

自适应刷新算法的核心目标是根据 属性的变化频率和敏感性 动态调整缓存的有效期(TTL)。TTL 决定了缓存何时过期,需要重新获取数据。算法的思路是,属性变化越频繁,其 TTL 越短;变化较少的属性可以延长 TTL,减少不必要的刷新请求。

基本概念

  • TTL (Time-To-Live):属性缓存的有效期。TTL 较短时,属性会更频繁地刷新,TTL 较长时,属性的刷新频率较低。

  • 显著变化:当属性的变化超过了一个设定的阈值时,认为发生了显著变化,TTL 应缩短。

  • 稳定状态:如果属性的值保持不变,TTL 可以逐渐增大,减少刷新频率。

自适应算法的核心逻辑

  1. 显著变化检测

    1. 当属性的值发生显著变化时(例如,电流或电压的变化超过一定的阈值),TTL 会缩短,强制进行更频繁的刷新。

    2. 显著变化的判定通常通过比较当前值与前一个值之间的差异(delta)来实现。

  2. 稳定状态检测

    1. 当属性值稳定并且变化幅度较小(低于设定的噪声门限和窗口振幅),TTL 会逐渐增大,减少刷新频率。

  3. 自适应更新

    1. 基于 噪声门限(noise threshold)窗口振幅(range threshold),调整 TTL。噪声门限用于避免小幅度的波动影响刷新频率,而窗口振幅则用于检测慢变趋势。

算法公式

  1. 显著变化检测

significant=(Δ>ϵ) or (range>range\_threshold)

其中:

  • Δ=∣current−previous∣是两次采样的差值

  • \epsilon是噪声门限或动态变化门限,通常通过噪声估计(EWMA)来计算

  1. TTL 更新

TTL = \begin{cases}\text{min\_ttl} & \text{if significant change (accelerated refresh)} \\\text{TTL} \times 1.5 & \text{if stable and not significant}\end{cases}

这样,TTL 在 显著变化时会被缩短(0.5倍系数),在 稳定时逐渐延长(1.5倍系数),但仍然在设置的min_ttl ~ max_ttl内。系数可以按需调整

比如:

  1. 当发现温度在一段时间t内变化很小,那下一次的缓存刷新时间就变成1.5t,

  2. 然后再1.5t时间内如果发现仍然变化很小,则再下一次的缓存刷新时间就变成了1.5t*1.5,

  3. 然后再1.5t*1.5时间内,发现缓存刷新时间很显著,则下一次缓存刷新时间变为 1.5t*1.5 / 2

当然所有的刷新时间都必须满足在min_ttl和max_ttl之内!

自适应刷新伪代码

// 获取当前的显著变化(delta)和噪声估计值
delta = abs(current_value - previous_value);
noise = calculate_noise_estimate();

// 计算当前的门限(阈值),根据噪声门限和窗口振幅动态调整
eps = max(abs_eps_floor, k_noise * noise);
range = calculate_window_range();

// 判断是否为显著变化
if (delta > eps || range > range_eps_floor) {
    // 显著变化:加速刷新,缩短 TTL
    ttl = max(min_ttl, ttl / 2);  // 缩短 TTL
    stable_count = 0;  // 重置稳定计数
} else {
    // 稳定状态:延长 TTL,减少刷新频率
    stable_count++;
    if (stable_count >= 3) {  // 3次稳定才延长TTL
        ttl = min(max_ttl, ttl + (ttl / 2));  // 延长 TTL
    }
}

// 更新缓存
update_cache(ttl, current_value);

噪声门限算法

噪声门限算法的目标是避免将小的测量误差、噪声或量化误差当作有效变化来进行处理。通过设定一个阈值(eps),当属性值变化小于该阈值时,认为其变化不足以触发刷新的操作。

噪声门限的作用

  • 过滤噪声:由于 ADC、传感器或者电路噪声,电流/电压等信号可能会产生小幅度波动,这些波动不代表实际的系统状态变化,通过噪声门限来避免这些微小的变化触发刷新。

  • 减少不必要的刷新:如果每次微小变化都触发 I2C 请求,会导致 I2C 总线的拥塞。噪声门限能够有效减少这种情况。

噪声门限算法原理

  • 噪声门限的基本思想是,通过计算变化值 delta(即当前值与前一个值的差异),与设定的门限值 eps 进行比较。

  • delta > eps 时,认为发生了显著变化,需要刷新缓存。

  • delta <= eps 时,认为变化太小,可以忽略,不进行刷新。

算法公式

  1. 噪声估计(EWMA)

\text{noise}_{\text{new}} = \frac{\Delta - \text{noise}}{8} + \text{noise}

这里Δ是当前值与前一个值的差异,noise是当前的噪声估计。

1/8的平滑系数可以改动,是一个暂时合理的数字,具体还是需要在实际中调整

  • 过大的平滑系数会使得噪声估计对短期波动反应过于敏感,导致噪声门限过低,有可能会触发误判

  • 过小的平滑系数会使得噪声估计对短期波动反应无反应,导致慢变的真实反映被平滑掉,系统无法相应这样的变化

  1. 门限计算

ϵ=max(abs\_eps\_floor,knoise×noise)

  其中:

  • \epsilon是计算出的门限,用于判断是否触发刷新。

  • \text{abs\_eps\_floor}是设置的最低门限,防止门限过小导致不必要的触发。

EWMA(指数加权移动平均):用于计算当前的噪声估计,避免瞬时波动对系统的影响。通过 k_noise 参数来控制噪声敏感度,k_noise 越大,系统对噪声的敏感度越低。

  1. 变化显著判定

\text{significant} = \Delta > \epsilon

只有当 变化幅度 delta 大于门限 $$\epsilon$$,才认为是 显著变化

噪声门限算法伪代码

// 更新噪声估计(采用EWMA)
noise = (current_delta - noise) / 8 + noise;

// 计算当前的噪声门限
eps = max(abs_eps_floor, k_noise * noise);

// 检测变化是否显著
if (abs(current_value - previous_value) > eps) {
    // 发生显著变化
    update_cache(current_value);
}

窗口振幅算法

窗口振幅算法的作用是检测属性的 累计变化即使单步的变化 delta 小于噪声门限,如果多次小的变化累计到一定程度,也应该触发刷新

窗口振幅的作用

  • 检测慢变变化:例如,充电电流可能逐步增加,而每次变化都很小。虽然这些变化小于噪声门限,但它们的累计效果可能对系统有影响。通过窗口振幅,我们可以检测到这些慢变的趋势。

  • 避免TTL过度延长:在某些情况下,尽管变化幅度很小,但如果变化累积起来,它可能会导致系统状态发生显著变化。窗口振幅能够确保系统能够及时响应这些变化。

窗口振幅算法原理

  • 在每次属性值变化时,都会将当前值与历史值存入一个滑动窗口。

  • 通过计算窗口内的最大值和最小值,得到窗口振幅(range = max - min)。

  • 如果窗口振幅超过设定的阈值,则认为发生了显著变化,并触发刷新

算法公式

  1. 窗口内值更新

\text{window}[i] = \text{current\_value}, \quad \text{win\_pos} = (win\_pos + 1) \% \text{win\_size}

每次有新值时,都将该值插入滑动窗口,并更新窗口位置。

  1. 窗口振幅计算

range=max(window)−min(window)

计算滑动窗口内的 最大值与最小值的差,作为变化的幅度。

  1. 显著变化判定

significant=range>range\_threshold

如果 窗口振幅 超过了设定的阈值,认为发生了显著变化,触发刷新。

窗口振幅算法伪代码

// 更新滑动窗口
push_to_window(current_value);

// 计算窗口振幅
range = window_max - window_min;

// 如果振幅超过阈值,认为是显著变化
if (range > range_eps_floor) {
    // 发生显著变化
    update_cache(current_value);
}

引擎适配

引擎驱动

charger_property_engine.ko已在本地编译成功

此处内容已隐藏,「密码可见」