当前位置: 首页 > news >正文

自动驾驶---Motion Planning之轨迹拼接

1 背景

        笔者在之前的专栏中已经详细讲解了自动驾驶Planning模块的内容:包括行车的Behavior Planning和Motion Planning,以及低速记忆泊车的Planning。

        本篇博客主要聊一聊Motion Planning中轨迹拼接的相关内容。从网络上各大品牌的车主拍摄的智驾视频来看,在开启自动驾驶过程中,车辆多多少少会出现 “画龙” 现象,原因主要包括:感知误差,path超调,轨迹平滑性等(感知误差和path超调本篇不做详细说明)。  

2 内容介绍

2.1 什么是“画龙”

        在讲解轨迹拼接(Trajectory Stitching)之前,先引入一个自动驾驶量产过程中会出现的一个普遍现象:“画龙”。那么什么是“画龙”呢?

        在自动驾驶中,“画龙” 现象是指车辆在辅助驾驶状态下无法稳定居中行驶,而是会向右或左偏移方向的现象。当车辆发生“画龙”现象时,驾驶的舒适性和安全性都会受到影响,这不仅可能导致乘客感到不适,还可能增加发生事故的风险。因此,解决自动驾驶量产中的“画龙”现象是一个重要的挑战。

2.2 “画龙”的原因

2.2.1 传感器及感知

        自动驾驶车辆出现“画龙”现象的原因主要是由于传感器的误判或者环境感知不准确所导致的。具体来说,可能的原因包括:

  • 传感器误判:自动驾驶车辆通常通过激光雷达、摄像头、雷达等传感器来感知周围环境,如果传感器出现故障、污垢或者受到干扰,就有可能导致“画龙”现象,即车辆错误地识别周围环境或者障碍物。
  • 地图数据不准确:自动驾驶车辆通常会使用高精度地图来辅助定位和导航,如果地图数据不准确或者更新不及时,就可能导致车辆在实际行驶中出现偏离预期路径的情况。
  • 复杂路况:某些复杂的路况,如交通拥堵、施工路段、复杂的道路标志等,可能会对自动驾驶系统造成困扰,导致车辆无法准确地规划行驶路径。
  • 环境变化:天气变化、光照条件变化等因素都可能影响自动驾驶系统的性能,导致系统无法准确感知周围环境,从而出现“画龙”现象。

        比如感知车道线或者动态车辆的决策或者预测轨迹出现偏差,导致Planning的约束构建出现一些误差,也会使得车辆横向的控制来回变化,间接导致 “画龙” 现象。

2.2.2 规划

        在本篇博客中,笔者主要探讨Motion Planning层面引起的 “画龙” 问题,其主要原因为:轨迹拼接不连续,导致轨迹不够平滑(如下图所示,图画的稍微夸张了一些)。为了实现比较好的轨迹平滑性,通常需要进行轨迹拼接的处理。

fd49b242cfcf43398a45d8e94a00d614.png

2.3 轨迹拼接

        轨迹拼接在自动驾驶中扮演着重要角色,能够保证车辆平稳、安全、高效地行驶。在自动驾驶量产任务中,轨迹拼接是Planning过程中的一个重要步骤,其主要有两个原因:

        平滑性: 轨迹拼接可以确保车辆在行驶过程中的轨迹是平滑的,避免了急刹或急加速等突变动作,提升了乘客的舒适度。

        连续性: 轨迹拼接可以保证车辆的轨迹在不同规划阶段之间是连续的,避免了不连续的轨迹对车辆行驶造成的不稳定性。

        综合来看,轨迹拼接体现在车上也包括两个方面:纵向相关(速度、加速度的连续性)以及横向相关(方向盘的稳定性)

2.3.1 Replan机制

        对于Control模块来说,Planning模块能够发送连续稳定的轨迹,是有利于控制去处理轨迹信息的,避免车辆产生横向的抖动以及纵向的不舒适。

        然后就是replan机制,在高速状态下,replan越少越安全。因此在每一个周期内,planning模块需要计算车辆当前姿态与规划轨迹的偏差,当偏差不大时,直接使用上一个运行周期的规划结果;当偏差比较大时,才需要replan。

2.3.2 轨迹拼接

        轨迹拼接在Planning模块中是一个关键的技术环节,它主要涉及到在连续的时间帧内,将规划出的局部轨迹进行有效地连接,以形成一条平滑且符合行驶要求的整体轨迹。以下是对轨迹拼接的详细介绍:

(1)起始点的确定

        在每个轨迹规划的开始,都需要确定一个起始点。这个起始点通常包含了车辆在当前时刻的位置、速度、加速度、航向、曲率等信息。这个起始点的选择对于后续的轨迹生成具有至关重要的影响。在轨迹拼接中,起始点的确定可以基于上一帧的部分有序点集,这样可以确保连续帧之间轨迹的平滑性和连续性。

(2)轨迹生成与拼接        

        根据起始点的信息以及当前帧的环境感知数据(如道路信息、障碍物位置等),规划算法会生成一条局部轨迹。这条轨迹需要与上一帧的轨迹进行拼接,以确保整个行驶过程的连续性和平滑性。在拼接过程中,需要考虑到各种因素,如车辆的动力学特性、行驶的安全性、以及乘客的舒适度等。

(3)平滑性处理

        轨迹拼接的一个重要目标是确保整体轨迹的平滑性。如果轨迹拼接不当,可能会导致车辆在行驶过程中出现不必要的抖动或偏移,这不仅会影响乘客的舒适度,还可能增加事故的风险。因此,在轨迹拼接过程中,需要采用各种算法和技术来处理轨迹的平滑性问题,如使用滤波算法、优化算法等。

(4)适应性与鲁棒性

        自动驾驶车辆在行驶过程中会遇到各种复杂的环境和场景,如道路变化、障碍物突然出现等。因此,轨迹拼接算法需要具备一定的适应性和鲁棒性,能够根据不同的环境和场景动态调整轨迹的生成和拼接方式,以确保车辆的安全和稳定行驶。

        通过有效地进行轨迹拼接,可以确保自动驾驶车辆在行驶过程中的连续性、平滑性和安全性,为乘客提供更加舒适和安全的出行体验。

2.3.3 实现方式

        见下图,车辆上一时刻的轨迹为:last trajectory,车辆实时的位置为:ego,车辆实时位置投影到last trajectory的投影点为:matched point,为了保证控制模块收到的轨迹是稳定的,因此需要基于车辆当前位置和运动学关系(比如方向盘转角和车速)递推得到下一周期的位置,该位置定义为 replan的起始位置,那么repan得到的轨迹为:latest trajectory,车辆不能完全跟踪好的情况下(也确实难保证Control有非常好的跟踪效果),last trajectory和latest trajectory在replan point处是有偏差存在的。所以此时需要轨迹拼接来处理前后两个周期的轨迹的平滑性。

7a25cb59642f4de39d795581e829c6f7.png

        Apollo源码中的做法:计算好拼接轨迹后,遍历这段拼接轨迹,对其时间戳和s进行赋值,使用stitching_trajectory的最后一个点作为当前帧轨迹规划的起始点(注意:上图中的stitching_trajectory最后一个点在平滑之前一般情况下是不会和replan_point重合的)。选择当前时间+规划周期的时间对应的上一帧轨迹的位置点作为本帧规划起点

std::vector<TrajectoryPoint> TrajectoryStitcher::ComputeStitchingTrajectory(const VehicleState& vehicle_state, const double current_timestamp,const double planning_cycle_time, const size_t preserved_points_num,const bool replan_by_offset, const PublishableTrajectory* prev_trajectory,std::string* replan_reason) {const double veh_rel_time =current_timestamp - prev_trajectory->header_time();size_t time_matched_index =prev_trajectory->QueryLowerBoundPoint(veh_rel_time);size_t position_matched_index = prev_trajectory->QueryNearestPointWithBuffer({vehicle_state.x(), vehicle_state.y()}, 1.0e-6);double forward_rel_time = veh_rel_time + planning_cycle_time;size_t forward_time_index =prev_trajectory->QueryLowerBoundPoint(forward_rel_time);ADEBUG << "Position matched index:\t" << position_matched_index;ADEBUG << "Time matched index:\t" << time_matched_index;auto matched_index = std::min(time_matched_index, position_matched_index);std::vector<TrajectoryPoint> stitching_trajectory(prev_trajectory->begin() +std::max(0, static_cast<int>(matched_index - preserved_points_num)),prev_trajectory->begin() + forward_time_index + 1);ADEBUG << "stitching_trajectory size: " << stitching_trajectory.size();const double zero_s = stitching_trajectory.back().path_point().s();for (auto& tp : stitching_trajectory) {if (!tp.has_path_point()) {*replan_reason = "replan for previous trajectory missed path point";return ComputeReinitStitchingTrajectory(planning_cycle_time,vehicle_state);}tp.set_relative_time(tp.relative_time() + prev_trajectory->header_time() -current_timestamp);tp.mutable_path_point()->set_s(tp.path_point().s() - zero_s);}return stitching_trajectory;
}

        为了保证平滑的效果更好,通常需要保留一段长度的历史轨迹作为输入。在进行轨迹拼接时,常用的算法包括基于曲线的拼接方法,如贝塞尔曲线、样条曲线等(确定起点和终点的约束求解得到参数方程,再由参数方程可得到坐标,速度,加速度等信息)。

        此外,还有基于优化算法的拼接方法(量产中使用较多,为了保证整个架构的一致性)(参考《自动驾驶---Motion Planning之轨迹Path优化》和《自动驾驶---Motion Planning之轨迹Speed优化》),二次规划等,相当于重新运行了一次Optimizer,这些算法可以优化轨迹,使输出的轨迹更加平滑和舒适。

2.3.2 轨迹拼接的优势

        自动驾驶planning需要进行轨迹拼接(Trajectory Stitching),通过轨迹拼接能提升哪些问题呢?

  • 时间一致性与连续性:理论上,规划算法应该具有时间一致性,即输入一致时,输出也是确定且可重复的。然而,由于现实世界的复杂性,包括输入噪声、执行端误差或延迟,甚至是算法本身的选择,都可能导致车辆实际执行的轨迹与规划结果存在差异。轨迹拼接能够确保在不同时刻规划的车辆轨迹之间具有连续性和一致性,使控制指令下发更加平滑,避免车辆产生不必要的抖动。
  • 平滑性:在自动驾驶系统中,planning模块输出trajectory信息作为control模块的输入。如果前后拍的轨迹不够平滑,会引起控制的抖动,实际表现就是方向盘抖动或者整车画龙。轨迹拼接有助于确保轨迹的平滑性,从而提高驾驶的舒适性和安全性。
  • 适应复杂场景:自动驾驶车辆需要处理的场景非常繁杂,包括空旷的道路、与行人或障碍物共用的道路、十字路口等。在这些场景中,自动驾驶车辆可能需要执行一系列简单的行为来完成复杂的驾驶任务。轨迹拼接能够帮助将这些简单的行为组合起来,形成连贯且符合道路交通规则的行驶轨迹。
  • 处理换道等复杂操作:在自动驾驶过程中,车辆可能需要进行换道等复杂操作。这些操作需要精确计算自车轨迹与相邻车道线的交点,并进行轨迹拼接处理。通过轨迹拼接,可以确保车辆在换道过程中能够平滑、安全地过渡到目标车道。

3 总结

        在自动驾驶过程中,为了解决轨迹不连续导致的“画龙”问题,引入了轨迹拼接策略。轨迹拼接在自动驾驶Planning模块中起到了至关重要的作用,它有助于确保车辆行驶的连续性、平滑性和安全性,并适应各种复杂的驾驶场景。

相关文章:

自动驾驶---Motion Planning之轨迹拼接

1 背景 笔者在之前的专栏中已经详细讲解了自动驾驶Planning模块的内容&#xff1a;包括行车的Behavior Planning和Motion Planning&#xff0c;以及低速记忆泊车的Planning。 本篇博客主要聊一聊Motion Planning中轨迹拼接的相关内容。从网络上各大品牌的车主拍摄的智驾视频来看…...

没资料的屏幕怎么点亮?思路分享

这次尝试调通一个没资料的屏幕&#xff0c;型号是HYT13264&#xff0c;这个是淘宝上面的老王2.9元屏&#xff0c;成色很好但是长期库存没有资料和代码能点亮&#xff0c;仅仅只有一个引脚定义。这里我使用Arduino Nano作为控制器尝试点亮这个模块。 首先&#xff0c;已知别人找…...

通信工程学习:什么是FEC前向纠错

FEC&#xff1a;前向纠错 FEC&#xff08;Forward Error Correction&#xff0c;前向纠错&#xff09;是一种增加数据通信可信度的技术&#xff0c;广泛应用于计算机网络、无线通信、卫星通信等多种数据传输场景中。其基本原理和特点可以归纳如下&#xff1a; 一、FEC前向纠错…...

【机器人工具箱Robotics Toolbox开发笔记(二十)】机器人工具箱SerialLink I类函数参数说明

机器人工具箱中的SerialLink表示串联机器人型机器人的具体类。该类使用D-H参数描述,每个关节一组。SerialLink I类包含的参数如表1所示。 表1 SerialLink I类参数 参 数 意 义 参 数 意 义 plot 显示机器人的图形表示 jacobn 工具坐标系中的雅可比矩阵 plot3D 显示机…...

单调栈的实现

这是C算法基础-数据结构专栏的第二十四篇文章&#xff0c;专栏详情请见此处。 引入 单调栈就是满足单调性的栈结构&#xff0c;它最经典的应用就是给定一个序列&#xff0c;找出每个数左边离它最近的比它大/小的数。 下面我们就来讲单调栈的实现。 定义 单调栈就是满足单调性…...

ffmpeg的安装和使用教程

在Linux上安装和使用FFmpeg可以方便地完成音视频的编码、解码、转码等操作。以下是详细的安装和使用教程。 安装FFmpeg FFmpeg的安装方法会因为不同的Linux发行版有所不同。下面是几种常见的安装方法&#xff1a; Ubuntu/Debian 打开终端&#xff0c;更新包列表并安装FFmpe…...

从计组中从重温C中浮点数表示及C程序翻译过程

目录 移码​编辑 传统浮点表示格式 浮点数的存储&#xff08;ieee 754&#xff09;->修炼内功 例子&#xff1a; ​编辑 浮点数取的过程 C程序翻译过程 移码 传统浮点表示格式 浮点数的存储&#xff08;ieee 754&#xff09;->修炼内功 根据国际标准IEEE&#xff0…...

MySQL常用函数(总结)详细版

1. 字符串函数 CONCAT(str1, str2, ...)&#xff1a;将多个字符串连接成一个字符串。 SELECT CONCAT(Hello, , World); LENGTH(str)&#xff1a;返回字符串的长度&#xff08;字节数&#xff09;。 SELECT LENGTH(Hello); SUBSTRING(str, pos, len)&#xff1a;从字符串 …...

学习记录——day41 C++ 类的静态成员 static

静态成员&#xff0c;是类中不依赖于类对象而独立存在的成员变量&#xff0c;但仍然属于类&#xff0c;是成员的一种 静态成员的空间分配发生在出现编译阶段&#xff0c;不占用类的空间 静态成员分为&#xff0c;静态成员变量和静态成员函数 静态成员变量 1、相关概念 1&…...

JVM - Java内存区域

文章目录 目录 文章目录 运行时数据区域 程序计数器 栈 Java虚拟机栈 本地方法栈 栈帧的组成 局部变量表 操作数栈 帧数据 堆 方法区 直接内存 总结 运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域。这些区…...

本地电脑交叉编译ffmpeg 到 windows on arm64

本地电脑交叉编译ffmpeg 到 windows on arm64 我这里有编译好的win on arm 的 ffmpeg : https://github.com/wmx-github/ffmpeg-wos-arm64-build 使用 llvm-mingw 工具链 https://github.com/mstorsjo/llvm-mingw/releases 前缀 aarch64-w64-mingw32- 这个库是ubuntu 交叉编译…...

使用 @NotEmpty、@NotBlank、@NotNull 注解进行参数校验

使用 NotEmpty、NotBlank、NotNull 注解进行参数校验 一、前言二、依赖三、使用 NotEmpty、NotBlank、NotNull 注解进行参数校验1. NotNull2. NotEmpty3. NotBlank4. 区别与适用场景 四、实践中的应用五、总结 一、前言 在 Java 开发中&#xff0c;参数校验是确保数据一致性和…...

关于Qt在子线程中使用通讯时发生无法接收数据的情况

在多线程应用中&#xff0c;串口通讯或TCP通讯的场景常常涉及到持续的读写操作&#xff0c;如果子线程处理不当&#xff0c;可能会导致信号阻塞问题。本文将通过串口通讯或TCP通讯为例&#xff0c;详细解释如何在多线程环境中避免信号阻塞&#xff0c;并提供代码示例。 1. 问题…...

HTML:从历史演进到未来创新的网页基石

该论文为AI生成&#xff0c;请勿运用到正式的论文上&#xff0c;以下仅供参考 一、引言 1.1 研究背景 HTML&#xff08;Hypertext Markup Language&#xff09;作为网页构建的基础语言&#xff0c;在互联网的发展历程中占据着至关重要的地位。自 1993 年诞生以来&#xff0c…...

向量的叉积、点积、外积

向量的叉积、点积和外积是向量代数中非常重要的操作,用于描述向量间的关系。它们广泛应用于物理、计算机图形学、几何以及蛋白质结构分析等领域。下面对每个运算进行详细介绍,并通过 PyTorch 示例代码展示其实现。 1. 点积 (Dot Product) 点积是两个向量之间的数量积,结果…...

UNI-APP 溢出隐藏显示省略号

✍经常在项目里面使用到&#xff0c;又没有记住懒得找了&#xff0c;故此写一篇记录一下! CSS单行显示省略号 /* CSS样式 */ .ellipsis {overflow: hidden; /* 隐藏超出的内容 */text-overflow: ellipsis; /* 显示省略号 */white-space: nowrap; /* 不换行 */ } CS…...

SAP学习笔记 - 开发03 - CDSView开发环境搭建,Eclipse中连接SAP,CDSView创建

上一章讲了BTP的账号创建&#xff0c;环境搭建等内容。 SAP学习笔记 - 开发02 - BTP实操流程&#xff08;账号注册&#xff0c;BTP控制台&#xff0c;BTP集成开发环境搭建&#xff09;-CSDN博客 本章继续讲SAP开发。 - CDSView 的开发环境&#xff08;Eclipse&#xff09;搭建…...

uniapp写的一个年月日时分秒时间选择功能

代码: <template><view><picker mode"multiSelector" :value"multiIndex" :range"multiRange" change"onMultiChange"><view class"picker">当前选择&#xff1a;{{ formattedDateTime }}</vie…...

golang hertz框架入门

两种模式新建项目 1、手动新建项目 2、使用hz工具新建项目 一、手动创建项目&#xff0c;并拉取框架 1、新建项目目录 hertz_demo_w 2、在项目跟目录新建main.go 文件 package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.…...

Android Home应用程序启动流程

Android系统在启动时安装应用程序的过程&#xff0c;这些应用程序安装好之后&#xff0c;还需要有一个Home应用程序来负责把它们在桌面上展示出来&#xff0c;在Android系统中&#xff0c;这个默认的Home应用程序就是Launcher了&#xff0c;本文将详细分析Launcher应用程序的启…...

C++笔试强训12、13、14

文章目录 笔试强训12一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训13一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训14一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训12 一、选择题 1-5题 引用&#xff1a;是一个别名&#xff0c;与其被引用的实…...

Excel和Word日常使用记录:

Excel使用总结 表格颜色填充&#xff1a; 合并单元格&#xff1a; 选中你要合并的单元格区域。 按下快捷键 Alt H&#xff0c;然后松开这些键。 再按下 M&#xff0c;接着按 C。 这个组合键执行的操作是&#xff1a;Alt H&#xff1a;打开“主页”选项卡。 M&#xff1a;选…...

用Git把本地仓库上传到远程仓库

用Git把本地仓库上传到远程仓库 GitHub创建远程仓库 在创建新仓库界面里输入你的仓库名后点击Create repository就好了。 创建本地项目 当你已经有一个项目后在命令行输入如下指令即可 git init git commit -m "first commit" git branch -M main git remote a…...

OneHotEncoder一个不太合理的地方

OneHotEncoder&#xff0c;在Xtrain上fit&#xff0c;在Xtest上transform 如果遇到某个值出现在Xtest&#xff0c;而没有在Xtrain出现过时&#xff0c;会抛出如下错误&#xff1a; OneHotEncoder Found unknown categories [xxx] in column xx during transform OneHotEncoder …...

如何修复软件中的BUG

笔者上一篇博文《如何开发出一款优秀的软件》主要讲了如何开发一款优秀的软件及相应的必要条件。但对一个已上线&#xff0c;已经成型的产品&#xff0c;该如何解决存在的bug呢&#xff1f;这是本文要阐述的内容。 在这里&#xff0c;首先说一下bug的种类及bug严重程度分类&…...

分享一个基于微信小程序的医院挂号就诊一体化平台uniapp医院辅助挂号应用小程序设计(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…...

HTML生日蛋糕

目录 写在前面 完整代码 代码分析 系列文章 写在最后 写在前面 HTML实现的生日蛋糕来喽&#xff0c;小编亲测&#xff0c;发给好友可以直接打开哦。在代码的第183行可以写下对朋友的祝福&#xff0c;快拿去送给你的好朋友吧&#xff01; 完整代码 <!DOCTYPE html>…...

【软件逆向】第27课,软件逆向安全工程师之(二)寄存器寻址,每天5分钟学习逆向吧!

寄存器寻址是汇编语言中的一种寻址方式&#xff0c;在这种方式中&#xff0c;操作数位于CPU的寄存器中。寄存器是CPU内部的高速存储位置&#xff0c;用于快速访问数据。以下是关于寄存器寻址的详细信息&#xff1a; 寄存器寻址的特点&#xff1a; 操作数在寄存器中&#xff1…...

前缀和 — 利用前缀信息解决子数组问题

【前缀和的核心思想是预先处理数组来快速计算任意子数组的和&#xff0c;基本上用于数组和序列问题。】 前缀和算法具体步骤 构造前缀和数组&#xff1a; 给定一个数组nums&#xff0c;其前缀和数组prex定义为prex[i]表示为数组nums从起始位置到第i个位置的元素累加和。构建前…...

2024年最新版Ajax+Axios 学习【包含原理、Promise、报文、接口等...】

基础知识 AJAX概念 AJAX概念&#xff1a;是浏览器与服务器进行数据通信的技术。 认识URL 定义&#xff1a;统一资源定位符&#xff0c;简称网址&#xff0c;用于访问网络上的资源。 组成&#xff1a; http协议&#xff1a;超文本传输协议&#xff0c;规定浏览器和服务器之…...