首页 > 其他分享 >激光雷达定位与建图-拟合问题

激光雷达定位与建图-拟合问题

时间:2024-11-29 17:33:55浏览次数:5  
标签:Eigen 建图 plane 拟合 coeffs 平面 data 激光雷达

本篇文章介绍如何在点云中提取线段和平面。

一、平面拟合

1. 问题提出

给定一组由N个点组成的点云 X = { x 1 , ⋯   , x n } X =\left \{x_{1}, \cdots , x_{n} \right \} X={x1​,⋯,xn​} ,其中每个点取三维欧式坐标 x k x_{k} xk​,寻找一组平面参数n,d,使得:
n T x k + d = 0 n^{T}x_{k} + d = 0 nTxk​+d=0
其中:n为法向量,d为截距。
在给出的一组点云数据,满足上述平面方程,且误差最小,于是可直接用最小二乘求解。
误差最小化:

在这里插入图片描述
使用齐次坐标,将上试简化:
在这里插入图片描述
其中: x ~ = [ x T 1 ] \tilde{x} = \begin{bmatrix} x^T & 1 \end{bmatrix} x~=[xT​1​], n ~ = [ n T d ] T \tilde{n} = \begin{bmatrix} n^T & d \end{bmatrix}^{T} n~=[nT​d​]T。
将 x ~ k \tilde{x}_{k} x~k​看做矩阵A,进行SVD分解找到最小奇异值对应的右奇异向量即是方程的解。

2. 实现

/**
 * @brief 测试平面拟合功能
 * 
 * 该函数生成带有噪声的平面点,然后使用FitPlane函数进行平面拟合,
 * 最后比较估计的平面参数与真实参数。
 */
void PlaneFittingTest() {
    // 定义真实平面参数
    Vec4d true_plane_coeffs(0.1, 0.2, 0.3, 0.4);
    true_plane_coeffs.normalize();

    std::vector<Vec3d> points;

    // 随机生成仿真平面点
    cv::RNG rng;
    for (int i = 0; i < FLAGS_num_tested_points_plane; ++i) {
        // 生成随机点并投影到平面上
        Vec3d p(rng.uniform(0.0, 1.0), rng.uniform(0.0, 1.0), rng.uniform(0.0, 1.0));
        double n4 = -p.dot(true_plane_coeffs.head<3>()) / true_plane_coeffs[3];
        p = p / (n4 + std::numeric_limits<double>::min());  // 防止除零
        
        // 添加高斯噪声
        p += Vec3d(rng.gaussian(FLAGS_noise_sigma), rng.gaussian(FLAGS_noise_sigma), rng.gaussian(FLAGS_noise_sigma));

        points.emplace_back(p);

        // 验证点是否在平面上(考虑到噪声,结果应接近但不完全等于0)
        LOG(INFO) << "res of p: " << p.dot(true_plane_coeffs.head<3>()) + true_plane_coeffs[3];
    }

    // 使用FitPlane函数进行平面拟合
    Vec4d estimated_plane_coeffs;
    if (sad::math::FitPlane(points, estimated_plane_coeffs)) {
        // 拟合成功,输出估计的平面参数和真实参数
        LOG(INFO) << "estimated coeffs: " << estimated_plane_coeffs.transpose()
                  << ", true: " << true_plane_coeffs.transpose();
    } else {
        // 拟合失败
        LOG(INFO) << "plane fitting failed";
    }
}

/**
 * @brief 拟合平面
 * 
 * @tparam S 数据类型(通常为float或double)
 * @param data 输入的3D点集
 * @param plane_coeffs 输出的平面方程系数 (ax + by + cz + d = 0)
 * @param eps 拟合误差阈值
 * @return bool 拟合是否成功
 */
template <typename S>
bool FitPlane(std::vector<Eigen::Matrix<S, 3, 1>>& data, Eigen::Matrix<S, 4, 1>& plane_coeffs, double eps = 1e-2) {
    // 确保至少有3个点才能拟合平面
    if (data.size() < 3) {
        return false;
    }

    // 构建系数矩阵A
    Eigen::MatrixXd A(data.size(), 4);
    for (int i = 0; i < data.size(); ++i) {
        A.row(i).head<3>() = data[i].transpose();
        A.row(i)[3] = 1.0;
    }

    // 使用SVD求解最小二乘问题
    Eigen::JacobiSVD svd(A, Eigen::ComputeThinV);
    plane_coeffs = svd.matrixV().col(3);

    // 检查拟合误差是否在阈值范围内
    for (int i = 0; i < data.size(); ++i) {
        double err = plane_coeffs.template head<3>().dot(data[i]) + plane_coeffs[3];
        if (err * err > eps) {
            return false;
        }
    }

    return true;
}

二、线性拟合

1. 问题提出

给定一组由N个点组成的点云 X = { x 1 , ⋯   , x n } X =\left \{x_{1}, \cdots , x_{n} \right \} X={x1​,⋯,xn​} ,其中每个点取三维欧式坐标 x k x_{k} xk​,寻找一组直线参数d,p使得:
x = d t + p x = dt +p x=dt+p
其中:d为直线方向,满足\left | d \right | = 1,p为直线上一点;t为直线参数。
构造最小二乘,对于任意一个不在直线上的点 x k x_{k} xk​,利用勾股定理,计算与直线的垂直距离的平方,如下:
在这里插入图片描述
然后构造最小二乘问题,求解d 和 p。
在这里插入图片描述
继续:
在这里插入图片描述
故p可以取点云的中心。于是可以先确定p,再求d。
令 y k = x k − p y_{k} = x_{k} - p yk​=xk​−p,对上述构造的最小二乘进行化简,如下:
在这里插入图片描述
由于第一项不包含d,而求第二项最小值,只需取 d T y k y k T d d^{T}y_{k}y^{T}_{k}d dTyk​ykT​d的最大值即可。
将 y k T y^{T}_{k} ykT​看做矩阵A,进行SVD分解找到最大奇异值对应的右奇异向量即是方程的解。

2. 实现

/**
 * @brief 拟合三维空间中的直线
 * 
 * @tparam S 数据类型(通常为float或double)
 * @param data 输入的3D点集
 * @param origin 输出的直线上的一点(通常为点集的质心)
 * @param dir 输出的直线方向向量
 * @param eps 拟合误差阈值
 * @return bool 拟合是否成功
 */
template <typename S>
bool FitLine(std::vector<Eigen::Matrix<S, 3, 1>>& data, Eigen::Matrix<S, 3, 1>& origin, Eigen::Matrix<S, 3, 1>& dir,
             double eps = 0.2) {
    // 确保至少有2个点才能拟合直线
    if (data.size() < 2) {
        return false;
    }

    // 计算点集的质心作为直线上的一点
    origin = std::accumulate(data.begin(), data.end(), Eigen::Matrix<S, 3, 1>::Zero().eval()) / data.size();

    // 构建矩阵Y,每行是点到质心的向量
    Eigen::MatrixXd Y(data.size(), 3);
    for (int i = 0; i < data.size(); ++i) {
        Y.row(i) = (data[i] - origin).transpose();
    }

    // 使用SVD求解主方向
    Eigen::JacobiSVD svd(Y, Eigen::ComputeFullV);
    dir = svd.matrixV().col(0);

    // 检查每个点到拟合直线的距离是否在阈值范围内
    for (const auto& d : data) {
        if (dir.template cross(d - origin).template squaredNorm() > eps) {
            return false;
        }
    }

    return true;
}


三、最小二乘的解法

1. 线性最小二乘SVD法

SVD相关介绍请看参考文献1
在这里插入图片描述

2.非线性最小二乘

(1)迭代最小二乘法

在这里插入图片描述

(2)优化方法
a. 高斯牛顿法

在这里插入图片描述

b. LM

在这里插入图片描述

四、参考

1.https://blog.csdn.net/qq_34213260/article/details/115345986
2.<<自动驾驶与机器人中的SLAM技术从理论到实践>>

标签:Eigen,建图,plane,拟合,coeffs,平面,data,激光雷达
From: https://blog.csdn.net/u010196944/article/details/143690302

相关文章

  • ubuntu使用Livox mid360激光雷达
    注意事项首先建议买一分三线然后关于电源适配器,说明书上是电压9-27v,功率6.5w,但是我使用12v1A的确不能正常使用。连接上后雷达会震动如果遇见了什么硬件方面的问题建议去“骚扰”客服,一般都会很好地解答那么接下来就是正式配置 下载编译安装Livox-SDK2建议直接安装到主......
  • 全网首发:香橙派5MAX安装ubuntu20.04(换了官方桌面)及ros,运行LIVOX-mid360混合固态激光雷
    简单介绍这块板子:我这款OrangePi 5 Max采用Rockchip RK3588 8核64位处理器,4个Cortex-A76(主频2.4GHz)、4个Cortex-A55(主频1.8GHz)及独立的NEON协处理器。配备16GB LPDDR5,集成ARM Mali-G610,内置3D GPU。这里在安装ubuntu20.04时,我没用香橙派官方桌面镜像安装的,而是在server......
  • 树莓派4b4g(已安装ubuntu20.04及ros1)+大疆混合固态激光雷达mid360(已在rviz中可显示数据
    首先安装FileZilla文件传输软件,获取树莓派ip后,可实现ubuntu与windows文件自由传递。推荐下面这篇安装使用方法。https://blog.csdn.net/SoloVersion/article/details/124579221目录1、配置大疆mid360并在rviz显示2、树莓派启动mid360,运行fast-lio1、配置大疆mid360并在rvi......
  • 如何利用ros搭建虚拟场景通过仿真机器人完成一次简单的SLAM建图、导航规划(超简单)?——
    一:什么是SLAM,SLAM和导航规划又有什么关系?SLAM(SimultaneousLocalizationandMapping,即同时定位与建图)是一种在未知或动态环境中自行驶的重要技术。主要通过设备上的传感器(如激光雷达、深度摄像头、惯性测量单元IMU等)实时感知周围环境,同时估计自身的位姿(位置和朝向)以及构建......
  • ROS1,用C++实现获取激光雷达数据,并使用gazebo测试
    实现步骤构建一个新的软件包,包名叫做lidar_pkg。cdcatkin_ws/src/catkin_create_pkglidar_pkgroscpprospysensor_msgs输入code,打开vscode在软件包中新建一个节点,节点名叫做lidar_node。在节点中,向ROS大管家NodeHandle申请订阅话题/scan,并设置回调函数为......
  • 机器学习/数据分析--用通俗语言讲解时间序列自回归(AR)模型,并用其预测天气,拟合度98%+
    时间序列在回归预测的领域的重要性,不言而喻,在数学建模中使用及其频繁,但是你真的了解ARIMA、AR、MA么?ACF图你会看么??时间序列数据如何构造???,我打过不少数学建模,但是都没有弄得很清楚;这篇将详细讲解了基础模型—AR的原理.文章目录1、自回归(AR)详解1、简要说明2、原理讲解......
  • 超参数优化过拟合了?试试滚动优化回测
    本文将回答下面的问题:超参数优化后的参数可以在未来继续有良好表现吗?进行超参数优化后,我应该选择表现最好的那组参数吗?很多人应该都有过用超参数优化让初始策略的收益翻好几倍的经历。这看起来很不错,然而当你面对这样一份非常漂亮的回测报告时,你到底有多大把握这组参数在未来......
  • TryOnDiffusion——生成拟合图像的最强大模型
    介绍论文地址:https://arxiv.org/pdf/2306.08276虚拟试穿是以人的图像和服装的图像为基础,目的是想象服装穿在人身上的效果。虚拟试穿可以改善网上购物体验,但大多数传统试穿方法只有在身体姿势和形状变化较小时才能奏效。主要的挑战在于如何根据目标体形对服装进行非刚性变......
  • pytorch线性/非线性回归拟合
    一、线性回归1.导入依赖库importnumpyasnpimportmatplotlib.pyplotaspltimporttorchfromtorchimportnn,optimfromtorch.autogradimportVariablenumpy:用来构建数据matplotlib.pyplot: 将构建好的数据可视化torch.nn:包含了torch已经准备好的层,激活函数、全......
  • Python实现:时间序列趋势外推法应用-龚珀兹曲线拟合
    龚珀兹曲线下表数据为某跨国公司1989-2021年的年销售量数据,使用适合的模型预测该公司2022年的销售额,并得出理由。部分数据如下表(具体数据从主页资源下载):年份时序(t)总额(yt)时序应该从0开始19891138.40019902174.00119913190.55219924196.10319935230.50419946237.10519957274.......