自动驾驶-轨迹拼接

自动驾驶在进行规划之前,要确定当前帧轨迹规划的起点,这个起点常被误认为是当前车辆的位置,即每次以车辆的当前位置进行轨迹规划;其实不是这样的,直观上,这会导致本次次规划的轨迹同上次规划的轨迹之间是不连续的,这个不连续传递到控制模块,由于轨迹规划出的轨迹对于控制而言就是参考线,那么由于参考线是不连续的,对控制器而言就是朝令夕改。

轨迹重规划

上述所讲的直接以当前位置进行规划起点,进行本次轨迹规划的方法,其实称为轨迹重规划,即当前位置与上一帧的参考轨迹差距过大,需要重新规划轨迹;对于这种情况,apollo也做了一定优化,根据自车当前实际位置状态信息,通过车辆运动学推导0.1s后的位置状态信息,作为规划起始点状态,向后推导0.1s,这主要是为了减少执行机构的响应延迟问题,将未来的状态作为执行器的参考输入。

参考链接:apollo轨迹拼接

轨迹拼接

轨迹拼接的核心思想是将当前的规划起点,设置在上次(上一帧)规划出轨迹上,从而保证轨迹的连续性,提升控制效果。
上述思想的实现,需要得到当前自车状态,在上一帧轨迹中的匹配点,匹配点的确定有两种方式:

  1. 相对时间匹配
    根据当前时间戳和上一帧轨迹起点的时间戳对比,计算当前时间自车在上一帧轨迹中的时间匹配点(下图中的绿色点)及该匹配点在上一帧轨迹中对应的索引值t_index。
  2. 相对里程匹配
    结合自车的定位信息与上一帧轨迹信息,将自车信息从笛卡尔坐标系→Frenet坐标(s,d),得到当前的位置s,根据当前的s在上一帧的轨迹中,即可查询到在里程维度上的匹配点(下图中的蓝色点)。
    轨迹拼接根据index,选取min{时间匹配点,里程匹配点}作为当前车辆在上一帧映射的匹配点。如上图所示,由于绿色点索引更小,故即选择绿色点为匹配点。

在选择完规划起点后,为缓解执行机构的延时,同样向前预测del_t时间,以del_t时刻的点,作为起点,进行当前时刻的轨迹规划,并在上一帧帧的轨迹上截取出matched_index往前n个点开始,至forward_rel_time的一段轨迹,作为stitching_trajectory。
在这里插入图片描述现在的疑问是为何要生成一个stitch trajectory呢?即使不选择,也是和直接的轨迹是平滑与连续的啊?
但是规划起点的选择是明晰的。
参考链接:轨迹拼接

轨迹拼接apollo代码

/* Planning from current vehicle state if:
1. the auto-driving mode is off
(or) 2. we don't have the trajectory from last planning cycle
(or) 3. the position deviation from actual and target is too high
*/
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) {
    //a.是否使能轨迹拼接
    if (!FLAGS_enable_trajectory_stitcher) {
        *replan_reason = "stitch is disabled by gflag.";
        return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);
    }

    //b.上一帧是否生成轨迹
    if (!prev_trajectory) {
        *replan_reason = "replan for no previous trajectory.";
        return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);
    }

    //c.是否处于自动驾驶模式
    if (vehicle_state.driving_mode() != canbus::Chassis::COMPLETE_AUTO_DRIVE) {
        *replan_reason = "replan for manual mode.";
        return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);
    }

    //d.上一帧是否存在轨迹点 
    size_t prev_trajectory_size = prev_trajectory->NumOfPoints();
    if (prev_trajectory_size == 0) {
        *replan_reason = "replan for empty previous trajectory.";
        return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);
    }

    const double veh_rel_time = current_timestamp - prev_trajectory->header_time();
    size_t time_matched_index = prev_trajectory->QueryLowerBoundPoint(veh_rel_time);

    //e.判断当前时间相对于上一帧的相对时间戳是否小于上一帧起点相对时间戳
    if (time_matched_index == 0 &&
        veh_rel_time < prev_trajectory->StartPoint().relative_time()) {
        *replan_reason =
            "replan for current time smaller than the previous trajectory's first "
            "time.";
        return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);
    }
    
    //f.判断时间匹配点是否超出上一帧轨迹点范围
    if (time_matched_index + 1 >= prev_trajectory_size) {
        *replan_reason =
            "replan for current time beyond the previous trajectory's last time";
        return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);
    }

    auto time_matched_point = prev_trajectory->TrajectoryPointAt(
    static_cast<uint32_t>(time_matched_index));

    //g.判断时间匹配点处是否存在path_point
    if (!time_matched_point.has_path_point()) {
        *replan_reason = "replan for previous trajectory missed path point";
        return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);
    }

    size_t position_matched_index = prev_trajectory->QueryNearestPointWithBuffer(
    {vehicle_state.x(), vehicle_state.y()}, 1.0e-6);

    //计算实际位置点和匹配点的横纵向偏差
    auto frenet_sd = ComputePositionProjection(
    vehicle_state.x(), vehicle_state.y(),
    prev_trajectory->TrajectoryPointAt(
    static_cast<uint32_t>(position_matched_index)));

    //h.判断横纵向偏差
    if (replan_by_offset) {
        auto lon_diff = time_matched_point.path_point().s() - frenet_sd.first;
        auto lat_diff = frenet_sd.second;
        //h.1:横向偏差不满足条件
        if (std::fabs(lat_diff) > FLAGS_replan_lateral_distance_threshold) {
            const std::string msg = absl::StrCat(
            "the distance between matched point and actual position is too "
            "large. Replan is triggered. lat_diff = ",
            lat_diff);
            *replan_reason = msg;
            return ComputeReinitStitchingTrajectory(planning_cycle_time,
            vehicle_state);
    	}
        //h.2:纵向偏差不满足条件
        if (std::fabs(lon_diff) > FLAGS_replan_longitudinal_distance_threshold) {
            const std::string msg = absl::StrCat(
            "the distance between matched point and actual position is too "
            "large. Replan is triggered. lon_diff = ",
            lon_diff);
            *replan_reason = msg;
            return ComputeReinitStitchingTrajectory(planning_cycle_time,
            vehicle_state);
    	}
    } else {
        ADEBUG << "replan according to certain amount of lat and lon offset is "
        "disabled";
    }

    //计算当前时刻后T时刻的时间,并计算其在上一帧轨迹中对应的索引值
    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);

    //构建拼接轨迹,<匹配索引点前n个点,当前时刻后的T时刻所对应的匹配点>
    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);

    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);
        }
        //适配时间和s值
        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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop之WordCount测试

1、Hadoop简介&#xff1a; Hadoop是Apache旗下的一个用Java语言实现的开源软件框架&#xff0c;是一个开发和运行处理大规模数据的软件平台。 Hadoop的核心组件包括Hadoop分布式文件系统&#xff08;HDFS&#xff09;和MapReduce编程模型。HDFS是一个高度容错的系统&#xf…

Python | Leetcode Python题解之第456题132模式

题目&#xff1a; 题解&#xff1a; class Solution:def find132pattern(self, nums: List[int]) -> bool:candidate_i, candidate_j [-nums[0]], [-nums[0]]for v in nums[1:]:idx_i bisect.bisect_right(candidate_i, -v)idx_j bisect.bisect_left(candidate_j, -v)if…

MFC有三个选项:MFC ActiveX控件、MFC应用程序、MFC DLL,如何选择?

深耕AI&#xff1a;互联网行业 算法研发工程师 ​ 目录 MFC ActiveX 控件 控件的类型 标准控件 自定义控件 ActiveX控件 MFC ActiveX控件 标准/自定义控件 MFC ActiveX控件分类 3种MFC如何选择&#xff1f; MFC ActiveX控件 MFC 应用程序 MFC DLL 总结 举例说明…

不只是前端,后端、产品和测试也需要了解的浏览器知识(二)

目录标题 一、业务系统呈现给用户的节点1. 输入 URL 并解析1.1 用户输入 URL 并按下回车键1.2 浏览器解析 URL1.3 DNS 解析 2. 建立连接、发送请求并接收响应2.1 建立 TCP 连接2.2 发送 HTTP 请求2.3 服务器处理请求2.4 发送 HTTP 响应2.5 浏览器接收响应 3. 解析和加载资源、渲…

模拟算法(4)_外观数列

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 模拟算法(4)_外观数列 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 题目链…

golang学习笔记27-反射【重要】

本节也是GO核心部分&#xff0c;很重要。包括基本类型的反射&#xff0c;结构体类型的反射&#xff0c;类别方法Kind()&#xff0c;修改变量的值。 目录 一、概念&#xff0c;基本类型的反射二、结构体类型的反射三、类别方法Kind()四、修改变量的值 一、概念&#xff0c;基本…

有些硬盘录像机接入视频汇聚平台EasyCVR后通道不显示/显示不全,该如何处理?

EasyCVR视频监控汇聚管理平台是一款针对大中型项目设计的跨区域网络化视频监控集中管理平台。该平台不仅具备视频资源管理、设备管理、用户管理、运维管理和安全管理等功能&#xff0c;还支持多种主流标准协议&#xff0c;如GB28181、RTSP/Onvif、RTMP、部标JT808、GA/T 1400协…

Linux忘记root用户密码怎么重设密码

直接说步骤&#xff1a; 1.重启客户机 2.在选择内核页面快速按e键&#xff0c;进入编辑模式 进入后应该是这个样子 在这里只能按上下键切换行 找到Linux16这里 3.按右方向键切换到行尾&#xff0c;也就是UTF-8处&#xff0c;在后面添加一个空格&#xff0c;然后加上这段话 …

【ubuntu】ubuntu20.04安装chrome浏览器

1.下载 https://download.csdn.net/download/qq_35975447/89842972 https://www.google.cn/chrome/ 2.安装 sudo dpkg -i google-chrome-stable_current_amd64.deb 3.使用

SkyWalking监控SQL参数

前言 SkyWalking可以记录每个请求中执行的所有SQL&#xff0c;但是默认情况下&#xff0c;SkyWalking不记录SQL参数导致使用起来不是很方便&#xff0c;每次都得看日志才能知道具体的参数。不过SkyWalking提供了一个配置参数&#xff0c;开启后&#xff0c;便可记录SQL执行的参…

【目标检测】yolo的三种数据集格式

目标检测中数据集格式之间的相互转换--coco、voc、yolohttps://zhuanlan.zhihu.com/p/461488682?utm_mediumsocial&utm_psn1825483604463071232&utm_sourcewechat_session【目标检测】yolo的三种数据集格式https://zhuanlan.zhihu.com/p/525950939?utm_mediumsocial&…

CNN模型对CIFAR-10中的图像进行分类

代码功能 这段代码展示了如何使用 Keras 和 TensorFlow 构建一个卷积神经网络&#xff08;CNN&#xff09;模型&#xff0c;用于对 CIFAR-10 数据集中的图像进行分类。主要功能包括&#xff1a; 加载数据&#xff1a;从 CIFAR-10 数据集加载训练和测试图像。 数据预处理&#…

解决:使用layui.treeTable.updateNode,更新表格数据后,done里面的事件丢失问题

1. 背景 在给树形表格添加行点击事件&#xff0c;并且只更新当前行数据。 treeTable.updateNode("SpeProjListId", result.LAY_DATA_INDEX, result);更新数据后&#xff0c;点击事件失效。 1. 给字段绑定事件&#xff1a; class"link_a link_style" , {…

企业级数据备份一般都是怎么做的?来唠唠嗑

小白最近去了很多企业看了一下他们的存储方案&#xff0c;基本上都是单硬盘数据存储&#xff0c;一个硬盘10TB&#xff08;实际可用8TB左右&#xff09;。 这些大概是大部分微小企业存储数据的办法&#xff0c;也是他们能想到的最好办法了吧。 截至2024年的今天&#xff0c;咱…

Web安全 - 安全防御工具和体系构建

文章目录 安全标准和框架1. 国内安全标准&#xff1a;等级保护制度&#xff08;等保&#xff09;2. 国际安全标准&#xff1a;ISO27000系列3. NIST安全框架&#xff1a;IDPRR方法4. COBIT与ITIL框架 防火墙防火墙的基本作用防火墙的三种主要类型防火墙的防护能力防火墙的盲区 W…

【蚂蚁HR-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

CSS | 面试题:你知道几种移动端适配方案?

目录 一、自适应和响应式 二、为什么要做移动端适配&#xff1f; 三、当前流行的几种适配方案 (1) 方案一&#xff1a;百分比设置&#xff08;不推荐&#xff09; (2) 方案二&#xff1a;rem 动态设置 font-size px 与 rem 的单位换算 手动换算 less/scss函数 webpac…

C0004.Qt中QComboBox设置下拉列表样式后,下拉列表样式无效的解决办法

问题描述 我们平时在使用Qt Creator对控件QComboBox的样式进行设置后&#xff0c;在运行程序启动界面时&#xff0c;发现设置的样式无效&#xff0c;效果如下&#xff1a; /* 设置下拉菜单框的样式 */ QComboBox QAbstractItemView {border: 1px solid rgb(161,161,161); /* …

TransFormer 视频笔记

TransFormer BasicsAttention单头注意力 single head attentionQ&#xff1a; query 查寻矩阵 128*12288K key matrix 128*12288SoftMax 归一 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/19e3cf1ea28442eca60d5fc1303921f4.png)Value matrix 12288*12288 MLP Bas…

【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…