# 海上故障只出现一次，先录全数据再修

> 假设一艘无人船靠港时反复偏航，回办公室重放日志却一切正常，这说明为何复现必须排在修复之前。

原文: https://fdetimes.net/zh/guides/field-debugging-autonomous-vessel-docking/

设想一艘无人船，整个上午都在近海按计划航线稳定行驶。到了下午进港时，它开始偏离航向，自行修正，然后又偏。你把日志带回办公室重跑一遍，一切正常。

处理这类缺陷，是从事自主系统的前线部署工程师（FDE）的本职工作之一。制造自主水面船艇的 Saronic 这样描述自己的 FDE：在真实环境中集成、部署并改进自主系统的人。

按照岗位描述，FDE 在部署过程中要同时处理软件、硬件和网络问题，而且 50% 以上的时间在国内外出差。

你也许一辈子都不会登船。但几乎每个 FDE 都遇到过只在客户现场出现的缺陷：挂不上调试器，网络很慢，也没人能把那一刻原样重放给你看。海上是这个问题最严酷的版本，因此也是学习它最清楚的地方。

## 在现场，难的是复现缺陷

海上的每一刻都只发生一次。海浪、风、周边船只和卫星信号，不会再以同样的方式凑到一起。如果什么都不录就在现场匆忙修复，你无法证明修复是对的；缺陷再出现时，你也认不出来。

在 Saronic，海上测试规划岗位用一套结构化流程跟踪缺陷，每个缺陷都记录复现步骤、严重程度和纠正措施。值得借鉴的原则是：在能复现缺陷之前，你没有任何可靠的东西可修。

复现需要数据。Saronic 称，其 Corsair 船艇的耐久试验产生了约 500 小时的多摄像头视频，总计约 17 TB。

该公司表示，会把这批数据用于模型开发、回放和回归测试。这次试验设计为连续运行，用来评估可靠性和耐久性，也就是暴露短时测试发现不了的缺陷。

## 链路太慢，逼你选择往回发什么

一项关于自主船舶的研究指出，它们在海上受限于低带宽卫星链路。没人会通过这样的链路传 17 TB 数据。于是任务一分为二：所有数据录在船上的机器里，岸上只收到足够让人知道“出事了”的信息。

下面是这个思路的简单示意。阈值、冷却时间和字段名都只是示例：

```python
import collections, json, time

def send_over_satlink(msg):
    """桩函数：替换为带队列和大小限制的真实卫星客户端。"""
    payload = json.dumps(msg).encode()
    assert len(payload) < 512, "摘要必须足够小"
    print("SATLINK", payload)

class FieldRecorder:
    """所有数据录在本地机器上，慢速链路上只发摘要。"""
    def __init__(self, window_s=120, rate_hz=50, cooldown_s=60):
        self.buf = collections.deque(maxlen=window_s * rate_hz)
        self.cooldown_s = cooldown_s
        self.last_snap = {}  # reason -> 最近一次快照的时间

    def record(self, s):  # s：包含 t、heading_err_deg、gps_fix、link_ok、sw_version 的 dict
        self.buf.append(s)
        if abs(s["heading_err_deg"]) > 5:
            self.maybe_snapshot("heading_error")

    def maybe_snapshot(self, reason):
        now = time.monotonic()
        if now - self.last_snap.get(reason, float("-inf")) < self.cooldown_s:
            return  # 仍是同一段航向误差，不再生成新快照
        self.last_snap[reason] = now
        path = f"/data/snap_{int(time.time())}_{reason}.jsonl"
        with open(path, "w") as f:
            for s in self.buf:
                f.write(json.dumps(s) + "\n")
        # 卫星链路上只走几百字节，完整文件留在船上
        send_over_satlink({"reason": reason, "file": path, "n": len(self.buf)})
```

关键在环形缓冲区：检测到异常时，你手里已经有之前两分钟的数据，也就是导致缺陷的那一段。冷却时间同样重要。没有它，一段持续几分钟的航向误差会生成成千上万个快照，既写满磁盘，又堵死卫星链路。

还要在同一条记录里写下三层的状态：软件版本、GPS 状态和链路状态。这样隔离缺陷时，证据就在手边。

## 示例：靠港时的航向误差

回到开头那艘船。这是一个用来练习方法的设想场景，并非 Saronic 公布过的事故。纸面上有三组假设：导航逻辑没处理好只在近岸出现的情况；某个传感器在港口环境下读数不同；连接中断导致系统切换了模式。

定位层的假设是有依据的。关于自主船舶的研究警告，当 GPS 失去信号、遭到干扰或欺骗时，船上无人值守，可能陷入危险境地。这正是记录中 `gps_fix` 字段如此重要的原因。

决定性的测试是回放。把录下的那份快照原样输入办公室机器上的导航栈。如果虚拟船像在海上一样偏离航向，缺陷就在逻辑里，你也有了一个可以对着修的测试用例。

如果回放结果完美无缺，就有两种可能。第一，你输入的数据和海上真实系统收到的不一样，这时要怀疑时序、硬件或网络。

第二，技术栈本身不确定：线程顺序、处理延迟或某个随机步骤，都可能让同样的输入产生不同的输出。

所以要多次回放，固定随机种子，使用模拟时钟。如果每次结果不同，先解决技术栈的确定性问题，再对硬件下任何结论。

结果按如下方式写进缺陷报告：

| 字段 | 航向误差案例的填写方式 |
|---|---|
| 现象 | 接近港口时，航向误差反复超过阈值 |
| 条件 | 时间、位置、GPS 状态、链路状态、软件版本 |
| 复现步骤 | 回放指定快照；记录运行次数及其中复现缺陷的次数 |
| 严重程度 | 是否影响运行安全 |
| 怀疑层 | 软件、硬件或网络，附回放得到的证据 |
| 纠正措施 | 修复方案，外加用这份快照构建的回归测试 |

很多人会跳过最后一行。触发缺陷的那份快照应当成为永久测试，下一次试验就不必再去海上追查老缺陷。

## 在真实环境之前主动触发失效

避免海上意外的最好办法，是在受控试验中自己制造失效。2013 年的一次海上试验中，GPS 被有意干扰，一套原型系统自动切换到了 eLoran。

那次测试针对的是导航系统而非自主船艇，但方法完全适用。

最近一次自主能力演示中，包括了对模拟机械故障的应急响应，同时岸上在远程监控这艘船。

这类测试只有在事先定好标准时才有价值。Saronic 围绕测试矩阵组织测试，每项需求都对应一个客观的通过/不通过标准。

“降级机制运行良好”只是一种印象。“GPS 丢失时切换到备用系统，偏差不超过允许值”才是可以检验的标准。

还有安全问题。在 Saronic，现场测试总监实时指挥试验并对其安全负责。想要主动触发失效，必须事先与这个人商定。任何人都不能擅自切断正在运行的系统。

## 常见错误

最常见的错误是只在自己熟悉的那一层调试。软件工程师埋头看代码，证据却指向天线或链路。这也是 FDE 岗位描述把软件、硬件和网络写在同一行的原因。

第二个错误是想把所有数据都通过慢速链路传回来，偏偏在最需要它的时候把它堵死。第三个是不保存快照就在现场修复，修复永远无法被证明正确。第四个是没有通过/不通过标准就开测，事后再争论结果算不算好。

## 缩短现场与代码之间的回路

Saronic 的测试规划人员每天与软件团队、船艇运营团队和供应商合作，推动每个问题尽快解决。好的缺陷报告能让这个回路转起来：办公室的人拿到快照和复现步骤，不用亲自去现场也能理解发生了什么。

职业方面，阅读 FDE 招聘信息时，留意“software, hardware, and networking”或“real-world environments”这类措辞：它们说明这个岗位需要现场调试。简历上别只写“处理过生产环境事故”。

讲一个具体缺陷的故事：你怎样录下数据，怎样复现，把它隔离到哪一层，又把它变成了什么回归测试。面试中，这样的故事远比一串工具清单更有说服力。

大海不会给你第二次机会。好工程师能把那唯一一次失效带回办公室，想重放多少次就重放多少次。

**本周动手试试:**

- 挑一个你过去遇到的生产环境缺陷，按六个字段重写成缺陷报告：现象、条件、复现步骤、严重程度、怀疑层、纠正措施。
- 给你负责的某个服务加上带冷却时间的环形缓冲区：遇到异常时，服务把最近 2 分钟的数据存成文件，只回传一条简短通知。
- 在预发布环境中有意断网或关掉某个依赖，再对照事先设定的通过/不通过标准，检查系统的降级表现。

## 信源

- [Senior Forward Deployed Engineer (Saronic Technologies)](https://jobs.8vc.com/companies/saronic-technologies-2/jobs/59439558-senior-forward-deployed-engineer)

- [Autonomous Sea Trials Test Planner (Saronic Technologies)](https://jobs.generalcatalyst.com/companies/saronic-technologies-2/jobs/81666065-autonomous-sea-trials-test-planner)

- [Saronic runs endurance trials on Corsair ASV for Navy program](https://www.workboat.com/saronic-runs-endurance-trials-on-corsair-asv-for-navy-program)

- [VR Teleoperation to support a GPS-free Positioning System in a Marine Environment](https://www.transnav.eu/html,1061.html)

- [Successful trial of automatic GPS backup](https://thedigitalship.com/news/electronics-navigation/successful-trial-of-automatic-gps-backup)

- [368-ton ex-US Navy vessel shows off advanced autonomy in high-stakes sea trials](https://interestingengineering.com/military/368-ton-ex-us-navy-vessel-tests-autonomy)
