- 自动驾驶
- 计算机视觉
【免费下载链接】SensorsCalibration
OpenCalib: A Multi-sensor Calibration Toolbox for Autonomous Driving
SPLINTER(SPLine INTERpolation)是一个面向多变量函数逼近的开源 C++ 库,以张量积 B-spline 为核心,支持函数逼近、回归、数据平滑与数据降维,并被 SensorsCalibration 项目中的 pose_sensor2car(GNSS/INS 偏航角标定)与 lidar2car 模块作为第三方依赖深度使用。本文以 SPLINTER README 为主线,结合其官方指南(基本用法、C++ 接口、编译指南)与仓库源码,完整梳理其数学模型、构建流程、四类语言接口以及编译集成方式,并落到 yawCalib.cpp 的真实调用链上,帮助你既会用、又懂原理。
一、SPLINTER 是什么:为多变量函数逼近而生的样条库
SPLINTER 是一个用于**多变量函数逼近(multivariate function approximation)的库,核心数据载体是张量积 B-spline(tensor product B-spline)**的快速 C++ 实现。它覆盖了以下典型任务:
- 函数逼近(function approximation)
- 回归(regression)
- 数据平滑(data smoothing)
- 数据降维/约简(data reduction)
其数学基础是分段多项式基函数(piecewise polynomial basis functions)构成的 B-spline,在灵活性与光滑性之间取得良好平衡。样本拟合采用普通最小二乘(ordinary least squares, OLS),并可选正则化;此外还内置了**惩罚样条(penalized splines,P-splines)**构造能力。
从源码结构看,库的核心实现位于 include 与 src 目录,关键类包括:
BSpline(bspline.h):多变量张量积 B-spline 本体,继承自Function抽象基类,除eval外还提供evalJacobian、evalHessian——这正是文档所述"为数学规划/非线性优化而设计"的能力;BSpline::Builder(bsplinebuilder.h):链式构建器,封装回归拟合流程;BSplineBasis/BSplineBasis1D(bsplinebasis.h):多维基函数张量积与一维基函数;DataTable(datatable.h):多维样本 (x, y) 的有序存储与网格管理;Serializer:B-spline 与 DataTable 的序列化(save/load)。
说明:本文所述版本为仓库中
version文件记录的3-0,即静态库文件名为splinter-static-3-0(见 CMakeLists.txt)。
二、核心原理:张量积 B-spline 如何逼近任意采样函数
B-spline 由 B-spline 基函数构造而来,这些基函数是高度光滑的分段多项式。用户可以选择线性(degree 1)、二次(degree 2)、三次(degree 3)乃至更高次数的 B-spline,对采样数据进行平滑或插值。B-spline 从样本出发,通过求解一个线性方程组得到控制点系数。
几个关键性质:
- 局部支撑(local support):求值时只有采样点附近的样本影响该点的 B-spline 取值,因此求值时间与样本总数无关,只随 B-spline 的**次数(degree)与变量数(number of variables)**增长。这是它适合在线快速查询的根本原因。
- 构建规模限制(文档给出的一般性经验值):在现代桌面机上,普通 B-spline 构建的样本量上限约为10 万,对应变量数约5~7 个;P-spline 因需要求解大规模最小二乘问题,样本量上限降至约1 万。构建成本随样本与变量增长,求值成本则与之无关。
- 光滑性分级:线性 B-spline 连续,二次 B-spline 一阶连续可导,三次 B-spline 具有连续的二阶导数。SensorsCalibration 的偏航角标定正是利用了高阶导数信息(见下文第六节)。
2.1 三种平滑/正则化模式
SPLINTER 的BSpline::Builder通过smoothing参数控制拟合策略(枚举定义见 bsplinebuilder.h):
| 模式 | 枚举值 | 行为 |
|---|---|---|
| 无平滑(插值) | Smoothing::NONE | 直接 OLS 拟合,B-spline 插值全部样本点 |
| 恒等正则化 | Smoothing::IDENTITY | 在 OLS 目标中加入二次惩罚项alpha * c' * I * c(即 Tikhonov 正则化),抑制过拟合 |
| P-spline | Smoothing::PSPLINE | 对控制点施加(近似)二阶差分惩罚,得到平滑而非插值的样条 |
- Tikhonov 正则化:在 OLS 目标函数上加入对系数的二次惩罚,可降低过拟合影响。当前实现以单位矩阵作为 Tikhonov 矩阵,平滑强度由
alpha控制。 - P-spline:从源码看,其惩罚矩阵由
getSecondOrderFiniteDifferenceMatrix(bsplinebuilder.cpp)生成——即对控制点序列施加二阶有限差分惩罚,且每个维度至少需要 3 个基函数/系数。P-spline 构建需解大规模最小二乘问题,因此更耗时。
两种方式的 Python 构建示例(来自 basic_usage.md):
# P-spline,平滑参数 alpha = 0.1 bspline = splinter.BSplineBuilder(data, smoothing=splinter.BSplineBuilder.Smoothing.PSPLINE, alpha=0.1).build() # Tikhonov(恒等)正则化,三次 B-spline,alpha = 0.1 bspline = splinter.BSplineBuilder(data, smoothing=splinter.BSplineBuilder.Smoothing.IDENTITY, alpha=0.1).build()三、基本使用工作流:采样 → 构建 → 评估 → 验证
官方给出的工作流非常简洁:采样一个函数,然后构造逼近。该过程可迭代执行,直到得到满意的逼近效果;评估逼近精度时,既可用已有样本做交叉验证,也可补充采样。当前版本 SPLINTER 主要承载"采样与模型构建"两个环节(见 basic_usage.md)。
3.1 二次 B-spline 构建示例(Python 单行完成)
bspline = splinter.BSplineBuilder(data, degree=2).build()其中data为样本集合,通常要求落在规则网格(regular grid)上。SPLINTER 也接受非规则网格数据,但该行为目前属于实验性质、官方不推荐。
四、C++ 接口实战:原生 API 与完整示例
C++ 接口是 SPLINTER 的原生接口,在任何时候都是最全面、暴露特性最多的接口。官方示例(cpp_interface.md)以"六峰驼背函数(Six-hump camelback function)"为被采样函数,演示了完整流程:
#include <iostream> #include "datatable.h" using std::cout; using std::endl; using namespace SPLINTER; // 六峰驼背函数 double f(DenseVector x) { assert(x.rows() == 2); return (4 - 2.1*x(0)*x(0) + (1/3.)*x(0)*x(0)*x(0)*x(0))*x(0)*x(0) + x(0)*x(1) + (-4 + 4*x(1)*x(1))*x(1)*x(1); } int main(int argc, char *argv[]) { // 创建 DataTable 管理样本 DataTable samples; // 采样函数 DenseVector x(2); double y; for(int i = 0; i < 20; i++) { for(int j = 0; j < 20; j++) { x(0) = i*0.1; x(1) = j*0.1; y = f(x); samples.addSample(x,y); // 存储样本 } } // 构建插值 B-spline BSpline bspline1 = BSpline::Builder(samples).degree(1).build(); BSpline bspline3 = BSpline::Builder(samples).degree(3).build(); // 构建平滑 P-spline BSpline pspline = BSpline::Builder(samples) .degree(3) .smoothing(BSpline::Smoothing::PSPLINE) .alpha(0.03) .build(); /* 在 x = (1,1) 处评估三个逼近器 * 注意:该点是我们采样过的点,因此误差为 0(P-spline 除外, * 它可能为了平滑而引入误差) */ x(0) = 1; x(1) = 1; cout << "Function at x: " << f(x) << endl; cout << "Linear B-spline at x: " << bspline1.eval(x) << endl; cout << "Cubic B-spline at x: " << bspline3.eval(x) << endl; cout << "P-spline at x: " << pspline.eval(x) << endl; return 0; }要点:
BSpline::Builder(samples).degree(d).build()一行完成 B-spline 构建;degree支持 0~5 次(源码getBSplineDegrees对 degree > 5 抛异常,见 bsplinebuilder.h);smoothing与alpha组合实现 P-spline 平滑;- 编译需使用C++11 兼容编译器(README 的硬性要求)。
4.1 使用 DataTable 管理采样
为简化采样管理,SPLINTER 提供DataTable数据结构:
DataTable samples; // 添加样本 (x,y),其中 y = f(x) samples.addSample(1,0); samples.addSample(2,5); samples.addSample(3,10); samples.addSample(4,15); // 样本添加顺序无关紧要,DataTable 内部保持排序从 datatable.h 可见,样本以std::multiset<DataPoint>存储(内部自动排序),并提供getNumSamples()、getNumVariables()、getTableX()、getVectorY()、isGridComplete()等查询接口,以及save/load序列化能力。
五、Builder 构建细节:degree、smoothing、alpha 与网格限制
5.1 Builder 链式参数一览
BSpline::Builder是构建 B-spline 的唯一入口(默认以 3 次、无平滑、AS_SAMPLED节点间距构建)。可配置项(见 bsplinebuilder.h):
| 参数 | 默认值 | 说明 |
|---|---|---|
degree(d)/degree(vector) | 3 | 基函数次数,支持 0~5;传向量可为每个维度分别指定次数 |
smoothing(Smoothing) | NONE | 平滑策略:NONE/IDENTITY/PSPLINE |
alpha(a) | — | 平滑/正则化强度,必须非负(源码中对负 alpha 抛异常) |
numBasisFunctions(vector) | 自动 | 各维度基函数个数,可显式指定 |
knotSpacing(KnotSpacing) | AS_SAMPLED | 节点向量构造策略:AS_SAMPLED(移动平均模拟样本分布)、EQUIDISTANT(等距节点)等 |
build() | — | 校验数据并执行构建 |
5.2 网格完整性限制(重要)
当前实现要求样本构成一个完整网格(complete grid / regular grid):若被采样函数是二维的(变量x0、x1),则x0与x1的所有组合都必须出现在样本中。例如二维情形可想象为坐标纸——所有交叉点都应存在,缺一个样本就缺一个交叉点,网格即不完整。
不完整网格示例:
| x₀ | x₁ | y |
|---|---|---|
| 2.1 | 1 | -7 |
| 2.3 | 3 | 10 |
| 2.1 | 3 | 9.3 |
完整网格示例:
| x₀ | x₁ | y |
|---|---|---|
| 2.1 | 1 | -7 |
| 2.3 | 3 | 10 |
| 2.1 | 3 | 9.3 |
| 2.3 | 1 | 0 |
注意:网格是否完整只取决于 x 的取值组合,与 y 无关。可通过DataTable::isGridComplete()检查;从源码看,BSpline::Builder::build()会直接对不完整网格抛异常(bsplinebuilder.cpp)。该限制在后续版本计划移除。
有意思的是:SensorsCalibration 的 yawCalib.cpp 中样本按等间隔时间戳
i采样,天然构成一维完整网格,因此可顺利通过构建校验——这正是"按采样网格取数"这一限制在真实工程中的典型应对。
六、在 SensorsCalibration 中的真实应用:P-spline 驱动的偏航角标定
SPLINTER 并非孤立的第三方库,它直接服务于 SensorsCalibration 的传感器标定算法。在 SensorX2car/pose_sensor2car(GNSS/INS 航向角标定)与 SensorX2car/lidar2car 中,SPLINTER 被链接为静态库(splinter-static-3-0)。
6.1 集成方式
SensorX2car/pose_sensor2car/CMakeLists.txt 中的典型集成:
# Get splinter include_directories(${PROJECT_SOURCE_DIR}/3rdparty/splinter-3.0/include) link_directories(${PROJECT_SOURCE_DIR}/lib) ... target_link_libraries(... splinter-static-3-0 ...)6.2 源码级调用链
yawCalib.cpp 中,GetYawSegs用两条 P-spline 分别拟合车辆轨迹的 x、y 坐标,再借助evalJacobian与evalHessian求取航向与曲率:
BSpline bspline_x = BSpline::Builder(sample_x).degree(bspine_degree_).smoothing(BSpline::Smoothing::PSPLINE).alpha(0.03).build(); BSpline bspline_y = BSpline::Builder(sample_y).degree(bspine_degree_).smoothing(BSpline::Smoothing::PSPLINE).alpha(0.03).build(); ... double dx = bspline_x.evalJacobian(t)(0, 0); // 一阶导数 -> 速度分量 double dy = bspline_y.evalJacobian(t)(0, 0); double ddx = bspline_x.evalHessian(t)(0, 0); // 二阶导数 -> 曲率判断 double ddy = bspline_y.evalHessian(t)(0, 0); double yaw = atan2(dx, dy); // 由速度方向反解航向角而CalibrateSingle中再对每段 yaw 序列构建一条alpha=0.06的三次 P-spline 平滑(bspine_degree_默认 4,见 yawCalib.h):
BSpline bspline_yaw = BSpline::Builder(sample_yaw).degree(bspine_degree_).smoothing(BSpline::Smoothing::PSPLINE).alpha(0.06).build();随后将 IMU 原始 yaw 与 B-spline 平滑 yaw 做差、剔除离群点(Util::DeleteOutliers)后取均值,得到车体与 IMU 之间的安装偏航角。这条链路完整印证了 README 所述能力:P-spline 平滑 + Jacobian/Hessian 解析求导正是为数学规划与优化场景设计的。
七、多语言接口:Python、MATLAB 与 C
除原生 C++ 接口外,SPLINTER 提供 Python、MATLAB、C 三套绑定(C 接口是 MATLAB/Python 绑定的底层通道)。
7.1 Python 接口
官方说明其已测试兼容Python 2.7 与 3.x。make install后产生lib/<os>/<arch>下的共享库与python/splinter包;将python目录加入路径后即可import splinter。基本用法(python_interface.md):
import splinter import numpy as np def f1(x): return -1. + 2*x + 0.1*(x**2) + 10*np.random.rand(1)[0] x = np.arange(0, 11, 1) y = np.zeros((len(x),)) for i in range(len(x)): y[i] = f1(x[i]) data = list(zip(x, y)) # 构建插值三次 B-spline bspline = splinter.BSplineBuilder(data, degree=3).build() # 评估(多点多批次,比逐点调用更高效) xd = np.arange(0, 10, .01) yd = bspline.eval(xd) # 保存 / 加载 bspline.save("myfile.myextension") loadedBSpline = splinter.BSpline("myfile.myextension")7.2 MATLAB 接口
安装后在 MATLAB 中运行setup()(每次启动 MATLAB 需执行一次)即可加载;示例代码位于matlab/examples目录。
7.3 C 接口
C 接口几乎所有函数都以splinter_obj_ptr(定义为typedef void *splinter_obj_ptr)为首参,模拟面向对象风格。官方示例(c_interface.md)展示了完整流程:
#include <stdio.h> #include <stdlib.h> #include <cinterface/cinterface.h> double f(double x, double y) { return x*x*y + y*y*y; } int main(int argc, char **argv) { splinter_obj_ptr datatable = splinter_datatable_init(); // 以行主序批量添加 10x10 网格样本 double samples[300]; int sampleIdx = -1; for (int x = 0; x < 10; ++x) for (int y = 0; y < 10; ++y) { samples[++sampleIdx] = x; samples[++sampleIdx] = y; samples[++sampleIdx] = f(x, y); } splinter_datatable_add_samples_row_major(datatable, samples, 100, 2); splinter_obj_ptr bspline_builder = splinter_bspline_builder_init(datatable); unsigned int degrees[2] = {3, 3}; splinter_bspline_builder_set_degree(bspline_builder, degrees, 2); splinter_obj_ptr bspline = splinter_bspline_builder_build(bspline_builder); double x_eval[] = {0.1, 0.5}; double *val = splinter_bspline_eval_row_major(bspline, x_eval, 2); printf("Approximated value at (%f, %f): %f\n", x_eval[0], x_eval[1], val[0]); return 0; }C 接口还提供splinter_get_error()/splinter_get_error_string()进行错误检查,以及求值(eval)、Jacobian(eval_jacobian)、Hessian(eval_hessian)的行/列主序变体,详见 cinterface.h。
八、编译与安装:从一行脚本到精细化 CMake
官方建议优先使用预编译二进制;如需自行编译,提供了"快速脚本"与"进阶手动"两条路线(compile.md)。
8.1 快速方式(release 脚本)
依赖:CMake、Git;UNIX 下需 GCC 与/或 Clang,Windows 下需 MinGW 与/或 Visual Studio,Ubuntu 下需apt-get install build-essentials。脚本会为所有已知编译器/架构编译,较耗时:
git clone https://github.com/bgrimstad/splinter.git # 以官方仓库为例 cd splinter ./scripts/build_release.sh产物位于build/OS/COMPILER/ARCHITECTURE目录。README 还给出两条平台注意事项:MinGW 非 multilib,x86 与 x86-64 需分别安装;Ubuntu 下 clang++ 可能未符号链接,需sudo ln -s /usr/bin/clang++-3.5 /usr/bin/clang++。
8.2 进阶方式(UNIX / Windows 手动构建)
git clone https://github.com/bgrimstad/splinter.git cd splinter mkdir build && cd build export CXX=$(which g++) # 或 clang++ cmake .. make make installWindows(MinGW)下对应cmake .. -DCMAKE_MAKE_PROGRAM=mingw32-make -G "Unix Makefiles"后mingw32-make(可接install)。常见排错:
make: *** [install] Error 1:安装目录无写权限,改用sudo make install或调整安装路径;- 64 位系统编译 32 位报
bits/c++config.h缺失:安装g++-multilib。
8.3 CMake 选项表(所有平台通用)
| 变量名 | 默认值 | 说明 |
|---|---|---|
EIGEN_DIRECTORY | thirdparty/Eigen | Eigen 库路径 |
HEADER_INSTALL_DIRECTORY | include | 头文件安装目录 |
LIBRARY_INSTALL_DIRECTORY | lib | 库文件安装目录 |
ARCH | x86 | 目标架构(x86 / x86-64),仅 GCC/Clang 生效 |
CMAKE_BUILD_TYPE | Release | 构建类型(Debug / Release) |
用法示例:
cmake .. -DEIGEN_DIRECTORY=/path/to/eigen -DHEADER_INSTALL_DIRECTORY=/home/me/splinter/includes -DARCH=x86-64语法为-D<VARIABLE_NAME>=<VARIABLE_VALUE>,含空格的值需用双引号包裹。若头文件/库路径为相对路径,则相对CMAKE_INSTALL_PREFIX解析。仓库内 CMakeLists.txt 与文档一致:默认ARCH为x86-64、Release构建、同时产出共享库splinter-3-0与静态库splinter-static-3-0,并支持 Doxygen 文档目标(make doc)。
九、源码结构与测试佐证
仓库中 test 目录(基于 Catch 框架)对库的核心行为进行了覆盖,可作为理解与验证的补充材料:
test/approximation/bspline.cpp:线性/二次/三次/四次 B-spline 在密采样、正常采样、稀疏采样下的函数值、Jacobian、Hessian 对称性测试;test/approximation/pspline.cpp:P-spline(alpha=0.03)的函数值与 Jacobian 对比、Hessian 对称性检查;test/general/bspline.cpp:节点插入(knot insertion)测试;test/serialization/bspline.cpp:B-spline 的 save/load 往返一致性(1~4 次均有覆盖);test/unit/bsplinebasis1d.cpp、test/unit/knots.cpp:一维基函数与节点向量单元测试。
这些测试同样演示了BSpline::Builder(table).degree(n).build()的标准用法,与文档示例完全同构。
十、引用与贡献
SPLINTER 源于多年对"快速、通用的多变量函数逼近库"的开发积累,最初为数学规划(非线性优化)中的样条构建而生,因此对 Jacobian/Hessian 计算投入较多。若在科研工作中使用 SPLINTER,官方建议在论文中引用(可参考 README 中的 bibtex 条目,作者 Bjarne Grimstad 等,标题SPLINTER: a library for multivariate function approximation with splines,2015 年发布,注明访问日期)。
官方欢迎任何人使用与贡献,近期目标包括:改善接口与文档以提升可及性、实现新特性、通过测试改进现有代码。最直接的贡献方式是使用并反馈体验;编码贡献可从 GitHub issues 列表中选择,issue 按工作类型(Bug/Docs/Enhancement/New feature/Refactoring/Tests)与难度(Beginner/Intermediate/Advanced)标记,Critical标记表示需要优先处理。
小结
SPLINTER 以"张量积 B-spline + OLS/正则化/P-spline"的组合,为多变量函数逼近提供了高速、光滑、可求导的通用方案,并通过 C++/C/Python/MATLAB 四套接口覆盖不同研发链路。在 SensorsCalibration 中,它被 pose_sensor2car 用作轨迹平滑与解析微分的底层引擎——BSpline::Builder(...).degree(...).smoothing(PSPLINE).alpha(...)一行式构建配合eval/evalJacobian/evalHessian,正是自动驾驶传感器标定中"从带噪轨迹稳健提取航向"的实用利器。理解它的网格约束、次数范围与平滑参数语义,是正确使用的前提。
- 自动驾驶
- 计算机视觉
【免费下载链接】SensorsCalibration
OpenCalib: A Multi-sensor Calibration Toolbox for Autonomous Driving
相关推荐
SPLINTER 张量积 B 样条(B-spline)构建与平滑全解析:从插值、P-spline 到 Tikhonov 正则化,及其在 SensorsCalibration 航向角标定中的实战应用
SPLINTER 张量积 B 样条(B spline)构建与平滑全解析:从插值、P spline 到 Tikhonov 正则化,及其在 SensorsCalib
自动驾驶计算机视觉SensorsCalibration 内嵌 SPLINTER 库 Python 接口实战:B 样条拟合、批量求值与模型持久化指南
SensorsCalibration 内嵌 SPLINTER 库 Python 接口实战:B 样条拟合、批量求值与模型持久化指南 SPLINTER(SPLine
自动驾驶计算机视觉SPLINTER MATLAB 接口实战指南:环境配置、核心类与 B 样条拟合(SensorsCalibration)
SPLINTER MATLAB 接口实战指南:环境配置、核心类与 B 样条拟合(SensorsCalibration) SPLINTER(SPLine INTE
自动驾驶计算机视觉
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考