Apollo计算几何算法(一)

news2025/4/17 1:58:31

Planning模块,路径和速度曲线抽象成折线(Polyline),障碍物抽象成多边形(Polygon)。在碰撞检测、投影计算距离、平滑曲线等方面应用广泛。

1 几何算法

1.1 线段

moudles/common/math/line_segment2d.h

namespace apollo {
namespace common {
namespace math {
// 平面线段
class LineSegment2d {
 public:
  LineSegment2d();
  LineSegment2d(const Vec2d &start, const Vec2d &end);
  // 获取线段的起点
  const Vec2d &start() const { return start_; }
  // 获取线段的终点
  const Vec2d &end() const { return end_; }
  // 获取从起点到终点的方向
  const Vec2d &unit_direction() const { return unit_direction_; }
  // 获取线段的中心
  Vec2d center() const { return (start_ + end_) / 2.0; }
  // 旋转线段的终点
  Vec2d rotate(const double angle);
  // 返回线段的航向
  double heading() const { return heading_; }
  // cos(heading_)
  double cos_heading() const { return unit_direction_.x(); }
  // sin(heading_)
  double sin_heading() const { return unit_direction_.y(); }
  // 获取线段的长度
  double length() const;
  // 获取线段长度的平方
  double length_sqr() const;
  /**
   * @brief Compute the shortest distance from a point on the line segment
   *        to a point in 2-D.
   * @param point The point to compute the distance to.
   * @return The shortest distance from points on the line segment to point.
   */
  // 计算线段上的点到2-D中的点的最短距离。
  double DistanceTo(const Vec2d &point) const;
  /**
   * @brief 计算线段上一点到二维中一点的最短距离,得到线段上最近的点
   * @param point The point to compute the distance to.
   * @param nearest_pt The nearest point on the line segment
   *        to the input point.
   * @return 线段上的点到输入点的最短距离
   */
  double DistanceTo(const Vec2d &point, Vec2d *const nearest_pt) const;

  // 计算线段上的一点到2-D中的一点的最短距离的平方
  double DistanceSquareTo(const Vec2d &point) const;

  // 计算二维中线段上一点到一点的最短距离的平方,得到线段上最近的点。
  double DistanceSquareTo(const Vec2d &point, Vec2d *const nearest_pt) const;

  /**
   * @brief 检查一个点是否在线段内
   * @param point 检查它是否在线段内的点
   * @return 输入点是否在线段内
   */
  bool IsPointIn(const Vec2d &point) const;
  // 检查该线段是否与二维中的另一条线段相交
  bool HasIntersect(const LineSegment2d &other_segment) const;

  // 计算与二维中另一条线段的交点(如果有的话)
  bool GetIntersect(const LineSegment2d &other_segment,
                    Vec2d *const point) const;
   // 计算矢量在线段上的投影
  double ProjectOntoUnit(const Vec2d &point) const;

  // 计算向量与线段的叉积
  double ProductOntoUnit(const Vec2d &point) const;

  // 计算从线段展开的直线上的二维点的垂直脚部
  double GetPerpendicularFoot(const Vec2d &point,
                              Vec2d *const foot_point) const;
  // 获取包含基本信息的调试字符串
  std::string DebugString() const;

 private:
  Vec2d start_;
  Vec2d end_;
  Vec2d unit_direction_;
  double heading_ = 0.0;
  double length_ = 0.0;
};

}  // namespace math
}  // namespace common
}  // namespace apollo

moudles/common/math/line_segment2d.cc
计算点到直线的距离

double LineSegment2d::DistanceTo(const Vec2d &point) const {
  // 如果线段的长度小于等于一个阈值kMathEpsilon,那么点一定在线段上,直接返回点与线段起点的距离
  if (length_ <= kMathEpsilon) {
    return point.DistanceTo(start_);
  }
  const double x0 = point.x() - start_.x();
  const double y0 = point.y() - start_.y();
  // proj,表示点在方向向量上的投影
  const double proj = x0 * unit_direction_.x() + y0 * unit_direction_.y();
  // 如果投影小于等于0,说明点在直线段的同侧,直接返回点到线段起点的距离
  if (proj <= 0.0) {
    return hypot(x0, y0);
  }
  // 如果投影大于等于线段长度,说明点在直线段的延长线上,直接返回点到线段终点的距离
  if (proj >= length_) {
    return point.DistanceTo(end_);
  }
  return std::abs(x0 * unit_direction_.y() - y0 * unit_direction_.x());
}

给定一个点,计算到线段的最短距离,同时返回最近的点(过给定点的垂线与原线段的交点,或者线段的端点)

double LineSegment2d::DistanceTo(const Vec2d &point,
                                 Vec2d *const nearest_pt) const {
  // 检查nearest_pt是否为空
  CHECK_NOTNULL(nearest_pt);
  // 如果线段的长度小于等于一个阈值kMathEpsilon,那么点一定在线段上,直接返回点与线段起点的距离
  if (length_ <= kMathEpsilon) {
    *nearest_pt = start_;
    return point.DistanceTo(start_);
  }
  // 计算点与线段起点的坐标差x0和y0
  const double x0 = point.x() - start_.x();
  const double y0 = point.y() - start_.y();
  // 计算proj,表示点在方向向量上的投影
  const double proj = x0 * unit_direction_.x() + y0 * unit_direction_.y();
  // 如果投影小于等于0,说明点在直线段的同侧,直接返回点到线段起点的距离。如果投影大于等于线段长度,说明点在直线段的延长线上,直接返回点到线段终点的距离
  if (proj < 0.0) {
    *nearest_pt = start_;
    return hypot(x0, y0);
  }
  if (proj > length_) {
    *nearest_pt = end_;
    return point.DistanceTo(end_);
  }
  *nearest_pt = start_ + unit_direction_ * proj;
  // 计算点到线段的垂线与x轴正半轴的夹角,即x0 * unit_direction_.y() - y0 * unit_direction_.x(),取绝对值作为最终结果
  return std::abs(x0 * unit_direction_.y() - y0 * unit_direction_.x());
}

rotate:逆时针旋转angle度(注意是弧度)

Vec2d LineSegment2d::rotate(const double angle) {
  Vec2d diff_vec = end_ - start_;
  diff_vec.SelfRotate(angle);
  return start_ + diff_vec;
}

GetPerpendicularFoot:某个点到该线段垂点的距离

double LineSegment2d::GetPerpendicularFoot(const Vec2d &point,
                                           Vec2d *const foot_point) const {
  CHECK_NOTNULL(foot_point);
  if (length_ <= kMathEpsilon) {
    *foot_point = start_;
    return point.DistanceTo(start_);
  }
  const double x0 = point.x() - start_.x();
  const double y0 = point.y() - start_.y();
  const double proj = x0 * unit_direction_.x() + y0 * unit_direction_.y();
  *foot_point = start_ + unit_direction_ * proj;
  return std::abs(x0 * unit_direction_.y() - y0 * unit_direction_.x());
}
1.2 包围盒

二维平面上,Box特指矩形包围盒。Planning模块经常将自车和障碍物抽象成一个矩形框,简化计算
bounding box
在这里插入图片描述
Box2d:普通矩形包围盒
文件路径:modules/common/math/box2d.h

IsPointIn函数检查给定点是否位于由其中心、方向、长度和宽度定义的二维框内
(1)首先计算点相对于长方体中心的x和y分量
(2)然后,它计算点与长方体沿x和y轴的距离,同时考虑长方体的航向。使用航向的余弦和正弦来计算距离。
(3)最后,如果两个距离都小于或等于长方体长度和宽度的一半,加上一个小的ε值(kMathEpsilon),则函数返回true。添加此ε值是为了说明潜在的舍入误差。如果任一距离大于长方体的一半长度或宽度,则函数返回false。

总体思路就是:
(1)以Box的Center为原点,heading方向为X’建立车身坐标系
(2)计算点在车身坐标系下的坐标
假设点的坐标 ( x p , y p ) (x_p,y_p) (xp,yp),Box中心坐标 ( x c , y c ) (x_c,y_c) (xc,yc),Box的heading角度 θ \theta θ,在局部坐标系下的坐标
[ d x d y ] = [ c o s θ s i n θ − s i n θ c o s θ ] [ x p − x 0 y p − y 0 ] \begin{bmatrix} dx\\ dy \end{bmatrix}=\begin{bmatrix} cos\theta & sin\theta\\ -sin\theta &cos\theta \end{bmatrix}\begin{bmatrix} x_p-x_0 \\ y_p-y_0 \end{bmatrix} [dxdy]=[cosθsinθsinθcosθ][xpx0ypy0]
旋转矩阵是 [ c o s θ − s i n θ s i n θ c o s θ ] \begin{bmatrix} cos\theta & -sin\theta\\ sin\theta &cos\theta \end{bmatrix} [cosθsinθsinθcosθ]
(3)判断新坐标的x和y绝对值是否小于半个Box宽度和长度

bool Box2d::IsPointIn(const Vec2d &point) const {
  const double x0 = point.x() - center_.x();
  const double y0 = point.y() - center_.y();
  const double dx = std::abs(x0 * cos_heading_ + y0 * sin_heading_);
  const double dy = std::abs(-x0 * sin_heading_ + y0 * cos_heading_);
  return dx <= half_length_ + kMathEpsilon && dy <= half_width_ + kMathEpsilon;
}

判断一个点是否在Boundary上,思路和上面的一样

bool Box2d::IsPointOnBoundary(const Vec2d &point) const {
  const double x0 = point.x() - center_.x();
  const double y0 = point.y() - center_.y();
  const double dx = std::abs(x0 * cos_heading_ + y0 * sin_heading_);
  const double dy = std::abs(x0 * sin_heading_ - y0 * cos_heading_);
  return (std::abs(dx - half_length_) <= kMathEpsilon &&
          dy <= half_width_ + kMathEpsilon) ||
         (std::abs(dy - half_width_) <= kMathEpsilon &&
          dx <= half_length_ + kMathEpsilon);
}

double DistanceTo(const Vec2d& point):计算Box到一个点的距离
(1)计算点在局部坐标系下的值
(2)如果 x p ′ x^{'}_p xp绝对值小于半个车长,则直接用dy作为距离
(3)如果 y p ′ y^{'}_p yp绝对值小于半个车宽,则直接用dx作为距离
其他情况,返回 d x 2 + d y 2 \sqrt{dx^2+dy^2} dx2+dy2 ,到角点的距离作为最终的距离

double Box2d::DistanceTo(const Vec2d &point) const {
  const double x0 = point.x() - center_.x();
  const double y0 = point.y() - center_.y();
  const double dx =
      std::abs(x0 * cos_heading_ + y0 * sin_heading_) - half_length_;
  const double dy =
      std::abs(x0 * sin_heading_ - y0 * cos_heading_) - half_width_;
  if (dx <= 0.0) {
    return std::max(0.0, dy);
  }
  if (dy <= 0.0) {
    return dx;
  }
  return hypot(dx, dy);
}
1.3 多边形

不规则障碍物使用包围盒表示精度低,用多边形Polygon2d来抽象表示。
modules/common/math/polygon2d.h
多边形可以用多个点来表示,也可以用多个边来表示

std::vector<Vec2d> points_;
std::vector<LineSegment2d> line_segments_;

此函数用于从给定的一组点构建多边形

num_points_:多边形中的点数。
points_:Vec2d对象的矢量,表示多边形的点。
area_:多边形的面积。
line_segments_:LineSegment2d对象的矢量,表示连接多边形各点的线段。
is_convex_:一个布尔标志,指示多边形是否是凸的。
min_x_:多边形aabox的最小x值。
max_x_:多边形aabox的最大x值。
min_y_:多边形aabox的最小y值。
max_y_:多边形的aabox的最大y值。

void Polygon2d::BuildFromPoints() {
  num_points_ = static_cast<int>(points_.size());
  // 检查点的数量是否至少为3
  CHECK_GE(num_points_, 3);

  // 保证点顺时针
  area_ = 0.0;
  for (int i = 1; i < num_points_; ++i) {
    // 使用连接每对点的向量的叉积来计算多边形的面积
    area_ += CrossProd(points_[0], points_[i - 1], points_[i]);
  }
  // 如果面积为负值,则反转点的顺序,以确保它们按顺时针(ccw)顺序排列
  if (area_ < 0) {
    area_ = -area_;
    std::reverse(points_.begin(), points_.end());
  }
  area_ /= 2.0;
  CHECK_GT(area_, kMathEpsilon);

  // 构建线段
  line_segments_.reserve(num_points_);
  // 通过迭代点并将每个点按ccw顺序连接到下一个点来构建线段向量
  for (int i = 0; i < num_points_; ++i) {
    line_segments_.emplace_back(points_[i], points_[Next(i)]);
  }

  // 检查凸性质
  is_convex_ = true;
  for (int i = 0; i < num_points_; ++i) {
    // 通过计算连接前一点、当前点和下一点的向量的叉积来检查多边形是否是凸,如果叉积小于或等于-kMathEpsilon,则多边形不是凸的
    if (CrossProd(points_[Prev(i)], points_[i], points_[Next(i)]) <=
        -kMathEpsilon) {
      is_convex_ = false;
      break;
    }
  }

  // 计算aabox.
  // 最后,它通过找到点的最小值和最大值x和y来计算多边形的轴对齐边界框(aabox)
  min_x_ = points_[0].x();
  max_x_ = points_[0].x();
  min_y_ = points_[0].y();
  max_y_ = points_[0].y();
  for (const auto &point : points_) {
    min_x_ = std::min(min_x_, point.x());
    max_x_ = std::max(max_x_, point.x());
    min_y_ = std::min(min_y_, point.y());
    max_y_ = std::max(max_y_, point.y());
  }
}

叉积(CrossProd)物理含义是向量组成的平行四边形的面积除以二就是三角形的面积,将所有三角形面积累加可以得到area_,如果area_为负数,说明角点按顺时针(CW)排列,需要进行reverse,从而保证所有的点是逆时针排列的CCW
如果连续的三个点 P i − 1 , P i , P i + 1 P_{i-1},P_i,P_{i+1} Pi1,Pi,Pi+1在这里插入图片描述
说明多边形非凸,比如右侧就是
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1373381.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎么投稿各大媒体网站?

怎么投稿各大媒体网站&#xff1f;这是很多写作者及自媒体从业者经常面临的问题。在信息爆炸的时代&#xff0c;如何将自己的文章推送到广大读者面前&#xff0c;成为了一个不可避免的挑战。本文将为大家介绍一种简单有效的投稿方法——媒介库发稿平台发稿&#xff0c;帮助大家…

rke2 Online Deploy Rancher v2.8.0 latest (helm 在线部署 rancher v2.8.0)

文章目录 1. 简介2. 预备条件3. 安装 helm4. 安装 cert-manager4.1 yaml 安装4.2 helm 安装 5. 安装 rancher6. 验证7. 界面预览 1. 简介 Rancher 是一个 Kubernetes 管理工具&#xff0c;让你能在任何地方和任何提供商上部署和运行集群。 Rancher 可以创建来自 Kubernetes 托…

常用的网站

PIXEL MOTION 注册-YesPMP平台 模型下载 - Ourblender - 专业的三维素材库 Vega AI 创作平台 夏沫的AI小站 Tripo AI B站视频下载工具 | 极简纯净

如何通过 Prompt 优化大模型 Text2SQL 的效果

前言 在上篇文章中「大模型LLM在Text2SQL上的应用实践」介绍了基于SQLDatabaseChain的Text2SQL实践&#xff0c;但对于逻辑复杂的查询在稳定性、可靠性、安全性方面可能无法达到预期&#xff0c;比如输出幻觉、数据安全、用户输入错误等问题。 本文将从以下4个方面探讨通过Pr…

gem5学习(11):将缓存添加到配置脚本中——Adding cache to the configuration script

目录 一、Creating cache objects 1、Classic caches and Ruby 二、Cache 1、导入SimObject(s) 2、创建L1Cache 3、创建L1Cache子类 4、创建L2Cache 5、L1Cache添加连接函数 6、为L1ICache和L1DCache添加连接函数 7、为L2Cache添加内存侧和CPU侧的连接函数 完整代码…

大模型学习之书生·浦语大模型4——基于Xtuner大模型微调实战

基于Xtuner大模型微调实战 Fintune简介 海量数据训练的base model指令微调Instructed LLM 增量预训练微调 增量数据不需要问题&#xff0c;只需要答案&#xff0c;只需要陈述类的数据 指令跟随微调 指定角色指定问题给对应的user指定答案给assistant LIaMa2InternLM 不同的模…

【大数据】NiFi 中的处理器(二):PutDatabaseRecord

NiFi 中的处理器&#xff08;二&#xff09;&#xff1a;PutDatabaseRecord 1.基本介绍2.属性配置3.连接关系4.应用场景 1.基本介绍 PutDatabaseRecord 处理器使用指定的 RecordReader 从传入的流文件中读取&#xff08;可能是多个&#xff0c;说数组也成&#xff09;记录。这…

最实用的 8 个免费 Android 数据恢复软件

如果您正在寻找最好的免费 Android 数据恢复软件&#xff0c;那就不用再犹豫了&#xff0c;因为我已经列出了最好的软件。不可否认&#xff0c;智能手机和平板电脑等 Android 设备正在与技术一起发展。与以前相比&#xff0c;它们也更加融入了我们的日常生活。 Android 智能手…

Jenkins-Pipeline语法总结大全

这里写目录标题 pipeline的组成1、pipeline最简单结构1.1、pipeline1.2、stages1.3、stage1.4、steps1.5、agent 2、post3、pipeline支持的命令3.1、environment3.2、tools3.3、input3.4、options3.5、parameters3.6、parallel3.7、triggers3.8、when pipeline的组成 1、pipel…

Django 框架添加管理员,完成对普通用户信息管理

前情回顾&#xff1a;Django框架 完成用户登录注册 文章目录 1.创建管理员2.完善管理员功能2.1增加管理员登录功能2.2完善展示用户信息功能2.3完善修改用户信息功能2.4完善删除用户信息功能 1.创建管理员 一般管理员都是直接指定&#xff0c;不开放页面注册&#xff0c;可以直…

论文阅读1---OpenCalib论文阅读之factory calibration模块

前言 该论文的标定间比较高端&#xff0c;一旦四轮定位后&#xff0c;可确定标定板与车辆姿态。以下为本人理解&#xff0c;仅供参考。 工厂标定&#xff0c;可理解为车辆相关的标定&#xff0c;不涉及传感器间标定 该标定工具不依赖opencv&#xff1b;产线长度一般2.5米 Fa…

羌族特色民居----碉楼

羌族是四川的一个少数民族&#xff0c;他们独具特色的民居就是----碉楼。在羌语中&#xff0c;碉楼被称为“邓笼”&#xff0c;意为美丽、高贵的房子&#xff0c;羌族人有“依山而居&#xff0c;垒石为屋&#xff0c;高者十余丈”的习俗。碉楼的高度在十米至三十米之间。用于御…

基于Java的 人才管理系统的设计与实现

当前企业人才招揽的方式更多是通过传统的线下招聘会或职业介绍所来实现的&#xff0c;其不但受众具有很大的局限性&#xff0c;而且往往不可能在短时间内招聘的需要的人才。而互联网的普及为企业人才招聘和管理的方式带来了翻天覆地的改变。通过互联网将人才信息统一管理起来成…

【sklearn练习】模型评估

一、交叉验证 cross_val_score 的使用 1、不用交叉验证的情况&#xff1a; from __future__ import print_function from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifieriris…

Abp 创建一个模版demo并运行

Demo效果 &#xff1a;简单的单表crud后台服务。不包含UI 项目类型是模块ABP。生成的结构和 多应用/单应用 有差异。 结合文档以及git的源码分享一下demo的理解 abp文档&#xff1a;API/Auto API Controllers | Documentation Center | ABP.IO 前置准备&#xff1a; Net8 环境…

【Linux】Linux系统编程——Linux目录结构

Linux的文件系统呈现为一种树状结构&#xff0c;以根目录/为最顶层&#xff0c;其下分布着各种不同的子目录&#xff0c;每个目录都有其特定的用途和功能。下面是Linux目录结构的详细介绍&#xff1a; 1. 根目录 / 根目录是整个文件系统的基础。所有的目录和文件都从这里开始…

智能制造与MES:推动制造业转型升级的关键

随着科技的迅猛发展&#xff0c;智能制造已经成为推动制造业转型升级的重要手段。而制造执行系统&#xff08;MES&#xff09;作为智能制造的核心管理系统&#xff0c;在提高生产效率、优化生产流程、实现数字化转型等方面发挥着重要作用。 一、智能制造的概念与特点 智能制造…

极狐 GitLab 冷知识:使用 Email 也可以创建 Issue?

前言 在使用 GitLab 时&#xff0c;创建 Issue 和 Merge Request 的方法&#xff0c;除了常规的使用 GitLab Web UI 进行操作和通过 API 调用操作&#xff0c;还有一些比较好玩的&#xff0c;比如使用 Email 来创建。 Incoming email 如果是 Self-Manager 的 GitLab 用户&am…

少儿编程 2023年12月中国电子学会图形化编程等级考试Scratch编程三级真题解析(判断题)

2023年12月scratch编程等级考试三级真题 判断题 19、下列两段程序的运行效果相同 答案:对 考点分析:考查积木综合使用,重点考查循环积木的使用;左边属于有条件的循环,由变量的值控制,当变量值大于50时,循环停止,而变量始终为零,不满足条件,所以一直循环,和右边的…

市场复盘总结 20240110

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 昨日主题投资 连板进级率 33% 二进三&#xff1a; 进级率低 50% 最常用的二种方法&#xff1a; 方法一&…