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

1 阅读7分钟

摘要:在自动驾驶和机器人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),UCM模型的投影过程如下:

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

步骤2:球面到平面投影(核心公式) xu=xz+ξrx_u = \frac{x}{z + \xi \cdot r} yu=yz+ξry_u = \frac{y}{z + \xi \cdot r}

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

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

步骤3:径向畸变多项式 ρ2=xu2+yu2\rho^2 = x_u^2 + y_u^2 radial=1+k1ρ2+k2ρ4+k3ρ6+k4ρ8\text{radial} = 1 + k_1 \rho^2 + k_2 \rho^4 + k_3 \rho^6 + k_4 \rho^8 xd=xuradialx_d = x_u \cdot \text{radial} yd=yuradialy_d = y_u \cdot \text{radial}

步骤4:内参变换到像素坐标 u=fxxd+cxu = f_x \cdot x_d + c_x v=fyyd+cyv = f_y \cdot y_d + c_y

2.3 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(x2+y2z)\theta = \arctan\left(\frac{\sqrt{x^2 + y^2}}{z}\right)

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

步骤3:归一化坐标 如果 θ0\theta \neq 0xu=rdθxzx_u = \frac{r_d}{\theta} \cdot \frac{x}{z} yu=rdθyzy_u = \frac{r_d}{\theta} \cdot \frac{y}{z}

步骤4:像素坐标 u=fxxu+cxu = f_x \cdot x_u + c_x v=fyyu+cyv = f_y \cdot y_u + c_y

3.3 代码实现(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)来更好地拟合超大视场角鱼眼镜头。

4.2 数学公式

步骤1:第一次球面投影 r=x2+y2+z2r = \sqrt{x^2 + y^2 + z^2} x1=xz+ξ1rx_1 = \frac{x}{z + \xi_1 \cdot r} y1=yz+ξ1ry_1 = \frac{y}{z + \xi_1 \cdot r}

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

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

4.3 DS模型优势

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

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

5.1 模型原理

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

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

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

5.2 特点

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

六、模型对比总结

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

特性UCMKannala-BrandtDouble SpherePinhole
数学核心球面投影+代数畸变入射角θ+多项式双球面投影平面投影
计算开销⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
适用FOV120°~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中做鱼眼可视化时:

// 在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高性能实现

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

相关项目


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