当前位置: 首页 > news >正文

OpenIPC开源FPV之Adaptive-Link新版本算法v0.60.0

OpenIPC开源FPV之Adaptive-Link新版本算法v0.60.0

  • 1. 源由
  • 2. 思路
    • 2.1 线性丢包
      • 2.1.1 线性RSSI归一
      • 2.1.2 线性SNR归一
      • 2.1.3 线性加权平均
    • 2.2 FEC丢包
    • 2.3 遮挡丢包
      • 2.3.1 一维卡尔曼滤波
      • 2.3.2 误码率
      • 2.3.3 惩罚机制
    • 2.4 FEC动态调整
    • 2.5 冲突弃包
  • 3. 场景事件
  • 4. 关键问题
  • 5. 实际效果
  • 6. 参考资料

1. 源由

无线RF链路受到比较多因素的影响,且在高速FPV机动过程中,需要高效的调整适应环境,是一项非常艰巨的任务。

越是艰巨就越是有挑战,如何理论与实践相结合,需要非常多的努力。但是,首先还是要理解内在关系。

本章就是结合最新v0.60.0版本来看下当前实现的逻辑关系,以及结合代码和实测,分析下一些现象,以及有可能改善的点位。

2. 思路

结合之前《OpenIPC开源FPV之Adaptive-Link信号干扰》,分析给出的思路:

  • “冲突”弃包
  • “遮挡”丢包
  • “FEC”丢包
  • “线性”丢包
  1. 接收端无法区分是因为发射端弃包导致的丢包,还是发射出来的包,因为传输问题导致的丢包。
  2. 接收端发现丢包,从时间角度来说,已经是后知后觉了。
  3. 如果能够更好的利用CSMA/CA特性,就有机会当由于外界干扰而导致弃包时的快速主动应对。

注1:上述“冲突”、“遮挡”、“FEC”、“线性”可能表达上面并非专业或者描述正确,简单表意,大家理解,消化下。
注2:接下来v0.60.0将按照这些分类来讨论,期望能够进一步理解RF链路信号质量,以及相关的一些概念。

2.1 线性丢包

“线性”丢包,大致的意思就是随着信号的衰减,并且伴随外界环境底噪的影响(通俗说信噪比),随着距离的增加,能量以半径平方的关系逐步衰减(这个是能量三维空间衰减模型)。

注:当然,天线也并非各项同性,苹果图也有各种形状,性能也各有差异,还有各种定向,甚至跟随天线,所以情况是比较复杂的。

2.1.1 线性RSSI归一

rssi_normalized = max ⁡ ( 0 , min ⁡ ( 1 , best_rssi − RSSI_MIN RSSI_MAX − RSSI_MIN ) ) \text{rssi\_normalized} = \max\left(0, \min\left(1, \frac{\text{best\_rssi} - \text{RSSI\_MIN}}{\text{RSSI\_MAX} - \text{RSSI\_MIN}}\right)\right) rssi_normalized=max(0,min(1,RSSI_MAXRSSI_MINbest_rssiRSSI_MIN))

这个公式的作用是best_rssi 归一化到 [0,1] 之间,保证不会超出范围。

    rssi_normalized = max(0, min(1, (best_rssi - RSSI_MIN) / (RSSI_MAX - RSSI_MIN)))

2.1.2 线性SNR归一

snr_normalized = max ⁡ ( 0 , min ⁡ ( 1 , best_snr − SNR_MIN SNR_MAX − SNR_MIN ) ) \text{snr\_normalized} = \max\left(0, \min\left(1, \frac{\text{best\_snr} - \text{SNR\_MIN}}{\text{SNR\_MAX} - \text{SNR\_MIN}}\right)\right) snr_normalized=max(0,min(1,SNR_MAXSNR_MINbest_snrSNR_MIN))

这个公式的作用是best_snr 归一化到 [0,1] 之间,确保不会超出范围。

    snr_normalized = max(0, min(1, (best_snr - SNR_MIN) / (SNR_MAX - SNR_MIN)))

2.1.3 线性加权平均

score_normalized = ( snr_weight × snr_normalized ) + ( rssi_weight × rssi_normalized ) \text{score\_normalized} = (\text{snr\_weight} \times \text{snr\_normalized}) + (\text{rssi\_weight} \times \text{rssi\_normalized}) score_normalized=(snr_weight×snr_normalized)+(rssi_weight×rssi_normalized)

这个公式的作用是根据 SNR 和 RSSI 的权重计算一个归一化得分,用于衡量信号质量的综合指标。

    score_normalized = (snr_weight * snr_normalized) + (rssi_weight * rssi_normalized)
    raw_score = 1000 + score_normalized * 1000

2.2 FEC丢包

FEC(前向纠错)是一种增加冗余数据以提高可靠性的技术,目的是当 FEC 冗余度较高时,减少 fec_rec 的贡献,以防止其对系统状态造成过大的影响。

这是一种介于“线性”丢包和“遮挡”丢包的一种临界状态,因此,在“遮挡”丢包会引入卡尔曼滤波来权衡“线性”丢包和“遮挡”丢包。

  • fec_k: 原始数据块的数量
  • fec_n: 发送的总数据块数量(包含冗余)
  • fec_n - fec_k: 额外添加的冗余数据块数量

如果冗余度 fec_n - fec_k 较大,说明 FEC 机制提供了更多的冗余数据,因此即使恢复了较多的 fec_rec,它的贡献也应该适当减少,以防止统计上过度依赖恢复数据。

数学原理

  • 计算冗余度
    redundancy = fec n − fec k \text{redundancy} = \text{fec}_n - \text{fec}_k redundancy=fecnfeck
  • 计算加权因子
    weight = 6.0 1 + redundancy \text{weight} = \frac{6.0}{1 + \text{redundancy}} weight=1+redundancy6.0
    • 6.0 是一个调节因子,它确保当 fec_k = 8fec_n = 12(即 redundancy = 4)时,权重接近 1.0(中性)。
    • 这个因子随着冗余度增加而递减,保证 fec_rec 在冗余较高的情况下不会被过度放大。
  • 调整后的 FEC 恢复值

adjusted fec_rec = fec_rec × weight \text{adjusted fec\_rec} = \text{fec\_rec} \times \text{weight} adjusted fec_rec=fec_rec×weight

  • 如果冗余较小,则 weight 近似 6.0,保持较大权重。
  • 如果冗余较大,则 weight 变小,降低 fec_rec 的影响。
def adjust_fec_recovered(fec_rec, fec_k, fec_n):
    """
    If redundancy is high (fec_n - fec_k is large), then we expect more fec_rec,
    so its contribution is reduced.
    """
    if fec_k is None or fec_n is None or fec_n == 0:
        return fec_rec  # fallback if values are not available

    redundancy = fec_n - fec_k
    weight = 6.0 / (1 + redundancy)  # 6 makes 8/12 fec neutral
    return fec_rec * weight

2.3 遮挡丢包

“遮挡”丢包,大致的意思就是信号急剧衰减,导致出现瞬间丢包,丢包率陡然上升。

在这个过程中,可以想象一些可能的情况:

  1. FEC直接介入,恢复数据包数量陡然增加;
  2. 随着进一步衰减,即使FEC能够恢复一部分数据包,仍然出现丢包;
  3. 接收端需要请求关键帧,以求得恢复画面;应对手段,降低比特率,降低k/n比率,增加发射功率(提高穿透性),切换低带宽模式,增加GI值等手段;
  4. 只要上述方法能够起到一定效果,就有机会恢复通讯,甚至视频;若继续恶化,那么就是灾难;

2.3.1 一维卡尔曼滤波

卡尔曼滤波是一种基于贝叶斯估计的递归最优估计方法,通常用于对带噪声的测量数据进行状态估计

  • 当测量值变化剧烈时,卡尔曼增益会调整,使估计值能更快跟随变化。
  • 当测量值噪声较大时,卡尔曼滤波会更依赖于历史预测值,减少波动。

其基本步骤包括:

  1. 预测(Prediction)
    根据前一次估计值,预测当前状态。
  2. 更新(Correction/Update)
    结合新的测量值,更新估计值,使其更加准确。

数学原理

假设:

  • 真实状态值: x t x_t xt
  • 预测状态: x ^ t − \hat{x}_t^- x^t(当前的预测值)
  • 预测误差方差: P t − P_t^- Pt(预测的不确定性)
  • 观测值: z t z_t zt(测量值)
  • 观测噪声方差: R R R(测量的不确定性)
  • 过程噪声方差: Q Q Q(模型的不确定性)
  • 更新后状态估计: x ^ t \hat{x}_t x^t
  • 更新后误差方差: P t P_t Pt
  1. 预测阶段(Prediction)
    x ^ t − = x ^ t − 1 \hat{x}_t^- = \hat{x}_{t-1} x^t=x^t1
    P t − = P t − 1 + Q P_t^- = P_{t-1} + Q Pt=Pt1+Q

其中:

  • x ^ t − \hat{x}_t^- x^t 继承上一次的估计值
  • 误差方差 P t − P_t^- Pt 累计过程噪声 Q Q Qprocess_variance
  1. 计算卡尔曼增益(Kalman Gain)
    K t = P t − P t − + R K_t = \frac{P_t^-}{P_t^- + R} Kt=Pt+RPt
  • 这里的 K t K_t Kt 是卡尔曼增益,用于平衡预测值与测量值之间的影响。
  • 当测量噪声较大( R R R 大), K t K_t Kt 小,更信任预测值。
  • 当测量噪声较小( R R R 小), K t K_t Kt 大,更信任测量值。
  1. 更新阶段(Correction)
    x ^ t = x ^ t − + K t ( z t − x ^ t − ) \hat{x}_t = \hat{x}_t^- + K_t (z_t - \hat{x}_t^-) x^t=x^t+Kt(ztx^t)
    P t = ( 1 − K t ) P t − P_t = (1 - K_t) P_t^- Pt=(1Kt)Pt
  • 新的状态估计值 x ^ t \hat{x}_t x^t 是在预测值 x ^ t − \hat{x}_t^- x^t 基础上,按照增益 K t K_t Kt 调整误差(测量值与预测值的差)。
  • 误差方差 P t P_t Pt 也会更新,表示新的不确定性。
def kalman_filter_update(measurement):
    global kalman_estimate, kalman_error_estimate

    predicted_estimate = kalman_estimate
    predicted_error = kalman_error_estimate + process_variance

    kalman_gain = predicted_error / (predicted_error + measurement_variance)

    kalman_estimate = predicted_estimate + kalman_gain * (measurement - predicted_estimate)
    kalman_error_estimate = (1 - kalman_gain) * predicted_error

    return kalman_estimate

2.3.2 误码率

结合 FEC(前向纠错)和卡尔曼滤波(Kalman Filter),计算数据传输过程中的误码率,并对其进行平滑处理

  1. 基于噪声水平 (filtered_noise) 计算一个扣分比例 (deduction_ratio),如果噪声低就不扣分,如果噪声高就大幅扣分。
  2. 应用惩罚到 raw_score 计算 final_score,保证分数不会直接变成 0,而是逐步降低。
  3. 计算最终惩罚量 penalty,方便后续使用。
  • Step 1: 调整 FEC 恢复数据
    • 通过 adjust_fec_recovered() 函数对 fec_rec_packets 进行加权调整,以补偿 FEC 的冗余度对统计数据的影响。
    • 目的是: 防止高冗余度时 fec_rec_packets 影响过大,导致误判误码情况。
# Adjust the fec_rec_packets contribution based on FEC settings
adjusted_fec_rec = adjust_fec_recovered(fec_rec_packets, fec_k, fec_n)
  • Step 2: 计算误码率(Error Ratio)
    error_ratio = 5 × lost_packets + adjusted_fec_rec all_packets / num_antennas \text{error\_ratio} = \frac{5 \times \text{lost\_packets} + \text{adjusted\_fec\_rec}}{\text{all\_packets} / \text{num\_antennas}} error_ratio=all_packets/num_antennas5×lost_packets+adjusted_fec_rec
# Now calculate the error ratio with the adjusted fec recovery value
error_ratio = (5 * lost_packets + adjusted_fec_rec) / (all_packets / num_antennas)

其中:

  • lost_packets 是丢失的数据包数。

  • adjusted_fec_rec 是调整后的 FEC 恢复数据包数(经过 adjust_fec_recovered() 处理),减少了高冗余情况对统计的影响。

  • all_packets 是所有接收的数据包数。

  • num_antennas 代表接收天线的数量,分母计算时做了归一化处理。

  • 5 * lost_packets 5用于放大丢失包的影响

  • 最终计算的是一个归一化的误码率 error_ratio,表示数据传输的丢包情况,可能用于后续决策。

  • Step 3: 用卡尔曼滤波平滑误码率

filtered_noise = kalman_filter_update(error_ratio)

其中:

  • kalman_filter_update(error_ratio) 通过卡尔曼滤波error_ratio 进行平滑处理,减少短时波动带来的影响。
  • 由于 error_ratio 可能会有突变或者噪声干扰,卡尔曼滤波可以提供一个更稳定的估计值 filtered_noise
  • 这个平滑后的误码率 filtered_noise 可以用于动态调整 FEC 参数、调整传输策略,甚至用于 QoS(服务质量)优化。

2.3.3 惩罚机制

核心逻辑是基于噪声水平对分数 (final_score) 进行惩罚,确保高噪声环境下的评分不会过高。

  • Step 1: 计算扣分比例 (deduction_ratio)

deduction_ratio = ( filtered_noise − min_noise max_noise − min_noise ) deduction_exponent \text{deduction\_ratio} = \left(\frac{\text{filtered\_noise} - \text{min\_noise}}{\text{max\_noise} - \text{min\_noise}}\right)^{\text{deduction\_exponent}} deduction_ratio=(max_noisemin_noisefiltered_noisemin_noise)deduction_exponent

  • min() 限制最大值为 1.0,避免扣分比例超过 100%。
  • 这个公式的效果是:
    • 噪声接近 min_noise → 惩罚接近 0,不进行惩罚,deduction_ratio = 0.0
    • 噪声在 min_noisemax_noise之间时,计算扣分比例。
    • 噪声接近 max_noise → 惩罚接近 1.0
    • deduction_exponent 控制变化速度,如果指数大于 1,低噪声时影响较小,高噪声时影响加剧
if filtered_noise < min_noise:
    deduction_ratio = 0.0
else:
    deduction_ratio = min(((filtered_noise - min_noise) / (max_noise - min_noise)) ** deduction_exponent, 1.0)
  • filtered_noise 是经过卡尔曼滤波的噪声水平。

  • min_noise 是噪声的最小阈值。

  • max_noise 是噪声的最大阈值。

  • deduction_exponent 控制惩罚力度(指数关系)。

  • Step 2: 计算最终得分 (final_score)

final_score = 1000 + ( raw_score − 1000 ) × ( 1 − deduction_ratio ) \text{final\_score} = 1000 + (\text{raw\_score} - 1000) \times (1 - \text{deduction\_ratio}) final_score=1000+(raw_score1000)×(1deduction_ratio)

这个计算方式确保即使有扣分,最终分数不会低于 1000也不会直接把分数归零

final_score = 1000 + (raw_score - 1000) * (1 - deduction_ratio) if allow_penalty else raw_score
  • raw_score 是原始分数,假设1000 是基准分

  • allow_penalty 是一个开关,决定是否应用惩罚

    • 如果 allow_penalty = False,则 final_score = raw_score,不做任何调整。
    • 如果 allow_penalty = True,则根据 deduction_ratio 进行扣分
  • Step 3: 计算最终惩罚量 (penalty)

penalty = (final_score - raw_score) if allow_penalty else 0
  • 如果 allow_penalty = True,计算最终得分相比原始分数的下降值(负数)。
  • 如果 allow_penalty = Falsepenalty = 0(无扣分)。

2.4 FEC动态调整

这就是之前提到的应对手段之一:“降低比特率,降低k/n比率,增加发射功率(提高穿透性),切换低带宽模式,增加GI值等手段”

核心逻辑是根据噪声 (filtered_noise) 计算 FEC(前向纠错)调整值,决定是否增加 FEC 保护强度,以提高数据恢复能力。

    # FEC change logic  
    fec_change = (
        0 if not allow_fec_increase or filtered_noise <= min_noise_for_fec_change else  
        5 if filtered_noise >= noise_for_max_fec_change else  
        int(round(((filtered_noise - min_noise_for_fec_change) / (max_noise - min_noise_for_fec_change)) * 5))  
    )

其作用是根据噪声水平 (filtered_noise) 动态调整 FEC 保护

  1. 如果不允许增加 FEC 或噪声太低不调整 FEC (fec_change = 0)
  2. 如果噪声特别高FEC 保护达到最大 (fec_change = 5)
  3. 如果噪声在 min_noise_for_fec_changemax_noise_for_fec_change 之间计算 FEC 变化量 (fec_change 介于 0 到 5 之间,呈线性增长)

转换后的代码如下:

    # FEC change logic  
    if not allow_fec_increase or filtered_noise <= min_noise_for_fec_change:
        fec_change = 0  # 不允许增加 FEC 或者噪声低于调整阈值,则不改变 FEC
    elif filtered_noise >= noise_for_max_fec_change:
        fec_change = 5  # 噪声大于最大 FEC 变化阈值,FEC 设为最大值
    else:
        # 计算 FEC 变化量,按照噪声水平线性调整
        fec_change = int(round(((filtered_noise - min_noise_for_fec_change) / 
                                (max_noise - min_noise_for_fec_change)) * 5))
  • Step 1: 判断是否允许增加 FEC
0 if not allow_fec_increase or filtered_noise <= min_noise_for_fec_change
  • allow_fec_increase 是一个开关,决定是否允许增加 FEC 保护。

  • filtered_noise 是当前测得的噪声水平。

  • min_noise_for_fec_change 是允许调整 FEC 的最小噪声阈值。

  • 逻辑:

    • 如果 allow_fec_increase = Falsefiltered_noise 低于 min_noise_for_fec_change,则 FEC 变化量 (fec_change) 设为 0(不调整 FEC)。
  • Step 2: 确定最大 FEC 变化

5 if filtered_noise >= noise_for_max_fec_change
  • noise_for_max_fec_change 是最大 FEC 变化的噪声阈值。

  • 如果 filtered_noise 高于 noise_for_max_fec_change,则 fec_change = 5(最大 FEC 保护)。

  • 这个逻辑表示当噪声非常高时,FEC 增强达到最大值,确保数据传输可靠性。

  • Step 3: 计算线性 FEC 变化

int(round(((filtered_noise - min_noise_for_fec_change) / (max_noise - min_noise_for_fec_change)) * 5))
  • 计算 FEC 变化比例
    filtered_noise − min_noise_for_fec_change max_noise − min_noise_for_fec_change \frac{\text{filtered\_noise} - \text{min\_noise\_for\_fec\_change}}{\text{max\_noise} - \text{min\_noise\_for\_fec\_change}} max_noisemin_noise_for_fec_changefiltered_noisemin_noise_for_fec_change
    • 归一化噪声水平,得到 0 到 1 之间的值(当噪声从 min_noise_for_fec_change 增加到 max_noise_for_fec_change)。
  • 乘以 5 以得到 FEC 调整量
    ( filtered_noise − min_noise_for_fec_change max_noise − min_noise_for_fec_change ) × 5 \left( \frac{\text{filtered\_noise} - \text{min\_noise\_for\_fec\_change}}{\text{max\_noise} - \text{min\_noise\_for\_fec\_change}} \right) \times 5 (max_noisemin_noise_for_fec_changefiltered_noisemin_noise_for_fec_change)×5
    • 如果噪声接近 min_noise_for_fec_change,则 FEC 变化接近 0
    • 如果噪声接近 max_noise_for_fec_change,则 FEC 变化接近 5
  • int(round(...)) 取整,确保 FEC 变化是整数值

2.5 冲突弃包

略,暂时无上述应对方案

3. 场景事件

  • RX端发现真实丢包,那必须请求关键帧
    # Start or override a keyframe request if necessary
    if lost_packets > 0 and allow_idr:
        keyframe_request_code = ''.join(random.choices(string.ascii_lowercase, k=4))
        keyframe_request_remaining = idr_max_messages
        if verbose_mode:
            print(f"Generated new keyframe request code: {keyframe_request_code}")
  • RX端初次接收视频,需要关键帧
    if receiving_video:
        # When video transmission starts, trigger a keyframe request.
        # This block runs only on the first video stat update after a period of no video.
        if not video_rx_initial_message_printed:
            print("\nReceiving video_rx stats\nWorking...")
            video_rx_initial_message_printed = True

            # Always request a keyframe when video starts
            keyframe_request_code = ''.join(random.choices(string.ascii_lowercase, k=4))
            keyframe_request_remaining = idr_max_messages
            if verbose_mode:
                print(f"Generated new keyframe request code on video start: {keyframe_request_code}")

4. 关键问题

  1. “线性”丢包在可视距离(LOS, Line Of Sight)上是否能够维持链路稳定?
  2. “FEC”丢包计算公式中的调节因子6是否合理?出处来自何种理论或者经验逻辑?
  3. “遮挡”丢包的惩罚计算公式指数式噪音衰减模型是否符合穿越障碍物场景?
  4. “冲突”弃包为何没有采用?从MAC驱动角度看,发射端能够利用CSMA/CA硬件特性,可以合理的采取措施,比如:增加功率,降低比特率等

5. 实际效果

  • FPV Interference Scenarios - v0.58.0
  • FPV Interference Scenarios - v0.60.0

6. 参考资料

【1】OpenIPC开源FPV之Adaptive-Link工程解析
【2】OpenIPC开源FPV之Adaptive-Link天空端代码解析
【3】OpenIPC开源FPV之Adaptive-Link地面站代码解析
【4】OpenIPC开源FPV之Adaptive-Link安装
【5】OpenIPC开源FPV之Adaptive-Link关键RF参数
【6】OpenIPC开源FPV之Adaptive-Link信号干扰

http://www.dtcms.com/a/107858.html

相关文章:

  • 强大而易用的JSON在线处理工具
  • python网络爬虫开发实战之Ajax数据提取
  • 链表的操作-反转链表
  • spring 设计模式、核心模块
  • 创建Linux虚拟环境并远程连接,finalshell自定义壁纸
  • 最短路径:Bellman-Ford算法
  • 抽象类与接口:特征与比较
  • 【实用技巧】电脑重装后的Office下载和设置
  • 自动化测试(web测试)
  • vue-系统登录后进入页面【el-input】输入框无法正常显示?需要手动刷新才可以恢复设置样式?让我来瞅瞅~
  • 关于 UPDATE 语句 和 SELECT ... FOR UPDATE 的对比分析,包括语法、功能、锁机制、使用场景及示例代码
  • java知识梳理(二)
  • 【电子通识】为什么电子元件的规格书常常要看英文版本
  • 从 Credit Metrics 到 CPV:现代信用风险模型的进化与挑战
  • Windows家庭版如何开启Hyper-V与关闭Hyper-V
  • 面试常考简单操作
  • ADS7822中文技术手册
  • Burp靶场 - HTTP走私请求【Part2】
  • Elasticsearch collapse 的使用场景及作用机制
  • Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践
  • es 集群存储字典 json字段----python实现
  • Conda安装ffmpeg
  • idea查看class字节码
  • Java高频面试题1:Java SE
  • SpaceX星舰商业载人首绕月球:私人太空旅行时代正式开启
  • mycat --分片规则--
  • [Android] 共生地球 v1.1.19 国产卫星地图
  • 详细介绍一下C++中的extern关键字
  • 搭建qemu环境
  • 【pcdet3D检测】——OPenpcdet如何进行测试文件配置?能否自定义测试数据?一文看懂pointpillar(pcdet)中的test.py