自动驾驶---Parking端到端架构

​​​​​​1 背景

        自动泊车也是智能驾驶低速功能中比较重要的一部分,低速功能其中还包括记忆泊车,代客泊车等。传统的泊车算法通常使用基于规则或者搜索优化的方案来实现。然而,由于算法的复杂设计,这些方法在复杂的泊车场景中效果有限,或者说上限不高。相比之下,基于神经网络的方法往往比基于规则的方法更直观、更通用。通过采集大量专家泊车轨迹数据并且通过基于学习的方法模拟人类策略,也可以有效地完成泊车任务。

        传统的泊车算法通常基于多个模块,需要将整个停车过程分解为多个阶段,如环境感知、车位检测、定位、建图,路径规划以及控制等,复杂的模型架构导致其在紧凑的停车位或复杂场景中容易遇到困难。

        为什么要先介绍泊车端到端呢?因为这篇论文非常详细地介绍了感知---规控两段式端到端的架构,非常清晰,并且论文作者也开源了代码,对于初入门端到端的读者更加友好,行车的两段式端到端也是类似的架构,只不过训练的数据集差别会比较大。

2 端到端架构

        采用模仿学习来执行从RGB图像到路径规划的端到端规划,并且模仿人类驾驶的轨迹。本篇博客介绍的端到端方法利用了目标查询编码器来融合图像和目标特征,并且使用基于Transformer的解码器来自回归预测未来的路径点。整个泊车端到端架构如下所示,虽然看起来比较简单,但其中涉及到的环节还是比较多的。

e40b279528784b29a84e8e6e8246a59e.png

        上面这一套框架其实也适用于行车,只不过输入来源和训练的数据不同。在泊车功能上,关注更多的是环视摄像头的数据,而在行车功能上,更多使用的是其它的摄像头的信息,并且训练的主要是行车的人驾数据,像特斯拉一样的话(终极状态:行泊共用一个模型),那就是行泊的人驾数据一起训练。因为论文作者在该端到端方案的介绍上比较详细,有兴趣的读者可以了解模型的架构及整个端到端研发的流程,不管是行车,还是泊车都是比较有帮助的。

2.1 模型架构

        模型架构看起来比较简单(如下图所示),但其中串的小模型比较多,也并不是一段式端到端,这一点需要读者注意。

9a83c9d916f244aba3d2c53f1bab3c91.png

  • 数据预处理:首先将环视摄像头捕获的图像作为输入,并对图像进行必要的预处理,如裁剪、归一化等操作,以适应网络的输入要求。
  • 特征提取与融合:这个地方涉及两个方面,如下。
    • Camera Encoder:通过EfficientNet方法提取图像特征,再通过LSS方法提取深度信息,最终可得到BEV(鸟瞰图)表示形式。
    • Target Encoder:结合BEV信息和停车位的目标信息进行编码,得到目标特征。
  • 特征融合:然后,利用目标查询编码器将目标特征与图像特征进行融合(使用了Transormer中经典的交叉注意力机制),使网络能够更好地理解图像中的停车场景和目标信息。

3b37b08a0f314169a608c84d8f1ab68a.png

  • 轨迹预测:基于融合后的特征,使用基于 Transformer 的解码器以自回归的方式预测未来的航点。在预测过程中,解码器会根据之前预测的航点以及输入的特征信息,逐步生成后续的轨迹点,从而得到完整的停车轨迹。有以下两点优势:
    • 由于轨迹点的顺序特性,采用基于Transformer解码器的自回归方法来生成轨迹点,能够更好地捕捉轨迹的动态变化和长期依赖关系,提高了轨迹预测的准确性;
    • 通过交叉注意力机制,网络能够有效地将相机特征和目标特征结合起来,以生成更准确的路径规划。
  • 控制执行:根据预测的航点,与车辆的控制器协作,将车辆操纵到指定的停车位中,直到车辆完全停放好。

        整个代码也比较清晰,基本围绕上面描述的框架执行,主文件位于parking_model_real.py中,代码结构:

14404c59d1004d9aa901eb2fbe2d14c2.png

先看整个模型的主文件:

import torch
from torch import nnfrom model_interface.model.bev_encoder import BevEncoder, BevQuery
from model_interface.model.gru_trajectory_decoder import GRUTrajectoryDecoder
from model_interface.model.lss_bev_model import LssBevModel
from model_interface.model.trajectory_decoder import TrajectoryDecoder
from utils.config import Configurationclass ParkingModelReal(nn.Module):def __init__(self, cfg: Configuration):super().__init__()self.cfg = cfg# Camera Encoderself.lss_bev_model = LssBevModel(self.cfg)self.image_res_encoder = BevEncoder(in_channel=self.cfg.bev_encoder_in_channel)# Target Encoderself.target_res_encoder = BevEncoder(in_channel=1)# BEV Queryself.bev_query = BevQuery(self.cfg)# Trajectory Decoderself.trajectory_decoder = self.get_trajectory_decoder()def forward(self, data):# Encoderbev_feature, pred_depth, bev_target = self.encoder(data, mode="train")# Decoderpred_traj_point = self.trajectory_decoder(bev_feature, data['gt_traj_point_token'].cuda())return pred_traj_point, pred_depth, bev_targetdef predict_transformer(self, data, predict_token_num):# Encoderbev_feature, pred_depth, bev_target = self.encoder(data, mode="predict")# Auto Regressive Decoderautoregressive_point = data['gt_traj_point_token'].cuda() # During inference, we regard BOS as gt_traj_point_token.for _ in range(predict_token_num):pred_traj_point = self.trajectory_decoder.predict(bev_feature, autoregressive_point)autoregressive_point = torch.cat([autoregressive_point, pred_traj_point], dim=1)return autoregressive_point, pred_depth, bev_targetdef predict_gru(self, data):# Encoderbev_feature, _, _ = self.encoder(data, mode="predict")# Decoderautoregressive_point = self.trajectory_decoder(bev_feature).squeeze()return autoregressive_pointdef encoder(self, data, mode):# Camera Encoderimages = data['image'].to(self.cfg.device, non_blocking=True)intrinsics = data['intrinsics'].to(self.cfg.device, non_blocking=True)extrinsics = data['extrinsics'].to(self.cfg.device, non_blocking=True)bev_camera, pred_depth = self.lss_bev_model(images, intrinsics, extrinsics)bev_camera_encoder = self.image_res_encoder(bev_camera, flatten=False)# Target Encodertarget_point = data['fuzzy_target_point'] if self.cfg.use_fuzzy_target else data['target_point']target_point = target_point.to(self.cfg.device, non_blocking=True)bev_target = self.get_target_bev(target_point, mode=mode)bev_target_encoder = self.target_res_encoder(bev_target, flatten=False)# Feature Fusionbev_feature = self.get_feature_fusion(bev_target_encoder, bev_camera_encoder)bev_feature = torch.flatten(bev_feature, 2)return bev_feature, pred_depth, bev_targetdef get_target_bev(self, target_point, mode):h, w = int((self.cfg.bev_y_bound[1] - self.cfg.bev_y_bound[0]) / self.cfg.bev_y_bound[2]), int((self.cfg.bev_x_bound[1] - self.cfg.bev_x_bound[0]) / self.cfg.bev_x_bound[2])b = self.cfg.batch_size if mode == "train" else 1# Get target pointbev_target = torch.zeros((b, 1, h, w), dtype=torch.float).to(self.cfg.device, non_blocking=True)x_pixel = (h / 2 + target_point[:, 0] / self.cfg.bev_x_bound[2]).unsqueeze(0).T.int()y_pixel = (w / 2 + target_point[:, 1] / self.cfg.bev_y_bound[2]).unsqueeze(0).T.int()target_point = torch.cat([x_pixel, y_pixel], dim=1)# Add noiseif self.cfg.add_noise_to_target and mode == "train":noise_threshold = int(self.cfg.target_noise_threshold / self.cfg.bev_x_bound[2])noise = (torch.rand_like(target_point, dtype=torch.float) * noise_threshold * 2 - noise_threshold).int()target_point += noise# Get target point tensor in the BEV viewfor batch in range(b):bev_target_batch = bev_target[batch][0]target_point_batch = target_point[batch]range_minmax = int(self.cfg.target_range / self.cfg.bev_x_bound[2])bev_target_batch[target_point_batch[0] - range_minmax: target_point_batch[0] + range_minmax + 1,target_point_batch[1] - range_minmax: target_point_batch[1] + range_minmax + 1] = 1.0return bev_targetdef get_feature_fusion(self, bev_target_encoder, bev_camera_encoder):if self.cfg.fusion_method == "query":bev_feature = self.bev_query(bev_target_encoder, bev_camera_encoder)elif self.cfg.fusion_method == "plus":bev_feature = bev_target_encoder + bev_camera_encoderelif self.cfg.fusion_method == "concat":concat_feature = torch.concatenate([bev_target_encoder, bev_camera_encoder], dim=1)conv = nn.Conv2d(512, 256, kernel_size=3, stride=1, padding=1, bias=False).cuda()bev_feature = conv(concat_feature)else:raise ValueError(f"Don't support fusion_method '{self.cfg.fusion_method}'!")return bev_featuredef get_trajectory_decoder(self):if self.cfg.decoder_method == "transformer":trajectory_decoder = TrajectoryDecoder(self.cfg)elif self.cfg.decoder_method == "gru":trajectory_decoder = GRUTrajectoryDecoder(self.cfg)else:raise ValueError(f"Don't support decoder_method '{self.cfg.decoder_method}'!")return trajectory_decoder

        Parking Model里面的小模型(Camera Encoder,Target Encoder,BEV Query,Trajectory Decoder)代码也比较清晰,如下Trajectory Decoder所示,更多的是模型内部的结构,偏理论一些,比如embedding,mask,softmax等,都是Transformer里的基本元素,后面笔者也会单独针对小模型的结构再详细说明。

import torch
from torch import nn
from timm.models.layers import trunc_normal_
from utils.config import Configurationclass TrajectoryDecoder(nn.Module):def __init__(self, cfg: Configuration):super().__init__()self.cfg = cfgself.PAD_token = self.cfg.token_nums + self.cfg.append_token - 1self.embedding = nn.Embedding(self.cfg.token_nums + self.cfg.append_token, self.cfg.tf_de_dim)self.pos_drop = nn.Dropout(self.cfg.tf_de_dropout)item_cnt = self.cfg.autoregressive_pointsself.pos_embed = nn.Parameter(torch.randn(1, self.cfg.item_number*item_cnt + 2, self.cfg.tf_de_dim) * .02)tf_layer = nn.TransformerDecoderLayer(d_model=self.cfg.tf_de_dim, nhead=self.cfg.tf_de_heads)self.tf_decoder = nn.TransformerDecoder(tf_layer, num_layers=self.cfg.tf_de_layers)self.output = nn.Linear(self.cfg.tf_de_dim, self.cfg.token_nums + self.cfg.append_token)self.init_weights()def init_weights(self):for name, p in self.named_parameters():if 'pos_embed' in name:continueif p.dim() > 1:nn.init.xavier_uniform_(p)trunc_normal_(self.pos_embed, std=.02)def create_mask(self, tgt):tgt_mask = (torch.triu(torch.ones((tgt.shape[1], tgt.shape[1]), device=self.cfg.device)) == 1).transpose(0, 1)tgt_mask = tgt_mask.float().masked_fill(tgt_mask == 0, float('-inf')).masked_fill(tgt_mask == 1, float(0.0))tgt_padding_mask = (tgt == self.PAD_token)return tgt_mask, tgt_padding_maskdef decoder(self, encoder_out, tgt_embedding, tgt_mask, tgt_padding_mask):encoder_out = encoder_out.transpose(0, 1)tgt_embedding = tgt_embedding.transpose(0, 1)pred_traj_points = self.tf_decoder(tgt=tgt_embedding,memory=encoder_out,tgt_mask=tgt_mask,tgt_key_padding_mask=tgt_padding_mask)pred_traj_points = pred_traj_points.transpose(0, 1)return pred_traj_pointsdef forward(self, encoder_out, tgt):tgt = tgt[:, :-1]tgt_mask, tgt_padding_mask = self.create_mask(tgt)tgt_embedding = self.embedding(tgt)tgt_embedding = self.pos_drop(tgt_embedding + self.pos_embed)pred_traj_points = self.decoder(encoder_out, tgt_embedding, tgt_mask, tgt_padding_mask)pred_traj_points = self.output(pred_traj_points)return pred_traj_pointsdef predict(self, encoder_out, tgt):length = tgt.size(1)padding_num = self.cfg.item_number * self.cfg.autoregressive_points + 2 - lengthoffset = 1if padding_num > 0:padding = torch.ones(tgt.size(0), padding_num).fill_(self.PAD_token).long().to('cuda')tgt = torch.cat([tgt, padding], dim=1)tgt_mask, tgt_padding_mask = self.create_mask(tgt)tgt_embedding = self.embedding(tgt)tgt_embedding = tgt_embedding + self.pos_embedpred_traj_points = self.decoder(encoder_out, tgt_embedding, tgt_mask, tgt_padding_mask)pred_traj_points = self.output(pred_traj_points)[:, length - offset, :]pred_traj_points = torch.softmax(pred_traj_points, dim=-1)pred_traj_points = pred_traj_points.argmax(dim=-1).view(-1, 1)return pred_traj_points

2.2 训练

        首先是数据采集,高校在这一点上比较吃亏,没有丰富的训练数据集,车企在这一方面有着先天的优势,每天手动泊车的驾驶员还是比较多的,因此搜集人驾泊车的数据会比较容易(行车数据的收集类似)。

        论文中数据集是使用车载设备收集的。为了促进全面的视觉感知和轨迹,采用环视相机来捕获 RGB 图像。同时,集成了航位推算技术,利用传感器数据融合算法实现稳健和准确的车辆定位。数据是在各种停车场景中收集的,包括地下和地面车库,如下图所示,从不同的环境中收集数据有助于增强神经网络的泛化能力。

cc6dc8859bd24608829779ac88e91099.png

        在训练过程中,使用环视摄像头图像(一般环视摄像头的数量就是 4 个)作为输入,目标停车位由停车结束时的点来确定;采集到的人驾数据轨迹序列点用于监督端到端预测结果。

2.3 评价标准

    评价标准主要从两个层面来评价,分别是模型评价和实车测试结果评价。

(1)模型评价

  • Hausdorff Distance(豪斯多夫距离):用于衡量预测轨迹与真实轨迹之间的相似度。豪斯多夫距离越小,表示预测轨迹与真实轨迹越接近。

  • L2 Distance(L2距离):另一种衡量预测轨迹与真实轨迹之间差异的方法,L2距离越小,表示预测的准确性越高。

  • 四次差异(Four. Diff.):该指标可能指的是预测轨迹与真实轨迹在四个不同维度上的差异,具体维度未在摘要中详述,但可以推测可能包括位置、方向等关键参数。

(2)实车评价

  • 泊车成功率(PSR):论文中提到,在四个不同的真实车库中进行的实验结果表明,所提出的方法平均泊车成功率达到了87.8%。

  • 无时段率(NSR):未能在指定停车位停车的比率。

  • 违章停车率(PVR):违章停车率是指车辆略微超出指定停车位而不阻碍或阻碍相邻停车位的情况。

  • 平均位置误差(APE):平均位置误差是成功停车时目标停车位置与自主车辆停止位置之间的平均距离。

  • 平均方向误差(AOE):平均方向误差是成功停车时自主车辆的目标停车方向与停止方向之间的平均差。

  • 平均停车分数(APS):平均停车分数是通过综合评估计算得出的,其中包括停车过程中的位置误差、方向误差和成功率。分数分布在 0 到 100 之间。

  • 平均停车时间(APT):多次停车操作的平均停车持续时间。停车持续时间是从停车模式启动的那一刻开始计算的,直到车辆成功停在指定空间,或者停车过程因异常或故障而终止。

3 总结

        论文中提到的端到端方法和高度优化的基于规则的泊车方法之间仍然存在性能差距,在复杂场景的泊车效率以及成功率可能不如基于规则的泊车成功率。

        随着技术的发展,端到端肯定是未来自动驾驶或者具身智能的一个大方向。如何进一步提升端到端算法的性能,笔者相信在模型结构及数据训练上将会成为焦点。作为安全兜底或者作为端到端的备份,传统的规控还有其相对应的价值存在。

 

参考论文:《ParkingE2E: Camera-based End-to-end Parking Network, from Images to Planning》

 

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

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

相关文章

USB接口实现CDC(usb转串口功能)

主控:stm32f429 PHY芯片:usb3320 Cubemx System Core-RCC connectivity-USB_OTG_HS Middleware and Software Packs-USB_DEVICE 时钟配置:根据自己使用的MCU工作频率设置 Generate Code Keil5 打开工程 usbd_cdc_if.c这个文件&…

软件测试框架有什么作用?好用的测试框架分享

在当今软件开发中,软件测试框架扮演着至关重要的角色。测试框架是指用于支持自动化测试及测试管理的环境或平台。它提供了一系列的规则、标准和工具,以确保软件产品的质量。框架涵盖了测试的所有层面,包括单元测试、集成测试和系统测试等。更…

2.1.2 select poll epoll reactor

1. select 的使用方法 fd_set rdset; FD_ZERO(&rdset); // 清空 rdset rdset fdset; // 将 fdset 拷贝到 rdset,准备传给 select select(maxFd 1, &rdset, NULL, NULL, NULL);参数说明: maxFd: 被监控的文件描述符中最大的一个。maxFd 1…

vscode安装fortran插件配置

本章教程,主要介绍如何在vscode上安装fortran插件,以便于使用vscode运行fortran编写的程序。 一、安装插件 首先在插件商店安装这个扩展插件 然后再把Code Runner扩展插件装上 二、下载mingw64 通过网盘分享的文件:mingw64 链接: https://pan.baidu.com/s/1fwS-CwC7dgI

企业该如何进行合格文件外发管理

随着信息技术的迅猛发展,企业间的文件交换变得越来越频繁。但是,如何确保文件传输的安全性与效率,成为企业管理者面临的一个重大挑战。镭速(Raysync)文件外发管理方案以其独特的优势,成为众多企业的首选。本…

(Python+selenium)UI自动化测试详解

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 我们在进行UI自动化测试时,一般采用javaselenium或者pythonselenium的方式。由于python比较简单,上手快,因此建议大家采用pyt…

使用openvino加速部署paddleocr文本检测模型(C++版)

使用openvino加速部署paddleocr文本检测模型(C++版) 引言1,前处理2,后处理3,C++部署代码引言 文本检测在openvino部署端的前后处理与在paddleocr中的不太一样。 1,前处理 在将文本检测的模型转换成onnx格式(输入输出大小都已固定),并部署到openvino后,其预处理后的输…

2021-04-08 VSC++: 降序折半查找。

void 降序折半查找(int* a, int aa, int aaa) {//缘由https://bbs.csdn.net/topics/399166569int aaaa aaa / 2; bool k 0;if (a[0] aa){cout << 0, cout << ends << "查找&#xff1a;" << aa << endl;k 1;return;}else if (a[aa…

MySQL三层B+树能存多少数据

结论 bigint类型的索引&#xff08;8字节&#xff09;&#xff0c;一条数据假设是1KB的话&#xff0c; 三层B树 能存2000万条数据 该题主要考察数据如何在B树中存储的 计算思路 1.计算叶节点的大小 2.计算子节点的个数&#xff0c;由此算出第三层叶子节点的个数&#xff08;n*n…

win系统B站播放8k视频启用HEVC编码

下载HEVC插件 点击 HEVC Video Extension 2.2.20.0 latest downloads&#xff0c;根据教程下载安装 安装 Random User-Agent 点击 Random User-Agent 安装 配置 Random User-Agent ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/dda0ea75096c42c0a79ef6f6f5521…

中伟视界:AI识别摄像头+AI预警平台在矿山皮带空载监测中的应用

在矿山开采和矿物处理过程中&#xff0c;皮带运输机扮演着举足轻重的角色。它们负责将矿石、煤炭等物料从一处运送到另一处&#xff0c;是矿山生产流程中不可或缺的一环。然而&#xff0c;皮带运输机在运行过程中也面临着一些挑战&#xff0c;其中之一便是皮带空载问题。皮带空…

实训项目-人力资源管理系统-1Company子模块

目录 前言&#xff1a; 用例图设计&#xff1a; 系统设计 开发方式&#xff1a; 技术架构 系统结构&#xff1a; API文档&#xff1a; 工程搭建&#xff1a; 搭建父项目 pom&#xff1a; 创建公共子模块&#xff1a; 返回实体&#xff1a; 分布式id生成器&#xff1a; …

2.5.3 文件使用、共享、保护、安全与可靠性

文章目录 文件使用文件共享文件保护系统安全与可靠性 文件使用 操作系统向用户提供操作级、编程级文件服务。 操作级服务包括目录管理&#xff0c;文件操作&#xff08;复制、删除、修改&#xff09;&#xff0c;文件管理&#xff08;设置文件权限&#xff09;。 编程级服务包括…

路由器转发数据报的封装过程

✍作者&#xff1a;柒烨带你飞 &#x1f4aa;格言&#xff1a;生活的情况越艰难&#xff0c;我越感到自己更坚强&#xff1b;我这个人走得很慢&#xff0c;但我从不后退。 &#x1f4dc;系列专栏&#xff1a;网路安全入门系列 目录 路由器转发数据的封装过程 路由器转发数据的封…

vulnhub matrix-breakout靶机

1.搭建靶机 这样就是装好了 获取靶机IP nmap -O 192.168.47.129/24 2.信息收集 dirb http://192.168.47.128 dirb 首页 81端口一个登录页面 gobuster dir -u http://192.168.152.154 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,html gra…

微信小程序 不同角色进入不同页面、呈现不同底部导航栏

遇到这个需求之前一直使用的小程序默认底部导航栏&#xff0c;且小程序默认入口页面为pages/index/index&#xff0c;要使不同角色呈现不同底部导航栏&#xff0c;必须要在不同页面引用不同的自定义导航栏。本篇将结合分包&#xff08;subPackages&#xff09;展开以下三步叙述…

如何通过 Kafka 将数据导入 Elasticsearch

作者&#xff1a;来自 Elastic Andre Luiz 将 Apache Kafka 与 Elasticsearch 集成的分步指南&#xff0c;以便使用 Python、Docker Compose 和 Kafka Connect 实现高效的数据提取、索引和可视化。 在本文中&#xff0c;我们将展示如何将 Apache Kafka 与 Elasticsearch 集成以…

LLaMA-Factory GLM4-9B-CHAT LoRA 微调实战

&#x1f929;LLaMA-Factory GLM LoRA 微调 安装llama-factory包 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git进入下载好的llama-factory&#xff0c;安装依赖包 cd LLaMA-Factory pip install -e ".[torch,metrics]" #上面这步操作会完成…

view draw aosp15

基础/背景知识 如何理解Drawable&#xff1f; 在 Android 中&#xff0c;Drawable 是一个抽象的概念&#xff0c;表示可以绘制到屏幕上的内容。 它可以是位图图像、矢量图形、形状、颜色等。 Drawable 本身并不是一个 View&#xff0c;它不能直接添加到布局中&#xff0c;而是…

gridcontrol表格某一列设置成复选框,选择多行(repositoryItemCheckEdit1)

1. 往表格中添加repositoryItemCheckEdit1 2. 事件&#xff1a; repositoryItemCheckEdit1.QueryCheckStateByValue repositoryItemCheckEdit1_QueryCheckStateByValue; private void repositoryItemCheckEdit1_QueryCheckStateByValue(object sender, DevExpress.XtraEditor…