Linefit_ground_segmention文章梳理及代码阅读

2023-05-16

2013年专门针对地面分割的文章:

Fast segmentation of 3D point clouds for ground vehicles

代码链接:

https://github.com/lorenwel/linefit_ground_segmentation

代码阅读:

目录

1、新建一个GroundSegmentation::GroundSegmentation类对象,struct GroundSegmentationParams结构体在头文件中赋初值,用于初始化类,包含了如下参数:

2、segment函数:输入为需要进行地面分割的点云以及标签容器

3、insertPoints函数

4、insertionThread函数

5、getLines函数:用于获取拟合直线

6、lineFitThread()函数:

7、fitSegmentLines函数

8、assignCluster(segmentation)函数

9、assignClusterThread函数

1、新建一个GroundSegmentation::GroundSegmentation类对象,struct GroundSegmentationParams结构体在头文件中赋初值,用于初始化类,包含了如下参数:

visualize(false),                 //是否可视化
r_min_square(0.3 * 0.3),          //设置点云离lidar最近的距离
r_max_square(20*20),              //设置点云离lidar最远的距离
n_bins(30),                       //将点云分为多少个环形
n_segments(180),                  //将点云分为多少个扇形
max_dist_to_line(0.15),           //对点到所有直线的距离,如果小于此值,则认为是地面,反之则不是
min_slope(0),                     //拟合直线的斜率最小值
max_slope(1),                     //拟合直线的斜率最大值,大于此值则直线不满足要求,去掉最后一点
n_threads(4),                     //4个线程
max_error_square(0.01),           //点到线性拟合的点的最大误差
long_threshold(2.0),              //判断为一根长线的阈值,大于此阈值则is_long_line=true
max_long_height(0.1),             //判断拟合直线的最后一点是否符合条件,是长线但是期望值与真实值大于此阈值则剔除
max_start_height(0.2),            //不符合长直线,但是最后拟合点和传感器所在高度差小于此范围,则加入拟合点
sensor_height(0.2),               //传感器高度差,与max_start_height是相对关系
line_search_angle(0.2)            //搜索步长小于此角度,则在邻域内找拟合直线。

2、segment函数:输入为需要进行地面分割的点云以及标签容器

该函数就是最重要的函数,主要分为3个子函数:1、insertPoints(cloud)函数:用于获取每个点云的环向索引和扇形索引;2、 getLines()函数:用于获取每一个segment中的拟合直线;3、assignCluster(segmentation)函数:执行分割任务,计算点到拟合直线的距离,将地面点云标签置为1。segment函数的注释代码如下:

输入为:待分割的点云和标签
void GroundSegmentation::segment(const PointCloud& cloud, std::vector<int>* segmentation) 
{
  std::cout << "Segmenting cloud with " << cloud.size() << " points...\n";
  // 计时
  std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
  
  // segmention表示标签
  segmentation->clear();
  segmentation->resize(cloud.size(), 0);
  
  //bin_index_存储了每个点的bin索引和segment索引
  bin_index_.resize(cloud.size());
  
  //segment_coordinates_表示每个点在(d,z)上的索引
  segment_coordinates_.resize(cloud.size());
  
  // 获取了每个点云的bin索引和segment索引
  insertPoints(cloud);

  std::list<PointLine> lines;
  // 没用,不显示
  if (params_.visualize) {
    getLines(&lines);
  }
  // 执行这个
  else {
    getLines(NULL);
  }
  // 执行分割任务,输入就是标签
  assignCluster(segmentation);
  //不显示
  if (params_.visualize) 
  {
    // Visualize.
    PointCloud::Ptr obstacle_cloud(new PointCloud());
    // Get cloud of ground points.
    PointCloud::Ptr ground_cloud(new PointCloud());
    for (size_t i = 0; i < cloud.size(); ++i) {
      if (segmentation->at(i) == 1) ground_cloud->push_back(cloud[i]);
      else obstacle_cloud->push_back(cloud[i]);
    }
    PointCloud::Ptr min_cloud(new PointCloud());
    getMinZPointCloud(min_cloud.get());
    visualize(lines, min_cloud, ground_cloud, obstacle_cloud);
  }
  // 求取分割时间
  std::chrono::high_resolution_clock::time_point end =    std::chrono::high_resolution_clock::now();
  std::chrono::duration<double, std::milli> fp_ms = end - start;
  std::cout << "Done! Took " << fp_ms.count() << "ms\n";
}

3、insertPoints函数

void GroundSegmentation::insertPoints(const PointCloud& cloud) 
{
  // 线程数为4
  std::vector<std::thread> threads(params_.n_threads);
  // 将所有点分为4等分
  const size_t points_per_thread = cloud.size() / params_.n_threads;
  // Launch threads.0-1,1-2,2-3,3-4
  for (unsigned int i = 0; i < params_.n_threads - 1; ++i) {
    const size_t start_index = i * points_per_thread;
    const size_t end_index = (i+1) * points_per_thread;
    // 执行insertionThread函数
    threads[i] = std::thread(&GroundSegmentation::insertionThread, this,
                             cloud, start_index, end_index);
  }
  // 若未除尽,则需要另加线程处理剩下的点
  const size_t start_index = (params_.n_threads - 1) * points_per_thread;
  const size_t end_index = cloud.size();
  threads[params_.n_threads - 1] =
      std::thread(&GroundSegmentation::insertionThread, this, cloud, start_index, end_index);
  // Wait for threads to finish.
  for (auto it = threads.begin(); it != threads.end(); ++it) {
    // 等待线程完成
    it->join();
  }
}

再看一下其中的线程调用函数

4、insertionThread函数

void GroundSegmentation::insertionThread(const PointCloud& cloud,
                                         const size_t start_index,
                                         const size_t end_index) 
{
  // n_segments表示将360度分为多少等分,segment_step表示segment的步长
  const double segment_step = 2*M_PI / params_.n_segments;
  // bin_step表示环形的步长
  const double bin_step = (sqrt(params_.r_max_square) - sqrt(params_.r_min_square)) / params_.n_bins;
  const double r_min = sqrt(params_.r_min_square);
  
  // 4等分后的每个起点和终点索引
  for (unsigned int i = start_index; i < end_index; ++i) 
  {
    pcl::PointXYZ point(cloud[i]);
    // 求每个点云的d值
    const double range_square = point.x * point.x + point.y * point.y;
    const double range = sqrt(range_square);
    //若点云的d在设定的r值范围内
    if (range_square < params_.r_max_square && range_square > params_.r_min_square) 
    {
      // 求每个点云的水平角
      const double angle = std::atan2(point.y, point.x);
      // 每个点云的bin索引,从r_min圈外0开始
      const unsigned int bin_index = (range - r_min) / bin_step;
      // 每个点云的segment索引
      const unsigned int segment_index = (angle + M_PI) / segment_step;
      // segment_index_clamped存储点云的segment索引,包含了等于n_segments的情况
      const unsigned int segment_index_clamped = segment_index == params_.n_segments ? 0 : segment_index;
      // segments_存储每一个点云(bin和segment索引)的d和z,每一个segment和bin中都有多个range和z,其间也获取了每一个segment和bin中的一个最小range和z
      segments_[segment_index_clamped][bin_index].addPoint(range, point.z);
      // 每个点的bin索引和segment索引
      bin_index_[i] = std::make_pair(segment_index_clamped, bin_index);
    }
    else {
      bin_index_[i] = std::make_pair<int, int>(-1, -1);
    }
    // 每个点的d和z
    segment_coordinates_[i] = Bin::MinZPoint(range, point.z);
  }
}
// 最后,这里可以知道每一个点云所在的segment和bin,以及每一个segment和bin中的z的最低点云

5、getLines函数:用于获取拟合直线

void GroundSegmentation::getLines(std::list<PointLine> *lines) 
{
  std::mutex line_mutex;
  std::vector<std::thread> thread_vec(params_.n_threads);
  unsigned int i;
  for (i = 0; i < params_.n_threads; ++i) {
    // 按n_segment索引,0-45,46-90,91-135,136-180
    const unsigned int start_index = params_.n_segments / params_.n_threads * i;
    const unsigned int end_index = params_.n_segments / params_.n_threads * (i+1);
    thread_vec[i] = std::thread(&GroundSegmentation::lineFitThread, this,
                                start_index, end_index, lines, &line_mutex);
  }
  for (auto it = thread_vec.begin(); it != thread_vec.end(); ++it) {
    it->join();
  }
}

其中的线程调用函数

6、lineFitThread()函数

void GroundSegmentation::lineFitThread(const unsigned int start_index,
                                       const unsigned int end_index,
                                       std::list<PointLine> *lines, std::mutex* lines_mutex) 
{
  const bool visualize = lines;

  const double seg_step = 2*M_PI / params_.n_segments;

  double angle = -M_PI + seg_step/2 + seg_step * start_index;
  // i表示等某个segment,将按照segment等分索引
  for (unsigned int i = start_index; i < end_index; ++i) 
  {
    // segment.cc中的fitsegment函数进行直线拟合
    segments_[i].fitSegmentLines();
    
    //不可视化线
    if (visualize) {
      std::list<Segment::Line> segment_lines;
      segments_[i].getLines(&segment_lines);
      for (auto line_iter = segment_lines.begin(); line_iter != segment_lines.end(); ++line_iter) {
        const pcl::PointXYZ start = minZPointTo3d(line_iter->first, angle);
        const pcl::PointXYZ end = minZPointTo3d(line_iter->second, angle);
        lines_mutex->lock();
        lines->emplace_back(start, end);
        lines_mutex->unlock();
      }

      angle += seg_step;
    }
  }
}

7、fitSegmentLines函数

// 对于每一个segment,都进行如下操作:
void Segment::fitSegmentLines() 
{
  // line_start指针指向segment的第一个bin
  auto line_start = bins_.begin();
  // 如果没有点,则第二个bin
  while (!line_start->hasPoint()) 
  {
    ++line_start;
    // Stop if we reached last point.
    if (line_start == bins_.end()) return;
  }
  // Fill lines.
  bool is_long_line = false;
  // 传感器离地面的高度,取负号是因为后面用的减号
  double cur_ground_height = -sensor_height_;
  // 第一个bin中的最小点
  std::list<Bin::MinZPoint> current_line_points(1, line_start->getMinZPoint());
  //存储拟合直线的k和b
  LocalLine cur_line = std::make_pair(0,0);
  // 从第二个点开始
  for (auto line_iter = line_start+1; line_iter != bins_.end(); ++line_iter) 
  {
    if (line_iter->hasPoint()) {
      Bin::MinZPoint cur_point = line_iter->getMinZPoint();

      // 前后两个bin中的最低点的d的差大于2,则判断为一个长线
      if (cur_point.d - current_line_points.back().d > long_threshold_) is_long_line = true;

      // 第一次为否,什么都不做
      // 第三个点来时,
      if (current_line_points.size() >= 2) 
      {
        // Get expected z value to possibly reject far away points.
        double expected_z = std::numeric_limits<double>::max();
        //利用k、b求期望
        if (is_long_line && current_line_points.size() > 2) 
        {
          expected_z = cur_line.first * cur_point.d + cur_line.second;
        }
        current_line_points.push_back(cur_point);
        // 利用eigen求解,获取拟合直线的k、b
        cur_line = fitLocalLine(current_line_points);
        // 返回三个点拟合的最大误差
        const double error = getMaxError(current_line_points, cur_line);
        // 如果满足下列条件则弹出第三个bins
        if (error > max_error_ ||
            std::fabs(cur_line.first) > max_slope_ ||
            (current_line_points.size() > 2 && std::fabs(cur_line.first) < min_slope_) ||
            is_long_line && std::fabs(expected_z - cur_point.z) > max_long_height_) 
        {
          // Add line until previous point as ground.
          // 遇到坡面则弹出
          current_line_points.pop_back();
          // Don't let lines with 2 base points through.
          if (current_line_points.size() >= 3) {
            const LocalLine new_line = fitLocalLine(current_line_points);
            lines_.push_back(localLineToLine(new_line, current_line_points));
            // 获取bin4的地面高度
            cur_ground_height = new_line.first * current_line_points.back().d + new_line.second;
          }
          // Start new line.
          is_long_line = false;
          // 只保留最后一个
          current_line_points.erase(current_line_points.begin(), --current_line_points.end());
          --line_iter;
        }
        // Good line, continue.
        else { }
      }
      else {
        // Not enough points.
        // 两者不属于长直线,并且拟合直线的最后一个点的高度减去传感器高度的值在一定范围内
        if (cur_point.d - current_line_points.back().d < long_threshold_ &&
            std::fabs(current_line_points.back().z - cur_ground_height) < max_start_height_) {
          // Add point if valid.
          current_line_points.push_back(cur_point);
        }
        else {
          // 如果不在一水平面,则新建bin2为起点
          // Start new line.
          current_line_points.clear();
          current_line_points.push_back(cur_point);
        }
      }
    }
  }
  // Add last line.
  // 如果大于两个点则拟合直线,将拟合的直线存储至lines_中,在下一函数中使用。
  if (current_line_points.size() > 2) {
    const LocalLine new_line = fitLocalLine(current_line_points);
    lines_.push_back(localLineToLine(new_line, current_line_points));
  }
}

8、assignCluster(segmentation)函数

// 分为四个线程进行处理
void GroundSegmentation::assignCluster(std::vector<int>* segmentation) 
{
  std::vector<std::thread> thread_vec(params_.n_threads);

  const size_t cloud_size = segmentation->size();
  for (unsigned int i = 0; i < params_.n_threads; ++i) {
    const unsigned int start_index = cloud_size / params_.n_threads * i;
    const unsigned int end_index = cloud_size / params_.n_threads * (i+1);
    thread_vec[i] = std::thread(&GroundSegmentation::assignClusterThread, this,
                                start_index, end_index, segmentation);
  }
  for (auto it = thread_vec.begin(); it != thread_vec.end(); ++it) {
    it->join();
  }
}

其中的线程处理函数

9、assignClusterThread函数

void GroundSegmentation::assignClusterThread(const unsigned int &start_index,
                                             const unsigned int &end_index,
                                             std::vector<int> *segmentation) 
{
  const double segment_step = 2*M_PI/params_.n_segments;
  for (unsigned int i = start_index; i < end_index; ++i) 
  {
    // 表示每个点的d个z存储在point—_2d里面
    Bin::MinZPoint point_2d = segment_coordinates_[i];
    // 每个点的segment和bin索引存储在bin_index_;
    const int segment_index = bin_index_[i].first;
    if (segment_index >= 0) 
    {
      // 求每一个点到直线的距离
      double dist = segments_[segment_index].verticalDistanceToLine(point_2d.d, point_2d.z);
      // 在邻域内搜索
      int steps = 1;
      while (dist == -1 && steps * segment_step < params_.line_search_angle) {
        // Fix indices that are out of bounds.
        int index_1 = segment_index + steps;
        while (index_1 >= params_.n_segments) index_1 -= params_.n_segments;
        int index_2 = segment_index - steps;
        while (index_2 < 0) index_2 += params_.n_segments;
        // 计算点在邻域拟合直线的距离
        const double dist_1 = segments_[index_1].verticalDistanceToLine(point_2d.d, point_2d.z);
        const double dist_2 = segments_[index_2].verticalDistanceToLine(point_2d.d, point_2d.z);
        // 选取最大的距离
        if (dist_1 > dist) {
          dist = dist_1;
        }
        if (dist_2 > dist) {
          dist = dist_2;
        }
        ++steps;
      }
      //若投影误差在一定范围内,则标签值为1
      if (dist < params_.max_dist_to_line && dist != -1) {
        segmentation->at(i) = 1;
      }
    }
  }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linefit_ground_segmention文章梳理及代码阅读 的相关文章

  • PHP中使用CURL之php curl详细解析和常见大坑

    这篇文章主要介绍了PHP中使用CURL之php curl详细解析和常见大坑 xff0c 现在分享给大家 xff0c 也给大家做个参考 一起跟随小编过来看看吧 七夕啦 xff0c 作为开发 xff0c 妹子没得撩就 撩 下服务器吧 xff0c
  • KMP字符串

    给定一个字符串 S xff0c 以及一个模式串 P xff0c 所有字符串中只包含大小写英文字母以及阿拉伯数字 模式串 P 在字符串 S 中多次作为子串出现 求出模式串P在字符串S中所有出现的位置的起始下标 输入格式 第一行输入整数 N x
  • 一个简单实用的分离器件锂电池充电电路

    下面推荐一个由分离器件搭建的锂电池充电电路 xff0c 如下图 简单说明一下各器件的功能及电路原理 xff1a 简单说明一下各器件的功能及电路原理 F 43 为充电器的正极 xff0c BT 43 为电池正极 xff0c CH与单片机的一个
  • Android自定义节点进度条NodeProgressBar

    NodeProgressBar 一 简介 Android日常开发中我们可能会遇到开发一个带节点的进度条的需求 xff0c 这个需求看似简单 xff0c 实际上可以挖掘出不少东西 做的好的话也可以做成相对通用的自定义组件 二 自定义属性 sp
  • Http工具类 HttpUtils

    import java io IOException import java nio charset Charset import java security KeyManagementException import java secur
  • 【STM32基础】第二篇、STM32串口的使用

    目录 第一部分 如何取出串口接收到的数据 xff1f 第二部分 如何将串口接收的数据与目标数据进行匹配 xff1f 第三部分 串口常用的发送数据的函数 1 发送一个字符 xff08 8位 xff09 2 发送一个16位数据 xff08 16
  • 使用FlyMCU往STM32中烧写程序

    0 软硬件环境 1 操作系统 xff1a Windows 2 软件 xff1a KeilFlyMcu 3 硬件 xff1a PCSTM32最小系统开发板USB转TTL CH340G 1 生成hex文件 编写好要烧写的程序后 xff0c 点击
  • LVI-SAM安装与测试

    1 介绍 就在昨天 xff0c LVI SAM开源了 xff0c 它是一个lidar visual inertial里程计和建图系统 xff0c 在系统级别结合了LIO SAM和Vins Mono的优势 作者之前还开源了LeGO LOAM和
  • 在Ubuntu 18.04上安装Apollo 6.0

    文章目录 1 前期准备1 1 安装Ubuntu1 2 安装NVIDIA GPU驱动1 3 安装Docker Engine1 4 安装NVIDIA Container Toolkit 2 下载Apollo源文件3 启动Docker容器4 进入
  • R2LIVE安装与测试

    文章目录 1 R2LIVE2 安装依赖2 1 Ubuntu和ROS2 2 Ceres Solver2 3 livox ros driver 3 编译R2LIVE4 运行示例 1 R2LIVE R2LIVE 是一个强大的 实时的 紧密耦合的多
  • 图像标注工具labelme在WIndows系统上的安装和使用

    1 前言 labelme可对图像进行标注 xff0c 包括多边形 xff0c 矩形 xff0c 线 xff0c 点和图像级标注 它是用Python编写的 xff0c 并使用Qt作为其图形界面 详细内容见 xff1a https github
  • Windows环境使用和编译CMake记录

    以下为两种使用方式 xff0c 第一种较为简单 xff0c 第二种需提前安装vs软件 1 二进制方式安装 下载win平台的安装包 xff0c 安装解压后将bin目录添加到环境变量即可 打开命令窗口 xff0c 查看当前版本 百度云下载链接
  • 激光雷达和相机的联合标定(Camera-LiDAR Calibration)之Autoware

    1 前言 单一传感器不可避免的存在局限性 xff0c 为了提高系统的稳健性 xff0c 多采取多传感器融合的方案 xff0c 融合又包含不同传感器的时间同步和空间同步 这里要讲的激光雷达和相机的联合标定就属于空间同步范畴 另外 xff0c
  • 如何使用Keras fit和fit_generator(动手教程)

    写在前面 被Adrian Rosebrock圈粉后 xff0c 就一直期待他的更新 xff0c 作者每周一更新 xff0c 考虑到时差问题 xff08 作者在美国 xff09 xff0c 一般北京时间周二才能看到 作者根据读者留言中的问题写
  • CMake 的常用命令

    目录 0 CMake常用的命令或函数 xff1a 1 定义项目 project 2 多个目录 add subdirectory 3 常用命令 add executable add library 4 常用命令 改变最终目标文件输出位置 5
  • Libcurl的编译_HTTP/HTTPS客户端源码示例

    HTTP HTTPS客户端源码示例 环境 zlib 1 2 8 openssl 1 0 1g curl 7 36 Author Kagula LastUpdateDate 2016 05 09 阅读前提 xff1a CMake工具的基本使用
  • CNN卷积神经网络原理详解(上)

    CNN卷积神经网络原理详解 xff08 上 xff09 前言卷积神经网络的生物背景我们要让计算机做什么 xff1f 卷积网络第一层全连接层训练 前言 卷积网络 xff08 convolutional network 也叫作卷积神经网络 xf

随机推荐

  • 啥也不会照样看懂交叉熵损失函数

    啥也不会照样看懂交叉熵损失函数 什么是损失函数损失函数的作用有哪些损失函数交叉熵 xff08 Cross Entroy 损失函数 什么是损失函数 损失函数 loss function 是用来估量模型的预测值与真实值的不一致程度 xff0c
  • 可变形卷积从概念到实现过程

    可变形卷积从概念到实现过程 什么是可变形卷积 xff1f 为什么要可变形卷积 xff1f 可变形卷积结构形式 xff1f 可变形卷积的学习过程 xff1f 可变形卷积如何实现 xff1f 上期回顾 卷积神经网络进阶用法 残差网络如何解决梯度
  • 导航定位系统的原理解析(一个小白写给另一个小白)

    导航定位系统的原理解析 xff08 写给小白 xff09 前言 三星 定位基本原理 xff08 导航定位的原理 xff09 传输误差后记 前言 无人驾驶是这几年大火的一个研究方向 xff0c 研究无人驾驶需要了解的知识非常多 xff0c 但
  • 一张图详细说明自动驾驶车辆如何搭建硬件系统

    一张图详细说明自动驾驶车辆如何搭建硬件系统 文章结构说明第一部分 xff08 1 xff09 一图展示自动驾驶硬件系统的总体架构 xff08 2 xff09 庖丁解牛说内容1 线控模块2传感器模块 第二部分 xff08 1 xff09 传感
  • Tensorflow安装教程详解(图文详解,深度好文)

    Tensorflow安装教程详解 xff08 图文详解 xff0c 深度好文 xff09 前言安装前的准备工作关于python关于Anaconda 开始使用Tensorflow系统内配置Anaconda使用路径Anaconda Naviga
  • 二级指针 *(unsigned char**)(buf+0) = (unsigned char*)(buf+1)

    RTT里面的代码 1 rt err t rt mp init struct rt mempool mp 2 const char name 3 void start 4 rt size t size 5 rt size t block si
  • 子类以private方式继承父类

    子类以private方式继承父类 xff0c 则父类的pubic protected接口在子类变为private接口 xff0c 而父类的private接口在子类变为不可访问的接口 xff0c 而且不存在子类到父类的转换 所以子类以priv
  • CNN实战之如何分析影评-好看又有趣的讲解

    CNN实战之如何分析影评 好看又有趣的讲解 前言认识影评数据集了解TextCNN模型获取影评数据生成文本数据集生成TextCNN模型评估模型 前言 话说老王买了两张电影票打算请女神小丽去看电影 xff0c 老王希望看完电影趁着热度可以和小丽
  • 无人驾驶时代的室外组网技术研究

    无人驾驶时代的室外组网技术研究 车载自组网车载自组网简介车载自组网特点车载自组网组成及建构 主流自组网通信方式ZigBeeWIFIBlue ToothWiMAXDSRC4G 5G 参考文献 车载自组网 车辆通信网络就是在汽车上装载移动通信设
  • 这本关于机器学习的书---牛XXX

    机器学习好书推荐 如图所示 xff0c 这是一本可读性非常强 xff0c 非常有趣的一本介绍机器学习概率论的书 xff0c 让人看了会上瘾 看到这里 xff0c 作者摊牌了 本书作者即本人
  • ROS下运行rqt报错

    解决方案 xff1a 从上面可以看到ROS是通过python2 7编译 xff0c 查看自己python版本 xff0c 修改为对应版本即可成功运行rqt和rqt graph
  • zed2相机SDK安装及ROS安装

    一 安装相机SDK 相机SDK即相机的软件开发工具包 1 查看CUDA版本 xff1a nvcc version 2 相机SDK xff08 Software Development Kit xff09 下载网址 xff1a ZED SDK
  • zed2相机标定

    一 标定相机 1 刷新ros工作空间 source devel setup bash 2 打开相机ros节点 roslaunch zed wrapper zed2 launch 3 准备棋盘格标定板 xff0c 修改标定板checkboar
  • zed2相机标定(IMU)

    二 IMU标定 陀螺仪模型 xff1a 其中 xff0c 为陀螺仪测量值 xff1b 为陀螺仪真实值 xff1b 为陀螺仪零偏 xff08 也叫偏置 xff09 xff1b 为陀螺随机噪声项 xff08 包括白噪声和随机游走噪声 xff09
  • zed2相机标定(相机+imu)

    相机和imu单独标定请参考前面的博客 1 准备文件 checkboard yaml相机标定文件camera calibration yamlimu标定文件imu calibration yaml IMU标定文件格式需要改为如下 xff1a
  • Opencv中三个光流跟踪函数

    在slam里 xff0c 光流跟踪判断图像中某一物体的动态性 xff0c 主要包括3个函数 xff1a 1 goodFeaturesToTrack函数 作用 xff1a 提取输入图像中像素级别的角点 xff0c 支持harris角点和Shi
  • 算法:二分查找

    给定一个n个元素有序的 xff08 升序 xff09 整型数组 nums 和一个目标值 target xff0c 写一个函数搜索 nums 中的 target xff0c 如果目标值存在返回下标 xff0c 否则返回 1 1 条件 查找的数
  • 一行代码解决selenium进入抖音出现验证滑块

    我正常从浏览器进入抖音是不出现验证滑块的 xff0c 然后用selenium进入抖音网站发现会出现滑块验证 如下如这是原代码 xff1a 运行代码后就会发现浏览器出现验证滑块 xff0c 这是是因为网站识别出你是使用selenium 这个时
  • 激光点云有关目标检测与目标跟踪的消息定义

    1 jsk recognition msgs BoundingBoxArray msg 安装jsk recognition msgs xff1a sudo apt get install ros melodic jsk recognitio
  • Linefit_ground_segmention文章梳理及代码阅读

    2013年专门针对地面分割的文章 xff1a Fast segmentation of 3D point clouds for ground vehicles 代码链接 xff1a https github com lorenwel lin