FDE Times 数据在招 FDE 职位 826▼35 近 7 天在招职位数的变化(同口径比较):不计入本周新开始追踪的公司的职位近 7 天新增 77在招公司 154支持远程 25%美国薪资中位数 $202k招聘最多 Databricks 104
ENVI

前线部署工程师(FDE)的专业媒体

技能指南 · 指南

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

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

分享微博

要点速览

  • 在现场,复现缺陷往往比修复更难。先录数据,再修复。
  • 回放能复现,问题就在软件;回放复现不了,就怀疑输入(硬件、网络)或技术栈本身不确定。
  • GPS 丢失、动力故障这类失效,要在受控试验中主动触发,别等真实环境替你触发。
分为两个区域的示意图。船上:传感器数据流入 2 分钟环形缓冲区,写出为完整快照,再由快照生成摘要。只有几百字节的摘要经卫星传出,让岸上的人知道出了状况;完整快照文件在航行结束后带回办公室回放。
慢速卫星链路只传摘要,完整快照留在船上,之后带回办公室回放。来源:文中的 FieldRecorder 代码示意。

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

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

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

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

在现场,难的是复现缺陷

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

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

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

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

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

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

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

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”这类措辞:它们说明这个岗位需要现场调试。简历上别只写“处理过生产环境事故”。

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

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

这篇文章对你有帮助吗?

6 个信源
路线图上的下一篇 · 阶段 5: 部署上线迁移到 MongoDB Atlas,最快的做法恰恰是错的逐表搬成集合只是延续关系型思维,真正的工作是按应用一起读取的数据重新组织文档。