深蓝学院《从零开始手写VIO》作业六

本文深入解析了深蓝学院《从零开始手写VIO》作业中的证明题与代码题,详细阐述了证明Dy=0的最优解为DTDD^TDDTD的最小奇异值对应奇异值向量的过程,以及通过奇异值分解求解超定方程的最小二乘解的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

深蓝学院《从零开始手写VIO》作业五

深蓝学院《从零开始手写VIO》作业六

1. 证明题

证明 D y = 0 Dy=0 Dy=0的最优解 y y y等于 D T D D^TD DTD的最小奇异值对应的奇异值向量

矩阵 D T D D^TD DTD的奇异值分解如下:
D ⊤ D = ∑ i = 1 4 σ i 2 u i u j ⊤ \mathbf{D}^{\top} \mathbf{D}=\sum_{i=1}^{4} \sigma_{i}^{2} \mathbf{u}_{i} \mathbf{u}_{j}^{\top} DD=i=14σi2uiuj
回顾我们的原始问题如下:
求解 D 2 n × 4 y 4 × 1 = 0 D_{2n×4}y_{4×1}=0 D2n×4y4×1=0,这是一个超定方程,本质上求解得到的是一个最小二乘解,使用如下表示:
min ⁡ y ∣ ∣ D y ∣ ∣ 2 = ( D y ) T ( D y ) = y T D T D y \min _{y} ||Dy||^2 = (Dy)^T(Dy) = y^TD^TDy yminDy2=(Dy)T(Dy)=yTDTDy
其中 ∣ ∣ y ∣ ∣ = 1 ||y|| = 1 y=1

y y y可以由 D T D D^TD DTD的奇异值向量线性组合得到,也就是可以表示成如下形式
y = ∑ i = 1 4 k i u i = k i u i + v y = \sum_{i=1}^{4} k_iu_i = k_iu_i+v y=i=14kiui=kiui+v
其中 v = ∑ j = 1 , j ≠ i 4 k j u j v=\sum_{j=1,j\not=i}^{4}k_ju_j v=j=1,j=i4kjuj
k i , k j ∈ R k_i,k_j \in R ki,kjR
容易知道 u i u_i ui v v v正交。
y y y代入 y T D T D y y^TD^TDy yTDTDy得到
min ⁡ y ∣ ∣ D y ∣ ∣ 2 = ( k i u i + v ) T D T D ( k i u i + v ) = k i 2 u i T D T D u i + v T D T D v + k i u i T D T D v + k i v T D T D u i \min _{y} ||Dy||^2 = (k_iu_i+v)^TD^TD(k_iu_i+v) = k_i^2u_i^TD^TDu_i+ v^TD^TDv + k_iu_i^TD^TDv +k_iv^TD^TDu_i yminDy2=(kiui+v)TDTD(kiui+v)=ki2uiTDTDui+vTDTDv+kiuiTDTDv+kivTDTDui

由于 u i u_i ui v v v正交,所以后两项为0;并且 D u i = σ i u i Du_i = \sigma_iu_i Dui=σiui,带入得到
min ⁡ y ∣ ∣ D y ∣ ∣ 2 = ( k i u i + v ) T D T D ( k i u i + v ) = k i 2 u i T D T D u i + v T D T D v = k i 2 σ i 2 ∣ ∣ u i ∣ ∣ 2 + v T D T D v > = k i 2 σ i 2 ∣ ∣ u i ∣ ∣ 2 \min _{y} ||Dy||^2 = (k_iu_i+v)^TD^TD(k_iu_i+v) = k_i^2u_i^TD^TDu_i+ v^TD^TDv = k_i^2\sigma_i^2||u_i||^2 + v^TD^TDv >= k_i^2\sigma_i^2||u_i||^2 yminDy2=(kiui+v)TDTD(kiui+v)=ki2uiTDTDui+vTDTDv=ki2σi2ui2+vTDTDv>=ki2σi2ui2
当且仅当 v = 0 v=0 v=0的时候等号成立。
如果想取得最小值,则 σ i = σ 4 \sigma_i=\sigma_4 σi=σ4,也就是取得最小奇异值的时候,目标函数取得最小值,此时
y = k 4 u 4 + v = k 4 u 4 y = k_4u_4+v=k_4u_4 y=k4u4+v=k4u4
由于 ∣ ∣ y ∣ ∣ = 1 ||y||=1 y=1,所以 k 4 = 1 k_4=1 k4=1,所以
y = u 4 y = u_4 y=u4
证明完毕。

2. 代码题

代码如下:

    /// TODO::homework; 请完成三角化估计深度的代码
    // 遍历所有的观测数据,并三角化
    Eigen::Vector3d P_est;           // 结果保存到这个变量
    P_est.setZero();
    /* your code begin */
    Eigen::MatrixXd matD = ConstructMatrixD(camera_pose);
    Eigen::BDCSVD<Eigen::MatrixXd> bcdsvd(matD.transpose()*matD, Eigen::ComputeFullV | Eigen::ComputeFullU);
    auto singular_values = bcdsvd.singularValues();
    auto matU = bcdsvd.matrixU();
    auto matV = bcdsvd.matrixV();


    // result 1
    auto tmp_y = matU.rightCols(1);
    auto real_y = tmp_y/tmp_y(3);

    // result2
    double s = matD.maxCoeff();
    Eigen::Matrix4d scale_mat = Eigen::Matrix4d::Identity()/s;
    auto matD_new = matD*scale_mat;
    Eigen::BDCSVD<Eigen::MatrixXd> bcdsvd_new(matD_new.transpose()*matD_new, Eigen::ComputeFullV | Eigen::ComputeFullU);
    auto matU_new = bcdsvd_new.matrixU();
    auto tmp_y_new = scale_mat*matU_new.rightCols(1); //结果一样
    auto real_y_new = tmp_y_new/tmp_y_new(3);
    /* your code end */

结果如下:

ground truth: 
  -2.9477 -0.330799   8.43792
[normal solve] your result1: 
  -2.9477 -0.330799   8.43792         1
[using Scale solve] your result2: 
  -2.9477 -0.330799   8.43792         1
<think>好的,我现在要帮助用户了解如何从零开始实现视觉惯性里程计(VIO)。首先,我需要回忆VIO的基本概念和组成部分。VIO结合了视觉(如摄像头)和惯性测量单元(IMU)的数据,用于估计设备的运动轨迹和周围环境的结构。用户提到他们想手写代码实现,所以需要从基础开始逐步构建。 根据用户提供的引用,特别是引用[1]、[3]、[4],VIO的实现涉及初始化、传感器数据融合、状态估计、后端优化等。首先,我需要确定VIO的框架,比如松耦合还是紧耦合。引用[4]提到松耦合和紧耦合的分类,而通常紧耦合效果更好,但实现更复杂。因此,可能需要从松耦合开始,逐步深入。 接下来,步骤可能包括传感器数据的同步和时间戳对齐,这在引用[3]中提到了时间戳标定的重要性。然后是IMU和视觉数据的预处理,比如IMU的零偏校正和视觉特征点的提取与跟踪。 然后是状态估计,可能需要使用滤波方法(如卡尔曼滤波)或者优化方法(如非线性最小二乘)。引用[2]提到了高斯牛顿法用于曲线拟合,这可能在后端优化中使用。因此,后端优化部分需要实现如Bundle Adjustment结合IMU数据的方法。 此外,初始化过程是关键,引用[1]和[4]都提到需要正确的初始化和外参标定。可能需要使用视觉信息来初始化IMU的参数,比如尺度、重力方向等。 最后,代码实现部分需要分模块,比如数据读取、预处理、状态预测、优化、地图管理等。可能需要使用C++或Python,但用户提供的示例代码是Python,但实际VIO系统可能更常用C++以提高效率。 在实现过程中,需要注意IMU和视觉数据的同步,时间戳的处理,以及如何处理传感器丢失的情况,如引用[3]提到的视觉丢失时IMU顶替。此外,累积误差的问题需要通过回环检测或者全局优化来解决,但可能在初始实现中先处理局部优化。 可能还需要参考现有的开源项目,如VINS-Mono或OKVIS,了解它们的结构,但用户要求手写代码,所以需要从零开始构建基本框架。 总结步骤可能为: 1. 传感器数据读取与同步 2. IMU数据预处理(去噪、零偏校正) 3. 视觉特征提取与匹配 4. 初始化和外参标定 5. 紧耦合的滤波或优化方法实现 6. 后端非线性优化(如基于高斯牛顿法或LM算法) 7. 尺度估计和重力对齐 8. 处理累积误差和闭环检测 需要确保每个模块的正确性,逐步集成测试。例如,先实现IMU的积分和预测,再处理视觉的位姿估计,然后将两者融合。可能出现的问题包括时间不同步、参数标定不准确、优化过程中的数值不稳定等,需要通过调试和测试来解决。</think>### 从零开始实现视觉惯性里程计(VIO)的步骤详解 #### 一、基础理论与框架设计 1. **VIO核心原理** VIO通过融合相机(视觉)和IMU(惯性)数据,估计载体在短时间内的相对运动。视觉提供环境特征点约束,IMU提供高频运动预测,两者互补: - IMU适合快速运动但存在零偏漂移[^3] - 视觉适合慢速运动但能校正IMU零偏并提供尺度信息[^4] 2. **系统框架选择** 推荐从**紧耦合**框架入手(直接融合原始数据),尽管实现复杂度高于松耦合,但精度更高: ```text VIO Pipeline: 传感器数据 → 时间同步 → 视觉特征提取 + IMU预积分 → 紧耦合优化 → 位姿输出 ``` --- #### 二、核心模块实现步骤 ##### 1. 传感器数据同步与预处理 - **时间戳对齐**:使用线性插值或滑动窗口对齐相机与IMU的时间戳[^1] - **IMU预处理**: - 零偏校正:通过静止状态下的均值计算初始零偏 - 角速度积分公式(四元数更新): $$ q_{t+1} = q_t \otimes \left[1, \frac{1}{2}\omega\Delta t\right]^T $$ 其中$\omega$为陀螺仪测量值 ##### 2. 视觉前端实现 ```cpp // 示例:ORB特征提取(OpenCV) cv::Ptr<cv::ORB> orb = cv::ORB::create(500); std::vector<cv::KeyPoint> kps; cv::Mat descriptors; orb->detectAndCompute(img, cv::Mat(), kps, descriptors); ``` - **光流跟踪**:使用LK光流法匹配相邻帧特征点 - **关键帧选择**:基于平移/旋转量阈值判断 ##### 3. IMU预积分 - 计算两帧图像间的IMU运动增量: $$ \Delta v = \sum_{k=i}^{j-1} (R_k a_k - g)\Delta t $$ $$ \Delta p = \sum \Delta v_k \Delta t $$ 其中$R_k$为姿态矩阵,$a_k$为加速度计测量值[^4] ##### 4. 紧耦合非线性优化 使用**滑动窗口优化**最小化视觉重投影误差与IMU预积分误差: $$ \min_{\mathbf{x}} \left( \sum \rho \left( \| r_p \|^2_{\Sigma_p} \right) + \sum \| r_I \|^2_{\Sigma_I} \right) $$ - 残差项: - 视觉残差$r_p = z - \pi(T_{cw}X)$ - IMU残差$r_I = \log\left( \Delta \hat{T}_{ij}^{-1} T_i^{-1} T_j \right)$ - 求解方法:高斯牛顿法或Levenberg-Marquardt算法[^2] --- #### 三、关键问题解决方案 1. **初始化** - **视觉-IMU外参标定**:通过手眼标定法计算$T_{imu}^{cam}$ - **尺度恢复**:单目视觉通过SFM计算特征点深度,结合IMU加速度约束求解尺度因子 2. **零偏在线估计** 将加速度计和陀螺仪零偏作为状态量加入优化变量: $$ \mathbf{x} = [T_{wb}, v, b_a, b_g, \lambda_1, ..., \lambda_n] $$ 3. **关键代码示例(C++伪代码)** ```cpp class VIOOptimizer { public: void addImuResidual(const ImuData& imu) { // 添加IMU预积分残差块 problem.AddResidualBlock( new ImuCostFunction(imu.delta_p, imu.delta_v, imu.delta_q), nullptr, pose_i, pose_j, bias_a, bias_g ); } void addVisionResidual(const Feature& feat) { // 添加视觉重投影残差块 problem.AddResidualBlock( new ReprojectionCost(feat.uv_observed), new CauchyLoss(0.5), pose, landmark ); } }; ``` --- #### 四、测试与调试建议 1. **仿真数据验证** 使用EuRoC MAV数据集,对比轨迹的ATE(绝对轨迹误差) 2. **真机调试技巧** - 优先调试IMU积分:静止状态下积分速度应趋近于零 - 可视化特征跟踪:确保匹配点分布均匀且跟踪稳定 3. **性能优化方向** - 使用Schur补加速H矩阵求逆 - 引入FEJ(First Estimate Jacobians)避免线性化点不一致 ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值