ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

openpilot 数据加载实战:Route 与 LogReader 如何读取路线日志(rlog/qlog)

openpilot 数据加载实战:Route 与 LogReader 如何读取路线日志(rlog/qlog) openpilot 数据加载实战Route 与 LogReader 如何读取路线日志rlog/qlog【免费下载链接】openpilotopenpilot is an operating system for robotics. Currently, it upgrades the driver assistance system on 300 supported cars.项目地址: https://gitcode.com/GitHub_Trending/op/openpilotopenpilot 的所有运行时数据——CAN 信号、传感器、控制量、事件——都以 Capn Proto 编码后压缩成 rlog/qlog 文件按“路线route 分段segment”组织存储。本文以 tools/lib 库的 README 为骨架结合 route.py 与 logreader.py 的源码实现讲清楚如何用最少的代码定位并读取一段路线的日志、如何表达分段范围segment range与日志类型选择器r/q以及底层的多来源文件发现与解压机制帮助你在回放、分析、挖掘 openpilot 数据时快速上手。Route路线命名规范与文件路径命名格式openpilot 中一条路线由dongle id和时间戳唯一标识二者以|分隔若再加分段号则以/或--衔接344c5c15b34f2d8a / 2024-01-03--09-37-12 / 2:6 / q [ dongle id ] [ timestamp ] [ selector ] [ query type]从 route.py 的RouteName类L206-L232可以看到严格的长度约束dongle_id必须是 16 位十六进制字符time_str必须是 20 位YYYY-MM-DD--HH-MM-SS否则构造时直接断言失败。helpers.py中的正则helpers.py定义了同一套格式DONGLE_ID16 位小写十六进制[a-f0-9]{16}TIMESTAMP[0-9]{4}-[0-9]{2}-[0-9]{2}--[0-9]{2}-[0-9]{2}-[0-9]{2}SEGMENT_RANGE路线名后接可选的--或/分段切片再接可选的/q、/r、/a选择器分段类型与文件名route.py顶部的FileName常量L14-L21定义了每个分段中可能出现的数据文件类型文件名含义RLOGrlog.zst/rlog.bz2全量原始日志rlog 保留完整 CAN、传感器等高频消息QLOGqlog.zst/qlog.bz2精简日志qlog 只保留低频关键消息体积更小FCAMERAfcamera.hevc前视道路相机视频DCAMERAdcamera.hevc驾驶员监测相机视频ECAMERAecamera.hevc事件相机视频QCAMERAqcamera.ts时间戳化的相机流BOOTLOGbootlog.zst/bootlog.bz2启动日志Route 类基本用法README 给出的核心示例如下可直接运行from openpilot.tools.lib.route import Route from openpilot.tools.lib.logreader import LogReader r Route(a2a0ccea32023010|2023-07-27--13-01-19) # 获取该路线所有 rlog 文件的路径列表按分段编号对齐缺失分段为 None print(r.log_paths()) # 获取道路相机fcamera.hevc文件路径 print(r.camera_paths()) # 用 LogReader 读取该路线第一个分段的 rlog lr LogReader(r.log_paths()[0]) # 打印日志中的全部消息 import codecs codecs.register_error(strict, codecs.backslashreplace_errors) for msg in lr: print(msg) # 读取路线第二个分段的 qlog lr LogReader(r.log_paths()[1]) # 打印所有转向角值 for msg in lr: if msg.which() carState: print(msg.carState.steeringAngleDeg)对照 route.py 的实现Route的行为是构造时__init__L25-L32二选一远程模式不带data_dir时通过CommaApi请求v1/route/name/files_get_segments_remoteL67-L97把返回的文件 URL 按分段聚合为Segment列表。这依赖 auth_config.py 提供的 API token本地模式传入data_dir时_get_segments_localL99-L164扫描目录识别三种布局——explorer 下载的单文件dongle|time--seg/rlog.bz2这类文件名由RE.EXPLORER_FILE匹配、openpilot 分段目录、以及以canonical_name为名的目录内含0/、1/数字子目录。找不到任何分段时抛出ValueError。log_paths()/qlog_paths()/camera_paths()/dcamera_paths()/ecamera_paths()/qcamera_paths()均返回按分段编号对齐的列表{分段号: 路径}映射后按0..max_seg_number展开缺失分段的槽位为NoneL42-L64。例如log_paths()[0]就是 README 示例中“第一个分段的 rlog”。每个SegmentL167-L203还带events属性懒加载{segment_url}/events.json可用于查询该分段内的事件如接管、警报。名称解析测试用例test_route_library.py 用参数化用例固化了名称解析规则值得作为格式参考输入解析出的路线名分段号data_dira2a0ccea32023010\|2023-07-27--13-01-19a2a0ccea32023010\|2023-07-27--13-01-19-1整条路线Nonea2a0ccea32023010/2023-07-27--13-01-19--1同上1Nonea2a0ccea32023010\|2023-07-27--13-01-19/2同上2None/data/media/0/realdata/a2a0ccea32023010\|2023-07-27--13-01-19同上-1/data/media/0/realdata注意SegmentNameroute.py会自动剥离本地目录前缀当字符串中|之前出现/时前半部分被识别为data_dir后半部分才是路线名。分段范围Segment Range这是 README 的重点可以在路线名后直接追加selector与query type表达“只读哪些分段、读哪种日志”。分段切片lr LogReader(a2a0ccea32023010|2023-07-27--13-01-19/4) # 第 4 个分段 lr LogReader(a2a0ccea32023010|2023-07-27--13-01-19/4:6) # 第 4、5 个分段 lr LogReader(a2a0ccea32023010|2023-07-27--13-01-19/-1) # 最后一个分段 lr LogReader(a2a0ccea32023010|2023-07-27--13-01-19/:5) # 前 5 个分段 lr LogReader(a2a0ccea32023010|2023-07-27--13-01-19/1:) # 除第一个分段外全部切片语义与 Pythonslice完全一致由SegmentRange.seg_idxsroute.py实现seg_idxs先按RE.SLICEstart:end:step三段式均可为空解析只给了单个分段号无:时返回[start]负数会通过get_max_seg_number_cached查询路线最大分段号后换算start max_seg 1只有当切片用到相对索引end为空或为负、start为负时才需要向 API 请求maxqlog字段纯正数闭区间如/0:10完全离线可算。test_logreader.py 中的参数化用例覆盖了完整行为包括步长切片/2:-1→ 倒数第二个之前、/:10:2→ 前 10 个里隔一个取一个、/5::2→ 从第 5 个开始隔一个取一个。同一个测试文件还固化了非法输入test_bad_rangesL156-L170///、---、/-4:--2、/j、/0:1:2:3等都会触发断言错误说明格式校验是构造期 fail-fast 的。日志类型选择器lr LogReader(a2a0ccea32023010|2023-07-27--13-01-19/4/q) # 只读 qlog lr LogReader(a2a0ccea32023010|2023-07-27--13-01-19/4/r) # 只读 rlog默认从源码看logreader.py 的ReadMode枚举与 helpers.py 正则中[qra]的 selector完整支持四种模式选择器ReadMode行为/rRLOG只读 rlog缺失即失败/qQLOG只读 qlog/aAUTO优先 rlog缺失分段自动回退 qlog无提示无选择器时的默认参数RLOG/AUTO/AUTO_INTERACTIVE由LogReader(default_mode...)指定/iAUTO_INTERACTIVE优先 rlog回退 qlog 前交互询问y/NReadMode.AUTO的语义在测试test_auto_modetest_logreader.py中得到验证当 rlog 不可用且应答“y”时回退后的消息条数与直接读 qlog 完全一致回答“n”则抛出LogsUnavailable。README 只展示了/q与/r而/a与交互式模式是源码中真实可用的补充能力。LogReader从字符串到消息流构造与迭代LogReaderlogreader.py构造函数签名为LogReader(identifier: str | list[str], default_mode: ReadMode ReadMode.RLOG, sources: list[Source] | None None, sort_by_timeFalse, only_union_typesFalse)identifier可以是本地文件路径、HTTP(S) URL、cd:/...内部端点前缀、useradmin.comma.ai/connect.comma.ai的分享链接或上面任意形式的 segment range 字符串也支持传入字符串列表一次加载多段sort_by_timeTrue时按logMonoTime排序事件_LogFileReaderL119-L120——这对回放多个分段拼接场景很有用only_union_typesTrue时迭代器会跳过无法通过which()识别的损坏消息L122-L131测试test_only_union_typesL261-L287验证了它能把被写入非法 discriminant 的消息过滤掉。迭代返回的是 Capn Proto 事件消息常用辅助方法lr.filter(carState) # 生成器所有 carState 消息 lr.first(carParams) # 第一条 carParams 消息 lr.time_series # 消息转时间序列log_time_series.msgs_to_time_series解压与解析细节底层_LogFileReaderL89-L131做了三件事获取原始字节通过 filereader.py 的FileReader——HTTP URL 走URLFile本地路径走DiskFilecd:/xxx前缀会解析到DATA_ENDPOINT环境变量指向的内部端点默认http://data-raw.comma.internal/L11。识别并解压扩展名为.bz2或文件头为BZh9时用bz2解压扩展名为.zst或文件头为 zstd magic\x28\xB5\x2F\xFD时用zstandard流式解压L104-L108。注释明确指出这是为了兼容旧版未压缩 rlog 与新旧两代压缩格式FileName中每类文件都列出.zst与.bz2两个名字。批量解码capnp_log.Event.read_multiple_bytes(dat)一次解出全部事件包装为CachedEventReaderL52-L86。该类缓存了which()的结果——因为 Capn Proto 的属性访问开销较大且which()在分析代码里经常被多次调用它还提供__reduce__以支持快速 picklerun_across_segments多进程分发时用到。另外 logreader.py 的save_log提供了对称的写路径把消息列表拼接成字节后按目标扩展名选择bz2.compress或zstd.compress(level10)写出常用于裁剪/合并日志。多进程处理长路线对于分段很多的路线run_across_segmentsL275-L281用multiprocessing.Pool把每个分段的处理函数并行执行带 tqdm 进度条。CachedEventReader的 pickle 支持正是为这一步服务的——每个子进程独立解码各自分段。测试test_run_across_segmentstest_logreader.py验证了并行处理后的消息总数与串行一致。文件从哪来多来源发现链LogReader真正“聪明”的地方在于把“字符串 → 具体文件 URL”的发现过程抽象成可插拔的来源Source。默认来源列表在构造函数中写死logreader.pysources [internal_source, comma_api_source, openpilotci_source, comma_car_segments_source]对应 file_sources.py 中的四个实现来源说明internal_source内部数据端点DATA_ENDPOINT构造{dongle_id}/{log_id}/{seg}/{file}形式的 URL端点不可达时抛InternalUnavailableExceptioncomma_api_source走 route.py 的Route类经 comma API 的v1/route/name/files拿已校验存在的文件路径openpilotci_source从 openpilot CI 的 blob 存储openpilotci.py拼 URLcomma_car_segments_source从 comma 车队公开 segment 数据集comma_car_segments.py拼 URLauto_sourcelogreader.py的解析流程是把标识符解析为SegmentRange算出需要的分段下标seg_idxs按选择器决定尝试顺序/q只试 qlog其余模式先试 rlogAUTO/AUTO_INTERACTIVE模式再追加 qlog 回退对每种文件名逐个来源查询用file_existsHEAD 校验 URL、os.path.exists校验本地取每个分段第一个存在的文件所有分段都找到才返回否则AUTO模式打印cloudlog.warning回退、交互式模式询问用户最终失败则抛出LogsUnavailable并在异常信息中附上每个来源的失败原因方便排查。分享链接的识别由parse_indirectL203-L224处理useradmin.comma.ai/?onebox...直接取onebox查询参数connect.comma.ai链接若带秒数会自动把秒换算成分段区间start//60 : end//601因此可以直接把分享页 URL 喂给LogReader。测试用例test_logreader.py甚至验证了|被 URL 编码成%7C的 onebox 链接也能正确解析。视频读取FrameReader 简介README 提到与LogReader对应的FrameReader用于读取视频。framereader.py 的实现要点仅支持 HEVCfcamera.hevc等通过hevc_indexvidindex.py扫描帧头建立“帧类型 字节偏移”索引再ffprobe取分辨率FfmpegDecoderL102-L143按 GOP 边界读取并解码定位到目标帧所属 GOP 的起始 I 帧get_gop_start从该处 seek 后解码整段丢弃目标帧之前的帧支持start_fidx/end_fidx/frame_skip稀疏取帧FrameReader.get(fidx)L151-L173内置 LRU 帧缓存默认容量 30跨 GOP 跳帧时自动重置解码迭代器解码走 ffmpeg 管道hwaccel默认auto输出为rgb24的numpy数组-1, h, w, 3。也就是说日志结构化消息与视频图像帧分别由LogReader和FrameReader承担二者共享同一套Route路径定位与FileReader远程读取基础设施。使用前提与限制基于当前仓库源码使用这套工具链时需要注意以下事实路线名格式严格dongle id 16 位、时间戳 20 位RouteName的断言route.py|与/可作为 dongle 与时间戳之间的分隔符SegmentName同时兼容两种但 dongle id 本身必须是十六进制。远程访问需要 API 权限Route远程模式、get_max_seg_number_cachedL308-L316都要经CommaApitoken 来自 auth_config.py权限不足时异常信息会提示“确保你有该路线的访问权限或该路线是公开的”。负数/开区间切片会触发 API 查询test_slicing_api_calltest_logreader.py验证了/0、/:2不需要 API 而/0:、/-1、整条路线需要 API 获取maxqlog在离线环境下应优先使用正数闭区间。日志文件压缩格式当前为.zstzstandard兼容历史.bz2_LogFileReader遇到未知扩展名且非旧版未压缩日志会抛ValueError。qlog 与 rlog 的信息量不同qlog 是精简子集测试test_modesL185-L189断言同一分段rlog 消息数 6 × qlog 消息数需要完整 CAN/高频传感器数据时必须用 rlog。小结围绕 tools/lib/README.md 的核心内容可以归纳为三点Route负责把“dongle id 时间戳”映射到每个分段的 rlog/qlog/相机文件路径LogReader负责把“分段范围字符串”解析成具体文件并解码为可迭代的消息流segment range/4、/4:6、/-1、/1:乃至步长切片加/q、/r、/a选择器构成了路线数据的“查询语言”。配合 test_logreader.py 与 test_route_library.py 中的用例可以完整验证每一种取法的行为这也是在编写自己的数据回放、离线挖掘或 CI 校验脚本时的可靠参照。【免费下载链接】openpilotopenpilot is an operating system for robotics. Currently, it upgrades the driver assistance system on 300 supported cars.项目地址: https://gitcode.com/GitHub_Trending/op/openpilot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表