简介nuScenes数据集的下载与解压环节相对繁琐较大且分块的zip文件、地图扩展Map expansion、CAN bus expansion等目录若不妥善放置会直接影响后续模型训练。这套源码正是面向自动驾驶感知、定位与地图构建方向的开发者提供配套的数据准备与解压逻辑帮助规避常见路径错误。压缩包体积仅6KB共3个文件inscode适合在线环境集成与启动HTML文件帮助快速理解项目结构与操作说明.gitignore则用于规范工程版本管理。目前已有341人学习使用适合需要快速上手真实道路多模态数据处理的中高级研发人员。借助该资源可掌握Trainval/Test部分下载后的目录安置方法明确maps文件夹的创建时机并借鉴unzip与shell脚本批量解压的思路有效提升多压缩包数据集准备效率。代码量虽小但围绕数据落盘、解压验证的细节要点集中便于直接嵌入个人数据准备流程。1. 先别急着点下载数据准备阶段最容易翻车的不是网速做过一次 nuScenes 数据集下载解压的人都知道真正花时间的不是把下载链接丢进 wget而是解压、校验和目录整理这三步。网速再快文件不完整、路径错位后面跑训练脚本时照样报错。这份源码包把nuScenes 数据集下载解压的完整流程——并发下载、断点续传、pgp 校验、分批解压——封装成了可以直接改参数就跑的脚本配套一份按文件夹拆解的目录说明。适合正在做多传感器融合、BEV 感知、目标检测又不想在数据准备上反复折腾的开发者。这篇笔记把脚本怎么用、参数怎么调、哪些坑值得躲按我实际跑过的顺序写清楚。2. nuScenes 目录结构速览下载之前先搞清这四个文件夹2.1 四类文件分开放读取入口在 JSON 里nuScenes 数据集不像普通分类数据集那样一个大文件夹装完它把原始数据、标注、地图、传感器状态拆成四类分散在不同目录里。下载之前先搞清楚这四类文件的物理位置和逻辑关系能少走很多弯路。目录层面的组织方式大致如下目录装了什么说明samples关键帧的完整传感器数据标注只挂在关键帧上训练时主力读取sweeps相邻帧的传感器数据体积最大用于时序模型和历史上下文maps地图位图 矢量数据需要和标注 JSON 配合坐标对齐sensor传感器标定与位姿文件多传感器融合必用体积很小根目录 JSON标注、场景、实例关系表devkit 的get()入口全在这里这五类文件里samples和sweeps是磁盘占用的大头也是下载中断后最容易出问题的地方。maps看起来不起眼但缺少它BEV 视角下地图叠加就会黑屏。根目录的 JSON 标注文件通常只有几 MB却是整个数据集的索引中枢没有它后面所有传感器数据都变成一堆无主文件。第一次拿到这个资源包时我建议先把里面的目录说明文件打开对着上面表格去对应实际解压出来的文件夹。很多人下载完直接跑训练脚本报错后才发现maps解压到了一级目录而 devkit 默认去二级目录找两边路径差一级读出来全是缺文件。2.2 标注和地图的关系坐标对齐是这里最容易被忽略的一步samples里的每一条关键帧记录都有一个唯一的 token标注 JSON 通过 token 关联到对应的传感器数据。你从 devkit 里get(sample, token)拿到的是一整个字典里面包含了这一帧所有传感器的数据文件路径和标注信息。换句话说标注不是单独的文件而是 JSON 里按 token 组织起来的字段集合。maps目录下的地图文件则是另一种形式它提供的是一个统一坐标系下的静态底图。车辆位姿、传感器外参标定都在sensor目录里要把相机拍到的东西投影到地图上需要先把这几个坐标系串起来。常见的做法是先用 devkit 里的get(calibrated_sensor, token)拿外参再通过位姿记录把数据转到全局坐标系。我在某次做 BEV 可视化时遇到过把地图和点云叠在一起但对不齐的情况。排查到最后发现是加载顺序的问题先加载了地图后加载了校准文件坐标变换的顺序反了两边的数据在数学上就相差一个旋转矩阵。这个坑在目录结构里看不出来但数据准备阶段如果下载解压时缺了sensor目录下的某个标定文件类似的错位现象就会在其他机器上复现。2.3 mini 版与完整版的取舍磁盘空间按三倍预留资源包里通常会说明有 mini 版和完整版两种拉取方式。mini 版适合先把代码流程跑通占用的磁盘空间在十 GB 以内解压后可以完整验证 devkit 的读取逻辑完整版则是几十 GB 起步的大包需要按「压缩包 解压后数据 中间临时文件」三份空间来预留加起来往往超过磁盘余量的六成。我一般会这样处理先下载 mini 版把第 5 章的 devkit 体检脚本跑一遍确认读取链路没有路径问题再去拉完整版。完整版就不要用同一个目录了单独建一个data/nuscenes_full避免和 mini 版的文件混在一起。混放是另一个高频翻车点因为两版的标注 JSON 结构不完全一样devkit 加载时会去读版本字段版本不一致会报错。如果你只做目标检测不需要时序信息sweeps目录可以先不下载如果做多帧融合或轨迹预测sweeps是必需品。下载前把需求写清楚能省下大量磁盘空间。另外完整版的下载脚本建议在后台跑日志重定向到文件里方便第二天醒来排查断点。3. 跑通下载脚本参数调整、断点续传与分批解压3.1 下载脚本的三个配置项链接前缀、访问令牌与并发数这个源码包里的下载脚本是download_nuscenes.sh它需要三个运行时参数数据落地目录、访问令牌、下载前缀。访问令牌从发布方网站注册后获取下载前缀是数据文件所在的 URL 根路径。注意前缀里如果带了签名参数通常是有时效的过期后之前的下载进度全部作废这一点后面第 4 章会专门展开。#!/usr/bin/env bash # 用法: ./download_nuscenes.sh data_dir token base_url set -euo pipefail DATA_DIR${1:?需要数据落地目录} TOKEN${2:?需要访问令牌} BASE_URL${3:?需要下载前缀} mkdir -p $DATA_DIR # 先由 python 脚本生成待下载文件清单 python3 prepare_manifest.py $BASE_URL $TOKEN $DATA_DIR/manifest.txt while read -r remote local; do if [ -f $DATA_DIR/$local ] [ -s $DATA_DIR/$local ]; then echo 跳过已存在文件: $local continue fi curl -L -C - --retry 3 --retry-delay 5 \ -H Authorization: Bearer $TOKEN \ -o $DATA_DIR/$local.part \ $remote mv $DATA_DIR/$local.part $DATA_DIR/$local done $DATA_DIR/manifest.txt脚本的核心逻辑是「先清单后下载」。prepare_manifest.py负责把远程目录的文件列表拉下来生成一行一个的清单文件每行由远程 URL 和本地相对路径构成。curl -C -是断点续传的关键参数它让 curl 自动从文件当前大小处继续拉取而不是从头再来。--retry 3 --retry-delay 5表示失败后等 5 秒重试一次最多重试三轮对弱网环境很管用。这里有个细节值得注意-o指定输出到.part临时文件下载完成后才通过mv改名。这样做的好处是断点续传判断「文件是否已存在」时只认完整文件不认半截的临时文件万一下载中断重跑脚本时.part会被重新覆盖不会污染已有数据。set -euo pipefail是 bash 的严格模式任何一条命令失败就退出避免带病继续跑下去。如果你在下载完整版建议把脚本放到nohup或screen里执行日志重定向到download.log。用tail -f download.log观察进度比盯着终端窗口靠谱得多。并发的问题下面单独说。3.2 校验脚本md5 比对为什么不能省下载完成后最忌直接解压。数据包在传输过程中被截断、被篡改或者服务器返回了错误页面这些情况在压缩包层面试不出来只有解压到一半报错时你才知道出问题了而那往往是几个小时之后。源码包里的verify_checksums.py解决的就是这个「后悔药」问题。import hashlib import json import sys from pathlib import Path def md5_of(path: Path, chunk_size: int 1024 * 1024) - str: 分块计算 md5避免一次性读入大文件占用过多内存 h hashlib.md5() with path.open(rb) as f: while chunk : f.read(chunk_size): h.update(chunk) return h.hexdigest() def main(manifest_path: str, data_dir: str) - int: entries json.loads(Path(manifest_path).read_text()) failed [] for entry in entries: local Path(data_dir) / entry[local] if not local.exists(): failed.append((entry[local], 文件缺失)) continue if md5_of(local) ! entry[md5]: failed.append((entry[local], 校验不通过)) if failed: for name, reason in failed: print(f[FAIL] {name}: {reason}) return 1 print(全部文件 md5 校验通过) return 0 if __name__ __main__: sys.exit(main(sys.argv[1], sys.argv[2]))这里有两个关键设计。第一分块读取chunk_size设为 1 MB每次只把 1 MB 数据读进内存对几十 GB 的大文件也能控制在较低内存占用。如果一次性读整个文件机器内存小的话算到一半就可能被系统杀掉。第二清单文件里不仅记录下载地址还记录了每个文件的 md5 值校验脚本逐个比对输出失败文件清单。实际跑的时候manifest.json如果是从发布方同步下来的校验结果可信如果清单本身是脚本生成的你需要自己确认清单的 md5 来源。最稳妥的做法是第一轮用verify_checksums.py跑出失败名单第二轮针对失败文件单独重下重下之后再跑第三轮。三轮校验全绿才轮到解压步骤。3.3 分批解压策略先 samples 后 sweeps 的实测理由解压也有顺序讲究。源码包里的extract_batches.sh是按「小文件优先、大文件缓冲」的顺序组织的先解压maps和 JSON然后是samples最后才轮到体积最大的sweeps。#!/usr/bin/env bash set -euo pipefail ARCHIVES_DIR$1 DEST_DIR$2 # 先解体积小的标注与地图再解大体积传感器数据 for batch in maps samples sweeps; do for archive in $ARCHIVES_DIR/${batch}_*.tgz; do tar -tzf $archive /dev/null || { echo 损坏压缩包: $archive; exit 1; } tar -xzf $archive -C $DEST_DIR done done脚本里tar -tzf这一步是解压前的完整性预检它只列出压缩包里的文件清单不实际写入磁盘。预检通过后再真正解压最大程度避免解到一半才发现压缩包损坏。这个顺序背后的实测理由是maps和 JSON 文件几秒就解压完先落地可以立即配合 devkit 做一次小规模读取验证如果目录结构有错当场就能发现不用等到sweeps解完。ARCHIVES_DIR是存放压缩包的目录DEST_DIR是解压目标目录。注意这两个目录不要放在同一个磁盘分区下会造成读写互踩。如果你把压缩包和解压数据放在同一个机械硬盘上解压时磁头来回跳速度会明显下降有条件的话压缩包放一块盘解压目标放另一块盘速度提升立竿见影。如果在解压过程中磁盘满了tar会直接报No space left on device已经解压出来的部分文件是完整的但后续文件缺失。这时不要立刻删除重来先把DEST_DIR里已有文件和清单比对缺哪些补哪些能用verify_checksums.py精确找出缺口再单独补下对应压缩包。4. 下载解压常见问题排查四条能救命的踩坑记录4.1 下载到一半报 403临时链接过期与签名失效现象下载脚本跑了几十分钟突然一批文件连续报 403 Forbidden日志里 curl 退出码为 22重试几轮依旧失败。原因发布方提供的下载 URL 通常是带签名参数的临时链接有效期从几分钟到几小时不等。脚本里用的BASE_URL如果是在下载开始前一小时生成的跑到中途签名过期服务器就拒绝服务了。这不是网络问题也不是令牌问题是 URL 签名本身有时效。解决重新登录发布方后台生成新的下载前缀替换BASE_URL后再跑脚本。因为有断点续传逻辑已经完整下载的文件会被跳过不需要从头开始。从那次之后我习惯把「生成新链接」作为一个独立步骤写进每日流程凡是下载时长预计超过签名有效期的任务一律把prepare_manifest.py放在下载循环内部下载前重新获取一次链接。4.2 解压报 unexpected end of file校验永远跑在解压前现象tar -xzf解压到一半终端报unexpected end of file或gzip: invalid compressed data进程退出已解压文件不完整。原因压缩包在下载过程中被截断但下载脚本认为「文件已存在」就跳过了。通常是因为上一次下载中断时.part文件被误改名为完整文件或者curl -C -断点续传时服务器返回了完整文件内容导致文件尾部多出半个数据块。解决不要直接重传先把所有压缩包对应的 md5 跑一遍确认损坏范围。损坏的压缩包删除后重新下载下载完成后再校验一次。这条原则我强制执行任何情况下解压命令前必须先跑校验脚本校验全绿才允许解压。顺序一旦反了排查时间至少翻倍。4.3 数据读取为空路径错配是头号错觉现象devkit 加载NuScenes(version...)成功但sample里的传感器文件路径读出来是无效的训练脚本打印一堆 FileNotFoundError。原因大部分情况不是文件缺失而是路径前缀不一致。devkit 用dataroot参数拼接数据路径如果你解压到/data/raw但传给NuScenes的是/data/raw/nuscenes多出的这一级目录就把所有路径指向了错误位置。另一个常见情况是samples和sensor解压到了不同父目录下JSON 标注里的相对路径找不到对应文件。解决先核实解压后的目录树确认根目录下就是samples、maps、sensor这几个一级文件夹再确认传给 devkit 的dataroot指向它们的父目录。验证时不要只打印一个文件路径把所有传感器的文件名都打印出来逐个检查存在性。我一般会把第 5 章的体检脚本放在每个新环境里跑一遍而不是靠眼睛检查目录结构。4.4 断点续传翻车不是所有服务器都买 curl 的账现象curl -C -断点续传时服务器重新返回了 200 而不是 206curl 直接把文件从头开始重传之前几 GB 的进度全部作废。原因部分服务端不支持断点续传或者 URL 签名绑定的是完整文件下载不接受 Range 请求。curl 发现服务端不响应 Range 时默认行为是从零开始拉取并覆盖文件而且不会报错。解决在下载脚本里加一层保护下载前先检查本地.part文件大小如果服务端返回 200立刻中止脚本并提示用户重新生成下载链接。另外根据资源包里的说明部分发布方推荐使用官方的下载工具而不是 curl官方工具对分片下载和断点续传的支持更完整。遇到上述情况换成官方工具基本能解决。经验之谈续传前先确认服务器支持否则不如一开始就老老实实一次性下完。5. 装完先别急着删包用 devkit 做一次 5 分钟数据体检数据解压完成、校验通过这只是准备工作结束。真正验证nuScenes 数据集下载解压是否成功要看 devkit 能不能按 token 正确读取到每一类数据。源码包里附带了一个体检脚本把读取逻辑封装好了拿到数据后先跑它而不是直接开训练。from nuscenes.nuscenes import NuScenes from pathlib import Path # 初始化数据集dataroot 指向解压根目录 nusc NuScenes(versionv1.0-mini, dataroot./data/nuscenes, verboseTrue) # 取第一个场景的第一帧 sample scene nusc.scene[0] sample nusc.get(sample, scene[first_sample_token]) # 遍历该帧下所有传感器通道打印数据文件相对路径 for channel, token in sample[data].items(): record nusc.get(sample_data, token) print(f{channel:20s} - {record[filename]}) # 检查文件物理是否存在 missing [] for channel, token in sample[data].items(): record nusc.get(sample_data, token) full_path Path(nusc.dataroot) / record[filename] if not full_path.exists(): missing.append(record[filename]) print(缺失文件:, missing if missing else 无全部通过)这个脚本做的事分三层。第一层NuScenes(version...)初始化时devkit 会读取根目录下的 JSON 标注如果版本字段和数据不匹配这里就会报错这是最快的完整性检查。第二层通过scene到sample再到sample_data的链式查询验证标注 token 关系是否正确这一步能暴露 JSON 文件损坏或版本混用问题。第三层把各传感器文件的实际路径和磁盘文件比对确认物理文件不缺。体检通过后我还会随手统计一下各类文件数量和清单里的数量对比。数量对不上说明有漏解压的文件需要回去补。这套流程走完后压缩包才可以安全清理——注意清理前确认校验记录还在如果完整版后续要加新的sweeps数据没有校验记录对照会很麻烦。当初我第一次跑完下载解压兴冲冲地直接开训练结果在数据加载阶段卡了一个下午。从那以后我每次拿到新数据都强制走一遍「校验清单 → 跑 devkit 体检 → 统计文件数量」这三步看起来多花五分钟实际上省掉的是未来可能出现的两小时玄学排错。希望帮到你。本文还有配套的精品资源点击获取