自动驾驶必备:全面解析鱼眼相机投影模型(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点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P = ( x , y , z ) P = (x, y, z) </math>P=(x,y,z),UCM模型的投影过程如下:

步骤1:计算到原点的距离 <math xmlns="http://www.w3.org/1998/Math/MathML"> r = x 2 + y 2 + z 2 r = \sqrt{x^2 + y^2 + z^2} </math>r=x2+y2+z2

步骤2:球面到平面投影(核心公式) <math xmlns="http://www.w3.org/1998/Math/MathML"> x u = x z + ξ ⋅ r x_u = \frac{x}{z + \xi \cdot r} </math>xu=z+ξ⋅rx <math xmlns="http://www.w3.org/1998/Math/MathML"> y u = y z + ξ ⋅ r y_u = \frac{y}{z + \xi \cdot r} </math>yu=z+ξ⋅ry

其中 <math xmlns="http://www.w3.org/1998/Math/MathML"> ξ \xi </math>ξ(xi)是关键参数:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> ξ = 0 \xi = 0 </math>ξ=0:退化为标准针孔模型
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> ξ = 1 \xi = 1 </math>ξ=1:适用于抛物面反射镜系统
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> 0 < ξ < 1 0 < \xi < 1 </math>0<ξ<1:适用于鱼眼镜头

步骤3:径向畸变多项式 <math xmlns="http://www.w3.org/1998/Math/MathML"> ρ 2 = x u 2 + y u 2 \rho^2 = x_u^2 + y_u^2 </math>ρ2=xu2+yu2 <math xmlns="http://www.w3.org/1998/Math/MathML"> 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 </math>radial=1+k1ρ2+k2ρ4+k3ρ6+k4ρ8 <math xmlns="http://www.w3.org/1998/Math/MathML"> x d = x u ⋅ radial x_d = x_u \cdot \text{radial} </math>xd=xu⋅radial <math xmlns="http://www.w3.org/1998/Math/MathML"> y d = y u ⋅ radial y_d = y_u \cdot \text{radial} </math>yd=yu⋅radial

步骤4:内参变换到像素坐标 <math xmlns="http://www.w3.org/1998/Math/MathML"> u = f x ⋅ x d + c x u = f_x \cdot x_d + c_x </math>u=fx⋅xd+cx <math xmlns="http://www.w3.org/1998/Math/MathML"> v = f y ⋅ y d + c y v = f_y \cdot y_d + c_y </math>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:计算入射角θ <math xmlns="http://www.w3.org/1998/Math/MathML"> θ = arctan ⁡ ( x 2 + y 2 z ) \theta = \arctan\left(\frac{\sqrt{x^2 + y^2}}{z}\right) </math>θ=arctan(zx2+y2 )

步骤2:径向距离的多项式拟合 <math xmlns="http://www.w3.org/1998/Math/MathML"> 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 </math>rd=k1θ+k2θ3+k3θ5+k4θ7

步骤3:归一化坐标 如果 <math xmlns="http://www.w3.org/1998/Math/MathML"> θ ≠ 0 \theta \neq 0 </math>θ=0: <math xmlns="http://www.w3.org/1998/Math/MathML"> x u = r d θ ⋅ x z x_u = \frac{r_d}{\theta} \cdot \frac{x}{z} </math>xu=θrd⋅zx <math xmlns="http://www.w3.org/1998/Math/MathML"> y u = r d θ ⋅ y z y_u = \frac{r_d}{\theta} \cdot \frac{y}{z} </math>yu=θrd⋅zy

步骤4:像素坐标 <math xmlns="http://www.w3.org/1998/Math/MathML"> u = f x ⋅ x u + c x u = f_x \cdot x_u + c_x </math>u=fx⋅xu+cx <math xmlns="http://www.w3.org/1998/Math/MathML"> v = f y ⋅ y u + c y v = f_y \cdot y_u + c_y </math>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的改进版,使用两个球面参数 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( ξ 1 , ξ 2 ) (\xi_1, \xi_2) </math>(ξ1,ξ2)来更好地拟合超大视场角鱼眼镜头。

4.2 数学公式

步骤1:第一次球面投影 <math xmlns="http://www.w3.org/1998/Math/MathML"> r = x 2 + y 2 + z 2 r = \sqrt{x^2 + y^2 + z^2} </math>r=x2+y2+z2 <math xmlns="http://www.w3.org/1998/Math/MathML"> x 1 = x z + ξ 1 ⋅ r x_1 = \frac{x}{z + \xi_1 \cdot r} </math>x1=z+ξ1⋅rx <math xmlns="http://www.w3.org/1998/Math/MathML"> y 1 = y z + ξ 1 ⋅ r y_1 = \frac{y}{z + \xi_1 \cdot r} </math>y1=z+ξ1⋅ry

步骤2:第二次球面投影 <math xmlns="http://www.w3.org/1998/Math/MathML"> r 1 = x 1 2 + y 1 2 + 1 r_1 = \sqrt{x_1^2 + y_1^2 + 1} </math>r1=x12+y12+1 <math xmlns="http://www.w3.org/1998/Math/MathML"> x 2 = x 1 ξ 2 ⋅ r 1 + ( 1 − ξ 2 ) x_2 = \frac{x_1}{\xi_2 \cdot r_1 + (1 - \xi_2)} </math>x2=ξ2⋅r1+(1−ξ2)x1 <math xmlns="http://www.w3.org/1998/Math/MathML"> y 2 = y 1 ξ 2 ⋅ r 1 + ( 1 − ξ 2 ) y_2 = \frac{y_1}{\xi_2 \cdot r_1 + (1 - \xi_2)} </math>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提出,基于以下公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> r d = ω ⋅ arctan ⁡ ( 2 tan ⁡ ( θ 2 ) ) r_d = \omega \cdot \arctan\left(2 \tan\left(\frac{\theta}{2}\right)\right) </math>rd=ω⋅arctan(2tan(2θ))

其中 <math xmlns="http://www.w3.org/1998/Math/MathML"> ω \omega </math>ω是唯一的畸变参数。

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。欢迎交流讨论!

相关项目


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

相关推荐
云泽8081 小时前
蓝桥杯算法精讲:二分算法之二分答案深度剖析
算法·蓝桥杯
小龙报2 小时前
【数据结构与算法】环与相遇:链表带环问题的底层逻辑与工程实现
c语言·数据结构·c++·物联网·算法·链表·visualstudio
佩奇大王2 小时前
P2118 排列字母
java·开发语言·算法
仟濹2 小时前
【算法打卡day20(2026-03-12 周四)算法:前缀和,二维前缀和,快慢指针,哈希表set使用技巧,哈希表map使用技巧】7个题
数据结构·算法
一叶落4382 小时前
LeetCode 67. 二进制求和(C语言详解 | 双指针模拟加法)
c语言·数据结构·算法·leetcode
寒月小酒2 小时前
3.12 OJ
算法
CoovallyAIHub2 小时前
纯合成数据训练,真实图像Pose mAP达0.97:亚琛工大用YOLOv11实现风电关键点检测
深度学习·算法·计算机视觉
铭哥的编程日记2 小时前
贪心算法解决分糖果问题
算法·贪心算法