我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C语言lidar_align雷达里程计校准功能怎么用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C语言lidar_align雷达里程计校准功能怎么用

本篇内容主要讲解“C语言lidar_align雷达里程计校准功能怎么用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言lidar_align雷达里程计校准功能怎么用”吧!

功能介绍

功能包名称:lidar_align git网址:链接

一种 校准 3D 激光雷达和 6 自由度位姿传感器 外参 的 方法

适配的ROS版本有 Indigo、Kinect、Melodic

准确的结果需要 大量 非平面的运动,这使得该方法不适合 校准 安装在汽车上的传感器 以为只有在那个方向上有数据的变化才能计算出最终的结果。

整个功能包大体上实现了下面的功能

读取lidar和位姿传感器的数据

通过时间戳匹配lidar每帧里面的每个点和位姿传感器的坐标变换

通过上面的变换矩阵将 利用位姿信息将lidar的每帧拼接成点云

每个点和它最近邻点的距离总和求出,在优化中就是不断的迭代找到坐标变换使这个距离最小

功能包算法的整体思想

为每帧lidar的每个点通过时间戳去匹配一个位姿数据,并通过插值的方式得到更准确的位姿值,每个点的时间偏移也是优化因素之一

利用NLopt的库的非线性优化方法

目标函数:让拼接后的点云的每个点的最近邻点最小

优化向量:x、y 、z、roll、pitch、yaw、time_offset 总体来说就是不断的迭代,找到一个合适的优化向量(也就是lidar到里程计的坐标变换)使得拼在一起的点云每个点的最近邻点距离最小。这个功能包的优点就是可以离线计算,录一个rosbag,然后跑一下就可以求的外参 它只会读取一个bag ,所以 lidar和位姿都要在里面

最终的结果: 在运行的时候,功能包会输出当前的估计变化 优化结束的时候 坐标变换的参数会打印在终端上 也会在路径下面保存一个txt文件和ply文件。

可以查看ply文件,就是拼接后的点云和场景是否一致

编译

从git上下载后需要 下载 nlopt的库

sudo apt-get install libnlopt-dev

然后编译还是会报错

Could not find a package configuration file provided by "NLOPT"

解决办法 将 lidar_align 文件夹下的 NLOPTConfig.cmake 复制到 你ROS工作空间的class="lazy" data-src路径下面

相关参数

这些都没有在配置文件里面 想要修改的话需要改源码然后再编译

在每个类里面有个 Config的结构体, 初始化了相关参数

和lidar相关的在 Scan类里面设置的

class Scan { public:  struct Config {    float min_point_distance = 80;//lidar数据里面点的距离的最小值 大于此值被使用 0.0;    float max_point_distance = 200;// lidar数据里面点的距离的最大值 小于此值被使用  默认100.0;    float keep_points_ratio = 0.01;// 用于优化点的比例 (此值越大会极大增加运行时间) 默认0.01    float min_return_intensity = -1.0;//lidar数据里面点的强度的最小值  大于此值被使用  默认 -1.0    bool estimate_point_times = false;//是否估计lidar每个点的时间 用下面那俩变量   默认 false    bool clockwise_lidar = false;//lidar旋转的方向  顺时针还是逆时针              默认  false(逆时针)    bool motion_compensation = true;//是不是需要运行补偿    默认 true    float lidar_rpm = 600.0;// lidar的转速  仅用于  estimate_point_times   默认 600  };

输入输出的相关参数在launch里面设置的

    <param name="input_bag_path" value="$(arg bag_file)" />    <param name="input_csv_path" value="$(arg csv_file)" />    <param name="output_pointcloud_path" value="$(find lidar_align)/results/$(anon lidar_points).ply" />    <param name="output_calibration_path" value="$(find lidar_align)/results/$(anon calibration).txt" />    <param name="transforms_from_csv" value="$(arg transforms_from_csv)"/>

use_n_scans 执行优化开始的 lidar的帧数 默认 2147483647 input_bag_path ros bag 的路径
transforms_from_csv 是否通过 csv文件 读取位置 默认 false input_csv_path csv路径 output_pointcloud_path ply文件输出路径 output_calibration_path 校准结果输出路径

校正相关参数 在Aligner类里面设置的

class Aligner { public:  struct Config {    bool local = false;//执行局部优化还是全局优化 初始化为false, 执行全局的优化,并且结果用于初始估计 然后执行局部优化    std::vector<double> inital_guess{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};//校准的初始估计   仅用于运行 local 模式      double max_time_offset = 0.1;//最大的时间对准偏差  x[6]的上下浮动范围    double angular_range = 0.5;    //  在初始估计位置的搜索范围 (弧度)  角度偏移上下限 +-0.5弧度    double translation_range = 1.0;//在初始估计位置的搜索范围 (位置)  位置偏移上下限 +-1米    double max_evals = 200;// 最大的评估次数    double xtol = 0.0001;//x的容差    int knn_batch_size = 1000;//点的偏移数量 被用于  寻找最近邻点时    int knn_k = 1;//寻找最近邻点的个数 , 程序了 自加1了  因为在一个点云里面找其中一个点的最近邻一个是自己一个才是真的最近邻    float local_knn_max_dist = 0.1;//局部优化的时候 最近邻点的最大距离    float global_knn_max_dist = 1.0;//全局优化的时候 最近邻点的最大距离    bool time_cal = true;   //是否执行时间补充计算  就是在找每个点的位姿时根据时间对里程计插值    std::string output_pointcloud_path = "";  //点云文件保存路径    std::string output_calibration_path = ""; // 校准信息文件保存路径  };

代码文件结构

头文件四个:

  • aligner.h : Lidar 和 Odom 校正(外参计算)时用到的类

  • **loader.h :**从ROS的Bag或CSV格式载入数据的相关函数

  • **sensor.h :**主要包括Odom以及LIdar相关接口

  • transform.h : 一些SO3变化的计算及转换,在插值、优化时使用

cpp文件四个:

  • aligner.cpp : Lidar 和 Odom 校正(外参计算) 时 的实现函数

  • lidar_align_node.cpp : main的启动地方 实例各个类

  • loader.cpp : 加载数据

  • sensors.cpp : 处理lidar和里程计的数据

核心代码

前面的数据读取处理啥的就不整了,下面分析下主要的代码部分

在面函数里面 调用了 下面的 函数 前面就是数据读取和处理,从这个函数里面开启了校准的过程

aligner.lidarOdomTransform(&amp;lidar, &amp;odom);//执行校准

这个函数里面

    size_t num_params = 6;//初始化优化参数的个数  if (config_.time_cal) {    ++num_params;//使用时间补偿计算   优化参数个数为7  }

根据 是否使用 时间补偿 来确定 优化参数的 个数

   if (!config_.local) {  //默认  执行 全局优化  并用于初始估计    //打印 正在 执行 全局 优化    ROS_INFO("Performing Global Optimization...                             ");    std::vector<double> lb = {-M_PI, -M_PI, -M_PI};//设置下限    std::vector<double> ub = {M_PI, M_PI, M_PI};//设置上限    std::vector<double> global_x(3, 0.0);//设置 执行全局优化的  x参数向量 设置为3 个,初始值为0.0    optimize(lb, ub, &opt_data, &global_x);//执行优化(全局优化)  只求 角度的  优化结果    config_.local = true;//将全局优化的flag关闭  下次则执行 局部优化    //赋值 角度  优化 的结果    x[3] = global_x[0];    x[4] = global_x[1];    x[5] = global_x[2];  } else {//local 为 true的 话  则 为 默认的 初始值     x = config_.inital_guess;//初始值 全为0    局部优化则初始认为 lidar和imu的坐标轴一致对齐  其它情况最好按实际情况粗略配置 初始值   }

根据配置参数(是否执行全局优化) 执行 初始角度的赋值 ,如果执行全局优化则先进行一个 估计,得到角度 否则 直接附设置的初始值

  //设置 参数向量 的下限   ,默认参数 -1,-1,-1,-0.5,-0.5,-0.5  std::vector<double> lb = {      -config_.translation_range, -config_.translation_range,      -config_.translation_range, -config_.angular_range,      -config_.angular_range,     -config_.angular_range};  //设置 参数向量 的上限    默认参数 1,1,1,0.5,0.5,0.5  std::vector<double> ub = {      config_.translation_range, config_.translation_range,      config_.translation_range, config_.angular_range,      config_.angular_range,     config_.angular_range};  //将  全局优化得到的 角度的 初始估计值  叠加到 上下限中   也就是说  初始估计 roll 为0.1弧度,则 优化的范围是 0.1 +- 0.5 弧度    for (size_t i = 0; i < 6; ++i) {    lb[i] += x[i];//叠加下限    ub[i] += x[i];//叠加上限  }    //将时间补偿的 优化 范围 加 到 上下限中  if (config_.time_cal) {    ub.push_back(config_.max_time_offset);    lb.push_back(-config_.max_time_offset);  }

设置 优化因素的 上限和下限

   optimize(lb, ub, &opt_data, &x);

执行局部优化

下面看下执行局部优化的主要内容

也就是 optimize()函数 里面就是用的nlopt的方法进行的非线性优化

  if (config_.local) {//看是执行全局优化还是局部优化   用的算法不同  x的个数在前面配置的也不同, 全局优化为3个,只求角度 局部优化为7个    opt = nlopt::opt(nlopt::LN_BOBYQA, x->size());  } else {    opt = nlopt::opt(nlopt::GN_DIRECT_L, x->size());//选用 全局优化函数  不需要求导的  算法 DIRECT_L   x的个数在前面配置的是三个   }

根据执行全局优化还是局部优化 选择不同的算法 并生成了 nlopt 对象实例

  //设置下限  opt.set_lower_bounds(lb);  //设置上限  opt.set_upper_bounds(ub);  opt.set_maxeval(config_.max_evals);//设置最大估计次数  opt.set_xtol_abs(config_.xtol);//设置x容差停止值  opt.set_min_objective(LidarOdomMinimizer, opt_data);//设置目标函数  LidarOdomMinimizer函数名 opt_data外部数据

nlopt的相关设置 其中 LidarOdomMinimizer 是目标函数需要重点看的

目标函数里面

  //通过 kdtree 的 方法 求的 每个点的 最近邻距离 总和  double error = d->aligner->lidarOdomKNNError(*(d->lidar));

在这里 通过 kdtree 的 方法 求的 每个点的 最近邻距离 总和

然后不断的迭代让这个距离最小,得到的优化向量就是 校准信息了

主要的核心就是这些了,当然这个里面的小细节太多了,有问题的童鞋可以私信我。

结果

最后上一个测试时的结果把

Active Transformation Vector (x,y,z,rx,ry,rz) from the Pose Sensor Frame to the Lidar Frame: [0.770924, -0.25834, 0.105557, 1.67974, -1.88141, -1.40085]

Active Transformation Matrix from the Pose Sensor Frame to the Lidar Frame: -0.300413 -0.623731 -0.721603 0.770924 -0.870125 -0.130671 0.475193 -0.25834 -0.390685 0.770639 -0.503468 0.105557 0 0 0 1

Active Translation Vector (x,y,z) from the Pose Sensor Frame to the Lidar Frame: [0.770924, -0.25834, 0.105557]

Active Hamiltonen Quaternion (w,x,y,z) the Pose Sensor Frame to the Lidar Frame: [-0.127913, -0.577435, 0.646763, 0.481564]

Time offset that must be added to lidar timestamps in seconds: -0.00263505

ROS Static TF Publisher:

在txt里面功能包输出的结果就是这样的。

到此,相信大家对“C语言lidar_align雷达里程计校准功能怎么用”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C语言lidar_align雷达里程计校准功能怎么用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C语言lidar_align雷达里程计校准功能怎么用

本篇内容主要讲解“C语言lidar_align雷达里程计校准功能怎么用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言lidar_align雷达里程计校准功能怎么用”吧!功能介绍功能包名称:
2023-07-05

C语言lidar_align雷达里程计校准功能详解

这篇文章主要为大家介绍了C语言lidar_align雷达里程计校准功能详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-02

怎么用C语言实现简单的计算器功能

这篇文章主要讲解了“怎么用C语言实现简单的计算器功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用C语言实现简单的计算器功能”吧!简单计算器,实现简单的加减乘除功能,选择对应的运算符输
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录