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

打印 上一主题 下一主题

主题 1941|帖子 1941|积分 5823

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. 源由

无线RF链路受到比较多因素的影响,且在高速FPV机动过程中,必要高效的调整适应环境,是一项非常困难的任务。
越是困难就越是有挑战,如何理论与实践相结合,必要非常多的积极。但是,起首还是要理解内涵关系。
本章就是结合最新v0.60.0版原来看下当前实现的逻辑关系,以及结合代码和实测,分析下一些现象,以及有可能改善的点位。
2. 思路

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


  • “辩说”弃包
  • “遮挡”丢包
  • “FEC”丢包
  • “线性”丢包

  • 吸取端无法区分是因为发射端弃包导致的丢包,还是发射出来的包,因为传输题目导致的丢包。
  • 吸取端发现丢包,从时间角度来说,已经是后知后觉了。
  • 如果可以或许更好的使用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_MAX−RSSI_MINbest_rssi−RSSI_MIN​))
这个公式的作用是将 best_rssi 归一化到 [0,1] 之间,保证不会超出范围。
  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_MAX−SNR_MINbest_snr−SNR_MIN​))
这个公式的作用是将 best_snr 归一化到 [0,1] 之间,确保不会超出范围。
  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 的权重盘算一个归一化得分,用于权衡信号质量的综合指标。
  1.     score_normalized = (snr_weight * snr_normalized) + (rssi_weight * rssi_normalized)
  2.     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=fecn​−feck​
  • 盘算加权因子
                                                  weight                               =                                           6.0                                               1                                     +                                     redundancy                                                             \text{weight} = \frac{6.0}{1 + \text{redundancy}}                        weight=1+redundancy6.0​

    • 6.0 是一个调节因子,它确保当 fec_k = 8 和 fec_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 的影响。
  1. def adjust_fec_recovered(fec_rec, fec_k, fec_n):
  2.     """
  3.     If redundancy is high (fec_n - fec_k is large), then we expect more fec_rec,
  4.     so its contribution is reduced.
  5.     """
  6.     if fec_k is None or fec_n is None or fec_n == 0:
  7.         return fec_rec  # fallback if values are not available
  8.     redundancy = fec_n - fec_k
  9.     weight = 6.0 / (1 + redundancy)  # 6 makes 8/12 fec neutral
  10.     return fec_rec * weight
复制代码
2.3 遮挡丢包

“遮挡”丢包,大抵的意思就是信号急剧衰减,导致出现刹时丢包,丢包率陡然上升。
在这个过程中,可以想象一些可能的情况:

  • FEC直接介入,恢复数据包数量陡然增长;
  • 随着进一步衰减,纵然FEC可以或许恢复一部分数据包,仍旧出现丢包;
  • 吸取端必要哀求关键帧,以求得恢复画面;应对本领,低落比特率,低落k/n比率,增长发射功率(提高穿透性),切换低带宽模式,增长GI值等本领;
  • 只要上述方法可以或许起到一定结果,就有机会恢复通讯,乃至视频;若继续恶化,那么就是劫难;
2.3.1 一维卡尔曼滤波

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


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

  • 预测(Prediction)
    根据前一次估计值,预测当前状态。
  • 更新(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​

  • 预测阶段(Prediction)
                                                                           x                                     ^                                              t                                  −                                          =                                                        x                                     ^                                                           t                                     −                                     1                                                             \hat{x}_t^- = \hat{x}_{t-1}                        x^t−​=x^t−1​
                                                              P                                  t                                  −                                          =                                           P                                               t                                     −                                     1                                                      +                               Q                                      P_t^- = P_{t-1} + Q                        Pt−​=Pt−1​+Q
其中:


  •                                                                x                                  ^                                          t                               −                                            \hat{x}_t^-                     x^t−​ 继承上一次的估计值
  • 误差方差                                                    P                               t                               −                                            P_t^-                     Pt−​ 累计过程噪声                                         Q                                  Q                     Q(process_variance)

  • 盘算卡尔曼增益(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​ 大,更信托测量值。

  • 更新阶段(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​(zt​−x^t−​)
                                                              P                                  t                                          =                               (                               1                               −                                           K                                  t                                          )                                           P                                  t                                  −                                                 P_t = (1 - K_t) P_t^-                        Pt​=(1−Kt​)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​ 也会更新,表示新的不确定性。
  1. def kalman_filter_update(measurement):
  2.     global kalman_estimate, kalman_error_estimate
  3.     predicted_estimate = kalman_estimate
  4.     predicted_error = kalman_error_estimate + process_variance
  5.     kalman_gain = predicted_error / (predicted_error + measurement_variance)
  6.     kalman_estimate = predicted_estimate + kalman_gain * (measurement - predicted_estimate)
  7.     kalman_error_estimate = (1 - kalman_gain) * predicted_error
  8.     return kalman_estimate
复制代码
2.3.2 误码率

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

  • 基于噪声水平 (filtered_noise) 盘算一个扣分比例 (deduction_ratio),如果噪声低就不扣分,如果噪声高就大幅扣分。
  • 应用惩罚到 raw_score 盘算 final_score,保证分数不会直接变成 0,而是渐渐低落。
  • 盘算最终惩罚量 penalty,方便后续使用。


  • Step 1: 调整 FEC 恢复数据

    • 通过 adjust_fec_recovered() 函数对 fec_rec_packets 举行加权调整,以补偿 FEC 的冗余度对统计数据的影响。
    • 目的是: 防止高冗余度时 fec_rec_packets 影响过大,导致误判误码情况。

  1. # Adjust the fec_rec_packets contribution based on FEC settings
  2. 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​
  1. # Now calculate the error ratio with the adjusted fec recovery value
  2. 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: 用卡尔曼滤波平滑误码率
  1. 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_noise−min_noisefiltered_noise−min_noise​)deduction_exponent


  • min() 限制最大值为 1.0,避免扣分比例凌驾 100%。
  • 这个公式的结果是:

    • 噪声靠近 min_noise → 惩罚靠近 0,不举行惩罚,deduction_ratio = 0.0。
    • 噪声在 min_noise 和 max_noise之间时,盘算扣分比例。
    • 噪声靠近 max_noise → 惩罚靠近 1.0。
    • deduction_exponent 控制变化速率,如果指数大于 1,低噪声时影响较小,高噪声时影响加剧

  1. if filtered_noise < min_noise:
  2.     deduction_ratio = 0.0
  3. else:
  4.     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_score−1000)×(1−deduction_ratio)
这个盘算方式确保纵然有扣分,最终分数不会低于 1000也不会直接把分数归零
  1. 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)
  1. penalty = (final_score - raw_score) if allow_penalty else 0
复制代码


  • 如果 allow_penalty = True,盘算最终得分相比原始分数的下降值(负数)。
  • 如果 allow_penalty = False,penalty = 0(无扣分)。
2.4 辩说弃包

略,暂时无上述应对方案
3. 应用方法

3.1 前向纠错 (FEC) 级别调整

这就是之条件到的应对本领之一:“低落比特率,低落k/n比率,增长发射功率(提高穿透性),切换低带宽模式,增长GI值等本领”
核心逻辑是根据噪声 (filtered_noise) 盘算 FEC(前向纠错)调整值,决定是否增长 FEC 保护强度,以提高数据恢复能力。
  1.     # FEC change logic  
  2.     fec_change = (
  3.         0 if not allow_fec_increase or filtered_noise <= min_noise_for_fec_change else  
  4.         5 if filtered_noise >= noise_for_max_fec_change else  
  5.         int(round(((filtered_noise - min_noise_for_fec_change) / (max_noise - min_noise_for_fec_change)) * 5))
  6.   
  7.     )
复制代码
其作用是根据噪声水平 (filtered_noise) 动态调整 FEC 保护

  • 如果不允许增长 FEC 或噪声太低不调整 FEC (fec_change = 0)
  • 如果噪声特别高FEC 保护达到最大 (fec_change = 5)
  • 如果噪声在 min_noise_for_fec_change 和 max_noise_for_fec_change 之间盘算 FEC 变化量 (fec_change 介于 0 到 5 之间,呈线性增长)
转换后的代码如下:
  1.     # FEC change logic  
  2.     if not allow_fec_increase or filtered_noise <= min_noise_for_fec_change:
  3.         fec_change = 0  # 不允许增加 FEC 或者噪声低于调整阈值,则不改变 FEC
  4.     elif filtered_noise >= noise_for_max_fec_change:
  5.         fec_change = 5  # 噪声大于最大 FEC 变化阈值,FEC 设为最大值
  6.     else:
  7.         # 计算 FEC 变化量,按照噪声水平线性调整
  8.         fec_change = int(round(((filtered_noise - min_noise_for_fec_change) /
  9.                                 (max_noise - min_noise_for_fec_change)) * 5))
复制代码


  • Step 1: 判定是否允许增长 FEC
  1. 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 = False 或 filtered_noise 低于 min_noise_for_fec_change,则 FEC 变化量 (fec_change) 设为 0(不调整 FEC)。

  • Step 2: 确定最大 FEC 变化
  1. 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 变化
  1. 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_noise−min_noise_for_fec_changefiltered_noise−min_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_noise−min_noise_for_fec_changefiltered_noise−min_noise_for_fec_change​)×5

    • 如果噪声靠近 min_noise_for_fec_change,则 FEC 变化靠近 0。
    • 如果噪声靠近 max_noise_for_fec_change,则 FEC 变化靠近 5。

  • int(round(...)) 取整,确保 FEC 变化是整数值
3.2 bitrate/K/N动态调整

动态调整前向纠错 (FEC) 参数和比特率 (bitrate),确保在不同场景下优化 FEC 设置和带宽分配。

  • 盘算新的比特率和 FEC


  • fec_change 变量控制 FEC 变化级别,取值范围 1~5,影响 new_bitrate 的盘算:

    • 通过 denominators[] 数组,new_bitrate 变为 new_bitrate / denominator

  • fec_k_adjust 变量决定 FEC 的调整方式:

    • 若为 true,则 new_fec_k 变小 (new_fec_k /= denominator)
    • 若为 false,则 new_fec_n 变大 (new_fec_n *= denominator)


  • 更新全局 FEC OSD 信息
  1. snprintf(global_profile_fec_osd, sizeof(global_profile_fec_osd), "%d/%d", new_fec_k, new_fec_n);
复制代码


  • global_profile_fec_osd 存储当前 FEC 设置 (k/n 格式)。

  • 按比特率变化决定 FEC 和 Bitrate 调整序次
代码中存在两种情况:

  • 比特率增长 (new_bitrate > old_bitrate) → 先调整 FEC,再调整 Bitrate
  • 比特率减少 (new_bitrate <= old_bitrate) → 先调整 Bitrate,再调整 FEC
在每种情况下:


  • format_command() 负责将下令格式化为字符串
  • execute_command() 执行下令
示例格式
  1. format_command(fecCommand, sizeof(fecCommand), fecCommandTemplate, 2, fecKeys, fecValues);
  2. execute_command(fecCommand);
复制代码


  • fecCommandTemplate 是 FEC 设置下令模板
  • bitrateCommandTemplate 是比特率设置下令模板

  • 记录旧值


  • old_fec_k = new_fec_k;
  • old_fec_n = new_fec_n;
  • old_bitrate = new_bitrate;
    确保后续调整时有历史数据可比较。
  1. void manage_fec_and_bitrate(int new_fec_k, int new_fec_n, int new_bitrate) {    char fecCommand[MAX_COMMAND_SIZE];    char bitrateCommand[MAX_COMMAND_SIZE];    // Adjust fec and bitrate based on fec_change (if applicable)    if (allow_dynamic_fec && fec_change > 0 && fec_change <= 5) {        float denominators[] = { 1, 1.11111, 1.25, 1.42, 1.66667, 2.0 };        float denominator = denominators[fec_change];        new_bitrate = (int)(new_bitrate / denominator);        // divide k or multiply n depending on fec_k_adjust option                (fec_k_adjust) ? (new_fec_k /= denominator) : (new_fec_n *= denominator);    }        // Update the global FEC OSD regardless of order.    snprintf(global_profile_fec_osd, sizeof(global_profile_fec_osd), "%d/%d", new_fec_k, new_fec_n);
  2.         // If increasing bitrate, change FEC first; otherwise, bitrate first.    if (new_bitrate > old_bitrate) {        // Format fecCommand        const char *fecKeys[] = { "fecK", "fecN" };        char strFecK[10], strFecN[10];        snprintf(strFecK, sizeof(strFecK), "%d", new_fec_k);        snprintf(strFecN, sizeof(strFecN), "%d", new_fec_n);        const char *fecValues[] = { strFecK, strFecN };        format_command(fecCommand, sizeof(fecCommand), fecCommandTemplate, 2, fecKeys, fecValues);        execute_command(fecCommand);        old_fec_k = new_fec_k;        old_fec_n = new_fec_n;                // Format bitrateCommand        const char *brKeys[] = { "bitrate" };        char strBitrate[12];        snprintf(strBitrate, sizeof(strBitrate), "%d", new_bitrate);        const char *brValues[] = { strBitrate };        format_command(bitrateCommand, sizeof(bitrateCommand), bitrateCommandTemplate, 1, brKeys, brValues);        execute_command(bitrateCommand);        old_bitrate = new_bitrate;    } else {        // Format bitrateCommand first        const char *brKeys[] = { "bitrate" };        char strBitrate[12];        snprintf(strBitrate, sizeof(strBitrate), "%d", new_bitrate);        const char *brValues[] = { strBitrate };        format_command(bitrateCommand, sizeof(bitrateCommand), bitrateCommandTemplate, 1, brKeys, brValues);        execute_command(bitrateCommand);        old_bitrate = new_bitrate;                // Then format fecCommand        const char *fecKeys[] = { "fecK", "fecN" };        char strFecK[10], strFecN[10];        snprintf(strFecK, sizeof(strFecK), "%d", new_fec_k);        snprintf(strFecN, sizeof(strFecN), "%d", new_fec_n);        const char *fecValues[] = { strFecK, strFecN };        format_command(fecCommand, sizeof(fecCommand), fecCommandTemplate, 2, fecKeys, fecValues);        execute_command(fecCommand);        old_fec_k = new_fec_k;        old_fec_n = new_fec_n;    }}
复制代码
3.3 迟滞稳定


  • 盘算当前值 (combined_value) 与上次使用的值 (last_value_sent) 之间的百分比变化 (percent_change)
  • 选择滞后阈值 (hysteresis_threshold)

    • 上升趋势 用 hysteresis_percent。
    • 下降趋势 用 hysteresis_percent_down。

  • 如果百分比变化凌驾滞后阈值,尝试切换到新的 profile:

    • 若成功,更新 last_value_sent 和 last_exec_time。

  1.     // Calculate percentage change from smoothed baseline value
  2.     float percent_change = fabs((float)(combined_value - last_value_sent) / last_value_sent) * 100;
  3.     // Determine which hysteresis threshold to use (up or down)
  4.     float hysteresis_threshold = (combined_value >= last_value_sent) ? hysteresis_percent : hysteresis_percent_down;
  5.     // Check if the change exceeds the chosen hysteresis threshold
  6.     if (percent_change >= hysteresis_threshold) {
  7.         printf("Qualified to request profile: %d is > %.2f%% different (%.2f%%)\n", combined_value, hysteresis_threshold, percent_change);
  8.         // Request profile, check if applied
  9.         if (value_chooses_profile(combined_value)) {
  10.             printf("Profile %d applied.\n", combined_value);
  11.             last_value_sent = combined_value;
  12.             last_exec_time = current_time;
  13.         }
  14.     }
复制代码
4. 场景事件



  • RX端发现真实丢包,那必须哀求关键帧
  1.     # Start or override a keyframe request if necessary
  2.     if lost_packets > 0 and allow_idr:
  3.         keyframe_request_code = ''.join(random.choices(string.ascii_lowercase, k=4))
  4.         keyframe_request_remaining = idr_max_messages
  5.         if verbose_mode:
  6.             print(f"Generated new keyframe request code: {keyframe_request_code}")
复制代码


  • RX端初次吸取视频,必要关键帧
  1.     if receiving_video:
  2.         # When video transmission starts, trigger a keyframe request.
  3.         # This block runs only on the first video stat update after a period of no video.
  4.         if not video_rx_initial_message_printed:
  5.             print("\nReceiving video_rx stats\nWorking...")
  6.             video_rx_initial_message_printed = True
  7.             # Always request a keyframe when video starts
  8.             keyframe_request_code = ''.join(random.choices(string.ascii_lowercase, k=4))
  9.             keyframe_request_remaining = idr_max_messages
  10.             if verbose_mode:
  11.                 print(f"Generated new keyframe request code on video start: {keyframe_request_code}")
复制代码
5. 关键题目


  • “线性”丢包在可视间隔(LOS, Line Of Sight)上是否可以或许维持链路稳定?
  • “FEC”丢包盘算公式中的调节因子6是否公道?出处来自何种理论或者履历逻辑?
  • “遮挡”丢包的惩罚盘算公式指数式噪音衰减模型是否符合穿越障碍物场景?
  • “辩说”弃包为何没有接纳?从MAC驱动角度看,发射端可以或许使用CSMA/CA硬件特性,可以公道的接纳措施,比如:增长功率,低落比特率等
  • 前向纠错级别调整以及与bitrate一起调整的逻辑关联关系是什么?以何种模型举行调整?
6. 现实结果



  • FPV Interference Scenarios - v0.58.0
  • FPV Interference Scenarios - v0.60.0
7. 参考资料

【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信号干扰

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

钜形不锈钢水箱

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表