遥控器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)的 onLostonCapabilitiesChanged
    • 底层 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 应用层崩溃或重连中产生死锁而导致机载设备飞丢。
相关推荐
执明wa2 小时前
Android Studio 打包 APK
android·ide·android studio
waiting9711182 小时前
Ubuntu 26.04 + Android14安装与编译教程
android·linux·ubuntu
hunterandroid2 小时前
Android 测试全景:从单元测试到 UI 自动化的完整实践
android·前端
蜡台3 小时前
Kotlin 五大作用域函数详解|let/run/apply/also/with 选型指南+实战避坑
android·java·kotlin
2401_833269304 小时前
Android registerForActivityResult详解
android
Carson带你学Android4 小时前
9月 Android Drop:Gemini 终于不只是聊天助手了
android·ai编程
Sagittarius_A*4 小时前
【LitCTF2026】lit_ezsql
android·java·数据库
hai_android5 小时前
Messenger:Android 最轻量的 IPC 方式
android·kotlin
gxgldyh6 小时前
Android Framework源码解析(十):Service启动全流程——从startService到Service执行的源码深度解析
android