☰
FastBEV TensorRT部署实战:从PyTorch到Engine的完整链路
2026/10/12 12:09:59 网站建设 项目流程

简介:TensorRT部署FastBEV算法实战完整项目包,面向自动驾驶视觉感知方向的高校学生与开发者,适合毕业设计、课程设计及算法部署入门。资源围绕BEV感知模型在TensorRT环境下的落地展开,涵盖从ONNX导出、PTQ量化、TensorRT引擎构建到运行验证的完整链路,并附详细设计文档与部署说明,便于对照复现。压缩包共45个文件,以Python脚本、C++头文件与源文件、CUDA内核、Shell部署脚本等为主,整体仅1.96MB,轻量易下载;目录明确区分src、configs、tool、ptq、demo等模块,结构清晰。已有85人学习浏览,对正在选型或调试TensorRT部署方案的同学具有直接参考价值。

1. 第一次把 FastBEV 往 TensorRT 上搬,先想清楚你要换回什么

做过 BEV 感知上车的人应该都体会过那种窒息感:PyTorch 里精度漂亮得很,一搬到车载 Orin 或者 T4 上,帧率直接掉到个位数,整个感知链路被一个模型卡死。FastBEV 这类多相机 BEV 算法本来就是为“实时”设计的,但实时指的是 GPU 上的理论算力,不是工程上能直接用的帧率。TensorRT 部署 FastBEV 实战,说白了就是把这套模型从“能跑”变成“跑得动”。

这个毕设项目的价值不在算法本身,而在那条完整的落地链路:源码怎么组织、模型怎么从 PyTorch 变成 TensorRT engine、部署流程里哪些环节会让人翻车。适合正在做自动驾驶感知相关毕设、或者刚进车端部署团队的新人,也适合那些手头有 BEV 模型但一直在用 FP32 硬扛的工程师。你拿到的是一套工程模板,不是一篇调参笔记。

2. FastBEV 部署链路拆解:为什么 BEV 感知必须走 TensorRT

2.1 FastBEV 的模块构成:Backbone、视角变换与任务头

FastBEV 的核心思路不复杂:把多路相机的 2D 图像特征投影到统一的鸟瞰图栅格里,然后在 BEV 特征图上做检测或分割。整个网络大致能切成三截。

第一截是图像 Backbone,负责从每路相机图像里提 2D 特征,常见的是 EfficientNet、Twins 这类轻量结构。第二截是视角变换,这是 BEV 算法和普通 2D 检测最大的区别所在,它要把 N 路相机的特征图,结合内外参投影到 BEV 栅格上。FastBEV 在这个环节做了加速设计,相比原生 LSS 那套密集体素投射要轻很多。第三截是 BEV Encoder 加任务头,在俯视图特征上做检测或分割。

对部署工程师来说,这三截的“脾气”完全不同。Backbone 是最听话的,标准卷积加注意力,TensorRT 支持得很顺;任务头也还行,无非是卷积、归一化、反卷积上采样。真正让人头疼的是中间那截视角变换——里面有栅格采样、体素池化、坐标变换,这些操作落到 ONNX 里往往是 gather、scatter、grid_sample,恰好是 TensorRT parser 支持得最磨人的算子。

2.2 整体导出还是子网络拆分:部署前的第一道选择题

拿到 FastBEV 源码后,第一个要做的决定不是调参,而是确认导出策略。常见做法有两种:把整个模型一键导出 ONNX,或者拆成子网络分别导出。

我一般会选择拆分,理由很实际。整体导出看着省事,但视角变换里的 scatter/gather 很容易在转 ONNX 时产生 TensorRT 不支持的子图,一旦触发 fallback,性能直接归零。而且整体模型跑一次回传梯度没有问题,推理时却是纯前向,很多训练代码里的分支在推理时根本走不到,导出的 ONNX 里却原样保留,白白增加转换失败的概率。

方案优点缺点适用场景
整体导出端到端一条命令,部署后无需拼接自定义算子易卡壳,排错难模型内无特殊算子的简单任务
按子网络拆分每段可单独验证、替换、调试需要自己写前后处理拼接逻辑FastBEV 这类含视角变换的算法

拆分的粒度我习惯按上面那三截来:Backbone 一段、视角变换单独处理、BEV Encoder 加任务头一段。视角变换这段,常见做法是分离出来用 CUDA 实现,或者干脆保留在 PyTorch 里做,只把 Backbone 和任务头放进 TensorRT。这样每一段都能单独跑精度对比,出问题知道该查谁,不会整个模型黑匣子一锅端。

2.3 精度模式取舍:FP16 白捡、INT8 要还

TensorRT 加速最直接的来源是降低计算精度。FastBEV 的部署实践里,FP16 基本上是无脑开的,精度损失通常在可接受范围内,推理速度却能翻一倍左右。INT8 则需要更谨慎,因为 BEV 特征图是多个相机特征在栅格里累加出来的,数值分布和普通 2D 特征图不太一样,直接量化容易让远处的小目标消失在噪声里。

精度模式速度收益精度风险工程成本
FP32基线无最低
FP16约 1.5~2 倍低,注意大数值累加溢出低
INT8约 3~4 倍高,BEV 特征对量化敏感高,需校准集和敏感层分析

我踩过的坑是:FP16 模式下 FastBEV 的检测头输出偶尔出现 NaN,排查下来是视角变换里特征求和时数值过大,FP16 表示不了就溢出了。解决方法是把几处大数值累加单独保留 FP32 计算,其余层继续用 FP16。TensorRT 支持按层设置精度,不必为了几个敏感算子牺牲全局收益。

2.4 为什么是 TensorRT:不是单纯快,而是省出“余量”

很多人问为什么不直接用 PyTorch 的 JIT 或者 ONNX Runtime,我做过对比,TensorRT 在 FastBEV 这种多分支、多输入的模型上优势非常明显。原因有三:一是层融合,卷积加偏置加激活这种小组合会被合并成单一 kernel,省掉多次显存读写;二是 kernel 自动调优,同一个卷积 TensorRT 会在启动时跑一遍 benchmark,选出当前 GPU 上最快的实现;三是显存池复用,推理时输入输出 buffer 复用同一块显存,不会像 PyTorch 那样频繁分配释放。

这个优势放到多路视频流场景里特别直观。比如用 T4 跑 1080p 25 帧的视频流分析,单路模型延迟低到一定程度,才能在一个 GPU 上同时塞下多路推理而不掉帧。FastBEV 在车端也一样,多相机输入本身已经占了大量算力,TensorRT 省下来的每一毫秒都是给其他感知模块留的余量。

3. Ubuntu 下安装 TensorRT:版本匹配清单与最小验证

3.1 版本匹配三件套:CUDA、cuDNN、TensorRT

部署 FastBEV 的机器通常是 Ubuntu 20.04 或 22.04,安装 TensorRT 前第一个要确认的不是 TensorRT 本身,而是它和 CUDA、cuDNN 的版本匹配关系。TensorRT 是有编译期依赖的,用错版本最常见的结果是运行时直接报undefined symbol或者libcudnn.so.8: cannot open shared object file。

我整理过一个自己常用的匹配原则:TensorRT 8.x 对应 CUDA 11.x,TensorRT 10.x 对应 CUDA 12.x,具体小版本以 NVIDIA 官网的兼容性表格为准。cuDNN 的版本要求更严格,TensorRT 8.5 需要 cuDNN 8.6 以上,但也不能无脑装最新版,装成 cuDNN 9 反而可能不被识别。

组件推荐版本组合(示例)注意事项
CUDA11.8 / 12.2用nvcc -V确认,不是nvidia-smi里的驱动版本
cuDNN8.6 / 8.9与 TensorRT 小版本严格对应
TensorRT8.6.x / 10.x优先选长期支持版本,避免尝鲜
PyTorch对应 CUDA 的 cu11x / cu12x与模型训练时的版本尽量一致

一个常见的低级失误是把nvidia-smi显示的驱动版本当成 CUDA 版本。驱动支持高版本 CUDA 不代表你机器上装的就是那个版本,FastBEV 训练时的 PyTorch 是用 CUDA 11.x 编译的,TensorRT 却基于 CUDA 12.x 构建,两边混用会在运行时报错。我建议先统一一个 CUDA 大版本,再往上叠 PyTorch 和 TensorRT。

3.2 Ubuntu 上安装 TensorRT:deb 与 pip 两条路

安装 TensorRT 常见有两条路:用 NVIDIA 的 apt 仓库装 deb 包,或者用 pip 装 Python 版。做毕设和快速验证,我通常先用 pip,因为一条命令就能把 Python 接口、parser、runtime 全部装好,适合在这个阶段跑通流程。

# 用 pip 安装 TensorRT,注意先确认本机 CUDA 版本 pip install tensorrt==8.6.1 -i https://pypi.org/simple # 验证装的是哪个版本 python -c "import tensorrt as trt; print(trt.__version__)" # 如果要用 ONNX parser,装一下 onnx 和 onnxruntime 做对照参考 pip install onnx onnxruntime-gpu

这段命令里tensorrt==8.6.1这个版本号要和你机器上的 CUDA 匹配,装完第一件事是打印版本号确认导入成功。我遇到过 pip 装完 TensorRT 后 Python 里能导入,但trtexec命令行却找不到的情况,那是因为 pip 包装到的是 Python 的 site-packages,命令行工具不在 PATH 里。

如果后续要跑 C++ 接口,我建议改用 NVIDIA apt 仓库装完整版 deb 包,会把 include 头文件、trtexec可执行文件、libnvinfer.so都放到系统路径,省去手动配置环境变量的麻烦。

# NVIDIA apt 仓库方式安装 TensorRT(以 Ubuntu 22.04 + CUDA 12.x 为例) distribution=$(. /etc/os-release; echo $ID$VERSION_ID) curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/$distribution/x86_64/cuda-keyring_1.1-1_all.deb -o cuda-keyring.deb sudo dpkg -i cuda-keyring.deb sudo apt-get update sudo apt-get install tensorrt

装完后检查一下动态库路径,确保/usr/lib/x86_64-linux-gnu或者 TensorRT 安装目录下的libnvinfer.so能被找到,直接设环境变量是最稳的。

3.3 验证安装的最小命令:trtexec 跑通一个网络

装完 TensorRT 后,不要急着去转 FastBEV,先用trtexec跑一个最小模型验证环境可用。trtexec是 TensorRT 自带的命令行工具,能完成 ONNX 到 engine 的转换、性能测试、精度校验,是部署流程里出场率最高的工具。

# 最简单的验证:用内置的 softmax 测试网络跑一遍 trtexec --onnx=/path/to/test.onnx --saveEngine=test.engine # 如果还没有 ONNX 文件,可以直接构造一个高斯随机输入做性能测试 trtexec --shapes=input:1x3x256x704 --fp16 --iterations=100

第一条命令要求手头有一个测试 ONNX,第二条会在没有模型的情况下用--shapes指定虚拟输入跑一个空网络,验证 TensorRT runtime 能正常初始化。这里--shapes的格式是输入名:维度,维度默认是 NCHW。能看到类似[04/01/2025-10:00:00] [I] Engine built in X seconds的输出,说明环境基本没问题。

3.4 PyTorch 与 TensorRT 共存:小心 ABI 与 CUDA 版本

这个环节最后提醒一件事:PyTorch 和 TensorRT 都装在同一个 Python 环境里时,经常出现 CUDA runtime 版本冲突。FastBEV 训练环境如果用 PyTorch 的 cu118 预编译包,它内部绑定了 CUDA 11.8 的 runtime 库,而 TensorRT 若是基于 CUDA 12.x 构建的,两个库同时被导入,轻则警告重则崩溃。

我现在的习惯是建立一个独立的 conda 环境专门放部署相关依赖,把 FastBEV 源码里的训练依赖和部署依赖分开。部署环境只需要 PyTorch(导出权重时要用)、TensorRT、ONNX、OpenCV、numpy 这几个核心包,不需要训练时的分布式依赖和数据增强库。这样即使版本打架,出了错也知道是哪个环境的问题,最少能让排错范围缩小一半。

4. 从 PyTorch 到 Engine:导出 ONNX 与构建 TensorRT 的可复现步骤

4.1 导出前的输入侧对齐:归一化、图像布局与多相机维度

FastBEV 的输入和普通 2D 检测模型不一样,它是多相机图像一起进网络的。常见代码里输入 shape 是[B, N, C, H, W],N 是相机路数。导出 ONNX 之前,必须先决定怎么处理这个 N 维。

我习惯把 N 折进 batch 维,即把[B, N, C, H, W]reshape 成[B*N, C, H, W],让 Backbone 部分按普通 batch 处理。TensorRT 对动态 batch 的支持比对五维动态 shape 的支持成熟得多,这样做能减少一个动态维度,降低转换时的算子兼容性风险。代价是视角变换之前的特征要再 reshape 回五维,这个 reshape 在 ONNX 里只是 view 操作,TensorRT 能无缝处理。

# 导出前的 wrapper:把多相机输入折叠成伪 batch import torch class FastBEVDeployWrapper(torch.nn.Module): def __init__(self, model, num_cams=6): super().__init__() self.model = model self.num_cams = num_cams def forward(self, images): # images: [B*N, C, H, W],已经在外部 reshape 好 # 内部要把伪 batch 还原成 [B, N, C, H, W] B = images.shape[0] // self.num_cams imgs = images.view(B, self.num_cams, *images.shape[1:]) # inter 是内外参矩阵,导出时用固定值占位,运行时用真实参数替换 rots, trans, intrins, post_trans = self.model.get_calib_params(B) x = self.model.image_backbone(imgs) x = self.model.view_transformer(x, rots, trans, intrins, post_trans) x = self.model.bev_encoder(x) return self.model.task_head(x) # 实例化并加载训练好的权重 deploy_model = FastBEVDeployWrapper(origin_model).cuda().eval() checkpoint = torch.load("fastbev.pth", map_location="cuda") deploy_model.load_state_dict(checkpoint["state_dict"], strict=False)

这个 wrapper 的核心作用是剪掉训练代码里不属于推理路径的分支,同时固定住内外参矩阵这些在推理时不会变化的输入。注意get_calib_params在部署时应该返回固定常量,而不是像训练那样每 step 随机生成噪声。很多导出失败案例都是在这里埋的雷——训练代码里内外参可能带着 aug 参数,导出时忘了关掉,导致 ONNX 图里混入随机节点。

4.2 导出 ONNX:dynamic_axes 与 opset 的取舍

输入侧对齐后就可以做 ONNX 导出了。FastBEV 部署时最常见的导出报错都集中在一个点:某些算子的 opset 版本不够。grid_sample需要 opset 11 以上,部分新版 PyTorch 导出的注意力算子需要 opset 13。我一般直接用 opset=12 或更高,反正 TensorRT 8.6 对高 opset 的支持已经很稳定。

# 导出 ONNX 的核心代码 import torch dummy = torch.randn(1 * 6, 3, 256, 704).cuda() # 6 路相机,分辨率按训练配置 torch.onnx.export( deploy_model, # 上一步的 wrapper dummy, # 固定 shape 的假输入 "fastbev.onnx", opset_version=12, # 至少 11,推荐 12+ input_names=["images"], output_names=["bev_det", "bev_seg"], # 按任务头实际输出改 dynamic_axes={ "images": {0: "batch"}, "bev_det": {0: "batch"}, "bev_seg": {0: "batch"}, }, do_constant_folding=True, # 把 BN、常量运算折叠掉 verbose=False, )

dynamic_axes这里只给 batch 维开了动态,空间维度锁死。这样做是有意的:FastBEV 的图像分辨率是一个固定配置,部署时不会频繁改动,而 batch 维必须动态化,否则没办法做多路并发推理。do_constant_folding=True会把推理时不会变化的计算(比如归一化层里的均值方差)提前算掉,降低 ONNX 里的节点数量。

导出后用onnx.checker做一次快速校验是个好习惯。我见过不少导出过程没有报错、但 ONNX 里有 dangling 节点的情况,直接拿去 TensorRT 构建会报奇怪错误。

# 用 python 快速检查 ONNX 是否完整 python -c " import onnx m = onnx.load('fastbev.onnx') onnx.checker.check_model(m) print('graph node count:', len(m.graph.node)) print('inputs:', [i.name for i in m.graph.input]) print('outputs:', [o.name for o in m.graph.output]) "

这一步不需要额外装什么,ONNX 包本身就带 checker。看到graph node count在合理范围内(FastBEV 这类模型大概几百到上千个节点),输入输出名符合预期,再往下走。

4.3 用 trtexec 构建 Engine:FP16、动态 shape 与显存池

ONNX 文件没问题,下一步就是用trtexec构建 TensorRT engine。这一步的参数会直接影响最终性能和显存占用,值得逐项说清楚。

trtexec \ --onnx=fastbev.onnx \ --saveEngine=fastbev_fp16.engine \ --fp16 \ --minShapes=images:1x6x3x256x704 \ --optShapes=images:4x6x3x256x704 \ --maxShapes=images:8x6x3x256x704 \ --memPoolSize=workspace:2048MiB \ --builderOptimizationLevel=3

这里有几个参数需要解释。--fp16开启半精度,对应 2.3 节说的收益和风险。--minShapes/optShapes/maxShapes三者必须同时出现,min 和 max 定义了 batch 的上下界,opt 是 TensorRT 做 kernel 调优时用的参考值,一般设成实际使用场景最常见的大小,多路并发设 4 或 6 比较合理。--memPoolSize在 TensorRT 8.5 之后替代了旧的--workspace,设的是构建时允许使用的最大显存,不是运行时显存,不要调太低否则某些 kernel 找不到好实现。

--builderOptimizationLevel=3是调优等级,范围 0 到 5,等级越高构建时间越长。第一次构建用 3 就行,能把层融合和 kernel 选择做得很不错,又不会像 5 那样等半小时。构建日志里最后会打印每种 kernel 的耗时,留意有没有算子是走了CPU或者Fallback,这两类出现任何一个都说明有算子没被 GPU 内核覆盖,要去修。

4.4 用 Python API 构建 Engine:工程化的 build 函数

trtexec适合验证,但工程上还是建议把构建逻辑写进 Python 脚本,这样能控制异常处理、能自动拼接多段子网络。FastBEV 如果是按第 2 章说的拆分了子网络,每个子网络都要独自构建,再用 Python 串联起来。

# 用 TensorRT Python API 构建 engine 的模板函数 import tensorrt as trt def build_engine(onnx_path, engine_path, fp16=True, max_batch=8): logger = trt.Logger(trt.Logger.WARNING) builder = trt.Builder(logger) network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) parser = trt.OnnxParser(network, logger) with open(onnx_path, "rb") as f: if not parser.parse(f.read()): for i in range(parser.num_errors): print(parser.get_error(i)) return None config = builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 2 << 30) # 2GiB if fp16: config.set_flag(trt.BuilderFlag.FP16) # 设置动态 batch profile = builder.create_optimization_profile() input_name = network.get_input(0).name profile.set_shape(input_name, min=(1, 6, 3, 256, 704), opt=(max_batch // 2, 6, 3, 256, 704), max=(max_batch, 6, 3, 256, 704)) config.add_optimization_profile(profile) engine = builder.build_serialized_network(network, config) with open(engine_path, "wb") as f: f.write(engine) return engine_path

这段代码里有几个细节值得注意。EXPLICIT_BATCH标志必须设置,否则网络默认走隐式 batch 模式,开不了动态 shape。set_memory_pool_limit的第二个参数是字节数,我写2 << 30是 2GiB。build_serialized_network返回的是序列化后的二进制,直接写文件就是部署时要加载的 engine 文件。

解析错误打印用parser.get_error(i),这是排查 ONNX 转不过去的第一手资料。我见过很多人构建失败后只看到FAILED两个字就不知道怎么办了,其实错误列表里往往已经写清楚了哪个算子、哪一层出了问题。构建成功后会生成fastbev_fp16.engine文件,后续推理全部基于这个文件,离线构建一次,线上只做加载。

5. FastBEV 部署避坑指南:五个典型故障排查

5.1 推理结果全黑或全 NaN:预处理折叠与数据流错位

现象:engine 构建成功,推理不报错,但输出的 BEV 检测图全是背景,分割图全黑,或者直接输出 NaN。

原因:这个问题八成不在 TensorRT,而在预处理。FastBEV 训练时输入图像通常要做归一化,减均值除以方差,这些操作如果被写进了 PyTorch 模型内部,导出 ONNX 时do_constant_folding=True可能把它折叠成常量。折叠后,TensorRT 这边不知道输入还需要归一化,直接拿原始像素值喂了进去。另一个可能性是图像通道顺序,PyTorch 训练用的是 RGB,部署时读图用的 OpenCV 默认是 BGR,没有转换就直接喂给了 engine。

解决:先把预处理从模型里拆出来,让 ONNX 的输入就是“原始像素”。然后在部署代码里显式做归一化和通道转换,并用同一张图分别跑 PyTorch 和 TensorRT 推理,对比中间特征图。特征图对上了再往下查,对不上就逐层缩窄范围。

5.2 FP16 精度退化:远处小目标漏检与检测头数值溢出

现象:FP16 engine 在测试集上 mAP 比 FP32 掉了 2 到 5 个点,尤其远处的小目标明显漏检。

原因:我在 2.3 节说过,BEV 特征和多相机特征累加相关,数值范围比普通 2D 特征大。FP16 的指数位只有 5 位,大数一累加就丢精度。FastBEV 的检测头里如果还有 sigmoid 或者 softmax,FP16 下这些非线性函数的导数区域会变窄,输出概率分布被压平,小目标就没了。

解决:TensorRT 支持按层设置精度。先跑一遍层级 profiling,找出精度退化贡献最大的几个层,把它们单独设成 FP32。操作上可以在构建时遍历网络层,对特定名字的层调用layer.precision = trt.float32并设置set_output_type。工程上我习惯先无脑全 FP16 跑一遍,再对检测头和视角变换里的 reduce 层单独提精度,通常能把掉点控制在 1% 以内。

5.3 grid_sample 算子不支持:parser 版本与 fallback 陷阱

现象:构建时日志显示某个算子not supported,或者整个子图被标记 fallback,推理速度骤降。

原因:FastBEV 的视角变换如果用到了grid_sample,ONNX 里对应的是GridSample节点。TensorRT 8.4 之前对 grid_sample 的支持不完整,三线性插值和 padding mode 的某些组合会直接拒绝。另外,PyTorch 新版导出的 grid_sample 可能带align_corners属性,旧版 TensorRT parser 不认。

解决:先升级 TensorRT 到 8.6 以上,绝大多数 grid_sample 问题都能解决。如果还在报错,检查导出的 ONNX 里GridSample节点的属性,mode=bilinear、padding_mode=zeros、align_corners=False是最稳的组合。再不行就要自己写 plugin 或者在 CUDA 层面实现采样逻辑,这也是最初建议子网络拆分的原因——视角变换拆出来单独处理,就不会让整个 engine 构建失败。

5.4 动态 Batch 显存暴涨:optShapes 与池化策略

现象:启动时显存占用正常,一旦推理 batch 从 1 切到 4,显存突然翻了好几倍,甚至直接 OOM。

原因:TensorRT 构建时预留的显存池和 kernel 选择依赖optShapes,optShapes设多大,显存池就可能按多大预留。如果optShapes设了 batch 8,但实际只跑 batch 1,引擎占用的显存并不会按 batch 1 收缩。另一个原因是动态 shape 下 TensorRT 会为不同 shape 保留多个 kernel 实例,batch 跨度越大,中间产物越多。

解决:optShapes设成实际使用的最常见 batch。如果既想支持大 batch 又不希望常驻显存太高,就把 maxShapes 设成略高于实际需求的值,而不是往上无脑拉。运行时如果发现显存紧张,优先检查--memPoolSize是否设得过大,2GiB 够用就不用给 4GiB。

5.5 反复推理显存泄漏:binding 复用与缓存策略

现象:单次推理没问题,连续跑几百帧后显存占用缓慢爬升,最终CUDA Out Of Memory。

原因:最常见的原因是推理代码在循环里反复调用torch.cuda.empty_cache()或者每次推理都重新分配输入输出 buffer。TensorRT 推理本身不会泄漏显存,泄漏的是调用方没有正确复用 binding。

解决:在加载 engine 后,一次性创建所有 binding 对应的显存 buffer,推理循环里只做copy_htod和copy_dtoh,不新建张量。我习惯在推理代码里加一个固定 buffer 池,输入输出都从池子里取,跑完放回。加一个显存统计打印,连续推理 200 帧后确认显存曲线是平的,才算过这一关。

# 推理时复用 buffer 的核心逻辑 import tensorrt as trt import pycuda.driver as cuda engine = load_engine("fastbev_fp16.engine") context = engine.create_execution_context() # 创建持久化的输入输出 buffer d_input = cuda.mem_alloc(1 * 6 * 3 * 256 * 704 * 4) d_output = cuda.mem_alloc(1 * 200 * 256 * 704 * 4) h_input = np.zeros((6, 3, 256, 704), dtype=np.float32) h_output = np.zeros((200, 256, 704), dtype=np.float32) for frame in video_stream: # 只做拷贝,不重新分配 cuda.memcpy_htod(d_input, preprocess(frame)) context.execute_v2([int(d_input), int(d_output)]) cuda.memcpy_dtoh(h_output, d_output)

这里execute_v2接收的是 binding 地址列表,TensorRT 8.x 里推荐用它替代旧的execute。注意输入输出 buffer 一次性分配后循环复用,这既是性能优化手段,也是规避显存泄漏的正确姿势。

6. 把 Engine 推到多路并发:吞吐压测与精度回归

整个部署流程跑通后,最后一步不是直接发版,而是做两件事:压测吞吐和回归精度。吞吐压测的方法很简单,先让 engine 预热 50 帧,把 kernel autotune 和 CUDA context 初始化完成,然后连续推理 300 帧,记录每一帧的延迟,取 P99 而不是平均值——平均值会被偶尔的调度抖动拉低,P99 才是用户能感受到的最差情况。

# 用 trtexec 直接做性能压测,输出 P50/P90/P99 延迟 trtexec \ --loadEngine=fastbev_fp16.engine \ --shapes=images:4x6x3x256x704 \ --streams=4 \ --iterations=300 \ --warmup=50

--streams=4模拟 4 路并发推理,--warmup=50跳过前 50 帧的初始化不稳定期。压测得到的 P99 延迟乘以并发路数,就能预估一张卡能撑起多少路视频流。精度回归则拿一套固定的测试场景,对比 FP32 引擎和 FP16/INT8 引擎的检测 mAP,注意保持输入完全相同,不要一边归一化一边没归一化。

我自己的教训是第一次把 FP16 engine 丢上车时,只看平均帧率没看 P99,结果颠簸路段直接掉帧,后来养成了每次构建引擎先压 300 帧看延迟曲线的习惯。这个工程做完了,性能和精度都有了数字依据,才敢说这套部署流程真的落地了。FastBEV 是好算法,但把它变成稳定上车的引擎,靠的是这些不亮眼却关键的收尾动作。希望帮到你。

从 FastBEV 整体导出到多路并发压测,我踩过的坑基本都在这里了。这套从环境搭建开始的流程,你按着走一遍,比对着博客碎片拼凑要省下至少两三个通宵。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询