自动驾驶必备:全面解析鱼眼相机投影模型(UCM/KB/DS)及实战代码

摘要:在自动驾驶和机器人SLAM系统中,鱼眼相机因其超大视场角(FOV > 180°)而被广泛应用。但不同的鱼眼相机模型(UCM、Kannala-Brandt、Double Sphere等)该如何选择?本文深入解析主流鱼眼投影模型的数学原理、计算公式及代码实现,助你轻松应对智驾标注工具和可视化开发。

前言

在开发自动驾驶标注工具和机器人可视化系统时,你是否遇到过这样的困惑:

  • 为什么同一个鱼眼相机,在不同系统中使用的标定参数格式不一样?
  • OpenCV的fisheye模型和ORB-SLAM的相机模型有什么区别?
  • 如何选择合适的鱼眼投影模型来保证标注精度?

作为一名专注智驾、机器人标注工具和可视化的开发者,今天我就带大家深入理解鱼眼相机投影模型的本质,并提供完整的TypeScript/Rust实现代码。

一、为什么需要鱼眼相机模型?

传统的**针孔相机模型(Pinhole Model)**假设光线直线传播,适用于视场角小于120°的普通相机。但鱼眼镜头的视场角通常达到180°甚至220°以上,会产生严重的径向畸变,针孔模型已无法准确描述。

图:鱼眼图像畸变与校正对比

为了解决这个问题,研究者提出了多种鱼眼相机投影模型,主要包括:

  1. UCM(Unified Camera Model) - 统一相机模型
  2. Kannala-Brandt(KB) - 基于入射角的多项式模型
  3. Double Sphere(DS) - 双球面模型
  4. FOV模型 - 视场角模型

下面我们逐一解析。

二、UCM(统一相机模型)详解

2.1 模型原理

UCM(Unified Camera Model) ,也称为Mei氏全向相机模型,是目前SLAM和VIO系统中最常用的鱼眼模型之一。它的核心思想是:

将3D点先投影到单位球面,再通过参数ξ将球面点投影到归一化平面

图:UCM模型的球面投影原理

2.2 数学公式

给定3D点 P = ( x , y , z ) P = (x, y, z) P=(x,y,z),UCM模型的投影过程如下:

步骤1:计算到原点的距离 r = x 2 + y 2 + z 2 r = \sqrt{x^2 + y^2 + z^2} r=x2+y2+z2

步骤2:球面到平面投影(核心公式) x u = x z + ξ ⋅ r x_u = \frac{x}{z + \xi \cdot r} xu=z+ξ⋅rx y u = y z + ξ ⋅ r y_u = \frac{y}{z + \xi \cdot r} yu=z+ξ⋅ry

其中 ξ \xi ξ(xi)是关键参数:

  • ξ = 0 \xi = 0 ξ=0:退化为标准针孔模型
  • ξ = 1 \xi = 1 ξ=1:适用于抛物面反射镜系统
  • 0 < ξ < 1 0 < \xi < 1 0<ξ<1:适用于鱼眼镜头

步骤3:径向畸变多项式 ρ 2 = x u 2 + y u 2 \rho^2 = x_u^2 + y_u^2 ρ2=xu2+yu2 radial = 1 + k 1 ρ 2 + k 2 ρ 4 + k 3 ρ 6 + k 4 ρ 8 \text{radial} = 1 + k_1 \rho^2 + k_2 \rho^4 + k_3 \rho^6 + k_4 \rho^8 radial=1+k1ρ2+k2ρ4+k3ρ6+k4ρ8 x d = x u ⋅ radial x_d = x_u \cdot \text{radial} xd=xu⋅radial y d = y u ⋅ radial y_d = y_u \cdot \text{radial} yd=yu⋅radial

步骤4:内参变换到像素坐标 u = f x ⋅ x d + c x u = f_x \cdot x_d + c_x u=fx⋅xd+cx v = f y ⋅ y d + c y v = f_y \cdot y_d + c_y v=fy⋅yd+cy

2.3 TypeScript代码实现

typescript 复制代码
export const getFisheyeUV = (
    point: { x: number; y: number; z: number },
    K: [number, number, number, number],  // [fx, fy, cx, cy]
    D: [number, number, number, number],  // [k1, k2, k3, k4]
    xi: number = 1
) => {
    const [fx, fy, cx, cy] = K
    const [k1, k2, k3, k4] = D
    const { x, y, z } = point

    // 步骤1:计算距离
    const r = Math.sqrt(x * x + y * y + z * z)
    if (r === 0) return null

    // 步骤2:球面投影
    const denom = z + xi * r
    if (Math.abs(denom) < 1e-9) return null

    const xu = x / denom
    const yu = y / denom

    // 步骤3:径向畸变
    const rho2 = xu * xu + yu * yu
    const rho4 = rho2 * rho2
    const rho6 = rho4 * rho2
    const rho8 = rho4 * rho4

    const radial = 1 + k1 * rho2 + k2 * rho4 + k3 * rho6 + k4 * rho8

    const xd = xu * radial
    const yd = yu * radial

    // 步骤4:像素坐标
    const u = fx * xd + cx
    const v = fy * yd + cy
    return { x: u, y: v }
}

2.4 为什么SLAM系统偏爱UCM?

  1. 通用性强:一套公式可描述针孔、鱼眼、折反射相机
  2. 计算高效:仅涉及代数运算,无三角函数,适合实时系统
  3. 精度足够:在120°~220°视场角范围内拟合精度高

主流支持UCM的系统

  • ORB-SLAM3
  • VINS-Mono
  • OpenVINS

三、Kannala-Brandt(KB)模型详解

3.1 模型原理

Kannala-Brandt模型 是OpenCV fisheye模块的默认模型,它基于入射角θ的多项式拟合

图:KB模型基于入射角θ进行多项式拟合

3.2 数学公式

步骤1:计算入射角θ θ = arctan ⁡ ( x 2 + y 2 z ) \theta = \arctan\left(\frac{\sqrt{x^2 + y^2}}{z}\right) θ=arctan(zx2+y2 )

步骤2:径向距离的多项式拟合 r d = k 1 θ + k 2 θ 3 + k 3 θ 5 + k 4 θ 7 r_d = k_1 \theta + k_2 \theta^3 + k_3 \theta^5 + k_4 \theta^7 rd=k1θ+k2θ3+k3θ5+k4θ7

步骤3:归一化坐标 如果 θ ≠ 0 \theta \neq 0 θ=0: x u = r d θ ⋅ x z x_u = \frac{r_d}{\theta} \cdot \frac{x}{z} xu=θrd⋅zx y u = r d θ ⋅ y z y_u = \frac{r_d}{\theta} \cdot \frac{y}{z} yu=θrd⋅zy

步骤4:像素坐标 u = f x ⋅ x u + c x u = f_x \cdot x_u + c_x u=fx⋅xu+cx v = f y ⋅ y u + c y v = f_y \cdot y_u + c_y v=fy⋅yu+cy

3.3 代码实现(TypeScript)

typescript 复制代码
export const getKBProjection = (
    point: { x: number; y: number; z: number },
    K: [number, number, number, number],
    D: [number, number, number, number]
) => {
    const [fx, fy, cx, cy] = K
    const [k1, k2, k3, k4] = D
    const { x, y, z } = point

    // 计算入射角θ
    const r = Math.sqrt(x * x + y * y)
    const theta = Math.atan2(r, z)
    const theta2 = theta * theta
    const theta3 = theta2 * theta
    const theta5 = theta3 * theta2
    const theta7 = theta5 * theta2

    // 径向距离多项式
    let rd = theta * (k1 + k2 * theta2 + k3 * theta4 + k4 * theta6)
    
    // 处理θ接近0的情况
    if (theta < 1e-8) {
        rd = k1 * theta
    }

    // 归一化坐标
    const scale = rd / theta
    const xu = scale * x / z
    const yu = scale * y / z

    // 像素坐标
    const u = fx * xu + cx
    const v = fy * yu + cy
    return { x: u, y: v }
}

3.4 KB模型特点

优点

  • 精度高:对普通鱼眼镜头(FOV < 195°)拟合效果极佳
  • OpenCV原生支持cv::fisheye模块直接使用
  • 标定工具丰富:Kalibr等工具默认输出KB模型参数

缺点

  • 涉及atan三角函数,计算开销略高于UCM
  • 在超大视场角(>200°)下精度下降

四、Double Sphere(DS)双球面模型

4.1 模型原理

Double Sphere模型 是UCM的改进版,使用两个球面参数 ( ξ 1 , ξ 2 ) (\xi_1, \xi_2) (ξ1,ξ2)来更好地拟合超大视场角鱼眼镜头。

4.2 数学公式

步骤1:第一次球面投影 r = x 2 + y 2 + z 2 r = \sqrt{x^2 + y^2 + z^2} r=x2+y2+z2 x 1 = x z + ξ 1 ⋅ r x_1 = \frac{x}{z + \xi_1 \cdot r} x1=z+ξ1⋅rx y 1 = y z + ξ 1 ⋅ r y_1 = \frac{y}{z + \xi_1 \cdot r} y1=z+ξ1⋅ry

步骤2:第二次球面投影 r 1 = x 1 2 + y 1 2 + 1 r_1 = \sqrt{x_1^2 + y_1^2 + 1} r1=x12+y12+1 x 2 = x 1 ξ 2 ⋅ r 1 + ( 1 − ξ 2 ) x_2 = \frac{x_1}{\xi_2 \cdot r_1 + (1 - \xi_2)} x2=ξ2⋅r1+(1−ξ2)x1 y 2 = y 1 ξ 2 ⋅ r 1 + ( 1 − ξ 2 ) y_2 = \frac{y_1}{\xi_2 \cdot r_1 + (1 - \xi_2)} y2=ξ2⋅r1+(1−ξ2)y1

步骤3:应用畸变并转换到像素坐标 (与UCM类似,应用径向畸变多项式)

4.3 DS模型优势

  • 超高精度:在FOV > 180°或超大畸变下,重投影误差最低
  • 适用于超广角:适合220°+的极端视场角
  • 新兴SLAM系统支持:OpenVINS、DSO等

五、FOV(Field-of-View)模型

5.1 模型原理

FOV模型由Scaramuzza提出,基于以下公式:

r d = ω ⋅ arctan ⁡ ( 2 tan ⁡ ( θ 2 ) ) r_d = \omega \cdot \arctan\left(2 \tan\left(\frac{\theta}{2}\right)\right) rd=ω⋅arctan(2tan(2θ))

其中 ω \omega ω是唯一的畸变参数。

5.2 特点

  • 参数少:仅需1个畸变参数
  • 适合超广角:早期全景相机常用
  • 计算简单:但精度不如KB和DS模型

六、模型对比总结

图:不同投影模型的畸变曲线对比

特性 UCM Kannala-Brandt Double Sphere Pinhole
数学核心 球面投影+代数畸变 入射角θ+多项式 双球面投影 平面投影
计算开销 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
适用FOV 120°~220°+ 120°~195° 150°~220°+ <120°
拟合精度 极高 最高 低(鱼眼不适用)
OpenCV支持 omnidir模块 fisheye原生 omnidir新版本的 calibrateCamera
行业应用 VIO/SLAM主流 自动驾驶标定主流 新兴SLAM系统 普通相机

七、实战建议

7.1 标注工具开发

如果你正在开发自动驾驶标注工具,建议:

  1. 同时支持UCM和KB模型

    • ROS Bag / Kalibr标定常输出KB4参数
    • VINS / ORB-SLAM常关联UCM参数
    • 只支持一种模型会导致像素级误差
  2. 实现反投影(Undistortion)

    • 从2D像素点反推3D射线是标注的核心功能
    • UCM的反投影无解析解,需用牛顿迭代法求解

7.2 WebGL可视化优化

在Three.js中做鱼眼可视化时:

glsl 复制代码
// 在Fragment Shader中实现投影,而非CPU
vec3 getFisheyeUV(vec3 point, float xi) {
    float r = length(point);
    float denom = point.z + xi * r;
    return point.xy / denom;
}

void main() {
    vec3 uv = getFisheyeUV(worldPos, 0.8);
    // ... 纹理采样
}

性能提升技巧

  • 使用GPU并行计算代替CPU计算
  • 高频场景考虑Rust + WASM,性能可提升10-50倍

7.3 Rust高性能实现

rust 复制代码
#[inline]
pub fn project_fisheye_ucm(
    point: [f64; 3],
    k: [f64; 4],
    d: [f64; 4],
    xi: f64,
) -> Option<[f64; 2]> {
    let [x, y, z] = point;
    let [fx, fy, cx, cy] = k;
    let [k1, k2, k3, k4] = d;

    let r = (x * x + y * y + z * z).sqrt();
    if r < 1e-9 {
        return None;
    }

    let denom = z + xi * r;
    if denom.abs() < 1e-9 {
        return None;
    }

    let xu = x / denom;
    let yu = y / denom;

    let rho2 = xu * xu + yu * yu;
    let rho4 = rho2 * rho2;
    let rho6 = rho4 * rho2;
    let rho8 = rho4 * rho4;

    let radial = 1.0 + k1 * rho2 + k2 * rho4 + k3 * rho6 + k4 * rho8;

    let xd = xu * radial;
    let yd = yu * radial;

    let u = fx * xd + cx;
    let v = fy * yd + cy;

    Some([u, v])
}

八、总结

  1. UCM模型:SLAM/VIO系统的首选,计算高效、通用性强
  2. KB模型:自动驾驶标定的标准,OpenCV原生支持,精度极高
  3. DS模型:超广角(>200°)场景的最优解
  4. 模型选择:根据视场角和系统需求选择,标注工具建议同时支持多种模型

参考资料

  1. Mei, C. & Rives, P. (2007). Single View Point Omnidirectional Camera Calibration from Planar Grids.
  2. Kannala, J. & Brandt, S. (2006). A Generic Camera Model and Calibration Method for Conventional, Wide-Angle, and Fish-Eye Lenses.
  3. Usenko, V. et al. (2017). The Double Sphere Camera Model.
  4. OpenCV Documentation: Fisheye Camera Model

作者介绍:红波,专注于智驾、机器人标注工具和可视化开发,技术栈:TypeScript/Vue/WebGL/Three.js/Go/Rust。欢迎交流讨论!

相关项目


希望这篇文章能帮助你深入理解鱼眼相机模型!如有疑问,欢迎在评论区交流👇

相关推荐
科研online21 分钟前
基于多源数据和XGBoost-SHAP分析中国大陆绿地碳汇空间变异影响因素的非线性相关性与尺度差异
算法·学习方法
Cthy_hy41 分钟前
拓扑排序超详解:原理 + Kahn 贪心算法
python·算法·贪心算法
三品吉他手会点灯1 小时前
C语言学习笔记 - 43.运算符与表达式 - 运算符1 - 运算符的分类和简单介绍
c语言·笔记·学习·算法
VkN2X2X4b1 小时前
算法复杂度的实验验证与误差分析的技术8
算法
其利天下技术1 小时前
风扇灯无刷电机自适应算法实战指南
算法·cocos2d·无刷电机自适应算法·bldc驱动自适应算法·其利无刷电机驱动算法
8Qi82 小时前
LeetCode 494:目标和(Target Sum)—— 题解 ✅
算法·leetcode·职场和发展·动态规划·01背包
hujinyuan201602 小时前
2026年3月 中国电子学会青少年软件编程(Python)三级考试试卷 真题及答案
java·python·算法
froyoisle2 小时前
CSP-J 历年复赛 T1 及解析(2019~2025)
数据结构·c++·算法·csp-j·csp·算法竞赛·信息学
珊瑚里的鱼3 小时前
【动态规划】打家劫舍Ⅱ
算法·动态规划
chao1898443 小时前
SGM(Semi-Global Matching)立体匹配算法 — C++ 实现
开发语言·c++·算法