自动驾驶-轨迹拼接

news/2024/10/4 5:22:44 标签: 自动驾驶, 人工智能, 机器学习

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

轨迹重规划

上述所讲的直接以当前位置进行规划起点,进行本次轨迹规划的方法,其实称为轨迹重规划,即当前位置与上一帧的参考轨迹差距过大,需要重新规划轨迹;对于这种情况,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.niftyadmin.cn/n/5689491.html

相关文章

c#代码介绍23种设计模式_16迭代器模式

目录 1、迭代器模式的介绍 2、迭代器模式的定义 3、迭代器模式的结构 4、代器模式角色组成 5、迭代器实现 6、迭代器模式的适用场景 7、迭代器模式的优缺点 8、.NET中迭代器模式的应用 9、实现思路 1、迭代器模式的介绍 迭代器是针对集合对象而生的,对于集合对象而言…

[Day 83] 區塊鏈與人工智能的聯動應用:理論、技術與實踐

區塊鏈在物聯網中的應用 區塊鏈技術與物聯網&#xff08;IoT&#xff09;結合&#xff0c;為許多領域提供了強大的解決方案。傳統的IoT架構常面臨數據隱私和安全問題&#xff0c;而區塊鏈的去中心化和加密技術則能有效增強IoT系統的安全性、透明性和效率。本文將探討區塊鏈如何…

C++语言学习(7):《C++程序设计原理与实践》第二章笔记

这一章很简单&#xff0c;可以直接跳过。 编译&#xff1a; 链接&#xff1a; 可移植性的说明&#xff0c;以及错误的类型细分&#xff1a; 编译时错误链接时错误运行时错误&#xff08;逻辑错误&#xff09; 思考题 Q & A “Hello, World!” 程序的目的是什么&…

windows的一些容易忽视的使用记录

文章目录 快捷键更改电脑名字共享文件夹添加新账号&#xff08;本地的&#xff09;更改快捷访问 以下都基于 win 10。 快捷键 win I 直接打开设置。 win R 打开运行栏。这个非常常用。 更改电脑名字 先 win I 打开设置&#xff0c;然后点击系统。 左侧栏拉到最下面&…

从零开始实现RPC框架---------项目介绍及环境准备

一&#xff0c;介绍 RPC&#xff08;Remote Procedure Call&#xff09;远程过程调⽤&#xff0c;是⼀种通过⽹络从远程计算机上请求服务&#xff0c;⽽不需要 了解底层⽹络通信细节。RPC可以使⽤多种⽹络协议进⾏通信&#xff0c; 如HTTP、TCP、UDP等&#xff0c; 并且在 TCP/…

Python Web 与数据隐私保护

Python Web 与数据隐私保护 目录 &#x1f4dc; 数据隐私与合规要求&#x1f512; 用户数据保护策略&#x1f575;️‍♂️ 匿名化与伪匿名化&#x1f50d; 数据审计与监控 1. &#x1f4dc; 数据隐私与合规要求 数据隐私保护是现代数字时代的一项重要议题&#xff0c;涉及如…

SSL VPN | Easyconnect下载安装使用 (详尽)

EasyConnect是一款远程连接工具&#xff0c;为用户提供简便、快捷的远程访问和控制解决方案。 目录 下载 安装 使用 卸载 下载 通过链接进入官网技术支持板块 深信服技术支持-简单、高效、自助化服务 (sangfor.com.cn)https://support.sangfor.com.cn/ 选择软件下载 在安…

Linux 应用层协议HTTP

文章目录 一、初始HTTP协议二、URL格式网络中怎么通过URL进行定位资源呢&#xff1f;编码和解码 三、HTTP的请求格式和响应格式HTTP的请求格式HTTP的响应格式HTTP的请求方法GET方法POST方法GET Vs PostHTTP的封装和分用文件流操作浏览器获得一个完整的网页流程 HTTP的状态码对3…