遥控器APP端自动重连方案

一、 遥控器链路通信拓扑分析

根据硬件原理图与架构图:

  1. 私有宽带链路(CX6531CD RF) :通过 PCIe/以太网 PHY(RTL8125/RTL8201)映射为 Android 系统级网络接口(如 eth0),负责 高带宽双向通信(RTSP/RTP 高清图传视频流 + UDP/TCP 遥测数传数据)。
  2. 独立安全控制链路(GD32 MCU) :摇杆(Hall sticks ≥\ge ≥ 200Hz)、E-stop 急停、按键直通 MCU,MCU 与 CX6531CD 直接走 SBUS/UART,同时与 SLM580 走 UART 通信。
  3. 备份网络(4G Cat.4 / Wi-Fi):系统标准移动蜂窝/WLAN 接口。

因此,APP 端面对的"链路中断"并非单一网络断开,而是多层级、多通道的复合状态异常。


二、 链路中断判定机制(多维联合判决)

为了防止网络偶发抖动导致的误判,APP 必须采用 "物理/驱动层 + 传输/应用层 + 视频流解码层" 的三层判定漏斗模型。

diff 复制代码
+------------------------------------------------------------------------+
| 物理/射频状态 (PHY Link Down, RSSI/SNR 跌零, CX6531 RF 丢包率 > 阈值)     | L1/L2
+-----------------------------------+------------------------------------+
                                    | 并发融合
+-----------------------------------v------------------------------------+
| 数传遥测心跳 (Heartbeat 超时, RTT 持续恶化, 序列号连续丢失)               | L4/L7
+-----------------------------------+------------------------------------+
                                    | 交叉验证
+-----------------------------------v------------------------------------+
| 图传流健康度 (Socket Read Timeout, PTS 停滞, 解码关键帧丢帧)             | Video Pipeline
+-----------------------------------+------------------------------------+
                                    | 决策输出
                         [ APP 状态机裁决 ]

1. 物理与驱动层判定(Fast Detection, ≤200ms\le 200\text{ms} ≤200ms)

  • 以太网/PHY 接口状态检测 :
    • Android 系统层:注册 ConnectivityManager.NetworkCallback 监听图传网口(eth0)的 onLost 与 onCapabilitiesChanged。
    • 底层 Netlink 广播/Sysfs 节点:监听 RTL8125/RTL8201 PHY 状态。若网线/内部通信链路物理断开(Link Down),系统服务瞬间捕获。
  • RF 射频链路健康指标监控(私有 HAL/AIDL 服务) :
    • 系统开发提供专有 System Service,定期(如 100ms100\text{ms} 100ms)轮询 CX6531CD 驱动节点,暴露:RSSI、SNR、RF Lock(锁频状态)、PER(误码率)。
    • 快速判定规则 :当 RF Lock == false 且连续 300ms300\text{ms} 300ms 未恢复,或 RSSI < -95 dBm 持续 500ms500\text{ms} 500ms,判定为 "RF 射频层脱网"。

2. 数传应用层判定(Heartbeat Sliding Window, ≤1000ms\le 1000\text{ms} ≤1000ms)

  • 双向自适应滑动窗口心跳 :
    • APP 与机载端保持周期性 Ping-Pong 心跳(默认 200ms200\text{ms} 200ms 一次)。
    • 引入 序列号(Seq ID)与滑动窗口,统计最近 10 个心跳周期的丢失率。
  • 分级告警与判定阈值 :
    • 弱网(Degraded) :RTT >300ms> 300\text{ms} >300ms 或连续丢失 2 个心跳包。
    • 中断(Link Lost) :连续丢失 4 个心跳包( ≥800ms\ge 800\text{ms} ≥800ms,工业级阈值推荐 800ms∼1.2s800\text{ms}\sim 1.2\text{s} 800ms∼1.2s)。

3. 视频流层判定(Video Pipeline Check, ≤1500ms\le 1500\text{ms} ≤1500ms)

  • Socket / 解码管道检测 :
    • RTSP/RTP Socket 设置 SO_RCVTIMEO 为 1.0s1.0\text{s} 1.0s。
    • MediaCodec 解码器 PTS 监控 :APP 维护一个 PTS Watchdog,当表面渲染纹理(SurfaceTexture)在 1.2s1.2\text{s} 1.2s 内未收到新的 Presentation Time Stamp 时,视为"图传冻结/中断"。

三、 APP 链路重连状态机设计(FSM)

APP 内部维护统一的通信链路状态机,避免无脑死循环重连导致系统资源耗尽或 UI 界面卡死。

scss 复制代码
   [ CONNECTED ]
         |
    (心跳连续丢失 / RF失锁)
         v
   [ LINK_WARNING ] (弱网降级: OSD 提示, 降低视频码率请求)
         |
    (超时达到 1.0s)
         v
   [ DISCONNECTED ] (链路中断: 触发 Fail-Safe 警告, 通知 MCU)
         |
    (启动重连策略)
         v
   [ RECONNECTING ] (退避重连, 通道探测)
    /              \
(成功恢复)      (连续 N 次重连失败)
  /                  \
[ CONNECTED ]      [ FAIL_SAFE_LOST ] (彻底失联: 引导搜星/返航/切 4G 应急)

状态转移与行为定义:

  1. CONNECTED(正常连接):全功能可用,实时更新 OSD 延迟、信号强度指示条。
  2. LINK_WARNING(链路劣化) :
    • 触发原因:连续丢包率 >30%> 30\% >30% 或延时持续增高。
    • APP 行为:主动向机载端发送降低分辨率/码率指令;UI 弹出黄色轻量弱网提示。
  3. DISCONNECTED(中断确认) :
    • 触发原因:心跳超时达到 1.0s1.0\text{s} 1.0s 或 RF Lock 丢失。
    • APP 行为:
      1. 画面冻结并置灰,叠加 "链路中断,正在重连..." HUD 警告;
      2. 触发震动马达(通过硬件原理图中的马达驱动 PWM 输出短震);
      3. 通过 UART 通知 Safety MCU(MCU 可接管或进入自主保活逻辑)。
  4. RECONNECTING(重连中) :
    • 采用 截断二进制指数退避算法(Exponential Backoff with Jitter) 进行重连,防止网络端突发广播风暴。
    • 重试间隔计算公式 : Twait=min⁡(Tmax⁡,  Tbase×2attempt)±RandomJitter T_{\text{wait}} = \min(T_{\max}, \; T_{\text{base}} \times 2^{\text{attempt}}) \pm \text{RandomJitter} Twait=min(Tmax,Tbase×2attempt)±RandomJitter 推荐参数 : Tbase=200ms T_{\text{base}} = 200\text{ms} Tbase=200ms, Tmax⁡=2000ms T_{\max} = 2000\text{ms} Tmax=2000ms, Jitter =±50ms= \pm 50\text{ms} =±50ms。
    • 重连步骤:
      1. 重新解析与握手 Control Socket(优先恢复遥测/控制指令);
      2. 发送 TEARDOWN -> SETUP -> PLAY 重建 RTSP 视频 Session;
      3. 强制请求机载编码器发送 IDR 关键帧。
  5. FAIL_SAFE_LOST(失控保护) :
    • 重连超过阈值(如 10s10\text{s} 10s):APP 端判定为远距离失联,自动切换或弹出 "是否切换 4G 蜂窝链路接管",界面切换为离线地图与机载设备最后已知 GPS 定位轨迹引导。

四、 核心代码架构实现方案

1. 链路状态仲裁管理器(Kotlin 协程架构)

kotlin 复制代码
class LinkQualityManager(
    private val context: Context,
    private val rfHalService: IRfHalService // 绑定高通SLM580系统层的AIDL服务
) {
    private val _linkState = MutableStateFlow<LinkState>(LinkState.Disconnected)
    val linkState: StateFlow<LinkState> = _linkState.asStateFlow()

    private var lastHeartbeatTimestamp = 0L
    private var lastPtsTimestamp = 0L
    private val HEARTBEAT_TIMEOUT_MS = 1000L
    private val PTS_STALL_TIMEOUT_MS = 1500L

    fun startMonitor(scope: CoroutineScope) {
        scope.launch(Dispatchers.Default) {
            while (isActive) {
                val now = SystemClock.elapsedRealtime()
                val rfInfo = rfHalService.currentRfInfo // 获取 CX6531 信号: RSSI, Lock, PER
                
                // 1. 物理层判定
                if (!rfInfo.isLocked) {
                    handleStateTransition(LinkState.Disconnected("RF Lock Lost"))
                    delay(200)
                    continue
                }

                // 2. 遥测应用层心跳判定
                val isHeartbeatTimeout = (now - lastHeartbeatTimestamp) > HEARTBEAT_TIMEOUT_MS

                // 3. 视频流 PTS 停滞判定
                val isVideoStalled = (now - lastPtsTimestamp) > PTS_STALL_TIMEOUT_MS

                when {
                    isHeartbeatTimeout -> {
                        handleStateTransition(LinkState.Disconnected("Heartbeat Timeout"))
                    }
                    isVideoStalled -> {
                        handleStateTransition(LinkState.Degraded("Video Stream Frozen"))
                    }
                    rfInfo.rssi < -90 -> {
                        handleStateTransition(LinkState.Degraded("Weak RF Signal"))
                    }
                    else -> {
                        handleStateTransition(LinkState.Connected)
                    }
                }
                delay(100) // 10Hz 轮询仲裁
            }
        }
    }

    fun onHeartbeatReceived(seqId: Long) {
        lastHeartbeatTimestamp = SystemClock.elapsedRealtime()
    }

    fun onVideoFrameDecoded(pts: Long) {
        lastPtsTimestamp = SystemClock.elapsedRealtime()
    }

    private fun handleStateTransition(newState: LinkState) {
        if (_linkState.value != newState) {
            _linkState.value = newState
            notifySystemAndMcu(newState)
        }
    }

    private fun notifySystemAndMcu(state: LinkState) {
        // 通过串口通知 MCU,并联动震动/指示灯
    }
}

2. 重连引擎实现(平滑重连与防抖)

kotlin 复制代码
class ReconnectionEngine(
    private val commandChannel: CommandChannel,
    private val videoPipeline: VideoPipeline
) {
    private var reconnectJob: Job? = null

    fun triggerReconnect(scope: CoroutineScope) {
        if (reconnectJob?.isActive == true) return

        reconnectJob = scope.launch(Dispatchers.IO) {
            var attempt = 0
            val baseDelay = 200L
            val maxDelay = 2000L

            while (isActive) {
                attempt++
                val jitter = (-50..50).random()
                val backoff = min(maxDelay, baseDelay * (1 shl min(attempt, 6))) + jitter

                Log.w("ReconEngine", "Reconnecting attempt #$attempt, backoff: ${backoff}ms")

                // Step 1: 尝试重连指令控制通道 (轻量级)
                val cmdSuccess = commandChannel.reconnect()

                if (cmdSuccess) {
                    // Step 2: 控制通道成功,立即请求机载端刷新关键帧,并恢复视频管线
                    val videoSuccess = videoPipeline.reconnectStream()
                    if (videoSuccess) {
                        Log.i("ReconEngine", "Link fully restored on attempt #$attempt")
                        break // 重连成功,跳出循环
                    }
                }

                delay(backoff)
            }
        }
    }

    fun cancel() {
        reconnectJob?.cancel()
        reconnectJob = null
    }
}

五、 Android 16 系统级深度保障与优化策略

作为搭载 Android 16 的工业级平台,APP 层的健壮性必须由底层 OS 提供协同支持:

  1. 进程常驻与保活(Keep-Alive) :
    • 将遥控器主控 APP 设为系统签名应用(android:persistent="true"),加入 AOSP Doze 白名单 与系统后台无杀保护。
    • 配置实时调度策略:数传和重连线程提升至 Linux SCHED_FIFO 或设置实时优先级(Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO))。
  2. 网络接口独立绑定(Socket Multi-homing) :
    • SLM580 同时具备 4G(rmnet)、Wi-Fi(wlan0)和 CX6531CD(eth0)。
    • APP 端建立 Socket 时,必须使用 Network.bindSocket(socket) 显式绑定到 eth0 网络,避免在图传链路断开时,Android 系统自动将 Socket 漂移到 4G 导致重连路由混乱。
  3. 断网与安全闭环(Safety MCU 同步) :
    • 当 APP 判定彻底失联后,通过 UART 向 Safety MCU 发送 DISCONNECT_ALARM 帧。
    • Safety MCU 依据硬件协议触发机载端返航/停机保护,杜绝因 Android 应用层崩溃或重连中产生死锁而导致机载设备飞丢。
相关推荐
千里马学框架5 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone5 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc5 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo5 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077005 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼5 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone5 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen5 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone5 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui