参考线平滑 - FemPosDeviation算法

FemPosDeviation参考线平滑方法是离散点平滑方法
参考文章:
(1)参考线平滑-FemPosDeviation-OSQP
(2)Planning基础库——散点曲线平滑
(3)参考线平滑-FemPosDeviation-SQP
(4)Apollo planning之参考线平滑算法
(5)解析百度Apollo之参考线与轨迹
(6)百度Apollo代码阅读:参考线平滑FemPosDeviationSmoother

1 参考线的作用

参考线在planning中的作用相当于一个地基,所有决策与优化都是在参考线的基础上进行

在这里插入图片描述
🍎🍉为什么需要参考线🍉🍎
(1)HD map一般都是人为采集离散点,也就使得原始路径不平滑
(2)全局导航的路径过长,障碍物的投影点也可能不唯一
(3)生成一个局部一定长度且光滑的参考线节省算力

2 ReferenceLine数据结构

参考线是根据车辆位置相对局部的一个数据,它包含了车辆前后一定范围内的路径信息。在车辆行驶过程中,Planning会在每一个计算周期中生成ReferenceLine

// modules/planning/reference_line/reference_line.huint32_t priority_ = 0;  // 优先级
struct SpeedLimit { double start_s = 0.0;double end_s = 0.0;double speed_limit = 0.0;  // unit m/s
};
std::vector<SpeedLimit> speed_limit_; // 限速数据std::vector<ReferencePoint> reference_points_; // 一系列的点,点包含了位置的信息。因此这些点就是生成车辆行驶轨迹的基础数据
hdmap::Path map_path_;

在这里插入图片描述
说明:
Vec2d描述一个二维的点:

double x_:描述点的x坐标
double y_:描述点的y坐标

MapPathPoint描述了一个地图上的点:

double heading_:描述点的朝向。
std::vector<LaneWaypoint> lane_waypoints_:描述路径上的点。有些车道可能会存在重合的部分,所以地图上的一个点可能同时属于多个车道,因此这里的数据是一个vector结构。

ReferencePoint描述了参考线中的点:

double kappa_:描述曲线的曲率。
double dkappa_:描述曲率的导数。

ReferencePoint中的朝向和曲率直接影响车辆的方向控制。如果ReferenceLine中的ReferencePoint之间存在朝向和曲率大小震动或者数据跳变将可能导致车辆方向盘的相应变化,这种变化对于乘车体验来说是非常糟糕的。

3 参考线处理流程

① 生成参考线,这主要由Routing模块的输出决定
② 参考线平滑,接下来会详细讲解参考线的平滑的算法
在这里插入图片描述
在这里插入图片描述

4 参考线平滑算法

4.1 算法分类

Apollo参考线平滑有三种:
① 离散点平滑:Cos和Fem
② 螺线曲线平滑
③ QPSpline平滑

通过配置参数选择要采用的平滑算法

  if (smoother_config_.has_qp_spline()) {smoother_.reset(new QpSplineReferenceLineSmoother(smoother_config_));} else if (smoother_config_.has_spiral()) {smoother_.reset(new SpiralReferenceLineSmoother(smoother_config_));} else if (smoother_config_.has_discrete_points()) {smoother_.reset(new DiscretePointsReferenceLineSmoother(smoother_config_));} else {ACHECK(false) << "unknown smoother config "<< smoother_config_.DebugString();}is_initialized_ = true;

默认采用离散点平滑算法

4.2 参考线平滑算法流程

输入raw_reference_line,设置中间点(GetAnchorPoints),然后smooth,最后输出

bool ReferenceLineProvider::SmoothReferenceLine(const ReferenceLine &raw_reference_line, ReferenceLine *reference_line) {if (!FLAGS_enable_smooth_reference_line) {*reference_line = raw_reference_line;return true;}// 设置中间点std::vector<AnchorPoint> anchor_points;GetAnchorPoints(raw_reference_line, &anchor_points);smoother_->SetAnchorPoints(anchor_points);if (!smoother_->Smooth(raw_reference_line, reference_line)) {AERROR << "Failed to smooth reference line with anchor points";return false;}if (!IsReferenceLineSmoothValid(raw_reference_line, *reference_line)) {AERROR << "The smoothed reference line error is too large";return false;}return true;
}

AnchorPoint

struct AnchorPoint {common::PathPoint path_point;double lateral_bound = 0.0; // 裕度(留有一定余地)double longitudinal_bound = 0.0; // 裕度// 强制更平滑以严格遵循此参考点bool enforced = false;
};

根据原始的参考线来选取中间点

根据referenceline的S值在纵向的投影进行一个均匀的采样,采样的间隔大概是0.25米,采样完毕后就能得到一系列的AnchorPoint,每个AnchorPoint包含了一个path_point和一个横纵向的裕度(采样点周围可以平移的空间)。强约束表示必须必须严格遵守横纵向的裕度,所以在平滑过程中只有首尾两个点是强约束,中间的点都不是强约束。

默认配置:纵向边界(2.0),横向边界(最大值0.5,最小值0.1)

max_constraint_interval : 0.25
longitudinal_boundary_bound : 2.0
max_lateral_boundary_bound : 0.5
min_lateral_boundary_bound : 0.1
curb_shift : 0.2
lateral_buffer : 0.2

Smooth

bool status = false;const auto& smoothing_method = config_.discrete_points().smoothing_method();
std::vector<std::pair<double, double>> smoothed_point2d;
switch (smoothing_method) {case DiscretePointsSmootherConfig::COS_THETA_SMOOTHING:status = CosThetaSmooth(raw_point2d, anchorpoints_lateralbound,&smoothed_point2d);break;case DiscretePointsSmootherConfig::FEM_POS_DEVIATION_SMOOTHING:status = FemPosSmooth(raw_point2d, anchorpoints_lateralbound,&smoothed_point2d);break;default:AERROR << "Smoother type not defined";return false;
}if (!status) {AERROR << "discrete_points reference line smoother fails";return false;
bool DiscretePointsReferenceLineSmoother::FemPosSmooth(const std::vector<std::pair<double, double>>& raw_point2d,const std::vector<double>& bounds,std::vector<std::pair<double, double>>* ptr_smoothed_point2d) {const auto& fem_pos_config =config_.discrete_points().fem_pos_deviation_smoothing();FemPosDeviationSmoother smoother(fem_pos_config);// box contraints on pos are used in fem pos smoother, thus shrink the// bounds by 1.0 / sqrt(2.0)// 裕度收缩std::vector<double> box_bounds = bounds;const double box_ratio = 1.0 / std::sqrt(2.0);for (auto& bound : box_bounds) {bound *= box_ratio;}std::vector<double> opt_x;std::vector<double> opt_y;// 问题求解// 从其输入的参数来看,主要是AnchorPoint中的x,y和横纵向的裕度,输出是平滑后的pointbool status = smoother.Solve(raw_point2d, box_bounds, &opt_x, &opt_y);if (!status) {AERROR << "Fem Pos reference line smoothing failed";return false;}if (opt_x.size() < 2 || opt_y.size() < 2) {AERROR << "Return by fem pos smoother is wrong. Size smaller than 2 ";return false;}

Solve

如果考虑了曲率的约束,该优化问题就是非线性的,就可以用Ipopt非线性求解器进行求解,也可以将曲率约束进行线性化之后用Osqp进行求解。
如果不考虑曲率约束的话,就可以直接用Osqp进行求解该二次优化的问题。

bool FemPosDeviationSmoother::Solve(const std::vector<std::pair<double, double>>& raw_point2d,const std::vector<double>& bounds, std::vector<double>* opt_x,std::vector<double>* opt_y) {// 考虑曲率约束if (config_.apply_curvature_constraint()) {if (config_.use_sqp()) {// 线性求解return SqpWithOsqp(raw_point2d, bounds, opt_x, opt_y);} else {// 非线性求解return NlpWithIpopt(raw_point2d, bounds, opt_x, opt_y);}}// 不考虑曲率约束 else {// 线性求解(默认)return QpWithOsqp(raw_point2d, bounds, opt_x, opt_y);}return true;
}
4.3 具体算法原理

对于平滑度的衡量有两种方式:
① FemPosSmooth相对不精准,但是只需用二次规划能快速求解
② CosThetaSmooth相对精准,但是需要非线性规划,计算量大
优化目标
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

满足的约束:
在这里插入图片描述
二次规划求解首先要将问题转化为二次型式:
min ⁡ f ( x ) = 1 2 X T P X + q T X subject to  : l ≤ A X ≤ u \begin{array}{l} \min f(x)=\frac{1}{2} X^{T} P X+q^{T} X \\ \text { subject to }: l \leq A X \leq u \end{array} minf(x)=21XTPX+qTX subject to :lAXu
在这里插入图片描述

FemPosDeviation算法使用OSQP二次规划求解器进行求解,其代码实现在modules/planning/math/discretized_points_smoothing/FemPosDeviationOsqpInterface.cc

fem_pos_deviation_osqp_interface中通过 CalculateKernel实现P矩阵,通过CalculateOffset实现q矩阵,通过CalculateAffineConstraint计算系数A
osqp中表阵用了压缩列的系数矩阵csc的格式保存,通过osqp的csc_matrix函数构造出来,其中m为矩阵的行数,n为矩阵的列数,nzmax是非0元素的数目,x为保存的数据对应链接中的ENTRY,p为对应的COL,指出i中第一列对应的位置;i为非零元素所在的行号,对应的ROW。

csc* csc_matrix(c_int    m,c_int    n,c_int    nzmax,c_float *x,c_int   *i,c_int   *p);

对优化目标函数的实现:

void FemPosDeviationOsqpInterface::CalculateKernel(std::vector<c_float>* P_data, std::vector<c_int>* P_indices,std::vector<c_int>* P_indptr) {CHECK_GT(num_of_variables_, 4);// Three quadratic penalties are involved:// 1. Penalty x on distance between middle point and point by finite element// estimate;// 2. Penalty y on path length;// 3. Penalty z on difference between points and reference points// General formulation of P matrix is as below(with 6 points as an example):// I is a two by two identity matrix, X, Y, Z represents x * I, y * I, z * I// 0 is a two by two zero matrix// |X+Y+Z, -2X-Y,   X,       0,       0,       0    |// |0,     5X+2Y+Z, -4X-Y,   X,       0,       0    |// |0,     0,       6X+2Y+Z, -4X-Y,   X,       0    |// |0,     0,       0,       6X+2Y+Z, -4X-Y,   X    |// |0,     0,       0,       0,       5X+2Y+Z, -2X-Y|// |0,     0,       0,       0,       0,       X+Y+Z|// Only upper triangle needs to be filledstd::vector<std::vector<std::pair<c_int, c_float>>> columns;columns.resize(num_of_variables_);int col_num = 0;for (int col = 0; col < 2; ++col) {columns[col].emplace_back(col, weight_fem_pos_deviation_ +weight_path_length_ +weight_ref_deviation_);++col_num;}for (int col = 2; col < 4; ++col) {columns[col].emplace_back(col - 2, -2.0 * weight_fem_pos_deviation_ - weight_path_length_);columns[col].emplace_back(col, 5.0 * weight_fem_pos_deviation_ +2.0 * weight_path_length_ +weight_ref_deviation_);++col_num;}int second_point_from_last_index = num_of_points_ - 2;for (int point_index = 2; point_index < second_point_from_last_index;++point_index) {int col_index = point_index * 2;for (int col = 0; col < 2; ++col) {col_index += col;columns[col_index].emplace_back(col_index - 4, weight_fem_pos_deviation_);columns[col_index].emplace_back(col_index - 2,-4.0 * weight_fem_pos_deviation_ - weight_path_length_);columns[col_index].emplace_back(col_index, 6.0 * weight_fem_pos_deviation_ +2.0 * weight_path_length_ + weight_ref_deviation_);++col_num;}}int second_point_col_from_last_col = num_of_variables_ - 4;int last_point_col_from_last_col = num_of_variables_ - 2;for (int col = second_point_col_from_last_col;col < last_point_col_from_last_col; ++col) {columns[col].emplace_back(col - 4, weight_fem_pos_deviation_);columns[col].emplace_back(col - 2, -4.0 * weight_fem_pos_deviation_ - weight_path_length_);columns[col].emplace_back(col, 5.0 * weight_fem_pos_deviation_ +2.0 * weight_path_length_ +weight_ref_deviation_);++col_num;}for (int col = last_point_col_from_last_col; col < num_of_variables_; ++col) {columns[col].emplace_back(col - 4, weight_fem_pos_deviation_);columns[col].emplace_back(col - 2, -2.0 * weight_fem_pos_deviation_ - weight_path_length_);columns[col].emplace_back(col, weight_fem_pos_deviation_ +weight_path_length_ +weight_ref_deviation_);++col_num;}CHECK_EQ(col_num, num_of_variables_);int ind_p = 0;for (int i = 0; i < col_num; ++i) {P_indptr->push_back(ind_p);for (const auto& row_data_pair : columns[i]) {// Rescale by 2.0 as the quadratic term in osqp default qp problem setup// is set as (1/2) * x' * P * xP_data->push_back(row_data_pair.second * 2.0);P_indices->push_back(row_data_pair.first);++ind_p;}}P_indptr->push_back(ind_p);
}
void FemPosDeviationOsqpInterface::CalculateOffset(std::vector<c_float>* q) {for (int i = 0; i < num_of_points_; ++i) {const auto& ref_point_xy = ref_points_[i];q->push_back(-2.0 * weight_ref_deviation_ * ref_point_xy.first);q->push_back(-2.0 * weight_ref_deviation_ * ref_point_xy.second);}
}

对约束的代码实现:

void FemPosDeviationOsqpInterface::CalculateAffineConstraint(std::vector<c_float>* A_data, std::vector<c_int>* A_indices,std::vector<c_int>* A_indptr, std::vector<c_float>* lower_bounds,std::vector<c_float>* upper_bounds) {int ind_A = 0;for (int i = 0; i < num_of_variables_; ++i) {A_data->push_back(1.0);A_indices->push_back(i);A_indptr->push_back(ind_A);++ind_A;}A_indptr->push_back(ind_A);for (int i = 0; i < num_of_points_; ++i) {const auto& ref_point_xy = ref_points_[i];upper_bounds->push_back(ref_point_xy.first + bounds_around_refs_[i]);upper_bounds->push_back(ref_point_xy.second + bounds_around_refs_[i]);lower_bounds->push_back(ref_point_xy.first - bounds_around_refs_[i]);lower_bounds->push_back(ref_point_xy.second - bounds_around_refs_[i]);}
}

OSQP还需要设定迭代初值,设定迭代初值为原始参考点坐标

void FemPosDeviationOsqpInterface::SetPrimalWarmStart(std::vector<c_float>* primal_warm_start) {CHECK_EQ(ref_points_.size(), static_cast<size_t>(num_of_points_));for (const auto& ref_point_xy : ref_points_) {primal_warm_start->push_back(ref_point_xy.first);primal_warm_start->push_back(ref_point_xy.second);}

将相关参数输入osqp求解器

data->n = kernel_dim;
data->m = num_affine_constraint;
data->P = csc_matrix(data->n, data->n, P_data->size(), P_data->data(),P_indices->data(), P_indptr->data());
data->q = q->data();
data->A = csc_matrix(data->m, data->n, A_data->size(), A_data->data(),A_indices->data(), A_indptr->data());
data->l = lower_bounds->data();
data->u = upper_bounds->data();*work = osqp_setup(data, settings);osqp_warm_start_x(*work, primal_warm_start->data());// Solve Problem
osqp_solve(*work);

从work中抽取计算结果

x_.resize(num_of_points_);
y_.resize(num_of_points_);
for (int i = 0; i < num_of_points_; ++i) {int index = i * 2;x_.at(i) = work->solution->x[index];y_.at(i) = work->solution->x[index + 1];
}

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

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

相关文章

ICCV 2023 | NeRF-Det

NeRF-Det: Learning Geometry-Aware Volumetric Representation for Multi-View 3D Object Detection 介绍 本文介绍了一种新颖的方法&#xff0c;用于仅使用RGB图像作为输入进行室内3D目标检测。作者提出了利用神经辐射场&#xff08;NeRF&#xff09;来显式估计3D几何形状&…

OCP Java17 SE Developers 复习题09

答案 A, E. For the first scenario, the answer needs to implement List because the scenario allows duplicates, narrowing it down to options A and D. Option A is a better answer than option D because LinkedList is both a List and a Queue, and you just nee…

代码随想录 贪心算法-难度题目-其他题目

目录 53.最大子数组和 134.加油站 968.监控二叉树 53.最大子数组和 53. 最大子数组和 中等 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个…

git提交代码描述时如何换行(更新时间24/3/12)

问题复现&#xff08;信心满满使用转义字符换行&#xff09; 解决方法&#xff1a; 写多个-m字符串的结构可以实现自动换行 注意空格 git commit -m"第一行描述" -m"第二行描述" 效果演示&#xff1a;&#xff08;强迫症福利&#xff09;

网络学习:BGP路径属性分类

目录 前言&#xff1a; 路径属性分类 公认必遵 公认任意 可选过渡 可选非过渡 前言&#xff1a; 在默认情况下&#xff0c;到达同一目的地&#xff0c;BGP只走单条路径&#xff0c;并不会在多条路径之间执行负载均衡。对于IGP路由协议&#xff0c;当有多条路径可以到达同…

dangzero环境配置问题

文章目录 安装虚拟机dangzeroCompile the KML kernelObtain Ubuntu 20.04Create VMInstall UbuntuRun UbuntuMove KML kernel to VMInside VM: Install KernelUpdate grub to auto-select KML kernelBoot parametersRun KMLTest KMLObtain glibc-2.31Install gcc-5 for kernel …

KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记10 - STM32的SDIO学习2 - Card Identification

KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记10 - STM32的SDIO学习2 - Card Identification 一、问题回顾二、本次的任务三、 需要注意的问题3.1 Card Identification Mode时的时钟频率3.2 CMD0指令的疑似问题3.3 发送带参数的ACMD41时要注意时间时序和时效3.4 CPSM的指令发送问题…

【Linux】深入探索:Linux网络调试、追踪与优化

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 1. 调试网络问题 a. 使用ping和traceroute b. 使用netstat和ss c. 使用tcpdump和Wireshark 2. 追踪网络问题 a. 使用mtr b.…

北京公司注册地址想要迁到新疆该如何操作

尊敬的客户&#xff0c;您好&#xff01;我是经典世纪胡云帅&#xff08;游览器搜经典世纪胡云帅&#xff09;&#xff0c;您选择了北京经典世纪集团有限公司-资 质代办&#xff0c;我们将竭诚为您服务&#xff01;如果您的公司注册地址想要迁到新疆&#xff0c;这里有一些重要…

一台服务器,最大支持的TCP连接数是多少?

一个服务端进程最大能支持多少条 TCP 连接&#xff1f; 一台服务器最大能支持多少条 TCP 连接&#xff1f; 一、原理 TCP 四元组的信息&#xff1a;源IP、源端口、目标IP、目标端口。 一个服务端进程最大能支持的 TCP 连接个数的计算公式&#xff1a;最大tcp连接数客户端的IP…

基于springboot实现成人教育教务系统项目【项目源码+论文说明】

基于springboot实现成人教育教务系统演示 摘要 随着市场经济的产业化结构升级&#xff0c;人才结构也在不断发生这巨大的变化和变革。而且各大企业都在处于一个高速发展和壮大的阶段&#xff0c;在这个高速发展和结构化升级的时期对于人才的需求也在不断的增多。企业和用工单位…

【Python】新手入门学习:详细介绍组合/聚合复用原则(CARP)及其作用、代码示例

【Python】新手入门学习&#xff1a;详细介绍组合/聚合复用原则&#xff08;CARP&#xff09;及其作用、代码示例 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集…

Nmap最常用命令(非常详细)零基础入门到精通,收藏这一篇就够了

nmap是我们最常用的工具。但是命令太多了&#xff0c;没办法全部记下。我们在实际工作中只需要记住最常用的几条命令就行了。 主机发现 里面nmap&#xff0c;我们可以扫描在同一局域网内有哪些设备在线。常用命令如下&#xff1a; nmap 192.168.50.1/24 -sL 上面命令&#…

羊大师分析,羊奶和牛奶哪个更有营养

羊大师分析&#xff0c;羊奶和牛奶哪个更有营养 羊奶和牛奶都是营养丰富的奶制品&#xff0c;它们各自具有独特的营养价值和特点&#xff0c;因此无法简单地判断哪个更有营养。 羊奶中含有较高的脂肪和蛋白质&#xff0c;同时富含矿物质和维生素&#xff0c;如钙、磷、铁、锌以…

揭秘FastStone Capture:一款强大且高效的截图工具

目录 【引子】【FastStone Capture概述】【安装步骤】【使用攻略】【核心功能解析】【总结】 【引子】 在数字化信息时代&#xff0c;无论是工作汇报、在线教学&#xff0c;还是日常交流中&#xff0c;屏幕截图已经成为我们必不可少的辅助工具。今天&#xff0c;我要为大家详细…

git svn混用

背景 项目代码管理初始使用的svn, 由于svn代码操作&#xff0c;无法在本地暂存&#xff0c;有诸多不便&#xff0c;另外本人习惯使用git. 所以决定迁移至git管理 迁移要求&#xff1a; 保留历史提交记录 迁移流程 代码检出 git svn svn_project_url git代码提交 修改本…

得物布局构建耗时优化方案实践

一、背景 当谈到移动应用程序的体验时&#xff0c;页面启动速度是其中至关重要的一点&#xff0c;更快的页面展示速度确保应用程序可以迅速加载并响应用户的操作, 从而提高用户使用 App 时的满意度。在页面启动的整个流程中&#xff0c;随着 UI 复杂度的上升&#xff0c;布局的…

【Java探索之旅】解密Java中的类型转换与类型提升

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、类型转化1.1 自动类型转换&#xff08;隐式类型转换&#xff09;1.2 强制类型转换…

Arduino IDE的下载和安装

一、Arduino的介绍 Arduino是一款开源电子原型平台&#xff0c;主要包含两部分&#xff1a;硬件&#xff08;各种型号的Arduino板&#xff09;和软件&#xff08;Arduino IDE&#xff09;。这个平台由意大利的Massimo Banzi、David Cuartielles等人共同开发设计&#xff0c;并于…

ES分片均衡策略分析与改进

从故障说起 某日早高峰收到 Elasticsearch 大量查询超时告警&#xff0c;不同于以往&#xff0c;查看 Elasticsearch 查询队列监控后发现&#xff0c;仅123节点存在大量查询请求堆积。 各节点查询队列堆积情况 查看节点监控发现&#xff0c;123节点的 IO 占用远高于其他节点。…