ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

DreamView+离线数据包回放实战:链路、版本与故障排查

DreamView+离线数据包回放实战:链路、版本与故障排查

1. 先把“播放离线数据包”这件事看清楚

DreamView+ 里播放离线数据包,说白了就是把事先录制好的一段车端运行数据,原样灌回系统里,让界面像看录像一样把当时发生了什么重新演一遍。这件事看起来门槛不高——不就是点个播放按钮嘛。但我见过太多人第一次打开 DreamView+ 的离线数据面板,选了个包,时间轴开始走,屏幕中间却什么都没有,然后就开始怀疑环境有问题、镜像有问题、显卡有问题。实际上绝大多数情况下,问题出在对这条回放链路的理解上:你没有搞清楚 DreamView+ 到底在播什么、谁在控制时间、哪些数据是包里带过来的、哪些是它现场算出来的。

这篇文章面向三类人:刚接手自动驾驶可视化工具链、需要在本地复现车上问题的调试同学;想拿历史数据做算法回归、但被回放环境折腾过的人;以及已经能在界面上播起来、但还想搞清楚怎么提高回放效率和排查故障的熟手。我会把整条链路拆开讲——从数据包本身的结构,到容器环境、地图加载、通道订阅、播放控制,再到我实际踩过的那些坑。文中涉及的具体命令和参数,是基于这套工具链的常见实现给出的参考写法,不同版本之间可能有细微差异,以你手头仓库里的脚本和帮助信息为准。

1.1 回放链路里到底发生了什么

要理解回放,先理解这个平台的消息模型。这类自动驾驶系统内部一般不靠点对点调用传递数据,而是走一套基于发布订阅的中间件:每个模块把自己的输出写到一个带名字的“通道”里,其他模块按通道名订阅。感知模块往感知通道写障碍物,定位模块往定位通道写位姿,规划模块订阅这些通道然后算出轨迹。整个系统跑起来的时候,这些通道以很高的频率流动,看上去浑然一体。

离线数据包,本质上就是把这些通道上的消息,带着原始时间戳,一条条落盘保存下来的一个归档文件。里面没有视频、没有截图,只有结构化消息。你在 DreamView+ 里看到的点云、障碍物框、车道线、规划轨迹,全都是界面根据这些消息现场渲染出来的。这一点想通了,很多现象就顺理成章了:包里没有感知通道,你就不可能看到障碍物;包里没有定位通道,车模型就不知道该摆在哪儿。

回放的时候,播放器做的事只有一件——按原始时间间隔,把这些消息重新发布到同名通道上。它不关心下游谁在听,也不关心你界面开没开。所以严格来讲,“在 DreamView+ 里点播放”和“在命令行里敲播放命令”是同一件事的两种入口,前者只是把控制权交给了界面,底层还是那套录制回放工具在工作。这个认知很关键,因为当界面出问题时,你随时可以退到命令行去验证到底是不是播放器的问题,做到心里有数。

还有一层要区分:回放是“输入重放”,不是“过程重算”。如果你希望看到规划模块针对这段数据重新算一遍轨迹,那得把规划模块也启动起来,让它去订阅回放的感知和定位输出。这时候回放提供的是输入,模块提供的是新的输出,两者叠加在同一个界面里显示。这一点没分清,就会出现“我明明放了包,怎么规划轨迹和我上次看的不一样”的困惑——因为它压根不是包里那条轨迹了,是重新算的。

1.2 什么场景值得用回放,什么场景别硬上

回放最典型的价值是可复现。实车跑一次,路况过去了就过去了,想再看一遍那个加塞的电动车,只能靠数据。把当时的包拿回来反复播,同一个场景可以看二十遍,可以放慢到 0.25 倍去看障碍物 ID 切换的那一帧,可以关掉其他模块只看感知输出。这种“时间可以随便揉捏”的能力,是实车和纯仿真都给不了的。

第二个价值是低成本回归。你改了一版跟踪逻辑,不可能每次都上车验证。把手头积累的几十个典型包跑一遍,看输出有没有退化,是性价比最高的做法。我一般的习惯是维护一个小型“回归集”:三五个包含加塞、切入、路口排队、隧道弱定位的包,每次改动前先跑一遍存档,改动后再跑一遍对比。

但有些场景不适合硬套回放。比如涉及执行器闭环的控制调试,回放只能给你开环的输入,车不会真的动,控制器的响应也就无从验证。再比如多传感器时间同步问题,回放时所有消息都按录制时间戳走,本来就同步好了,你在回放里永远复现不出实车上那种因为驱动抖动导致的错帧。还有一种情况是数据包里缺失关键通道,你非要靠回放去调那个模块,那就是在做无用功,先把录制端的问题解决了再说。

1.3 版本对齐:最容易被忽略的第一道坎

我把它放在最前面讲,因为它造成的故障最隐蔽,表现也最像“玄学”。这套系统里的消息是用结构化描述文件定义的,录制的时候按当时的定义序列化,播放的时候按当前编译出来的定义反序列化。两边的定义只要有一处字段对不上,轻则某个字段变默认值,重则整条消息解析失败,界面上表现为“通道有数据,但什么都没显示”。

实际遇到的典型场景是这样的:同事给了你一个半年前的包,你拉了最新代码编译,界面能打开、时间轴能走、点云也出来了,唯独障碍物框一个都不显示。你查了半天感知模块、查了半天渲染,最后发现是障碍物消息里新增了一个必填字段,反序列化时旧包缺这一项,整条消息被判为无效丢掉了。这种问题靠猜是猜不出来的,得靠版本对齐。

我的做法是给每个数据包记一条“出生信息”:录制时间、代码分支或版本号、镜像标签、使用的地图名。存在包同目录的一个文本文件里,几十个字节,能省掉未来几个小时的排查。团队里如果能统一这套约定,新人接手历史数据的时候会少掉大量无效沟通。

注意:不要用“最新代码”去播“任意历史包”当成默认操作。能对上就对上,对不上就老老实实切到录制时的版本,这不是保守,是省时间。

2. 环境搭建:从宿主机到容器内的完整准备

这套工具链跑在一个容器化的环境里,原因是依赖太重——中间件、各种消息库、点云处理库、渲染依赖,装在宿主机上迟早会把系统搞乱。容器把这一坨封起来,你只需要在外面准备 Docker 和显卡驱动,进去之后就是一个干净的工作空间。理解这个分层,后面所有路径问题、挂载问题就都好解释了。

2.1 机器基线与依赖检查

先说硬件。单纯看回放,对机器要求没有训练那么夸张,但也不是随便一台笔记本能扛的。我的经验基线是:16 核以上 CPU、32G 内存起步、一块能跑起来的中端独显。为什么内存要这么大?因为大包的元信息索引、点云缓存、渲染缓冲是同时吃内存的,播一个几十分钟的城市道路包,内存占用爬到二十多 G 很常见。显卡这块,如果没有独显,界面能开、能播,但点云一多就掉帧,时间轴和画面会不同步,体验很差。

系统层面,Ubuntu 20.04 或 22.04 是比较稳的选择,内核不要太老,否则显卡驱动容易出问题。Docker 装好之后,记得把当前用户加进 docker 组,不然每条命令都要 sudo,脚本里也会频繁卡权限。显卡直通到容器里需要装好宿主机驱动和容器运行时支持,装完之后用一条跑容器内显卡信息查询的命令验证一下,能看到显卡型号才算通。

磁盘空间是最容易翻车的地方。一个中等规模的数据包动辄十几 G,你要是边播边录,占用涨得更快。我一般会给数据盘单独留 500G 以上,并且养成定期清理的习惯。有一次我在一个 256G 的机器上连着录了三个包,第四个包录到一半写不进去,录制进程直接挂掉,前面的包也因为文件句柄没正常关闭而损坏,白干一下午。

工作目录的结构建议一开始就规划好,别全堆在一个文件夹里:

~/work/ ├── records/ # 数据包,按日期或场景分目录 ├── maps/ # 自定义地图 ├── configs/ # 启动配置 └── notes/ # 每个包的出生信息和观察笔记

2.2 启动容器与工作空间

从仓库根目录执行启动脚本,它会拉取镜像、创建容器、把代码目录挂进去。第一次执行会花比较久,因为要下载镜像,几个 G 的量,网络不稳的话建议先去泡杯茶。镜像拉完之后,后续启动就是秒级的事。

# 在仓库根目录执行,首次会拉取镜像 bash docker/scripts/dev_start.sh # 进入容器 bash docker/scripts/dev_into.sh

进去之后你会落在容器内的工作目录下,看到编译产物和源码目录。这里有个细节很多人没注意:容器内的路径和宿主机路径是两套,你在宿主机上把包放在~/work/records,容器里不一定能看见,除非启动脚本里做了挂载。如果启动之后在容器内找不到自己的文件,先别怀疑路径写错,去宿主机上执行一下容器信息查询,看看挂载列表里有没有你的目录。没有的话,要么把包挪到已经挂载的目录下,要么修改启动脚本加上挂载再重建容器。

编译这一步,如果你只是播包看效果,通常不需要重新编译,镜像里带的是编译好的产物。但如果你要用自己改过的模块去叠加回放,那就得编译,而且编译耗时不短。我建议把编译和回放当成两件事分开做:先用镜像自带的产物把回放跑通,确认环境没问题,再去动代码。顺序反了的话,出了问题你根本分不清是环境问题还是代码问题。

进容器之后先看两个东西:一个是有没有数据目录,一个是有没有地图目录。数据目录通常默认在/apollo/data这样的位置,地图目录在/apollo/modules/map/data/下面,里面一般会带一两份演示地图。这两个目录的存在与否,直接决定了你后面能不能顺利开播。

2.3 启动可视化界面并确认通信正常

界面启动有专门的引导脚本,加了参数会起新版界面,不加就是老版本。新版界面在数据播放和模块管理上确实顺手不少,我基本都用新版。

# 在容器内执行,启动可视化界面 aem bootstrap start --plus

启动过程会拉起后端服务、前端静态资源和地图服务,日志刷得挺快,别被吓到。启动完之后在宿主机浏览器里访问对应端口(默认 8888 那一档),能看到界面就说明前端通了。如果打不开,先确认端口映射有没有做,再确认容器里进程是不是真的起来了——用进程查询看一眼,别光看日志打印了启动成功就信。

界面打开之后,先做一次“空跑”验证:不加载任何数据包,直接看地图能不能显示出来。这一步很关键,它把问题范围一刀切开——地图能显示,说明渲染链路、地图服务、坐标系这些都正常,后面播放出了问题就大概率出在数据包本身;地图都显示不出来,那还轮不到播包,先把地图这条链路修通。

通信这块还有个工具值得常用:通道监控工具。它会把当前系统里正在流动的通道、频率、数据量列出来。回放开始之后,用它在旁边看着,能立刻判断出“消息到底有没有发出来”。界面空白的时候,第一步不是去翻渲染代码,而是打开监控看通道有没有数据,这一招帮我省掉了无数次无效排查。

3. 数据包的检查、加载与播放实操

到这一步环境已经就位,接下来是正题。我强烈建议把“检查”和“播放”分成两个动作,先看后播。直接上手播,出了问题你连包里有几个通道都不知道,排查起来全靠猜。

3.1 先别急着播,先把包读一遍

录制回放工具自带的查看功能,能把包的基本信息一次性列出来:文件时长、总消息数、通道列表、每个通道的消息类型和帧数。这条命令成本极低,但信息量很大。

cyber_recorder info records/20240115_scene_a.record

输出大概长这样(字段名以实际版本为准):

Duration: 96.42 s Total Frames: 18432 Channel Count: 14 /apollo/localization/pose apollo.localization.LocalizationEstimate 2410 /apollo/perception/obstacles apollo.perception.PerceptionObstacles 960 /apollo/planning apollo.planning.ADCTrajectory 960 /apollo/sensor/lidar/points apollo.drivers.PointCloud 960 ...

拿到这份清单,我一般会做四件事。第一,看时长够不够,太短的包(几秒)只够看一眼画面,做不了什么分析。第二,看关键通道齐不齐——定位、感知、规划、底盘这几路在不在,缺哪路就提前知道界面上会少哪块内容。第三,看帧数比例合不合理,比如感知是 10Hz、定位是 100Hz,比例差十倍左右是正常的,如果某个通道只有两三帧,那基本是录制时出过问题,这个包的有效性要打问号。第四,看消息类型名称,跟你当前环境的定义做对照,类型名对不上就说明版本不匹配,先解决版本问题再谈播放。

还有一件事容易漏:确认这个包里的坐标系和你要用的地图是不是一套。有些包是在特定场地录的,原点定义和演示地图完全不是一回事,你拿演示地图去播,车会出现在地图外面几百米的地方,看起来像是定位飞了,实际上是地图选错了。

3.2 界面上操作:选包、选地图、开播

界面上的离线数据面板,核心就三个选择:数据包、地图、播放模式。顺序很重要,先选地图再选包,因为地图加载要时间,先让它加载着,你再去浏览包。

选包的时候有两种给法:一种是给容器内路径,一种是给 URL。本地调试当然用路径,把文件放在容器能访问到的目录下,填绝对路径。这里有个小坑,路径里不要带中文和空格,某些版本的解析逻辑对特殊字符处理不好,会静默失败,表现就是“选了但没反应”。

地图选择下拉框里列出来的,是地图目录下扫描到的所有地图。如果你自己有地图,把整个地图文件夹拷到地图目录下,重启界面服务就能看到。地图文件夹里至少要有地图描述文件和底图数据,缺一个都会加载失败,而且失败提示往往很含糊,只告诉你“地图加载失败”,不告诉你缺哪个文件。所以拷贝的时候整个目录一起拷,别只拷一半。

播放模式上,界面一般会给几个选项:单纯播放、播放并启动某些模块。前者只管把数据喂回去,后者会在播放的同时拉起指定模块,让它们吃回放数据、产出新的输出。我第一次做回归测试时用的是后者,看的是自己改过的感知模块在历史数据上的表现,这个能力非常实用,后面第 5 节会细讲。

开播之后,界面下方会有时间轴,上面标着进度。这时候先别急着看画面,看一眼时间轴有没有在动、总时长对不对。时间轴动、总时长对,说明播放器已经接管成功;时间轴不动,那就是文件没读进去或者解析出错,回到上一步重新确认路径和版本。

3.3 命令行播放与通道过滤

界面出问题的时候,命令行是你最好的朋友。这条命令直接调用播放器,绕开所有前端逻辑:

cyber_recorder play -f records/20240115_scene_a.record

常用参数我整理成了下面这张表,都是实际调试里高频用到的:

参数作用典型用法
-f指定数据包路径必填
-l循环播放反复观察同一场景
-r播放速率倍数-r 0.5慢放看细节
-k只播指定通道排除干扰,只看感知
-b从指定时间开始跳到问题发生的时刻
-e到指定时间结束只播一段窗口

-k这个参数被低估了。默认情况下所有通道一起播,点云通道数据量大,占掉大量带宽和内存,而你可能只想看障碍物和轨迹。只播几个轻量通道,机器瞬间就轻快了,播放也顺滑得多:

cyber_recorder play -f records/20240115_scene_a.record \ -k /apollo/perception/obstacles /apollo/planning /apollo/localization/pose

-r慢放的价值在于观察跳变。障碍物 ID 在相邻帧之间切换、轨迹在某几帧突然抖动,这些在一倍速下一闪而过,放到 0.25 倍就一目了然。但慢放有个副作用——如果同时启动了某些模块,它们也会被慢放的数据流影响,行为可能和正常速度下不同,这一点做对比实验时要注意。

-b和-e配合使用,能把一个几十 G 的大包切成一段几百兆的小窗口,反复看不心疼。它们接受的是相对包起点的时间偏移,不是绝对时间戳,刚开始用容易填错,填完之后看时间轴从哪儿开始走就知道了。

3.4 播放控制:倍速、循环、跳转与时间对齐

时间对齐这块,值得单独说两句。回放的时间基准来自包里的消息时间戳,播放器按这些时间戳的间距重放。所以如果你的包录制时消息时间戳就乱了(比如中途系统时间被同步过),回放时就会表现为某一瞬间大量消息涌出,然后长时间静止。这种情况你在时间轴上看得非常明显——进度条要么跳一大格,要么卡住半天不动。

判断方法很直接:把包的信息打印出来,看首尾时间戳和总时长的关系,再抽几个中间通道看有没有异常间隔。如果确认是录制端的问题,那就别在回放端折腾了,换一个包。

跳转功能在界面上很好用,但要注意:跳转不等于“从那一帧开始算”。因为很多模块在启动时需要一段历史数据来初始化(比如定位需要累积一段位姿才能收敛,跟踪需要几帧才能建立航迹),你直接跳到中间,模块可能有一两秒的“懵圈期”,输出是不可信的。我的做法是每次都从开头播,跳到目标位置之前的那一小段先播一遍,等模块输出稳定了再看。

循环播放适合做“一件事的反复确认”。比如你想确认某次减速是不是规划的问题,就把-l加上,让它一遍遍播那十几秒,你慢慢对照轨迹和障碍物看。但循环时要注意模块状态会累积——跟踪器不清空的话,第二遍看到的和第一遍可能不一样。做严格对比时,每遍都要重启模块,别图省事。

4. 回放里的坑:现象、原因与处理

这一节是我这些年攒下来的故障记录,按现象分类,每条都附上我当时是怎么定位的。看别人的踩坑记录最大的价值不是记住结论,而是学会那个“先查什么后查什么”的顺序。

4.1 画面空白、通道缺失与依赖缺失

最常见的现象:时间轴在走,3D 场景里除了地图什么都没有。这种情况下我的排查顺序是固定的三步。

第一步,用监控工具看通道。如果目标通道根本不在列表里,说明播放器没发出来,回去看info输出的通道列表里到底有没有它,有没有可能是-k过滤掉了。第二步,通道在列表里但频率是零或者极小,说明消息被发出来了但下游没接住,这种情况多半是模块没启动——比如你想看感知框,那感知模块得在跑,光有数据没人渲染。第三步,通道正常、模块也起了,还是空白,那就是渲染层的问题,去查地图有没有加载、视角有没有跑偏(车可能在地图几百米外,你看到的是一片空白,实际是“看不见”而不是“没有”)。

还有一种非常隐蔽的情况:包的版本和当前环境不匹配,导致消息反序列化失败。表现是通道有数据流动、帧数看着正常,但下游拿到的消息全是默认值,空对象。判断技巧是看一个必然有值的字段,比如障碍物数量,如果永远是零但通道频率正常,八成就是解析出了问题。这种问题除了版本对齐没有别的解法,别浪费时间。

依赖缺失也会导致空白,而且报错藏得很深。某些渲染依赖是运行时动态加载的,缺了之后前端控制台有报错,但界面上看不出来。养成习惯:界面行为异常时,顺手打开浏览器开发者工具看一眼控制台,很多问题在那儿一眼就能看明白。

4.2 点云和障碍物框位置错乱

点云飘在天上、车模型浮空、障碍物框和实际物体差两个车道——这类问题统称“位置不对”,但成因能分好几种,得分开看。

如果是所有东西整体偏移一个固定量,那基本是坐标系原点不一致。最常见的原因就是地图选错了。录制时用的是某个场地地图,原点定在场地某处,你拿另一份地图去播,两套原点差了几百米,所有东西自然整体平移。解法很简单,找到录制时对应的地图再播一遍。

如果是点云和障碍物框对不上,但各自和地图对得上,那是时间对齐问题。激光雷达和相机、定位的时间戳如果差了哪怕几十毫秒,在高速行驶时就会表现为点云“拖影”或者框“落后”半个车身。这种情况在回放时反而不好查,因为回放的时间戳是固定的,你只能接受它。真遇到这类问题,得回到录制端去查时间同步,回放只是把问题忠实地重现出来。

如果是车模型原地打转或者跳变,那去看定位通道。定位在隧道、地下车库这类场景容易丢,丢了之后位姿会保持上一帧或者跳到默认值。这种包拿来调感知没问题,调规划就不合适了,因为规划对定位精度的依赖非常高。

4.3 卡顿、内存爬升与播放中断

回放跑着跑着越来越卡,最后界面卡死,这是资源问题,而且往往是累积性的。原因一般有三个:点云缓存没释放、渲染帧堆积、模块内部的状态无限增长。

我的处理办法是先降负载再定位。关掉其他模块,只留界面;把倍速降到 1 倍甚至 0.5 倍;用-k只播必要通道。三个动作做完如果还卡,那就是这个包的数据量本身就超出了机器能力,考虑裁剪成小段来播。

内存爬升要盯。可以在宿主机上开一个监控,看容器内存随时间的变化曲线。健康的回放应该是内存上去之后稳住,如果是一条持续上升的斜线,那就是有东西在漏。这种情况跑长时间包一定会崩,提前发现比播到一半崩掉强。

播放中断还有一类原因跟文件本身有关。数据包在录制过程中如果进程被强杀、磁盘写满、机器断电,文件可能只有前半段是完整的,后半段的索引和实际数据对不上。表现是播到某个时间点突然报错退出。这种包别硬救,先确认能不能正常info出完整信息,info都读不全就基本可以判死刑了,把能播的部分裁出来用就行。

4.4 回放问题速查表

现象优先怀疑验证动作处理
时间轴不动文件路径或格式命令行直接播一次修正路径,确认版本
时间轴动但无画面通道缺失或模块未启动打开通道监控补通道或启模块
通道有数据但内容为空版本不匹配对照消息类型名切到录制时版本
整体位置偏移地图选错换地图重播用录制时地图
点云与框错位时间同步看定位与传感器时间差回录制端解决
越播越卡资源累积观察内存曲线降速、减通道、裁剪
播到中途报错退出文件损坏完整读一遍信息裁剪可用部分
地图显示不出来地图文件不全看目录结构整目录拷贝

提示:这张表建议抄下来贴在工位上。真出事的时候,人是慌的,按表走比凭记忆靠谱。

5. 把回放用出价值:录制端与回归测试的经验

回放做得好不好,一大半取决于录制端。你录的时候图省事,播的时候就得加倍还回来。这一节讲的是“怎么让回放变得有用”,而不只是“怎么能播起来”。

5.1 录制阶段就要为回放做准备

录制不是按一下开始就完事了,得想清楚这段数据将来要拿来干什么。如果是为了调感知,那传感器通道必须全,时间戳要干净;如果是为了调规划,那定位和底盘反馈必须准;如果只是做个演示,那随便录一段也行,但别指望它能用来做分析。

我的习惯是每段录制都配一个元信息文件,写清楚录制时间、地点类型(城市道路、园区、地下车库这种描述,不写具体位置)、天气、车辆状态、录制目的、用了哪份地图、代码版本。这份东西成本不到一分钟,价值极高。半年后你翻出一个包,光看文件名根本想不起来当时是什么情况。

录制时长也要控制。一口气录两小时,包大到打不开,想用的时候反而用不了。我一般按场景切,一个场景一段,几分钟到十几分钟,单独成包。切分的好处是定位问题时不需要在大包里翻,直接打开对应场景那段就行。

还有个小细节:录制开始和结束各留十几秒的稳定段。开头留一段是为了让各个模块有初始化时间,结束留一段是为了让数据完整收尾。不留的话,包的头尾往往是半截的,播起来很难受。

5.2 用回放跑模块回归的实操套路

回归的本质是“同样的输入,比不同的输出”。操作上分三步。

第一步,建立基线。用改动前的代码,把回归集里的每个包跑一遍,把关键输出落盘。落什么?看你改的是什么模块。改感知就落障碍物列表,改规划就落轨迹,落的时候注意带上时间戳和帧号,方便逐帧对齐。

第二步,跑新版本。同样的包、同样的播放参数、同样的启动配置,唯一变量是你改的代码。这一步最容易引入干扰,一定要保证除了代码之外所有条件一致,包括播放速率——前面说过,速率变化会影响某些模块的行为。

第三步,做差异比对。逐帧对比输出,看差异出现在哪些帧、差异量有多大、是不是集中在特定场景。这里要有个心理准备:回放本身不是完全确定的,多线程调度、浮点运算顺序都可能导致微小抖动。所以别追求“完全一致”,要设定一个合理的容差,超过容差才算退化。

# 基线跑法示例:只播必要通道,固定速率,落盘输出 cyber_recorder play -f records/regression/scene_01.record \ -r 1.0 -k /apollo/perception/obstacles /apollo/localization/pose

这套流程跑顺了之后,很多低级问题在本地就能拦住,不用等上车才发现。我个人觉得这是回放功能最大的价值所在,比“看录像”这件事重要得多。

5.3 数据裁剪与体积管理

包越来越大是必然的。我的目录里定期会做一次清理,原则是:原始包保留,但把大包裁成小段;已经分析完且结论已记录在笔记里的包,只留关键片段;重复场景的包只留一个最有代表性的。

裁剪有几种做法。按时间窗口切,用播放参数里的起止时间配合录制工具,把关心的那一段抽出来。按通道抽,把点云这种大体积通道丢掉,只留决策相关的轻量通道,体积能小一个数量级。这两种做法各有适用场景,做纯规划分析的时候我基本都会把点云丢掉,因为那个通道占了整个包八成以上的体积。

还有一件容易被忽略的事:定期检查磁盘。我给自己定了个规矩,每周看一眼数据盘使用率,超过百分之七十就开始清理。等满了再清,往往就是急着要用的时候发现写不进去,那种感觉相当糟糕。

我在实际使用中的体会是,回放这个功能本身并不复杂,复杂的是它周围的一圈东西——版本、地图、时间戳、资源、录制质量。把这一圈理顺了,回放就是一件非常顺手的工具;不理顺,它就是每次都要重新踩一遍的坑。所以别嫌那些准备工作繁琐,那是把时间花在刀刃上。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进