1. 项目概述与核心价值
最近在折腾一个挺有意思的开源项目,叫“THERION-SYSTEM”。这名字听起来有点神秘,像是某种地下探测或者洞穴测绘系统的代号。实际上,它也确实和这个领域紧密相关。简单来说,THERION-SYSTEM 是一个围绕“Therion”这款洞穴测绘软件构建的、用于数据采集、处理、可视化和管理的集成化系统方案。如果你是一个洞穴探险者、地质调查员、或者对三维空间数据采集与建模感兴趣的开发者,这个项目可能会让你眼前一亮。
我最初接触它,是因为团队需要为一个地下设施做数字化建档。市面上通用的三维扫描和建模方案要么太贵,要么在复杂、非结构化的地下环境中表现不佳。Therion 本身在洞穴测绘圈子里是鼎鼎大名的开源工具,但它更像一个强大的“后端引擎”,需要配合特定的数据采集流程和前端展示工具才能发挥最大价值。而erevusobolus/THERION-SYSTEM这个项目,在我看来,就是试图把这一整套流程给“产品化”和“系统化”,降低从野外数据采集到最终成果输出的门槛。
它的核心价值在于,将专业的洞穴测绘流程,通过相对平民化的硬件(如消费级运动相机、手机、激光测距仪等)和开源软件栈串联起来,形成一个从数据源到三维模型再到专题图输出的完整闭环。这不仅仅是几个脚本的堆砌,而是包含了对设备选型、数据格式转换、处理参数优化、成果集成展示等一系列环节的思考与实践总结。对于小型探险队、科研小组或预算有限的工程团队而言,这套系统提供了一个极具参考价值的起点。
2. 系统架构与核心组件拆解
要理解 THERION-SYSTEM,不能把它看成一个单一的软件,而应视为一个由多个软硬件模块协同工作的“数据流水线”。我们可以将其架构分解为四个核心层次:数据采集层、数据处理与转换层、核心建模层,以及成果输出与展示层。
2.1 数据采集层:硬件的选择与适配
这一层决定了原始数据的质量。项目通常不限定具体硬件型号,但会推荐几类经过验证的设备组合。
视觉采集设备:这是获取空间影像数据的关键。高端方案会使用经过标定的多目相机阵列或全景相机,但对于大多数应用,项目更倾向于使用 GoPro 之类的运动相机,甚至智能手机。选择它们的理由很直接:成本低、易获取、体积小便于在狭窄空间操作。不过,这里有个关键点:相机必须能输出带有精确时间戳的影像,并且最好能同步记录惯性测量单元(IMU)数据(如加速度计、陀螺仪信息)。这些元数据对于后续的视觉惯性里程计(VIO)或同步定位与地图构建(SLAM)算法至关重要。
注意:使用消费级设备的最大挑战在于标定和稳定性。相机的内参(焦距、畸变系数)需要在处理前精确获取。THERION-SYSTEM 的配套脚本通常会包含使用棋盘格或AprilTag进行相机标定的指导。此外,运动相机在低光环境下的画质下降和果冻效应,是需要在实际操作中密切关注的。
辅助测量设备:纯视觉SLAM在长距离、弱纹理或重复结构的环境中容易产生累积误差。因此,系统设计上会融合绝对测量数据。最常用的是激光测距仪,用于手动测量关键点之间的距离(例如,从洞穴入口到第一个支洞拐角的距离)。更专业的方案会集成激光雷达(LiDAR),如Livox Mid-40或Hesai Panda系列,它们能提供高精度的点云,与视觉数据互补。此外,数字罗盘和倾角仪用于测量方位角和倾斜角,为模型提供绝对的方向参考。
数据同步方案:这是采集层的“神经中枢”。所有设备的数据必须基于统一的时间轴。简单的做法是,在每次采集任务开始和结束时,用一个明显的视听信号(如闪灯、拍手)在所有设备的记录中制造一个同步标记。更可靠的方法是使用GPS时间同步模块(在洞口有信号时)或高精度晶振产生的同步脉冲信号。项目文档中一般会强调同步的重要性,并提供基于硬件触发或软件后同步的几种方案。
2.2 数据处理与转换层:从原始数据到 Therion 输入
这一层是“脏活累活”最多的地方,也是系统自动化的价值体现。原始数据(视频、IMU、激光点云、手动测量笔记)是杂乱无章的,需要被清洗、对齐、并转换成 Therion 能够识别的格式。
视觉惯性数据处理流程:这是最核心的环节。通常的流程是:
- 视频抽帧与IMU对齐:将视频按一定频率(如每秒2-5帧)抽取图像,并将IMU数据流与图像时间戳精确对齐。这里需要使用插值算法,因为IMU频率(通常几百Hz)远高于图像频率。
- 运行SLAM/VIO算法:使用开源的SLAM库,如ORB-SLAM3、VINS-Fusion或OpenVSLAM,处理图像和IMU数据,生成相机运动轨迹(位姿)和稀疏三维点云。
erevusobolus/THERION-SYSTEM的关键贡献之一,可能就是提供了针对地下环境调优过的SLAM配置参数文件。例如,增大特征点提取的阈值以适应光照不均,或关闭闭环检测以避免在相似洞道中误匹配。 - 尺度恢复与轨迹优化:单目VIO估计的轨迹缺乏真实尺度。这时,前期用激光测距仪测量的绝对距离就派上用场了。通过将SLAM轨迹中对应两点间的估计距离与真实测量距离进行比对,可以求解出一个尺度因子,将整个轨迹和地图缩放到真实尺寸。同时,手动测量的“导线”(由多个测站和距离、角度构成的测量网络)可以作为强约束,对SLAM轨迹进行全局捆绑调整(Bundle Adjustment),大幅降低累积误差。
点云数据处理与融合:如果使用了激光雷达,会得到独立的点云数据。需要利用SLAM提供的轨迹(经过尺度恢复和优化后),将每一帧LiDAR点云转换到全局坐标系下,拼接成完整的稠密点云。接着,需要将视觉SLAM生成的稀疏点云与LiDAR稠密点云进行配准融合,得到一个兼具精度和细节的复合点云。
数据格式转换:最终,优化后的相机位姿、稀疏特征点云、以及/或者融合后的稠密点云,需要被转换成 Therion 的输入格式。Therion 主要接受两种类型的输入:
.th文件:这是 Therion 的脚本文件,用文本描述测站(station)、测线(survey)之间的连接关系和测量数据(距离、方位角、倾斜角)。可以从优化后的SLAM轨迹中,提取出关键帧的位置作为“测站”,并计算出站与站之间的矢量关系,自动生成.th文件。- 三维点云文件:通常是
.ply或.xyz格式,作为模型表面的参考或直接用于建模。
这一层通常由一系列 Python 或 MATLAB 脚本构成,它们调用各种开源库,形成一个自动化处理管线。
2.3 核心建模层:Therion 的工作流
数据转换层准备好“食材”后,就交给了“大厨”Therion。Therion 本身是一个功能极其强大的洞穴地图编译器,它允许用户通过编辑文本文件来定义地图元素,并支持从外部导入三维数据。
Therion 项目文件组织:一个标准的 Therion 项目包含几个核心文件:
project.th:主项目文件,用于包含(input)其他文件和组织整个项目结构。survey.th:定义具体的测量数据,即“导线”。里面包含了测站(如1.0,1.1)和测量命令(如1.0 1.1 5.6 120 -5表示从测站1.0到1.1,距离5.6米,方位角120度,倾斜角-5度)。在 THERION-SYSTEM 中,这个文件很大程度上可以由数据处理层自动生成。map.th:定义地图的视觉表现,比如用什么符号表示石笋、水流,洞道的墙壁用什么线型,如何渲染剖面图等。model.th:定义三维模型,可以关联到.th文件描述的导线,也可以直接嵌入外部点云(.ply)作为表面模型。
从数据到模型:在 THERION-SYSTEM 的上下文中,流程通常是:
- 将自动生成的
survey.th导入。 - 在
model.th中,引用这个 survey,并指定使用“wall”命令来自动生成基于测线的三维隧道模型。Therion 会根据测线和预设的通道截面形状(如椭圆形、矩形)自动拉伸成三维体。 - 更高级的用法是,将融合后的稠密点云(
.ply)作为“surface”数据附加到模型上。这样,Therion 生成的平滑隧道模型就有了真实、粗糙的表面纹理,精度和真实感大幅提升。 - 运行
therion project.th命令进行编译。Therion 会进行一系列计算,最终输出可交互的 3D PDF 文件、静态的矢量图(SVG/PDF)和栅格图(PNG),以及三维模型文件(如.obj)。
2.4 成果输出与展示层:Web可视化与集成
Therion 编译出的 3D PDF 非常酷,但交互性仍有限,且难以集成到Web应用或地理信息系统中。因此,一个完整的 SYSTEM 必然包含成果展示环节。
三维 Web 可视化:这是当前的主流需求。通常的做法是,将 Therion 输出的三维模型(如.obj文件及其材质)或者直接使用处理阶段生成的融合点云(.ply),通过工具转换为适用于 WebGL 的格式,例如 glTF(.glb)或 3D Tiles。然后,利用 Three.js、CesiumJS 或 Potree(专门用于大规模点云)等库,构建一个在浏览器中可自由旋转、缩放、平移的在线三维查看器。
属性信息关联:一个先进的系统不会只展示几何模型。THERION-SYSTEM 的理想形态应该支持将属性信息(如地质样本采集点、生物观察记录、温度湿度传感器数据、照片拍摄位置)与三维模型中的特定位置关联起来。点击模型上的一个点,可以弹出该位置的详细信息、照片或传感器历史数据曲线。这通常需要自定义开发一个轻量级的后端(如 Flask, Django)和前端,建立空间数据库(如 PostGIS)来存储和管理这些带位置的信息。
专题图生成:除了三维视图,系统还可以调用 Therion 的强大绘图功能,按需生成各种二维专题图,如平面图、纵剖面图、横剖面图,并自动添加图例、比例尺和指北针。这些矢量图可以导出并发布为Web服务(WMS),方便与其他地图底图叠加。
3. 实战部署:搭建你自己的 THERION-SYSTEM
理论讲了不少,我们来点实际的。假设你现在想基于erevusobolus/THERION-SYSTEM的思路,搭建一套属于自己的简易流水线。以下是一个可行的部署和操作指南。
3.1 软件环境准备
首先需要一个稳定的 Linux 环境(Ubuntu 20.04/22.04 LTS 是首选),因为大部分开源地理空间和SLAM工具在Linux上支持最好。
步骤1:安装基础依赖
sudo apt-get update sudo apt-get install -y build-essential cmake git libeigen3-dev libopencv-dev python3-pip python3-dev libboost-all-dev libceres-dev步骤2:安装与编译 TherionTherion 的安装稍微复杂,因为它依赖一些特定的库。
# 1. 安装必要的图形和字体库 sudo apt-get install -y freeglut3-dev libfreetype6-dev libpng-dev libjpeg-dev libtiff-dev libwxgtk3.2-dev # 2. 从官方仓库克隆并编译(假设项目提供了修改版,则克隆项目内的therion分支) git clone https://github.com/therion/therion.git cd therion mkdir build && cd build cmake .. make -j$(nproc) sudo make install编译成功后,可以通过therion --version验证。
步骤3:安装 SLAM 处理工具链这里以 VINS-Fusion 为例,它是一个支持多传感器融合的VIO框架。
git clone https://github.com/HKUST-Aerial-Robotics/VINS-Fusion.git cd VINS-Fusion # 根据其README安装依赖(如Ceres, OpenCV等,我们在第一步已安装) mkdir build && cd build cmake .. make -j$(nproc)编译后,你会得到可执行文件,如vins_node、kitti_odom_test等。你需要准备对应的配置文件。
步骤4:安装点云处理工具CloudCompare和PDAL是处理点云的好帮手,可以通过 apt 安装或编译安装。
sudo apt-get install -y cloudcompare pdal pdal-python同时,安装 Python 点云库:
pip3 install numpy open3d laspy3.2 数据采集实战与预处理
假设你使用了一台 GoPro HERO10(设置线性视野,关闭机内防抖)和一个便宜的 USB-IMU 模块(如 ICM-20948),以及一个激光测距仪。
采集流程:
- 标定:在进入洞穴前,用棋盘格对 GoPro 进行相机内参标定。使用
OpenCV的calibrateCamera函数,得到焦距(fx, fy)、主点(cx, cy)和畸变系数(k1, k2, p1, p2, [k3])。保存为gopro_calib.yaml。 - 同步:开始录制视频前,同时启动 GoPro 和 USB-IMU 的数据记录(IMU数据可能通过一个单独的Python脚本记录到CSV)。在镜头前用力拍手并闪灯,制造同步事件。
- 采集:手持设备在洞内行走,尽量保持缓慢匀速,避免剧烈晃动。在关键位置(如洞口、拐点、岔路口)用激光测距仪测量到已知点的距离,并记录在笔记本上(或手机录音),同时口述当前对应的视频时间戳(如“视频开始后5分30秒,从A点到B点,距离8.7米”)。
- 控制点:如果条件允许,在洞内布设一些已知相对位置的控制点(如贴上的二维码标签),并用全站仪或高精度RTK测量其坐标(在洞口进行)。这能为SLAM提供最强的全局约束。
预处理:
- 视频抽帧:使用
ffmpeg从视频中按固定间隔抽取图像。ffmpeg -i GOPR0001.MP4 -r 2 -q:v 2 frames/frame_%06d.jpg - IMU数据对齐:编写Python脚本,读取IMU的CSV文件,根据同步事件的时间点,将IMU数据的时间轴与视频时间轴对齐,并重采样到图像时间戳上。
- 整理测量数据:将手写的测量记录和录音整理成一个结构化的文件,例如
manual_measurements.csv,包含:timestamp, from_station, to_station, distance, azimuth, inclination。
3.3 运行 SLAM 与数据融合
配置与运行 VINS-Fusion:你需要为 VINS-Fusion 准备一个配置文件config/underground.yaml,其中关键参数包括:
%YAML:1.0 # 相机内参 model_type: PINHOLE camera_name: camera image_width: 1920 image_height: 1080 fx: 950.0 # 替换为你的标定结果 fy: 950.0 cx: 960.0 cy: 540.0 k1: -0.1 k2: 0.01 p1: 0.0 p2: 0.0 # IMU噪声参数(需要根据你的IMU型号调整,可从数据手册估算) acc_n: 0.019 gyr_n: 0.0015 acc_w: 0.00016 gyr_w: 2.0e-05 # 外参(相机与IMU之间的变换矩阵,需要事先标定) body_T_cam0: !!opencv-matrix rows: 4 cols: 4 dt: d data: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] # 假设相机和IMU坐标系对齐,实际需要测量然后运行 stereo-inertial 模式(将单目相机视为一个虚拟的右目相机):
./vins_node ./config/underground.yaml ./config/underground_imu.yaml # 需要另一个终端发布图像和IMU话题,通常需要编写一个ROS节点或使用rosbag播放。由于 THERION-SYSTEM 可能不依赖ROS,项目更可能提供直接处理图像文件夹和IMU CSV文件的 Python 脚本,内部调用 VINS 或 ORB-SLAM 的库。运行后,你会得到pose_graph.txt或keyframe_trajectory.txt等位姿文件。
尺度恢复与全局优化:这是核心步骤。你需要写一个优化脚本(例如scale_optimize.py):
- 从
manual_measurements.csv中读取几条距离测量值。 - 从 SLAM 轨迹文件中,找到对应时间戳附近的相机位姿。
- 计算这两个位姿之间的平移距离(SLAM尺度下)。
- 建立优化问题:求解一个尺度因子
s,使得s * SLAM距离尽可能接近真实测量距离。可以使用最小二乘法。 - 将尺度因子
s应用到整个 SLAM 轨迹和所有三维点上。 - (可选)如果你有多个测量值构成导线网,可以将它们作为约束,使用 Ceres Solver 或 g2o 对全部位姿进行全局优化(捆绑调整),进一步修正漂移。
点云生成与融合:
- 使用 Open3D 或 PCL,将尺度恢复后的位姿和图像,通过多视图立体(MVS)算法,生成稠密的彩色点云。或者,如果你有LiDAR点云,就用位姿进行拼接。
- 在 CloudCompare 中,将视觉生成的稠密点云与 LiDAR 点云(如果有)进行手动粗配准+ICP精配准,融合成一个点云。
- 将最终的点云保存为
.ply格式。
3.4 生成 Therion 文件与编译
自动生成.th文件:编写脚本trajectory_to_therion.py,读取优化后的轨迹文件。将每个关键帧位姿视为一个 Therion 测站。计算连续测站之间的相对方位角、倾斜角和水平距离,然后按照 Therion 的语法写入auto_survey.th文件。
# auto_survey.th survey test_cave centerline data normal from to length compass clino 1.0 1.1 5.23 85.2 -3.1 1.1 1.2 7.89 92.5 1.8 ... endcenterline endsurvey编写 Therion 主项目文件:创建cave_project.th:
input auto_survey.th map mymap survey test_cave color map bg white color map fg black endmap model mymodel surface surface_model off surface.ply # 引用我们生成的点云文件 endsurface surfaceref surface_model endmodel编译输出:在终端执行:
therion cave_project.th这会在当前目录生成cave_project.pdf(3D PDF),cave_project.svg等文件。查看 PDF,你应该能看到一个基于你数据的三维洞穴模型。
3.5 Web 发布简易方案
要将模型放到网上,一个快速的方法是使用three.js。
- 将
.ply点云或从 Therion 导出的.obj模型,使用Blender或COLMAP的model_converter工具转换为.glb格式。 - 使用
three.js的GLTFLoader加载模型。 - 创建一个简单的 HTML 页面,利用
OrbitControls实现交互。 - 如果需要关联属性,可以在生成点云或模型时,为每个点附加一个ID,然后在前端通过点击事件,用这个ID向后台请求对应的属性信息。
一个更专业的方案是使用CesiumJS和3D Tiles,但这需要将点云转换为 3D Tiles 格式,流程更复杂,适合大型场景。
4. 常见问题、避坑指南与进阶思考
在实际操作中,你一定会遇到各种问题。下面是我在复现和类似项目中踩过的一些坑,以及对应的解决思路。
4.1 SLAM 跟踪丢失或漂移严重
这是地下环境最常见的问题。
- 问题现象:VIO初始化失败,或者运行一段时间后轨迹突然跳变,特征点跟踪大量丢失。
- 原因分析:
- 光照剧烈变化:从明亮洞口进入黑暗洞穴,相机曝光不足,图像信噪比太低。
- 纹理缺失:光滑的岩壁或积水区域缺乏可供跟踪的角点特征。
- 运动过于剧烈:手持设备晃动太大,导致图像模糊,IMU积分误差剧增。
- 闭环误匹配:在结构相似的洞道中,算法错误地将不同位置识别为同一地点,导致轨迹“折叠”。
- 解决方案:
- 硬件层面:为相机配备补光灯,确保环境光照均匀。使用广角镜头增加视野重叠。考虑使用主动光源(如结构光或激光散斑)人为增加纹理。
- 算法参数调优:
- 提高特征点提取阈值:在VINS或ORB-SLAM配置中,增加
min_dist或quality_level,提取更稳定、更分散的特征点。 - 启用光度标定:如果相机自动曝光,必须进行光度标定,补偿曝光变化对特征点亮度的影响。
- 禁用或严格限制闭环检测:在地下线性环境中,可以关闭基于外观的闭环检测,仅依赖IMU和测量值进行约束。或者大幅提高闭环验证的几何一致性阈值。
- 融合绝对测量:这是最关键的一步。务必在SLAM优化后端中,紧耦合(tightly-coupled)激光测距仪提供的距离观测值。这能有效抑制尺度漂移和绝对位置漂移。
erevusobolus/THERION-SYSTEM如果设计得好,其数据处理脚本应该包含了这种融合接口。
- 提高特征点提取阈值:在VINS或ORB-SLAM配置中,增加
- 操作技巧:移动时尽量缓慢平稳,在特征丰富的区域(如碎石堆、钟乳石)稍作停留,让SLAM积累足够的约束。
4.2 点云与模型空洞或噪声大
- 问题现象:生成的稠密点云有很多黑洞,或者表面充满噪声点像“雪花”。
- 原因分析:MVS算法在弱纹理、重复纹理或非朗伯表面(如湿滑反光的岩壁)区域难以找到正确的匹配点。
- 解决方案:
- 多源数据融合:这就是为什么强调要融合激光雷达数据。LiDAR不依赖纹理,能直接获得几何信息,完美补全视觉的短板。
- 调整MVS参数:使用
OpenMVS或COLMAP的patch_match_stereo时,可以调整窗口大小、一致性检查阈值。对于噪声,可以后续使用统计滤波(Statistical Outlier Removal)或半径滤波去除离群点。 - 后期手动修补:在 CloudCompare 中,可以使用“点云雕刻”工具或泊松重建(Poisson Reconstruction)来填补小的空洞,但需谨慎,避免引入虚假几何。
4.3 Therion 编译出错或图形显示异常
- 问题现象:运行
therion命令后报错,或者生成的PDF中模型位置错乱、线条缺失。 - 原因分析:
- 语法错误:
.th文件格式严格,缩进、拼写错误都会导致解析失败。 - 单位不统一:Therion 默认使用米为单位。如果你的SLAM轨迹单位是米,但手动测量数据单位是英尺,就会导致模型比例错误。
- 坐标系不一致:SLAM、激光测距仪、Therion可能使用不同的坐标系(右手系 vs 左手系,Z向上 vs Y向上)。没有进行统一转换。
- 语法错误:
- 解决方案:
- 仔细检查
.th文件:使用therion -debug project.th可以输出更详细的错误信息。确保所有survey、endsurvey、data语句正确闭合。 - 统一单位和坐标系:在生成
auto_survey.th的脚本中,确保将所有距离数据转换为米。同时,理解你的SLAM输出坐标系(例如,通常X向前,Y向左,Z向上),并将其转换为Therion期望的坐标系(通常东-北-上,或自定义)。可能需要进行轴交换和旋转。 - 从简开始:先用一两个简单的测站数据测试Therion,确保基础流程通顺,再导入复杂的自动生成数据。
- 仔细检查
4.4 系统集成与自动化程度低
- 问题现象:每个步骤都需要手动运行命令、转换格式,容易出错,效率低下。
- 解决方案:这正是
erevusobolus/THERION-SYSTEM这类项目要解决的。你需要构建一个自动化流水线脚本(例如pipeline.sh或pipeline.py)。
使用像# pipeline.py 示例框架 import subprocess, os def run_slam(video_path, imu_path, config): # 调用SLAM可执行文件或Python接口 pass def scale_optimize(traj_path, measure_path): # 尺度恢复与优化 pass def generate_therion_input(optimized_traj_path): # 生成 .th 文件 pass def export_to_web(model_path): # 转换格式并准备Web发布 pass if __name__ == "__main__": # 定义路径和参数 # 按顺序调用上述函数 # 添加错误处理和日志Apache Airflow或Prefect这样的工作流管理工具,可以更专业地调度和监控整个流程。
4.5 进阶思考:系统的边界与扩展
THERION-SYSTEM 提供了一个优秀的框架,但仍有扩展空间:
- 实时性:当前流程是离线的。对于某些应用(如救援机器人),需要在线SLAM。可以考虑集成
RTAB-Map或Kimera,它们支持在线稠密建图,并能实时输出网格。 - 多机器人协同:大型洞穴系统可能需要多个设备同时扫描。这涉及到多机轨迹对齐、点云拼接和全局优化问题,可以研究
COLMAP的分布式重建或基于特征点的跨会话匹配。 - 语义标注:在建模的同时,能否自动识别并标注出“石笋”、“地下河”、“崩塌区”等语义信息?可以尝试集成深度学习图像分割模型(如 Mask R-CNN),将2D分割结果通过相机位姿投影到3D模型上。
- 与BIM/GIS集成:生成的模型可以导出为
IFC或CityGML格式,与建筑信息模型或地理信息系统集成,用于工程分析和规划。
最后,我想强调的是,erevusobolus/THERION-SYSTEM这类项目最大的魅力在于其“开源集成”的理念。它不一定在每个环节都是最顶尖的算法,但它巧妙地将多个成熟的开源工具串联起来,解决了从数据到成果的实际工程问题。复现它的过程,本身就是一次对多传感器融合、三维视觉和地理信息系统的深度学习和实践。当你第一次用自己的数据跑通整个流程,看到那个粗糙但真实的地下洞穴模型出现在屏幕上时,那种成就感是无与伦比的。我的建议是,不要试图一步到位搭建完美系统,先从一个小数据集、一个简单的洞道开始,打通最基本的“视频+IMU -> SLAM -> Therion”流程,然后再逐步加入激光测距、点云融合、Web展示等更复杂的模块。每解决一个实际问题,你对这套系统的理解就会加深一层。