droid-w (1)安装和测试
安装
https://github.com/MoyangLi00/DROID-W
权重改了位置 默认从这里加载



操作指令



网页可视化跟不上实时速度



如果开启了高斯建图 瞬间饱满


执行
cd /home/dongdong/2project/1salm/DROID-W
#一定要装numpy1.x版本 适配torch 不能numpy2.0
pip install rerun-sdk<0.21 # 适配numpy1.x
pip install folium
#================================ 网页可视化 本地gui不可视化 不高斯渲染 保存我们的数据格式
conda activate droid-w
python run.py \
--dataset "/media/dongdong/新加卷/0ubuntu20/1slam/数据/2RTK/hf1_youtian/Location31_0307_sun_11pm" \
--output-dir "/media/dongdong/新加卷/0ubuntu20/1slam/数据/2RTK/hf1_youtian/Location31_0307_sun_11pm/out_slam/droid-w" \
--buffer 800 \
--save-traj \
--droidvis
#================================ 网页可视化 本地gui可视化 高斯渲染 可能会导致内存爆掉 保存我们的数据格式
conda activate droid-w
python run.py \
--dataset "/media/dongdong/新加卷/0ubuntu20/1slam/数据/2RTK/hf1_youtian/location41_fog_8pm" \
--output-dir "/media/dongdong/新加卷/0ubuntu20/1slam/数据/2RTK/hf1_youtian/location41_fog_8pm/out_slam/droid-w" \
--buffer 800 \
--save-traj \
--droidvis \
--gui \
--mapping
# 其他==============可视化===================
configs/droid_w.yaml
gui: False # GaussianSplatting 渲染窗口(需要 mapping 开启)
droidvis: False # Rerun 实时轨迹可视化(纯跟踪也能用)
buffer最大保存关键帧数目,我们这个场景基本上每张都存,600上限,显卡内存会递增。
--save-traj 自带轨迹 影响gps enu 评估要开启
--save-plots-final 庞大不用
--save-video-npz 庞大不用
┌────────────────────┬───────────────────────────────────────────┐
│ 标志 │ 控制 │
├────────────────────┼───────────────────────────────────────────┤
│ --droidvis │ rerun 网页可视化(http://localhost:9876) │
├────────────────────┼───────────────────────────────────────────┤
│ --gui │ 高斯地图 GUI(需配合 --mapping) │
├────────────────────┼───────────────────────────────────────────┤
│ --mapping │ 高斯泼溅 mapping(慢,产出 final_gs.ply 等) │
├────────────────────┼───────────────────────────────────────────┤
│ --save-traj │ traj/(含 ENU 评估输入) │
├────────────────────┼───────────────────────────────────────────┤
│ --save-plots-final │ plots_final/ 不确定度可视化 │
├────────────────────┼───────────────────────────────────────────┤
│ --save-video-npz │ video.npz 关键帧 dump │
└────────────────────┴───────────────────────────────────────────┘
buffer 主要吃显存,几个大数组按 buffer × H × W:
- disps_up、mono_disps_up、uncertainties 等 — 360×640 全分辨率 float32 ≈ 0.92
MB/帧
- images — 360×640×3 float32 ≈ 2.76 MB/帧
每 100 个 buffer 槽 ≈ 600+ MB 显存。350 → 600 大概多吃 1.5 GB 显存,你显卡能撑住就
OK。撑不住就调:
方案 │ 改动 │ 效果 │
├──────────────┼─────────────────────────────────┼─────────────────────────────┤
│ │ configs/droid_w.yaml 里 │ │
│ 提关键帧门槛 │ motion_filter.thresh: 3.0 → │ 同样数据更少关键帧 │
│ │ 5.0~`8.0` │ │
├──────────────┼─────────────────────────────────┼─────────────────────────────┤
│ 减少强制插入 │ force_keyframe_every_n_frames: │ 同上 │
│ │ 9 → 15 或 -1(关) │ │
├──────────────┼─────────────────────────────────┼─────────────────────────────┤
│ 跨帧 stride │ --stride 或 yaml stride: 1 → 2 │ 直接抽稀输入,关键帧自然变少 │
└──────────────┴─────────────────────────────────┴─────────────────────────────┘
雾天序列通常 motion_filter.thresh 调到 5~6 + buffer 500 比较稳。先上 --buffer 600
跑通,再视情况调阈值。
http://localhost:9876
===================================
● 默认是 350,定义在 configs/droid_w.yaml:
tracking:
buffer: 350 # 最多存储 350 个关键帧
buffer 控制系统能同时保留的关键帧数上限。每个关键帧都要在 GPU
上存特征图和相关矩阵,所以 buffer 越大,显存占用越高。
┌─────────────┬──────────────┬──────────────────┐
│ buffer │ 显存大致占用 │ 适用场景 │
├─────────────┼──────────────┼──────────────────┤
│ 350(默认) │ ~8-10 GB │ GPU 空闲、长序列 │
├─────────────┼──────────────┼──────────────────┤
│ 200 │ ~5-6 GB │ 中等显存压力 │
├─────────────┼──────────────┼──────────────────┤
│ 150 │ ~4-5 GB │ 显存紧张时 │
└─────────────┴──────────────┴──────────────────┘
344 张图片的序列,--buffer 150 意味着同时最多跟踪 150
个关键帧,超出的旧帧会被丢弃。对 344
帧的短序列影响不大,但对更长的序列可能影响精度。
建议先释放其他进程的显存再跑,不到万不得已不调小 buffer。
界面可视化问题
1. Rerun 网页空白 — 三个 bug 叠加:
- rr.serve_web(port=...) 在 rerun-sdk 0.20.3 里参数名是 web_port=,不是
port=,所以那行直接 TypeError → 落到 rr.serve() 旧 API,这玩意默认绑
9090(不是 9876),所以打开 9876 看不到东西。
- 紧接着 rr.save(record_path) 在 0.20.3 里没有多 sink 支持,会覆盖前面 serve 的
sink → 数据全写到 .rrd 文件了,网页拿不到。
- 之前 189M 的 rerun_stream.rrd 就是这么来的。
2. 本地 GUI 黑屏 — 这个是设计如此:GUI 进程消费 q_main2vis 队列,而队列只由 mapping
进程喂数据(slam.py:300)。你的配置里 mapping.enable: False,所以 mapping
进程根本没启动,GUI 自然是空的。这个 GUI 是高斯泼溅 mapping 的可视化,跟纯 SLAM
跑无关。
mapping 进程 → q_main2vis 队列 → slam_gui 进程 → 渲染高斯
保存结果



修改记录
# SLAM-GNSS 基准适配器规范文档
## 1. 概览
用途:将任意 SLAM 系统适配到统一带 GPS 图片数据集的基准测试流程
目标:实现多 SLAM 方法在同数据集、同输出格式下的可比较评估
核心原则:GNSS 仅用于后处理,不参与 SLAM 在线优化
## 2. 输入规范
### 2.0 模型权重
- 从 model_cache 目录直接读取权重文件
- 避免运行时重复下载
### 2.1 数据集路径
- 命令行参数:--dataset <dataset-root>
- 图片读取路径:<dataset-root>/images/
- 支持的图片格式:.jpg、.jpeg、.png
- 文件排序:自然排序
- 文件名:保留原始名称,不重命名/复制
### 2.2 配置文件规范
文件路径:<dataset-root>/slam_config/GNSS_config.yaml
配置文件示例:
Camera.color_order: RGB
Camera.cols: 1805
Camera.cx: 910.8785687265895
Camera.cy: 602.174293145834
Camera.fps: 10
Camera.fx: 1193.7076128098686
Camera.fy: 1193.1735265967602
Camera.k1: 0
Camera.k2: 0
Camera.k3: 0
Camera.k4: 0
Camera.model: perspective
Camera.name: NWPU monocular
Camera.p1: 0
Camera.p2: 0
Camera.rows: 1203
Camera.setup: monocular
GNSS_USE: 1
Initial.alt: 126.658
Initial.lat: 31.777688888888889
Initial.lon: 117.36676388888888
必需参数说明:
- 相机内参:Camera.fx、Camera.fy、Camera.cx、Camera.cy、Camera.k1-k4、Camera.p1-p2
- 图像参数:Camera.cols、Camera.rows、Camera.fps、Camera.model、Camera.setup
- GPS 参考原点:Initial.lat、Initial.lon、Initial.alt
- 启用标记:GNSS_USE: 1(必须为1才启用GNSS后处理)
### 2.3 输出目录
- 命令行参数:--output-dir <results-path>
## 3. 数据处理流程
### 3.1 GNSS 后处理规则
- 严禁将 GPS 数据用于:
- Tracking
- Mapping
- Relocalization
- Bundle adjustment
- Loop closure
- 任何在线尺度优化
- 仅在 SLAM 原始位姿产出后,才允许使用 GPS:
- 构建 ENU 真值轨迹
- 估计 SLAM→ENU 的相似变换
- 计算误差指标
- 导出对齐后的位姿
- 生成可视化
### 3.2 相似变换模型
变换公式:
p_enu = s * R * p_slam + t
R_enu = R * R_slam
其中:
- s:尺度因子
- R:3×3 旋转矩阵
- t:3×1 平移向量
### 3.3 鲁棒对齐流程
1. 数据匹配:按图片文件名匹配 SLAM 位姿与 GPS 真值
2. 剔除无效帧:排除 dropped frames
3. 模型估计:
- 随机采样最小 3 点集
- 估计候选相似变换
- 在 ENU 空间计算欧氏残差
4. 内点筛选:
- 保留内点数最多、平均误差最小的模型
- 用全部内点重新拟合最终变换
5. 退化保护:
- 至少需要 3 个有效匹配点
- 跳过非有限位姿
- 对齐失败时输出 status: skipped
### 3.4 EXIF GPS 解析兼容性
必须处理以下情况:
- GPS 信息已解码为字典
- GPS 信息为整数偏移(需调用 exif.get_ifd())
- GPSAltitudeRef 可能为:
- 整数
- 单字节 bytes(b'\x00'、b'\x01')
- 单元素 tuple/list
- 实现辅助函数统一转换为整数
示例辅助函数:
def safe_gps_altitude_ref(value):
"""安全处理GPSAltitudeRef值"""
if value is None:
return 0
if isinstance(value, bytes) and len(value) == 1:
return int(value[0])
if isinstance(value, (list, tuple)) and len(value) == 1:
return int(value[0])
return int(value)
## 4. 输出文件规范
### 4.1 轨迹文件
文件名 | 内容描述 | 格式
trajectory_frames.txt | 所有帧的原始 SLAM 位姿(camera_to_world) | 每行:image_name tx ty tz qx qy qz qw
trajectory_keyframes.txt | 关键帧的原始 SLAM 位姿 | 同上
trajectory_frames_enu.txt | 对齐到 ENU 的帧位姿 | 同上
trajectory_keyframes_enu.txt | 对齐到 ENU 的关键帧位姿 | 同上
trajectory_frames_gps.txt | 帧的 GPS 位置 | 每行:image_name lat lon alt
trajectory_keyframes_gps.txt | 关键帧的 GPS 位置 | 同上
trajectory_truth_enu.txt | ENU 真值轨迹 | 每行:image_name x y z
trajectory_truth_gps.txt | GPS 真值轨迹 | 每行:image_name lat lon alt
位姿格式说明:
- 方向约定:camera_to_world(相机坐标系到世界坐标系)
- 四元数顺序:qx qy qz qw
- 坐标系:右手坐标系
- 单位:平移单位为米,旋转为四元数
### 4.2 评估指标文件
evaluation_metrics.json:
{
"status": "success/skipped/failed",
"error_message": "可选,状态为failed时的错误信息",
"matched_pose_count": 整数,
"alignment_inlier_count": 整数,
"alignment_threshold": 浮点数,
"alignment_scale": 浮点数,
"alignment_rotation_matrix": [[r11, r12, r13], [r21, r22, r23], [r31, r32, r33]],
"alignment_translation": [tx, ty, tz],
"horizontal": {
"RMSE": 浮点数,
"max": 浮点数,
"mean": 浮点数,
"min": 浮点数,
"std": 浮点数
},
"vertical": {
"RMSE": 浮点数,
"max": 浮点数,
"mean": 浮点数,
"min": 浮点数,
"std": 浮点数
},
"total": {
"RMSE": 浮点数,
"max": 浮点数,
"mean": 浮点数,
"min": 浮点数,
"std": 浮点数
},
"dropped_frame_names": ["frame1.jpg", "frame2.jpg", ...],
"dropped_frame_count": 整数,
"per_frame_errors": [
{
"frame": "frame1.jpg",
"timestamp": 可选时间戳,
"horizontal_error": 浮点数,
"vertical_error": 浮点数,
"total_error": 浮点数,
"is_inlier": 布尔值
},
...
]
}
误差定义(单位:米):
- 水平误差:sqrt(dx² + dy²)
- 垂直误差:abs(dz)
- 总体误差:sqrt(dx² + dy² + dz²)
误差统计指标:
- RMSE:均方根误差
- max:最大误差
- mean:平均误差
- min:最小误差
- std:标准差
### 4.3 运行摘要文件
run_summary.json:
{
"dataset_info": {
"dataset_path": "路径字符串",
"image_count": 整数,
"image_extensions": ["jpg", "png", ...],
"original_resolution": {"width": 1805, "height": 1203},
"processed_resolution": {"width": 整数, "height": 整数},
"camera_model": "perspective",
"camera_setup": "monocular"
},
"processing_info": {
"results_dir": "路径字符串",
"start_time": "ISO时间字符串",
"end_time": "ISO时间字符串",
"total_runtime_seconds": 浮点数,
"average_frame_time_ms": 浮点数,
"median_frame_time_ms": 浮点数,
"overall_fps": 浮点数,
"exclude_model_load": 布尔值
},
"resource_usage": {
"peak_cpu_memory_mb": 浮点数,
"peak_gpu_memory_mb": 浮点数,
"average_cpu_usage_percent": 浮点数
},
"pose_statistics": {
"frame_pose_count": 整数,
"keyframe_pose_count": 整数,
"dropped_frame_count": 整数,
"pose_convention": "camera_to_world"
},
"output_files": {
"trajectory_frames.txt": "所有帧的原始SLAM位姿",
"trajectory_keyframes.txt": "关键帧的原始SLAM位姿",
"trajectory_frames_enu.txt": "对齐到ENU的帧位姿",
"trajectory_keyframes_enu.txt": "对齐到ENU的关键帧位姿",
"trajectory_frames_gps.txt": "帧的GPS位置",
"trajectory_keyframes_gps.txt": "关键帧的GPS位置",
"trajectory_truth_enu.txt": "ENU真值轨迹",
"trajectory_truth_gps.txt": "GPS真值轨迹",
"evaluation_metrics.json": "评估指标",
"dropped_frames.txt": "丢帧列表",
"run_summary.json": "运行摘要",
"localization_map.html": "可视化地图"
},
"slam_system": {
"name": "SLAM系统名称",
"version": "版本号",
"parameters": {
"key1": "value1",
"key2": "value2"
}
}
}
### 4.4 其他输出文件
dropped_frames.txt:
# 丢帧列表
# 格式:每行一个文件名
frame001.jpg
frame005.jpg
frame012.jpg
...
localization_map.html:Folium 地图可视化文件
## 5. 可视化规范
### 5.1 地图可视化(Folium)
- 真值轨迹:绿色实线,线宽2
- 估计点标记:
- <1m 误差:绿色圆形标记
- 1-2m 误差:蓝色圆形标记
- ≥2m 误差:橘色圆形标记
- 失败帧:红色叉形标记
- 无GT数据:灰色三角形标记
- 轨迹连线:
- 按顺序连接估计点
- 线段颜色跟随"上一个点"的误差分桶颜色
- 线宽1,透明度0.7
- 固定图例:
<1m (绿色)
1-2m (蓝色)
≥2m (橘色)
无GT (灰色)
失败 (红色)
- 地图设置:
- 初始缩放级别:18
- 包含比例尺
- 包含图层控制
- 标题:数据集名称 + SLAM系统名称
### 5.2 丢帧定义
以下情况视为丢帧:
- Tracker 明确报告丢失跟踪/重定位失败
- 位姿包含非有限值(NaN/Inf)
- 某张图片未得到位姿输出
- GPS数据解析失败
- 时间戳不匹配无法对齐
- 统一导出到 dropped_frames.txt
## 6. 运行时统计
- 总运行时间:从SLAM初始化到结果输出的完整流程时间
- 可选统计模式:若用户明确要求"不包含模型加载时间",则从SLAM处理第一张图开始计时
- FPS计算:处理帧数 / SLAM处理时间
- 逐帧时间:用于计算平均/中位数/标准差耗时
- 资源监控:记录峰值CPU/GPU内存使用
## 7. 实现注意事项
### 7.1 兼容性要求
1. 保持与原始 SLAM 代码的接口兼容
2. 支持多种相机模型(透视、鱼眼等)
3. 处理不同图片尺寸和比例
4. 支持单目、立体、RGB-D等多种设置
### 7.2 鲁棒性要求
1. EXIF解析必须兼容各种GPS格式
2. 相似变换求解必须包含外点剔除
3. 对齐失败时提供清晰的跳过状态和错误信息
4. 处理缺失GPS数据的情况
5. 验证输入数据的有效性
### 7.3 性能要求
1. 避免不必要的图片复制
2. 使用高效的数据结构
3. 支持并行处理(如适用)
4. 内存使用优化
### 7.4 可维护性要求
1. 清晰的代码结构和注释
2. 配置参数可外部调整
3. 详细的日志记录
4. 错误处理和恢复机制
5. 单元测试和集成测试
### 7.5 输出质量要求
1. 所有输出文件格式统一
2. 数值精度一致(建议保留6位小数)
3. 文件编码:UTF-8
4. 行结束符:LF(Unix风格)
5. JSON文件格式良好,便于解析
## 8. 使用示例
### 8.1 命令行调用
python run_slam_gnss_adapter.py \
--dataset /path/to/dataset \
--output-dir /path/to/results \
--slam-system orb-slam3 \
--exclude-model-load
### 8.2 目录结构
/path/to/dataset/
├── images/
│ ├── frame_000001.jpg
│ ├── frame_000002.jpg
│ └── ...
├── slam_config/
│ └── GNSS_config.yaml
└── (可选)gps_timestamps.txt
/path/to/results/
├── trajectory_frames.txt
├── trajectory_keyframes.txt
├── trajectory_frames_enu.txt
├── trajectory_keyframes_enu.txt
├── trajectory_frames_gps.txt
├── trajectory_keyframes_gps.txt
├── trajectory_truth_enu.txt
├── trajectory_truth_gps.txt
├── evaluation_metrics.json
├── dropped_frames.txt
├── run_summary.json
└── localization_map.html
### 8.3 GNSS_config.yaml 完整示例
# 相机参数
Camera.color_order: RGB
Camera.cols: 1805
Camera.cx: 910.8785687265895
Camera.cy: 602.174293145834
Camera.fps: 10
Camera.fx: 1193.7076128098686
Camera.fy: 1193.1735265967602
Camera.k1: 0
Camera.k2: 0
Camera.k3: 0
Camera.k4: 0
Camera.model: perspective
Camera.name: NWPU monocular
Camera.p1: 0
Camera.p2: 0
Camera.rows: 1203
Camera.setup: monocular
GNSS_USE: 1
# 初始参考点(ENU坐标系原点)
Initial.alt: 126.658
Initial.lat: 31.777688888888889
Initial.lon: 117.36676388888888
## 9. 故障排除
### 9.1 常见问题
1. GPS解析失败:检查EXIF格式,实现兼容性辅助函数
2. 对齐失败:检查轨迹长度,确保有足够运动
3. 内存不足:分块处理大尺寸图片
4. 时间不同步:检查时间戳对齐
5. 坐标系不一致:验证坐标系约定
### 9.2 调试输出
实现以下调试选项:
- --verbose:详细日志
- --debug:调试信息,保存中间结果
- --visualize:实时显示处理过程
- --save-intermediate:保存中间数据
### 9.3 验证步骤
1. 验证输入图片和GPS数据数量匹配
2. 验证相机参数有效性
3. 验证ENU转换正确性
4. 验证误差计算准确性
5. 验证输出文件完整性
## 10. 命令行标志位扩展(DROID-W / WildGS-SLAM 实现)
为避免修改 yaml 即可控制运行行为,所有可视化、保存、mapping 都通过命令行标志位控制,**默认全部关闭**。命令行优先级高于 yaml。
### 10.1 标志位总表
| 标志 | 默认 | 控制内容 | 备注 |
|---|---|---|---|
| `--droidvis` | 关 | rerun 网页可视化 | 启动后访问 http://localhost:9876 |
| `--gui` | 关 | MonoGS 风格高斯地图 GUI 窗口 | 必须配合 `--mapping` 才有数据 |
| `--mapping` | 关 | 高斯泼溅 mapping 进程 | 慢;产出 `final_gs.ply` 等大文件 |
| `--save-traj` | 关 | `traj/` 目录(位姿 + GT 评估图) | **关闭则 ENU 评估也跳过** |
| `--save-plots-final` | 关 | `plots_final/` 不确定度可视化 | 每关键帧 8 子图 + GIF,可达数百 MB |
| `--save-video-npz` | 关 | `video.npz` 关键帧 dump | 全分辨率 RGB+视差,可达数 GB |
### 10.2 实现要点
- `run.py` 解析后对 cfg 做无条件覆盖:
```python
cfg['droidvis'] = bool(args.droidvis)
cfg['gui'] = bool(args.gui)
cfg['mapping']['enable']= bool(args.mapping)
cfg['save_traj'] = bool(args.save_traj)
cfg['save_plots_final'] = bool(args.save_plots_final)
cfg['save_video_npz'] = bool(args.save_video_npz)
```
- 在 `slam.py:terminate()` 内的每个落盘点用 `self.cfg.get(...)` 读对应标志位,按需 gate。
- `--save-traj` 关闭时,`run_enu_evaluation` 找不到 `traj/est_poses_full.txt`,会打印 `WARNING: Trajectory not found` 然后正常退出(已在 `enu_eval.py:486-489` 兜底)。
### 10.3 典型调用
```bash
# 最简:只跑 SLAM,不存任何评估/可视化产物
python run.py --dataset <PATH> --output-dir <OUT>
# SLAM + 网页可视化
python run.py --dataset <PATH> --output-dir <OUT> --droidvis
# SLAM + ENU/GPS 评估
python run.py --dataset <PATH> --output-dir <OUT> --save-traj
# 完整:SLAM + mapping + GUI + rerun + 全部产物
python run.py --dataset <PATH> --output-dir <OUT> \
--droidvis --mapping --gui \
--save-traj --save-plots-final --save-video-npz
```
## 11. 已知 Bug 与适配修复
### 11.1 自定义数据集(无 GT 位姿)触发轨迹评估崩溃
**症状**:跑 `CustomDataset`(无 GT 位姿)时,`terminate()` 抛 `TypeError: 'NoneType' object is not subscriptable`,紧跟着第二个 `TypeError: can only concatenate str (not "TypeError") to str` 把整个 tracking 进程杀掉,`est_poses_full.txt` 没生成,ENU 评估失败。
**根因**:
1. `CustomDataset` 直接继承 `BaseDataset`,**没继承** `RGB_NoPose`,所以 `slam.py` 里 `not isinstance(stream, RGB_NoPose)` 判断为 True,进入 GT 评估分支,访问 `stream.poses[ts]`(`poses=None`)爆掉。
2. 异常处理 `self.printer.print(e, FontColor.ERROR)` 把 `TypeError` 对象当字符串拼接,触发**第二次** `TypeError`,搞死 tracking 进程。
**修复**:
- 把所有 `isinstance(stream, RGB_NoPose)` 检测改成语义化的 `getattr(stream, "poses", None) is None/None is not None`
- `src/slam.py:153, 180`
- `src/utils/eval_traj.py:163`
- `src/tracker.py:82`
- `src/utils/Printer.py:39-55` `Printer.print` / `TrivialPrinter.print` 把 msg 强制 `str(msg)`,保护异常对象、数值等非字符串入参。
**经验**:异常处理路径里的字符串拼接是常见次生故障源,永远 `str()` 包一层。
### 11.2 rerun 包名冲突(tartley/rerun vs rerun-sdk)
**症状**:`AttributeError: module 'rerun' has no attribute 'init'`。
**根因**:PyPI 上有两个不相干的包都注册了 `rerun` 这个导入名:
- `rerun`(tartley/rerun,文件变更监视器,与可视化无关)
- `rerun-sdk`(实际的可视化 SDK,导入名也是 `rerun`)
环境里装的是前者。pip warning 也明说:"If you are looking for the Rerun robotics library, please install the rerun-sdk package from pypi instead."
**修复**:
```bash
pip uninstall -y rerun
pip install "rerun-sdk<0.21" "numpy<2"
```
**为什么 `<0.21`**:直接 `pip install rerun-sdk` 装到 0.31.4,强制把 numpy 升级到 2.x,然后 torch 2.1.0(按 numpy 1.x ABI 编译)报 `Failed to initialize NumPy: _ARRAY_API not found`,所有 torch 相关代码崩。`rerun-sdk 0.20.x` 是兼容 numpy 1.x 的最后一个稳定线。
### 11.3 rerun-sdk 0.20 `serve_web` 参数名变化
**症状**:传 `--droidvis` 跑起来,浏览器开 9876 看不到数据。
**根因**:rerun-sdk 0.20 的 `serve_web` 签名是
```python
serve_web(*, open_browser=True, web_port=None, ws_port=None, ...)
```
旧代码 `rr.serve_web(port=web_port, ...)` 用 `port=` → `TypeError` → 走异常分支 `rr.serve()`(已废弃,绑定默认 9090,**不是** 9876),用户开 9876 自然空白。
**修复**:`src/utils/droid_visualization_rerun.py` 改用 `web_port=` 关键字,并显式指定 `ws_port=9877`:
```python
rr.serve_web(web_port=web_port, ws_port=9877, open_browser=auto_browser)
```
保留 `port=` 作为旧版兼容回退。
### 11.4 rerun-sdk 0.20 的 sink 互斥问题
**症状**:网页 server 启动后,紧接着 `rr.save(record_path)`,结果 `.rrd` 文件正常生成,但网页空。
**根因**:rerun-sdk 0.20 **不支持多 sink**——`rr.save()` / `rr.serve_web()` / `rr.connect()` 等任何一个 sink 设置函数都会**替换**当前 recording 的活跃 sink,没有 `rr.set_sinks(...)` 这种 API(要 0.22+ 才有)。后调用的覆盖前调用的。
**修复**:`droid_visualization_rerun.py` 只在 web server 启动**失败**时才落盘到 `.rrd`,二者互斥。如果用户确实要录像,应该自己关掉 `--droidvis` 单独录。
### 11.5 rerun 网页打开是欢迎页/广告页
**症状**:浏览器手动开 `http://localhost:9876`,显示 rerun.io 官方介绍页 / 欢迎页,看不到 SLAM 数据。
**根因**:`http://localhost:9876` 只是静态前端,前端要从 query 参数 `?url=ws://...` 拿到 WebSocket 数据流地址才能连。手动输入的 URL 没这个参数 → 前端 fallback 到欢迎页。
**修复**:
- 设 `open_browser=True`,让 rerun 自己拼好带参数的 URL 弹浏览器。
- 不再无脑 `os.environ.pop("DISPLAY", None)`——只在没显示器时才置 offscreen,否则保留 DISPLAY 让浏览器能弹。
- 启动后**显式打印**完整 URL 供手动访问:
```
http://127.0.0.1:9876/?url=ws://127.0.0.1:9877
```
### 11.6 经验小结
| 类型 | 教训 |
|---|---|
| 数据集兼容 | 用 `stream.poses is None` 这种语义判断,别用 `isinstance` 锁死类型 |
| 异常路径 | `printer.print(exc)` 务必 `str()` 包一层 |
| 包名冲突 | PyPI 有同名包时,先 `pip show` 确认装的是哪个 |
| 大版本依赖 | rerun-sdk / numpy / torch 要锁版本组合:`torch 2.1 + numpy 1.x + rerun-sdk 0.20.x` |
| API 漂移 | rerun `port` → `web_port`、单 sink → 多 sink,跨版本要试 keyword + 异常回退 |
| Web viewer | `serve_web` 必须靠 `?url=ws://...` 参数才能连数据,要么 `open_browser=True`,要么手动拼 URL |
文档版本:1.1
最后更新:2026-05-07
适用对象:SLAM系统开发者、评估人员、研究人员
浙公网安备 33010602011771号