深蓝学院-多传感器融合定位-第三章作业

  • 题目内容
  • 第一题:残差模型推导
  • 第二题: Ceres解析求导
    • Ceres的使用
    • Ceres解析求导:线特征残差
    • Ceres解析求导:面特征残差
    • Ceres和Eigen的坑点
  • 第三题:EVO结果分析

题目内容

第一题:残差模型推导


补充:鉴于写代码时发现A-LOAM的更新的参数块是四元数param_q和平移矢量param_t,所以我重新写了关于R,t的李代数求导,这样就可以分别对R和t进行残差更新:

第二题: Ceres解析求导

大师兄:Ceres库有自动求导、数值求导和解析求导,A-LOAM中用的是自动求导 但是效率会比较低。

本题代码就是要实现Ceres的解析求导,所以我们主要的工作是输入第一题中得到的Jacobian。
(听起来很简单对吧?)

首先我们可以看到在alom_laser_odometry_node.cpp中Line 382左右是线特征的CostFunction:

ceres::CostFunction *cost_function = LidarEdgeFactor::Create(curr_point, last_point_a, last_point_b, s);
problem.AddResidualBlock(cost_function, loss_function, para_q, para_t);

大师兄:这就是我们要着手的地方了,接着进入LidarEdgeFactor,看看这个它自动求导是怎么写的,以及我们如何自己手写解析求导。
小萝卜:大师兄,且慢!problem.AddResidualBlock()每个参数的含义是什么?
大师兄:小萝卜,这就对了,懂得问问题啦!
大师兄:这里的cost_function的我们要定义残差Residual和导数Jacobian的地方,loss_function其实就是Ceres的核函数(暂且不管他),para_q和para_t分别是我们要优化的参数块。

小萝卜:谢谢大师兄,我还有一问!为什么我们一开始手写的Jacobian是用了李代数,算Residual对Transformation矩阵的导数,现在怎么变成对q和t求导了呢?对四元数求导?我可不会呀!
大师兄:这个问题问得很好!老纳一开始也在这个问题上栽了根头。我就在纳闷儿,我们公式是对T求导,然后咱们分成了R和t求导,再后来还变成了q和t求导了呢!
大师兄:其实啊,就是咱们这里把Ceres对T的优化,拆分开了分别对R和t进行优化(SO3),然而由于咱们的输入parameters是四元数q和t,所以咱们还要把R里面3维的旋转向量v转成4维的q。(当然也可以不拆分开用SE(3))
大师兄:这一步,其实Ceres自带的LocalParameterization已经帮我们解决了!1

那我们再来看看他是怎么定义的自动求导:

struct LidarEdgeFactor
{LidarEdgeFactor(Eigen::Vector3d curr_point_, Eigen::Vector3d last_point_a_,Eigen::Vector3d last_point_b_, double s_): curr_point(curr_point_), last_point_a(last_point_a_), last_point_b(last_point_b_), s(s_) {}template <typename T>bool operator()(const T *q, const T *t, T *residual) const{Eigen::Matrix<T, 3, 1> cp{T(curr_point.x()), T(curr_point.y()), T(curr_point.z())};Eigen::Matrix<T, 3, 1> lpa{T(last_point_a.x()), T(last_point_a.y()), T(last_point_a.z())};Eigen::Matrix<T, 3, 1> lpb{T(last_point_b.x()), T(last_point_b.y()), T(last_point_b.z())};//Eigen::Quaternion<T> q_last_curr{q[3], T(s) * q[0], T(s) * q[1], T(s) * q[2]};Eigen::Quaternion<T> q_last_curr{q[3], q[0], q[1], q[2]};Eigen::Quaternion<T> q_identity{T(1), T(0), T(0), T(0)};q_last_curr = q_identity.slerp(T(s), q_last_curr);Eigen::Matrix<T, 3, 1> t_last_curr{T(s) * t[0], T(s) * t[1], T(s) * t[2]};Eigen::Matrix<T, 3, 1> lp;lp = q_last_curr * cp + t_last_curr;Eigen::Matrix<T, 3, 1> nu = (lp - lpa).cross(lp - lpb);Eigen::Matrix<T, 3, 1> de = lpa - lpb;residual[0] = nu.x() / de.norm();residual[1] = nu.y() / de.norm();residual[2] = nu.z() / de.norm();return true;}static ceres::CostFunction *Create(const Eigen::Vector3d curr_point_, const Eigen::Vector3d last_point_a_,const Eigen::Vector3d last_point_b_, const double s_){return (new ceres::AutoDiffCostFunction<LidarEdgeFactor, 3, 4, 3>(new LidarEdgeFactor(curr_point_, last_point_a_, last_point_b_, s_)));}Eigen::Vector3d curr_point, last_point_a, last_point_b;double s;
};

Ceres的使用

小萝卜:大师兄大师兄,这个Ceres的代码好复杂,我看不懂呀!
大师兄:看不懂就对了!我也是参考了好几个大佬的博客才明白呢。

请各位同学参考这几个博客网页1 2来学习Ceres。

Ceres解析求导:线特征残差

// EdgeAnalyticCostFunction
class LidarEdgeAnalyticCostFunction : public ceres::SizedCostFunction<3, 4, 3> {public:LidarEdgeAnalyticCostFunction(Eigen::Vector3d curr_point_, Eigen::Vector3d last_point_a_,Eigen::Vector3d last_point_b_, double s_): curr_point(curr_point_), last_point_a(last_point_a_), last_point_b(last_point_b_), s(s_) {}virtual bool Evaluate(double const* const* parameters,double* residuals,double** jacobians) const {Eigen::Map<const Eigen::Quaterniond> q_last_curr(parameters[0]);Eigen::Map<const Eigen::Vector3d> t_last_curr(parameters[1]);Eigen::Vector3d lp;Eigen::Vector3d lp_r;lp_r = q_last_curr * curr_point; // for computing Jacobian of Rotation: dp_by_drlp = q_last_curr * curr_point + t_last_curr; //new pointEigen::Vector3d nu = (lp - last_point_a).cross(lp - last_point_b);Eigen::Vector3d de = last_point_a - last_point_b;residuals[0] = nu.x() / de.norm();residuals[1] = nu.y() / de.norm();residuals[2] = nu.z() / de.norm();if(jacobians != NULL){if(jacobians[0] != NULL){Eigen::Vector3d re = last_point_b - last_point_a;Eigen::Matrix3d skew_re = skew(re);//  RotationEigen::Matrix3d skew_lp_r = skew(lp_r);Eigen::Matrix<double, 3, 3> dp_by_dr;dp_by_dr.block<3,3>(0,0) = -skew_lp_r;Eigen::Map<Eigen::Matrix<double, 3, 4, Eigen::RowMajor> > J_so3_r(jacobians[0]);J_so3_r.setZero();J_so3_r.block<3,3>(0,0) = skew_re * dp_by_dr / de.norm();// TranslationEigen::Matrix<double, 3, 3> dp_by_dt;(dp_by_dt.block<3,3>(0,0)).setIdentity();Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor> > J_so3_t(jacobians[1]);J_so3_t.setZero();J_so3_t.block<3,3>(0,0) = skew_re * dp_by_dt / de.norm();  }}return true;}protected:Eigen::Vector3d curr_point, last_point_a, last_point_b;double s;
};

这里写完以后,记得改problem的costFunction:

ceres::CostFunction *cost_function = new LidarEdgeAnalyticCostFunction(curr_point, last_point_a, last_point_b, s);
problem.AddResidualBlock(cost_function, loss_function, para_q, para_t);

Ceres解析求导:面特征残差

// PlaneAnalyticCostFunction
class LidarPlaneAnalyticCostFunction : public ceres::SizedCostFunction<1, 4, 3> {public:LidarPlaneAnalyticCostFunction(Eigen::Vector3d curr_point_, Eigen::Vector3d last_point_j_,Eigen::Vector3d last_point_l_, Eigen::Vector3d last_point_m_, double s_): curr_point(curr_point_), last_point_j(last_point_j_), last_point_l(last_point_l_), last_point_m(last_point_m_), s(s_) {}virtual bool Evaluate(double const* const* parameters,double* residuals,double** jacobians) const {Eigen::Map<const Eigen::Quaterniond> q_last_curr(parameters[0]);Eigen::Map<const Eigen::Vector3d> t_last_curr(parameters[1]);Eigen::Vector3d lp;Eigen::Vector3d lp_r;lp_r = q_last_curr * curr_point; // for computing Jacobian of Rotation: dp_drlp = q_last_curr * curr_point + t_last_curr; //new pointEigen::Vector3d de = (last_point_l-last_point_j).cross(last_point_m-last_point_j);double nu = (lp-last_point_j).dot(de);residuals[0] = nu / de.norm();if(jacobians != NULL){if(jacobians[0] != NULL){Eigen::Vector3d dX_dp = de / de.norm();double X = nu / de.norm();Eigen::Vector3d ddh_dp = X * dX_dp / std::abs(X);//  RotationEigen::Matrix3d skew_lp_r = skew(lp_r);Eigen::Matrix<double, 3, 3> dp_dr;dp_dr.block<3,3>(0,0) = -skew_lp_r;Eigen::Map<Eigen::Matrix<double, 1, 4, Eigen::RowMajor> > J_so3_r(jacobians[0]);J_so3_r.setZero();J_so3_r.block<1,3>(0,0) = ddh_dp.transpose() * dp_dr;// TranslationEigen::Matrix<double, 3, 3> dp_dt;(dp_dt.block<3,3>(0,0)).setIdentity();Eigen::Map<Eigen::Matrix<double, 1, 3, Eigen::RowMajor> > J_so3_t(jacobians[1]);J_so3_t.setZero();J_so3_t.block<1,3>(0,0) = ddh_dp.transpose() * dp_dt;}}return true;}protected:Eigen::Vector3d curr_point, last_point_j, last_point_l, last_point_m;double s;
};

Ceres和Eigen的坑点

  1. Eigen Library requires the data type to be consistent, eg.
// Since de is Vector3d, thus nu must be double
Eigen::Vector3d de = (last_point_l-last_point_j).cross(last_point_m-last_point_j);
double nu = (lp-last_point_j).dot(de);
  1. Eigen Vector product must use explicitly .dot() pr .cross()

第三题:EVO结果分析

  1. APE

  2. RPE


    RVIZ中的结果,熟悉的Kitti图,可以看到在最后转弯的时候还是有点偏离绿色的GNSS轨迹,不过整体效果还是不错!


  1. yuntian_li, Ceres详解(一) CostFunction ↩︎ ↩︎

  2. leeayu, Ceres Tutotial —— 最小二乘建模 ↩︎

深蓝学院-多传感器融合定位-第3章作业相关推荐

  1. 深蓝学院-多传感器融合定位-第7章作业

    深蓝学院-多传感器融合定位-第7章作业 1. 及格题 2. 良好题 Parameter - verison 1 Parameter - verison 2 Parameter - verison 3 ...

  2. 深蓝学院-多传感器融合定位课程-第2章-3D激光里程计I

    专栏文章: 深蓝学院-多传感器融合定位课程-第1章-概述_goldqiu的博客-CSDN博客 github保存了相关代码和学习笔记: Shenlan-Course-Multi-Sensor-Fusio ...

  3. 深蓝学院-多传感器融合定位课程-第5章-惯性导航原理及误差分析

    专栏文章: 深蓝学院-多传感器融合定位课程-第1章-概述_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第2章-3D激光里程计I_goldqiu的博客-CSDN博客 深蓝学院-多 ...

  4. 多传感器融合定位 第四章 点云地图构建及基于点云地图定位

    多传感器融合定位 第四章 点云地图构建及基于点云地图定位 代码下载 https://github.com/kahowang/sensor-fusion-for-localization-and-map ...

  5. 多传感器融合定位 第六章 惯性导航结算及误差模型

    多传感器融合定位 第六章 惯性导航结算及误差模型 参考博客:深蓝学院-多传感器融合定位-第6章作业 代码下载:https://github.com/kahowang/sensor-fusion-for ...

  6. 看深蓝学院多传感器融合课程的笔记

    贝叶斯推断和贝叶斯公式是整个卡尔曼的核心. 贝叶斯滤波比卡尔曼滤波比卡尔曼滤波更宽泛. 误差作为状态量 融合工程师很多时候是调参工程师,卡尔曼滤波的Q R,需要调参  掌握观测方程的推导.13章第一 ...

  7. 多传感器融合定位 第八章 基于滤波的融合方法进阶

    多传感器融合定位 第八章 基于滤波的融合方法进阶 参考博客:深蓝学院-多传感器融合定位-第8章作业 代码下载:https://github.com/kahowang/sensor-fusion-for ...

  8. 多传感器融合定位ch1

    多传感器融合定位ch1 目录 多传感器融合定位ch1 1 数据集下载 2 数据集测试 深蓝学院多传感器融合定位课程笔记, 采用的软硬件环境如下 硬件: 华擎DeskMini X300W迷你主机 软件: ...

  9. 多传感器融合定位三-3D激光里程计其三:点云畸变补偿

    多传感器融合定位三-3D激光里程计其三:点云畸变补偿 1. 产生原因 2. 补偿方法 Reference: 深蓝学院-多传感器融合 多传感器融合定位理论基础 文章跳转: 多传感器融合定位一-3D激光里 ...

  10. 多传感器融合定位五-点云地图构建及定位

    多传感器融合定位五-点云地图构建及定位 1. 回环检测 1.1 基于Scan Context 1.2 基于直方图 2. 后端优化 2.1 后端优化基本原理 2.2 李群.李代数基本知识 2.3 李群. ...

最新文章

  1. 自用Java爬虫工具JAVA-CURL已开源
  2. KCL:声明式的云原生配置策略语言
  3. java 开发人员工具_Java开发人员应该知道的7种新工具
  4. linux下面实时查看进程,内存以及cpu使用情况使用命令
  5. 2017 JavaScript生态圈调查报告
  6. Oracle 分析函数
  7. 优秀IT技术文章集(最新)(高质量)
  8. 猜一宋词名句 Java_宋词名句
  9. 华三服务器bios修改兼容模式,bios怎么设置兼容模式
  10. Linux date 命令
  11. 苹果mov格式的视频怎么转换mp4?
  12. 基于Vue实现的多条件筛选功能(类似京东和淘宝功能)
  13. 用过滤器防sql注入
  14. ant design vue table 高度自适应_很受欢迎的vue前端UI框架
  15. 朋友问我移居香港后悔没?这回答够真实!
  16. 通俗讲义 | 深入浅出云计算、大数据、人工智能
  17. Adobe25年软件精华CS3中文版发布.
  18. 蠕虫病毒疯狂传播如何预防
  19. 英语计算机简历范文模板,计算机英文简历范文模板
  20. webmatrix mysql_WebMatrix教程(一) (关注Microsoft 的最新武器:建立你的第一个WebMatrix网站)...

热门文章

  1. kernel input device
  2. uniapp 微信授权,微信分享,微信支付,微信跳转app集成
  3. 自然语言处理基础技术之成分句法分析
  4. 【语音识别】基于动态时间规整算法(DTW)实现中文语音识别系统含Matlab源码
  5. img图片在父元素中居中的方法
  6. java计算机毕业设计-智慧农业水果销售系统源码+mysql数据库+系统+lw文档+部署
  7. MSP430 单片机 读取 程序 LM75A LM75 温度传感器
  8. 大型复杂群体项目分解结构(PBS)概念与方法研究
  9. 面试字节跳动后台开发(实习)
  10. 【汉字】转【pīnyīn】