文章目录
一、前言
yolo26n目标检测+ByteTrack目标跟踪+RTM-pose姿态估计+PoseC3D动作识别的orin部署,其实就是把yolo、RTM-pose、PoseC3D这些pth的模型权重转onnx,然后再转.engine,转完之后就实现了模型的FP16量化(精度从FP32变成了FP16)。
1.1 最终架构
video
|
v
YOLO26n Detect TensorRT
|
person bbox
|
v
ByteTrack
|
stable ID
|
v
RTMPose TensorRT
|
Halpe26 keypoints
|
take first 17 points
|
v
48 frame skeleton buffer
|
GeneratePoseTarget
sigma=0.6
NCTHW heatmap
|
v
PoseC3D TensorRT
|
kick probability
1.2 确认 Orin TensorRT版本
板子是 JetPack 5.1.2 / L4T R35.4.1,所以应该是 TensorRT 8.5 系列。
执行:
/usr/src/tensorrt/bin/trtexec --version
以及:
python - <<'PY'
import tensorrt as trt
print(trt.__version__)
PY
注意:很多命令都是跟系统版本和 TensorRT有关系的,如果不是这个版本,命令可能会发生变化。
二、yolo26n.onnx 转 yolo26n.engine
2.1 小插曲:构建导出yolo的engine的conda环境
我们本来有一个kicking的conda环境,里面已经配好了用于运行PoseC3D的环境,库的版本如下:
(base) orin2@orin2-desktop:/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking$ conda activate kicking
(kicking) orin2@orin2-desktop:/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking$ pip list
WARNING: Ignoring invalid distribution -orch (/home/orin2/miniconda3/envs/kicking/lib/python3.8/site-packages)
Package Version Editable project location
----------------------- -------------------- ------------------------------------------------------------------------
absl-py 2.3.1
addict 2.4.0
aliyun-python-sdk-core 2.16.0
aliyun-python-sdk-kms 2.16.5
annotated-types 0.7.0
appdirs 1.4.4
attrs 25.3.0
audioread 3.0.1
av 12.3.0
beautifulsoup4 4.14.3
blinker 1.8.2
certifi 2026.5.20
cffi 1.17.1
charset-normalizer 3.4.7
chumpy 0.70
click 8.1.7
colorama 0.4.6
coloredlogs 15.0.1
contourpy 1.1.1
coverage 7.6.1
crcmod 1.7
cryptography 46.0.5
cuda-python 12.3.0
cupy-cuda11x 12.3.0
cycler 0.12.1
Cython 0.27.3
dataclasses 0.6
decorator 4.4.2
einops 0.8.1
eval_type_backport 0.3.1
exceptiongroup 1.3.1
fastrlock 0.8.3
filelock 3.14.0
flake8 7.1.2
Flask 3.0.3
flatbuffers 25.12.19
fonttools 4.57.0
fsspec 2025.3.0
ftfy 6.2.3
future 1.0.0
gdown 5.2.2
gitdb 4.0.12
GitPython 3.1.50
google-auth 2.50.0
google-auth-oauthlib 1.0.0
grpcio 1.70.0
humanfriendly 10.0
idna 3.15
imageio 2.35.1
imageio-ffmpeg 0.5.1
imgaug 0.4.0
importlib_metadata 8.5.0
importlib_resources 6.4.5
iniconfig 2.1.0
interrogate 1.7.0
isort 4.3.21
itsdangerous 2.2.0
Jetson.GPIO 2.1.12
Jinja2 3.1.4
jmespath 0.10.0
joblib 1.4.2
json-tricks 3.17.3
kiwisolver 1.4.7
lap 0.5.12
lazy_loader 0.4
librosa 0.11.0
llvmlite 0.41.1
lmdb 1.5.1
Mako 1.3.10
Markdown 3.7
markdown-it-py 3.0.0
MarkupSafe 2.1.5
matplotlib 3.7.5
mccabe 0.7.0
mdurl 0.1.2
ml-dtypes 0.2.0
mmaction2 1.2.0 /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/mmaction2-main
mmcv 2.1.0 /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/mmcv
mmdet 3.3.0
mmengine 0.10.7
mmpose 1.2.0
model-index 0.1.11
moviepy 1.0.3
mpmath 1.3.0
msgpack 1.1.1
munkres 1.1.4
networkx 3.1
ninja 1.13.0
numba 0.58.1
numpy 1.24.4
oauthlib 3.3.1
onnx 1.12.0
onnxruntime 1.19.2
onnxruntime-gpu 1.12.0
onnxslim 0.1.71
openai-clip 1.0.1
opencv-contrib-python 4.13.0.92
opencv-python 4.13.0.92
opendatalab 0.0.10
openmim 0.3.9
openxlab 0.1.3
ordered-set 4.1.0
oss2 2.17.0
packaging 24.2
pandas 2.0.3
parameterized 0.9.0
pillow 10.4.0
PIMS 0.7
pip 24.2
platformdirs 4.3.6
pluggy 1.5.0
polars 1.8.2
pooch 1.8.2
proglog 0.1.12
protobuf 3.20.1
psutil 7.2.2
py 1.11.0
py-cpuinfo 9.0.0
pyasn1 0.6.3
pyasn1_modules 0.4.2
pybind11 3.0.2
pycocotools 2.0.7
pycodestyle 2.12.1
pycparser 2.23
pycryptodome 3.23.0
pycuda 2022.2.1
pydantic 2.10.6
pydantic_core 2.27.2
pyflakes 3.2.0
Pygments 2.19.2
pyparsing 3.1.4
PySocks 1.7.1
pytest 8.3.5
pytest-runner 6.0.1
python-dateutil 2.9.0.post0
pytools 2024.1.14
PyTurboJPEG 2.2.0
pytz 2023.4
pyudev 0.24.3
pyusb 1.2.1
PyWavelets 1.4.1
PyYAML 6.0.3
regex 2024.11.6
requests 2.32.4
requests-oauthlib 2.0.0
rich 13.4.2
scikit-image 0.21.0
scikit-learn 1.3.2
scipy 1.10.1
seaborn 0.13.2
sentry-sdk 2.60.0
setuptools 60.2.0
shapely 2.0.7
six 1.17.0
slicerator 1.1.0
smmap 5.0.3
soundfile 0.13.1
soupsieve 2.7
soxr 0.3.7
sympy 1.13.3
tabulate 0.9.0
tensorboard 2.14.0
tensorboard-data-server 0.7.2
termcolor 2.4.0
terminaltables 3.1.10
thop 0.1.1.post2209072238
threadpoolctl 3.5.0
tifffile 2023.7.10
tomli 2.4.0
torch 1.11.0
torchvision 0.12.0a0
tqdm 4.65.2
typing_extensions 4.13.2
tzdata 2025.3
ultralytics 8.4.52
ultralytics-thop 2.0.18
urllib3 2.2.3
wandb 0.24.2
wcwidth 0.2.14
Werkzeug 3.0.6
wheel 0.44.0
xdoctest 1.3.2
xgboost 2.1.4
xtcocotools 1.14.3
yapf 0.43.0
zipp 3.20.2
但是如果我们要导出yolo的模型的话,上面这个环境不纯净,可能会产生版本冲突,因为我们要导出的模型是yolo26的模型,而上面的那个环境是兼容python3.8的对应的ultralytics。但是我们发现想要再创建一个conda环境,居然不够磁盘空间了,于是这里就出现了一个小插曲,就是我们在另一个磁盘又安装了一个anaconda的程序,然后我们通过在~/.bashrc的末尾写入以下命令来实现conda环境的转换,也就是说现在我只要使用use_conda1命令就可以转换到第1个conda环境,使用use_conda2命令就可以转换到第2个conda环境,然后我们就可以在第2个conda环境里面创建conda环境(这样磁盘空间就够了,因为在另一个use_conda2使用的是另一个磁盘的anaconda。
(base) orin2@orin2-desktop:/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking$ vi ~/.bashrc
use_conda1() {
while [ "${CONDA_SHLVL:-0}" -gt 0 ]; do
conda deactivate
done
unset -f conda 2>/dev/null || true
unset -f __conda_activate 2>/dev/null || true
unset -f __conda_reactivate 2>/dev/null || true
unset -f __conda_hashr 2>/dev/null || true
unset CONDA_EXE CONDA_PREFIX CONDA_PREFIX_1
unset CONDA_PYTHON_EXE _CE_M _CE_CONDA
source /home/orin2/miniconda3/etc/profile.d/conda.sh
hash -r
echo "当前 Conda base:$(conda info --base)"
}
alias conda2='/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/miniconda3/bin/conda'
use_conda2() {
while [ "${CONDA_SHLVL:-0}" -gt 0 ]; do
conda deactivate
done
unset -f conda 2>/dev/null || true
unset -f __conda_activate 2>/dev/null || true
unset -f __conda_reactivate 2>/dev/null || true
unset -f __conda_hashr 2>/dev/null || true
unset CONDA_EXE CONDA_PREFIX CONDA_PREFIX_1
unset CONDA_PYTHON_EXE _CE_M _CE_CONDA
source /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/minconda3/etc/profile.d/conda.sh
hash -r
echo "当前 Conda base:$(conda info --base)"
}
使命令生效:
source ~/.bashrc
然后我们用use_conda2切换到第2个anaconda环境之后,创建yolo_export的conda环境,将这个环境专门用于导出yolo模型
(kicking) orin2@orin2-desktop:/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking$ use_conda2
当前 Conda base:/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/minconda3
orin2@orin2-desktop:/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking$ conda env list
# conda environments:
#
# * -> active
# + -> frozen
/home/orin2/miniconda3
/home/orin2/miniconda3/envs/kicking
base /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/minconda3
yolo_export /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/minconda3/envs/yolo_export
(yolo_export) orin2@orin2-desktop:/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking$ pip list
Package Version Editable project location
----------------------- ----------------------- ------------------------------------------------------------------------
absl-py 2.3.1
addict 2.4.0
aliyun-python-sdk-core 2.16.0
aliyun-python-sdk-kms 2.16.5
annotated-types 0.7.0
appdirs 1.4.4
attrs 25.3.0
audioread 3.0.1
av 12.3.0
beautifulsoup4 4.14.3
blinker 1.8.2
certifi 2026.5.20
cffi 1.17.1
charset-normalizer 3.4.7
chumpy 0.70
click 8.1.7
colorama 0.4.6
coloredlogs 15.0.1
contourpy 1.1.1
coverage 7.6.1
crcmod 1.7
cryptography 46.0.5
cuda-python 12.3.0
cupy-cuda11x 12.3.0
cycler 0.12.1
Cython 0.27.3
dataclasses 0.6
decorator 4.4.2
einops 0.8.1
eval_type_backport 0.3.1
exceptiongroup 1.3.1
fastrlock 0.8.3
filelock 3.14.0
flake8 7.1.2
Flask 3.0.3
flatbuffers 25.12.19
fonttools 4.57.0
fsspec 2025.3.0
ftfy 6.2.3
future 1.0.0
gdown 5.2.2
gitdb 4.0.12
GitPython 3.1.50
google-auth 2.50.0
google-auth-oauthlib 1.0.0
grpcio 1.70.0
humanfriendly 10.0
idna 3.15
imageio 2.35.1
imageio-ffmpeg 0.5.1
imgaug 0.4.0
importlib_metadata 8.5.0
importlib_resources 6.4.5
iniconfig 2.1.0
interrogate 1.7.0
isort 4.3.21
itsdangerous 2.2.0
Jetson.GPIO 2.1.12
Jinja2 3.1.4
jmespath 0.10.0
joblib 1.4.2
json-tricks 3.17.3
kiwisolver 1.4.7
lap 0.5.12
lazy_loader 0.4
librosa 0.11.0
llvmlite 0.41.1
lmdb 1.5.1
Mako 1.3.10
Markdown 3.7
markdown-it-py 3.0.0
MarkupSafe 2.1.5
matplotlib 3.7.5
mccabe 0.7.0
mdurl 0.1.2
ml-dtypes 0.2.0
mmaction2 1.2.0 /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/mmaction2-main
mmcv 2.1.0 /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/mmcv
mmdet 3.3.0
mmengine 0.10.7
mmpose 1.2.0
model-index 0.1.11
moviepy 1.0.3
mpmath 1.3.0
msgpack 1.1.1
munkres 1.1.4
networkx 3.1
ninja 1.13.0
numba 0.58.1
numpy 1.24.4
oauthlib 3.3.1
onnx 1.12.0
onnxruntime 1.19.2
onnxruntime-gpu 1.12.0
onnxslim 0.1.71
openai-clip 1.0.1
opencv-contrib-python 4.13.0.92
opencv-python 4.13.0.92
opendatalab 0.0.10
openmim 0.3.9
openxlab 0.1.3
ordered-set 4.1.0
oss2 2.17.0
packaging 24.2
pandas 2.0.3
parameterized 0.9.0
pillow 10.4.0
PIMS 0.7
pip 24.2
platformdirs 4.3.6
pluggy 1.5.0
polars 1.8.2
pooch 1.8.2
proglog 0.1.12
protobuf 3.20.1
psutil 7.2.2
py 1.11.0
py-cpuinfo 9.0.0
pyasn1 0.6.3
pyasn1_modules 0.4.2
pybind11 3.0.2
pycocotools 2.0.7
pycodestyle 2.12.1
pycparser 2.23
pycryptodome 3.23.0
pycuda 2022.2.1
pydantic 2.10.6
pydantic_core 2.27.2
pyflakes 3.2.0
Pygments 2.19.2
pyparsing 3.1.4
PySocks 1.7.1
pytest 8.3.5
pytest-runner 6.0.1
python-dateutil 2.9.0.post0
pytools 2024.1.14
PyTurboJPEG 2.2.0
pytz 2023.4
pyudev 0.24.3
pyusb 1.2.1
PyWavelets 1.4.1
PyYAML 6.0.3
regex 2024.11.6
requests 2.32.4
requests-oauthlib 2.0.0
rich 13.4.2
scikit-image 0.21.0
scikit-learn 1.3.2
scipy 1.10.1
seaborn 0.13.2
sentry-sdk 2.60.0
setuptools 60.2.0
shapely 2.0.7
six 1.17.0
slicerator 1.1.0
smmap 5.0.3
soundfile 0.13.1
soupsieve 2.7
soxr 0.3.7
sympy 1.12
tabulate 0.9.0
tensorboard 2.14.0
tensorboard-data-server 0.7.2
termcolor 2.4.0
terminaltables 3.1.10
thop 0.1.1.post2209072238
threadpoolctl 3.5.0
tifffile 2023.7.10
tomli 2.4.0
torch 2.1.0a0+41361538.nv23.6
torchvision 0.16.2+c6f3977
tqdm 4.65.2
typing_extensions 4.13.2
tzdata 2025.3
ultralytics 8.4.52
ultralytics-thop 2.0.18
urllib3 2.2.3
wandb 0.24.2
wcwidth 0.2.14
Werkzeug 3.0.6
wheel 0.44.0
xdoctest 1.3.2
xgboost 2.1.4
xtcocotools 1.14.3
yapf 0.43.0
zipp 3.20.2
2.2 小插曲:转换遇到的问题
我们使用以下命令进行转换遇到了问题
yolo export \
model=yolo26n.pt \
format=engine \
imgsz=640 \
half=True \
batch=1 \
device=0
预期得到:
yolo26n.engine
实际输出:
[08/21/2026-13:29:43] [TRT] [I] Total Activation Memory: 65928623104
[08/21/2026-13:29:43] [TRT] [I] Detected 1 inputs and 1 output network tensors.
[08/21/2026-13:29:43] [TRT] [I] Total Host Persistent Memory: 312288
[08/21/2026-13:29:43] [TRT] [I] Total Device Persistent Memory: 763392
[08/21/2026-13:29:43] [TRT] [I] Total Scratch Memory: 2856448
[08/21/2026-13:29:43] [TRT] [I] [MemUsageStats] Peak memory usage of TRT CPU/GPU memory allocators: CPU 5 MiB, GPU 2124 MiB
[08/21/2026-13:29:43] [TRT] [I] [BlockAssignment] Started assigning block shifts. This will take 196 steps to complete.
[08/21/2026-13:29:43] [TRT] [I] [BlockAssignment] Algorithm ShiftNTopDown took 35.6463ms to assign 12 blocks to 196 nodes requiring 16384512 bytes.
[08/21/2026-13:29:43] [TRT] [I] Total Activation Memory: 16384512
[08/21/2026-13:29:43] [TRT] [I] [MemUsageChange] Init cuDNN: CPU +0, GPU +0, now: CPU 2193, GPU 19169 (MiB)
[08/21/2026-13:29:43] [TRT] [W] TensorRT encountered issues when converting weights between types and that could affect accuracy.
[08/21/2026-13:29:43] [TRT] [W] If this is not the desired behavior, please modify the weights or retrain with regularization to adjust the magnitude of the weights.
[08/21/2026-13:29:43] [TRT] [W] Check verbose logs for the list of affected weights.
[08/21/2026-13:29:43] [TRT] [W] - 84 weights are affected by this issue: Detected subnormal FP16 values.
[08/21/2026-13:29:43] [TRT] [W] - 3 weights are affected by this issue: Detected values less than smallest positive FP16 subnormal value and converted them to the FP16 minimum subnormalized value.
[08/21/2026-13:29:43] [TRT] [I] [MemUsageChange] TensorRT-managed allocation in building engine: CPU +5, GPU +8, now: CPU 5, GPU 8 (MiB)
TensorRT: export success ✅ 534.2s, saved as 'yolo26n.engine' (7.3 MB)
Export complete (538.6s)
Results saved to /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/yolo26n.engine
Predict: yolo predict task=detect model=yolo26n.engine imgsz=640 half
Validate: yolo val task=detect model=yolo26n.engine imgsz=640 data=/home/lq/codes/ultralytics/ultralytics/cfg/datasets/coco.yaml half
Visualize: https://netron.app
💡 Learn more at https://docs.ultralytics.com/modes/export
(yolo_export) orin2@orin2-desktop:/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin$ trtexec --loadEngine=yolo26n.engine
&&&& RUNNING TensorRT.trtexec [TensorRT v8502] # trtexec --loadEngine=yolo26n.engine
[08/21/2026-13:30:29] [I] === Model Options ===
[08/21/2026-13:30:29] [I] Format: *
[08/21/2026-13:30:29] [I] Model:
[08/21/2026-13:30:29] [I] Output:
[08/21/2026-13:30:29] [I] === Build Options ===
[08/21/2026-13:30:29] [I] Max batch: 1
[08/21/2026-13:30:29] [I] Memory Pools: workspace: default, dlaSRAM: default, dlaLocalDRAM: default, dlaGlobalDRAM: default
[08/21/2026-13:30:29] [I] minTiming: 1
[08/21/2026-13:30:29] [I] avgTiming: 8
[08/21/2026-13:30:29] [I] Precision: FP32
[08/21/2026-13:30:29] [I] LayerPrecisions:
[08/21/2026-13:30:29] [I] Calibration:
[08/21/2026-13:30:29] [I] Refit: Disabled
[08/21/2026-13:30:29] [I] Sparsity: Disabled
[08/21/2026-13:30:29] [I] Safe mode: Disabled
[08/21/2026-13:30:29] [I] DirectIO mode: Disabled
[08/21/2026-13:30:29] [I] Restricted mode: Disabled
[08/21/2026-13:30:29] [I] Build only: Disabled
[08/21/2026-13:30:29] [I] Save engine:
[08/21/2026-13:30:29] [I] Load engine: yolo26n.engine
[08/21/2026-13:30:29] [I] Profiling verbosity: 0
[08/21/2026-13:30:29] [I] Tactic sources: Using default tactic sources
[08/21/2026-13:30:29] [I] timingCacheMode: local
[08/21/2026-13:30:29] [I] timingCacheFile:
[08/21/2026-13:30:29] [I] Heuristic: Disabled
[08/21/2026-13:30:29] [I] Preview Features: Use default preview flags.
[08/21/2026-13:30:29] [I] Input(s)s format: fp32:CHW
[08/21/2026-13:30:29] [I] Output(s)s format: fp32:CHW
[08/21/2026-13:30:29] [I] Input build shapes: model
[08/21/2026-13:30:29] [I] Input calibration shapes: model
[08/21/2026-13:30:29] [I] === System Options ===
[08/21/2026-13:30:29] [I] Device: 0
[08/21/2026-13:30:29] [I] DLACore:
[08/21/2026-13:30:29] [I] Plugins:
[08/21/2026-13:30:29] [I] === Inference Options ===
[08/21/2026-13:30:29] [I] Batch: 1
[08/21/2026-13:30:29] [I] Input inference shapes: model
[08/21/2026-13:30:29] [I] Iterations: 10
[08/21/2026-13:30:29] [I] Duration: 3s (+ 200ms warm up)
[08/21/2026-13:30:29] [I] Sleep time: 0ms
[08/21/2026-13:30:29] [I] Idle time: 0ms
[08/21/2026-13:30:29] [I] Streams: 1
[08/21/2026-13:30:29] [I] ExposeDMA: Disabled
[08/21/2026-13:30:29] [I] Data transfers: Enabled
[08/21/2026-13:30:29] [I] Spin-wait: Disabled
[08/21/2026-13:30:29] [I] Multithreading: Disabled
[08/21/2026-13:30:29] [I] CUDA Graph: Disabled
[08/21/2026-13:30:29] [I] Separate profiling: Disabled
[08/21/2026-13:30:29] [I] Time Deserialize: Disabled
[08/21/2026-13:30:29] [I] Time Refit: Disabled
[08/21/2026-13:30:29] [I] NVTX verbosity: 0
[08/21/2026-13:30:29] [I] Persistent Cache Ratio: 0
[08/21/2026-13:30:29] [I] Inputs:
[08/21/2026-13:30:29] [I] === Reporting Options ===
[08/21/2026-13:30:29] [I] Verbose: Disabled
[08/21/2026-13:30:29] [I] Averages: 10 inferences
[08/21/2026-13:30:29] [I] Percentiles: 90,95,99
[08/21/2026-13:30:29] [I] Dump refittable layers:Disabled
[08/21/2026-13:30:29] [I] Dump output: Disabled
[08/21/2026-13:30:29] [I] Profile: Disabled
[08/21/2026-13:30:29] [I] Export timing to JSON file:
[08/21/2026-13:30:29] [I] Export output to JSON file:
[08/21/2026-13:30:29] [I] Export profile to JSON file:
[08/21/2026-13:30:29] [I]
[08/21/2026-13:30:29] [I] === Device Information ===
[08/21/2026-13:30:29] [I] Selected Device: Orin
[08/21/2026-13:30:29] [I] Compute Capability: 8.7
[08/21/2026-13:30:29] [I] SMs: 16
[08/21/2026-13:30:29] [I] Compute Clock Rate: 1.3 GHz
[08/21/2026-13:30:29] [I] Device Global Memory: 62800 MiB
[08/21/2026-13:30:29] [I] Shared Memory per SM: 164 KiB
[08/21/2026-13:30:29] [I] Memory Bus Width: 256 bits (ECC disabled)
[08/21/2026-13:30:29] [I] Memory Clock Rate: 1.3 GHz
[08/21/2026-13:30:29] [I]
[08/21/2026-13:30:29] [I] TensorRT version: 8.5.2
[08/21/2026-13:30:29] [I] Engine loaded in 0.00573987 sec.
[08/21/2026-13:30:30] [I] [TRT] Loaded engine size: 7 MiB
Segmentation fault
2.3 小插曲:解决方案
原因已经定位了:不是 yolo26n.engine 生成失败,而是 Ultralytics 的 .engine 文件格式和 trtexec 直接生成的 raw TensorRT engine 不完全一样。
Ultralytics 在真正的 TensorRT plan 前面会写入一段 metadata,格式大致是:
[4字节 metadata 长度]
[JSON metadata]
[真正的 TensorRT engine]
Ultralytics 自己加载时会先读掉这段 metadata,再调用 deserialize_cuda_engine();源码就是这样实现的。(GitHub)
所以你直接:
trtexec --loadEngine=yolo26n.engine
实际上是把 Ultralytics 封装文件 当成纯 TensorRT plan 去读,这就是这次 segfault 最可疑、也最符合现象的原因。
你前面 RTMPose、PoseC3D 没这个问题,是因为它们的 engine 是:
ONNX
↓
trtexec
↓
纯 TensorRT engine
最推荐:YOLO 也统一成 ONNX → trtexec
既然我们要做正式版、后面还要严谨测算力,那三个模型最好全部统一:
YOLO26n ONNX → trtexec → raw engine
RTMPose ONNX → trtexec → raw engine
PoseC3D ONNX → trtexec → raw engine
这样最干净。
先看看当前目录有没有:
ls -lh yolo26n.onnx
Ultralytics 在导 engine 时内部本来就会先导 ONNX,所以它很可能还在;当前 Ultralytics 源码的 TensorRT exporter 也是先 export_onnx() 再 build engine。(GitHub)
如果已经有 yolo26n.onnx
直接:
/usr/src/tensorrt/bin/trtexec \
--onnx=yolo26n.onnx \
--saveEngine=yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine \
--fp16 \
--workspace=4096
然后:
/usr/src/tensorrt/bin/trtexec \
--loadEngine=yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine \
--warmUp=1000 \
--duration=10
这次应该就不会有 Ultralytics metadata 的问题。
如果没有 yolo26n.onnx
就在 yolo_export 环境重新导一次 ONNX:
yolo export \
model=yolo26n.pt \
format=onnx \
imgsz=640 \
batch=1 \
dynamic=False \
simplify=True
然后再执行:
/usr/src/tensorrt/bin/trtexec \
--onnx=yolo26n.onnx \
--saveEngine=yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine \
--fp16 \
--workspace=4096
这样以后正式推理程序也统一用 raw engine。
你现在这个 yolo26n.engine 其实也可以验证
Ultralytics 自己认识 metadata,所以可以试:
yolo predict \
task=detect \
model=yolo26n.engine \
source=out_06.mp4 \
imgsz=640 \
device=0
Ultralytics 官方文档本身就是这样运行导出的 TensorRT engine。(GitHub)
如果这个能正常检测,就进一步证明:
engine模型本身 ✅
Ultralytics读取 ✅
trtexec直接读取 ❌ metadata封装问题
不过正式版我仍建议走 raw engine,不要依赖这种封装。
你也可以直接从现有 engine 剥掉 metadata
如果不想重新 build,实际上可以把现有:
yolo26n.engine
里的 metadata 去掉:
python - <<'PY'
import json
from pathlib import Path
src = Path("yolo26n.engine")
dst = Path("yolo26n_raw.engine")
with src.open("rb") as f:
meta_len_bytes = f.read(4)
meta_len = int.from_bytes(
meta_len_bytes,
byteorder="little",
signed=True
)
print("metadata length =", meta_len)
meta_raw = f.read(meta_len)
meta = json.loads(meta_raw.decode("utf-8"))
print("metadata:")
print(json.dumps(meta, indent=2, ensure_ascii=False))
engine_raw = f.read()
dst.write_bytes(engine_raw)
print()
print("raw engine saved:", dst)
print("raw engine size:", len(engine_raw), "bytes")
PY
然后:
trtexec \
--loadEngine=yolo26n_raw.engine \
--warmUp=1000 \
--duration=10
理论上也应该正常。
但对于我们的正式部署,我还是更推荐:
重新使用
yolo26n.onnx → trtexec → raw.engine
因为这样三个 engine 的构建过程完全一致,论文和算力测试也更容易复现。
你日志里的另外两个 warning 不用和这次 segfault 混在一起
这个:
Detected subnormal FP16 values
84 weights affected
3 weights converted...
是 TensorRT FP16 权重转换警告,不是本次 segfault 的直接原因。后面我们通过实际检测结果验证 FP16 精度即可。
还有:
Total Activation Memory: 65928623104
虽然看起来像 65 GB,很吓人,但后面最终 block assignment 已经重新得到:
Total Activation Memory: 16384512
也就是约:
15.6 MiB
所以前面的 65 GB 不是最终 engine 正常运行时要占 65 GB。
现在我建议你这样做
先执行:
ls -lh yolo26n.onnx
如果有,直接:
/usr/src/tensorrt/bin/trtexec \
--onnx=yolo26n.onnx \
--saveEngine=yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine \
--fp16 \
--workspace=4096
成功后再:
/usr/src/tensorrt/bin/trtexec \
--loadEngine=yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine \
--warmUp=1000 \
--duration=10 \
--dumpLayerInfo
2.4 转换命令
因为我们有yolo26n.onnx,所以我们直接将yolo26n.onnx转engine
/usr/src/tensorrt/bin/trtexec \
--onnx=yolo26n.onnx \
--saveEngine=yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine \
--fp16 \
--workspace=4096
成功后再:
/usr/src/tensorrt/bin/trtexec \
--loadEngine=yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine \
--warmUp=1000 \
--duration=10 \
--dumpLayerInfo
输出:
[08/21/2026-13:43:38] [I] === Performance summary ===
[08/21/2026-13:43:38] [I] Throughput: 293.909 qps
[08/21/2026-13:43:38] [I] Latency: min = 3.22021 ms, max = 8.69531 ms, mean = 3.71338 ms, median = 3.37793 ms, percentile(90%) = 4.44995 ms, percentile(95%) = 4.86377 ms, percentile(99%) = 6.18457 ms
[08/21/2026-13:43:38] [I] Enqueue Time: min = 1.35449 ms, max = 2.66516 ms, mean = 1.52601 ms, median = 1.48218 ms, percentile(90%) = 1.67432 ms, percentile(95%) = 1.80725 ms, percentile(99%) = 2.16516 ms
[08/21/2026-13:43:38] [I] H2D Latency: min = 0.155518 ms, max = 0.362549 ms, mean = 0.307552 ms, median = 0.310303 ms, percentile(90%) = 0.318359 ms, percentile(95%) = 0.32373 ms, percentile(99%) = 0.338379 ms
[08/21/2026-13:43:38] [I] GPU Compute Time: min = 3.01318 ms, max = 8.34326 ms, mean = 3.40007 ms, median = 3.06152 ms, percentile(90%) = 4.13403 ms, percentile(95%) = 4.5603 ms, percentile(99%) = 5.86938 ms
[08/21/2026-13:43:38] [I] D2H Latency: min = 0.00195312 ms, max = 0.0107422 ms, mean = 0.00575025 ms, median = 0.00585938 ms, percentile(90%) = 0.00683594 ms, percentile(95%) = 0.00683594 ms, percentile(99%) = 0.00732422 ms
[08/21/2026-13:43:38] [I] Total Host Walltime: 10.0099 s
[08/21/2026-13:43:38] [I] Total GPU Compute Time: 10.003 s
[08/21/2026-13:43:38] [W] * GPU compute time is unstable, with coefficient of variance = 18.3988%.
[08/21/2026-13:43:38] [W] If not already in use, locking GPU clock frequency or adding --useSpinWait may improve the stability.
[08/21/2026-13:43:38] [I] Explanations of the performance metrics are printed in the verbose logs.
[08/21/2026-13:43:38] [I]
&&&& PASSED TensorRT.trtexec [TensorRT v8502] # /usr/src/tensorrt/bin/trtexec --loadEngine=yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine --warmUp=1000 --duration=10
yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine可以了
可以看到,yolo26n_agx-orin_jp512_trt85_fp16_640_b1_raw.engine的GPU Compute Time的mean = 3.40007 ms,也就是说平均 GPU计算时间是3.40007 ms
三、RTMpose的onnx转FP16 engine
3.1 RTM-pose的onnx模型下载和训练时配置
如果我们要转换RTM-pose也有个问题,因为我们当时训练PoseC3D的数据使用了RTM-pose进行姿态估计,所以实际上我们也要把当时训练选择的RTM-pose模型和配置找出来,转换前要清楚模型名称和输入输出尺寸。
当时我们用于和 posec3d_hardpos_best.pth 配套评测的模型是:
RTMPose-m
Halpe26 / 26 keypoints
输入:256×192(H×W)
SimCC
模型:
rtmpose-m_simcc-body7_pt-body7-halpe26_700e-256x192-4d3e73dd_20230605
RTM_INPUT_W, RTM_INPUT_H = 192, 256
默认模型路径也是:
rtmpose_m_halpe26_official
...
rtmpose-m_simcc-body7_pt-body7-halpe26_700e-256x192-4d3e73dd_20230605
/end2end.onnx
而且代码最后只取了前17个关键点(本来共26个关键点):
return kpts[:17], scores[:17]
所以现在不用重新从 RTMPose .pth 导 ONNX,因为当时用的就是官方现成 end2end.onnx,所以部署RTM-pose的路线是直接把官方的RTMPose的onnx转engine:
官方 RTMPose-m Halpe26 end2end.onnx
↓
Orin 上 TensorRT 8.5
↓
FP16 engine
模型下载地址:https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/onnx_sdk/rtmpose-m_simcc-body7_pt-body7-halpe26_700e-256x192-4d3e73dd_20230605.zip?utm_source=chatgpt.com
下载之后是一个压缩包,解压之后找到里面的end2end.onnx,复制到我们自己的路径,并且重命名为:rtmpose-m_halpe26_256x192.onnx
3.2 查看rtmpose-m_halpe26_256x192.onnx的输入输出尺寸
python - <<'PY'
import onnx
path = "rtmpose-m_halpe26_256x192.onnx"
m = onnx.load(path)
def shape(x):
ret = []
for d in x.type.tensor_type.shape.dim:
if d.dim_value:
ret.append(d.dim_value)
elif d.dim_param:
ret.append(d.dim_param)
else:
ret.append("?")
return ret
print("=== INPUTS ===")
for x in m.graph.input:
print(x.name, shape(x))
print("\n=== OUTPUTS ===")
for x in m.graph.output:
print(x.name, shape(x))
PY
预期输出:
预计输出会类似:
INPUTS
input [1, 3, 256, 192]
OUTPUTS
simcc_x [1, 26, 384]
simcc_y [1, 26, 512]
输出的 384/512 不是猜的,原来的代码就是按:
if d == RTM_INPUT_W * 2: # 192*2 = 384
simcc_x = out
elif d == RTM_INPUT_H * 2: # 256*2 = 512
simcc_y = out
然后:
xs = argmax(simcc_x) / 2
ys = argmax(simcc_y) / 2
进行 SimCC 解码的。
实际输出:
=== INPUTS ===
input ['batch', 3, 256, 192]
=== OUTPUTS ===
simcc_x ['batch', 'MatMulsimcc_x_dim_1', 384]
simcc_y ['batch', 'MatMulsimcc_x_dim_1', 512]
3.2 转换命令
如果上面检查发现输入是固定:
[1,3,256,192]
转换命令:
/usr/src/tensorrt/bin/trtexec \
--onnx=rtmpose-m_halpe26_256x192.onnx \
--saveEngine=rtmpose-m_halpe26_agx-orin_jp512_trt85_fp16_256x192_b1.engine \
--fp16 \
--minShapes=input:1x3x256x192 \
--optShapes=input:1x3x256x192 \
--maxShapes=input:1x3x256x192 \
--workspace=4096
注意:
转换过程如果一直打印以下内容:
08/21/2026-11:37:37] [W] [TRT] Unknown embedded device detected. Using 59660MiB as the allocation cap for memory on embedded devices.
这不是问题,等待即可。原因如下:
现在是 Jetson AGX Orin + JetPack 5.1.2 / TensorRT 8.5,这个组合上 NVIDIA
官方明确确认过:Unknown embedded device detected. Using 59660MiB as the allocation
cap…属于 TensorRT 8.5 的已知 harmless warning,不会影响正常生成/运行 engine;NVIDIA 表示在后续
TensorRT 8.6 / JetPack 6 中修复了这个提示。所以单独看到:
[TRT] [W] Unknown embedded device detected. Using 59660MiB as the
allocation cap for memory on embedded devices.不是显存真的占用了 59660 MiB,也不是 RTMPose ONNX 有问题。
这块 AGX Orin 64GB 的统一内存本来就接近这个量级,TensorRT 只是因为没正确识别这个 embedded device
型号,所以退化成把大约 59.7 GiB 当成 tactic 搜索时的内存 allocation cap。
3.3 测试命令
/usr/src/tensorrt/bin/trtexec \
--loadEngine=/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/rtmpose_orin/rtmpose-m_halpe26_agx-orin_jp512_trt85_fp16_256x192_b1.engine \
--shapes=input:1x3x256x192 \
--warmUp=1000 \
--duration=10
输出:
[08/21/2026-11:41:52] [I] === Performance summary ===
[08/21/2026-11:41:52] [I] Throughput: 614.046 qps
[08/21/2026-11:41:52] [I] Latency: min = 1.54492 ms, max = 3.07791 ms, mean = 1.66169 ms, median = 1.60303 ms, percentile(90%) = 1.98413 ms, percentile(95%) = 2.06592 ms, percentile(99%) = 2.75903 ms
[08/21/2026-11:41:52] [I] Enqueue Time: min = 0.670898 ms, max = 1.49017 ms, mean = 0.730328 ms, median = 0.726273 ms, percentile(90%) = 0.750488 ms, percentile(95%) = 0.762695 ms, percentile(99%) = 0.788818 ms
[08/21/2026-11:41:52] [I] H2D Latency: min = 0.0236816 ms, max = 0.050415 ms, mean = 0.0271546 ms, median = 0.0268555 ms, percentile(90%) = 0.0284424 ms, percentile(95%) = 0.0293121 ms, percentile(99%) = 0.032959 ms
[08/21/2026-11:41:52] [I] GPU Compute Time: min = 1.50903 ms, max = 3.04175 ms, mean = 1.62658 ms, median = 1.56802 ms, percentile(90%) = 1.95032 ms, percentile(95%) = 2.03058 ms, percentile(99%) = 2.72412 ms
[08/21/2026-11:41:52] [I] D2H Latency: min = 0.00463867 ms, max = 0.00976562 ms, mean = 0.00795325 ms, median = 0.00805664 ms, percentile(90%) = 0.00854492 ms, percentile(95%) = 0.00878906 ms, percentile(99%) = 0.00906372 ms
[08/21/2026-11:41:52] [I] Total Host Walltime: 3.00466 s
[08/21/2026-11:41:52] [I] Total GPU Compute Time: 3.00104 s
[08/21/2026-11:41:52] [W] * GPU compute time is unstable, with coefficient of variance = 12.1878%.
[08/21/2026-11:41:52] [W] If not already in use, locking GPU clock frequency or adding --useSpinWait may improve the stability.
[08/21/2026-11:41:52] [I] Explanations of the performance metrics are printed in the verbose logs.
[08/21/2026-11:41:52] [I]
&&&& PASSED TensorRT.trtexec [TensorRT v8502] # /usr/src/tensorrt/bin/trtexec --onnx=rtmpose-m_halpe26_256x192.onnx --saveEngine=rtmpose-m_halpe26_agx-orin_jp512_trt85_fp16_256x192_b1.engine --fp16 --minShapes=input:1x3x256x192 --optShapes=input:1x3x256x192 --maxShapes=input:1x3x256x192 --workspace=4096
RTMPose 这一步已经完成,而且结果非常漂亮。
现在状态:
RTMPose-m Halpe26
↓
官方 end2end.onnx (我们把它重命名为了rtmpose-m_halpe26_256x192.onnx)
↓
TensorRT 8.5 FP16
↓
rtmpose-m_halpe26_agx-orin_jp512_trt85_fp16_256x192_b1.engine
性能:
| 指标 | 结果 |
|---|---|
| 吞吐 | 614 FPS |
| 平均延迟 | 1.66 ms |
| GPU Compute | 1.63 ms |
| P99延迟 | 2.76 ms |
对于 Orin 来说这个结果非常好。
那个:
GPU compute time is unstable, coefficient of variance = 12.1878%
暂时不用管,原因是你没有锁 GPU 频率,trtexec 自己也提示:
locking GPU clock frequency or adding --useSpinWait
后面正式测算力时再处理。
3.4 确认 engine I/O
/usr/src/tensorrt/bin/trtexec \
--loadEngine=/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/rtmpose_orin/rtmpose-m_halpe26_agx-orin_jp512_trt85_fp16_256x192_b1.engine \
--shapes=input:1x3x256x192 \
--dumpLayerInfo \
--profilingVerbosity=detailed
最终最关键的是确认:
input
1x3x256x192
simcc_x
1x26x384
simcc_y
1x26x512
如果是这样,RTMPose engine 这一步基本就完成了。
3.5 验证 RTMPose TensorRT 精度
注:这一步当时赶时间跳过了,一般正常转换精度应该是没问题的
因为 RTMPose 是整个链路的关键,我们先确认:
PyTorch/ONNX RTMPose
≈
TensorRT RTMPose
否则后面 PoseC3D 出问题不知道是谁的问题。
写一个 RTMPose engine 单独测试脚本
目标:
输入:
一张图片
+
一个人体bbox
输出:
26关键点
confidence
然后和你原来的 ONNXRuntime 版本比较。
流程:
image
|
bbox crop
|
affine transform
|
RTMPose TensorRT engine
|
simcc_x
simcc_y
|
argmax decode
|
inverse affine
|
26 keypoints
重点比较:
左髋
右髋
左膝
右膝
左踝
右踝
因为这些直接影响脚踢。
3.5 注意事项
原代码的前后处理千万不要改
RTMPose TensorRT 只替换神经网络执行部分,前处理和后处理完全继承历史验证代码。
历史代码实际是:
person bbox
↓
bbox 扩大 ×1.25
↓
保持 192:256 比例
↓
AffineTransform
↓
192×256
↓
BGR → RGB
↓
/255
↓
ImageNet normalize
↓
NCHW
↓
RTMPose
归一化参数就是:
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
输出:
simcc_x
simcc_y
↓
argmax
↓
/2
↓
逆 affine
↓
原图坐标 Halpe26
↓
[:17]
↓
PoseC3D
这部分一个参数都不要随便动。
这是确保:
以前训练数据里的 RTMPose ≈ 现在 Orin 上 TensorRT RTMPose 的关键。
四、PoseC3D的pth转onnx
mmaction2的官方代码里面有将PoseC3D的pth转onnx的工具。就在 tools/deployment 里的导出脚本,可以直接用,它本身构造的 PoseC3D 输入就是:
[1, num_joints, num_frames, image_size, image_size]
也就是说我们要的:
[1, 17, 48, 64, 64]
我们原先的脚本默认也是 num_frames=48、image_size=64,并通过 backbone + cls_head 导出 ONNX。
4.1 转换命令
cd /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/mmaction2-main
python tools/deployment/export_onnx_posec3d.py \
/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_finetune.py \
/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_best.pth \
--num_frames 48 \
--image_size 64 \
--num_joints 17 \
--device cuda:0 \
--output_file /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_t48_64.onnx
输出:
This script serves the sole purpose of converting PoseC3D skeleton models in MMAction2 to ONNX files. Please note that attempting to convert other models using this script may not yield successful results.
Loads checkpoint by local backend from path: /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_best.pth
Successfully export the onnx file to /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_t48_64.onnx
Traceback (most recent call last):
File "tools/deployment/export_onnx_posec3d.py", line 159, in <module>
main()
File "tools/deployment/export_onnx_posec3d.py", line 148, in main
session = onnxruntime.InferenceSession(args.output_file)
File "/home/orin2/miniconda3/envs/kicking/lib/python3.8/site-packages/onnxruntime/capi/onnxruntime_inference_collection.py", line 343, in __init__
self._create_inference_session(providers, provider_options, disabled_optimizers)
File "/home/orin2/miniconda3/envs/kicking/lib/python3.8/site-packages/onnxruntime/capi/onnxruntime_inference_collection.py", line 371, in _create_inference_session
raise ValueError(
ValueError: This ORT build has ['AzureExecutionProvider', 'CPUExecutionProvider'] enabled. Since ORT 1.9, you are required to explicitly set the providers parameter when instantiating InferenceSession. For example, onnxruntime.InferenceSession(..., providers=['AzureExecutionProvider', 'CPUExecutionProvider'], ...)
4.2 报错解决方案
很好,这次真正的 ONNX 已经导出来了,问题不在导出,而是在脚本最后的自动验证阶段。
关键日志:
Successfully export the onnx file to ...
posec3d_hardpos_t48_64.onnx
说明:
.pth
↓
PyTorch模型
↓
ONNX
这一阶段已经成功。
现在失败的位置:
session = onnxruntime.InferenceSession(args.output_file)
错误:
ValueError:
This ORT build has ['AzureExecutionProvider', 'CPUExecutionProvider'] enabled.
Since ORT 1.9, you are required to explicitly set the providers parameter
这是 onnxruntime 版本兼容问题,不是模型问题。
方案1(推荐):直接修改 exporter 的验证部分
打开:
vim tools/deployment/export_onnx_posec3d.py
找到:
session = onnxruntime.InferenceSession(args.output_file)
大约第148行。
改成:
session = onnxruntime.InferenceSession(
args.output_file,
providers=['CPUExecutionProvider']
)
保存。
然后重新运行:
python tools/deployment/export_onnx_posec3d.py \
/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_finetune.py \
/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_best.pth \
--num_frames 48 \
--image_size 64 \
--num_joints 17 \
--device cuda:0 \
--output_file /media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_t48_64.onnx
方案2(其实也可以):不用重新导,直接检查现有 ONNX
因为已经生成:
posec3d_hardpos_t48_64.onnx
所以你完全可以跳过验证。
先检查 shape:
python - <<'PY'
import onnx
path="/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_t48_64.onnx"
m=onnx.load(path)
for x in m.graph.input:
print("INPUT",x.name)
print(x.type.tensor_type.shape)
for x in m.graph.output:
print("OUTPUT",x.name)
print(x.type.tensor_type.shape)
PY
重点看:
应该类似:
INPUT
input_tensor
[batch_size,17,num_frames,64,64]
OUTPUT
cls_score
[batch_size,2]
如果是这个:
说明已经解决历史最大的坑:
❌ [1,17,1,56,56]
↓
✅ [1,17,48,64,64]
五、PoseC3D的onnx转FP16 engine
5.1 确认输入输出尺寸
PoseC3D训练时的配置文件
因为转换onnx跟训练时的配置文件的设置有密切的关系,所以要先看看PoseC3D训练时的配置文件
mmaction2/configs/base/default_runtime.py
default_scope = 'mmaction'
default_hooks = dict(
runtime_info=dict(type='RuntimeInfoHook'),
timer=dict(type='IterTimerHook'),
logger=dict(type='LoggerHook', interval=20, ignore_last=False),
param_scheduler=dict(type='ParamSchedulerHook'),
checkpoint=dict(type='CheckpointHook', interval=1, save_best='auto'),
sampler_seed=dict(type='DistSamplerSeedHook'),
sync_buffers=dict(type='SyncBuffersHook'))
env_cfg = dict(
cudnn_benchmark=False,
mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
dist_cfg=dict(backend='nccl'))
log_processor = dict(type='LogProcessor', window_size=20, by_epoch=True)
vis_backends = [dict(type='LocalVisBackend')]
visualizer = dict(type='ActionVisualizer', vis_backends=vis_backends)
log_level = 'INFO'
load_from = None
resume = False
posec3d_hardpos_finetune.py
_base_ = '/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/mmaction2_official_benchmark/third_party/mmaction2/configs/_base_/default_runtime.py'
model = dict(
type='Recognizer3D',
backbone=dict(
type='ResNet3dSlowOnly',
depth=50,
pretrained=None,
in_channels=17,
base_channels=32,
num_stages=3,
out_indices=(2, ),
stage_blocks=(4, 6, 3),
conv1_stride_s=1,
pool1_stride_s=1,
inflate=(0, 1, 1),
spatial_strides=(2, 2, 2),
temporal_strides=(1, 1, 2),
dilations=(1, 1, 1)),
cls_head=dict(
type='I3DHead',
in_channels=512,
num_classes=2,
dropout_ratio=0.5,
average_clips='prob'))
dataset_type = 'PoseDataset'
ann_file = '/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/mmaction2_official_benchmark/data/kicktrace_rtmpose_hardpos.pkl'
left_kp = [1, 3, 5, 7, 9, 11, 13, 15]
right_kp = [2, 4, 6, 8, 10, 12, 14, 16]
train_pipeline = [
dict(type='UniformSampleFrames', clip_len=48),
dict(type='PoseDecode'),
dict(type='PoseCompact', hw_ratio=1., allow_imgpad=True),
dict(type='Resize', scale=(-1, 64)),
dict(type='RandomResizedCrop', area_range=(0.56, 1.0)),
dict(type='Resize', scale=(56, 56), keep_ratio=False),
dict(type='Flip', flip_ratio=0.5, left_kp=left_kp, right_kp=right_kp),
dict(
type='GeneratePoseTarget',
sigma=0.6,
use_score=True,
with_kp=True,
with_limb=False),
dict(type='FormatShape', input_format='NCTHW_Heatmap'),
dict(type='PackActionInputs')
]
val_pipeline = [
dict(type='UniformSampleFrames', clip_len=48, num_clips=1, test_mode=True),
dict(type='PoseDecode'),
dict(type='PoseCompact', hw_ratio=1., allow_imgpad=True),
dict(type='Resize', scale=(-1, 64)),
dict(type='CenterCrop', crop_size=64),
dict(
type='GeneratePoseTarget',
sigma=0.6,
use_score=True,
with_kp=True,
with_limb=False),
dict(type='FormatShape', input_format='NCTHW_Heatmap'),
dict(type='PackActionInputs')
]
test_pipeline = [
dict(
type='UniformSampleFrames', clip_len=48, num_clips=10, test_mode=True),
dict(type='PoseDecode'),
dict(type='PoseCompact', hw_ratio=1., allow_imgpad=True),
dict(type='Resize', scale=(-1, 64)),
dict(type='CenterCrop', crop_size=64),
dict(
type='GeneratePoseTarget',
sigma=0.6,
use_score=True,
with_kp=True,
with_limb=False,
double=True,
left_kp=left_kp,
right_kp=right_kp),
dict(type='FormatShape', input_format='NCTHW_Heatmap'),
dict(type='PackActionInputs')
]
train_dataloader = dict(
batch_size=8,
num_workers=4,
persistent_workers=True,
sampler=dict(type='DefaultSampler', shuffle=True),
dataset=dict(
type='RepeatDataset',
times=8,
dataset=dict(
type=dataset_type,
ann_file=ann_file,
split='xsub_train',
pipeline=train_pipeline)))
val_dataloader = dict(
batch_size=8,
num_workers=4,
persistent_workers=True,
sampler=dict(type='DefaultSampler', shuffle=False),
dataset=dict(
type=dataset_type,
ann_file=ann_file,
split='xsub_val',
pipeline=val_pipeline,
test_mode=True))
test_dataloader = dict(
batch_size=1,
num_workers=4,
persistent_workers=True,
sampler=dict(type='DefaultSampler', shuffle=False),
dataset=dict(
type=dataset_type,
ann_file=ann_file,
split='xsub_val',
pipeline=test_pipeline,
test_mode=True))
val_evaluator = [dict(type='AccMetric')]
test_evaluator = val_evaluator
train_cfg = dict(
type='EpochBasedTrainLoop', max_epochs=15, val_begin=1, val_interval=1)
val_cfg = dict(type='ValLoop')
test_cfg = dict(type='TestLoop')
param_scheduler = [
dict(
type='CosineAnnealingLR',
eta_min=0,
T_max=40,
by_epoch=True,
convert_to_iter_based=True)
]
optim_wrapper = dict(
optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0003),
clip_grad=dict(max_norm=40, norm_type=2))
这里的关键是:
dict(type=‘UniformSampleFrames’, clip_len=48)
…
dict(
type=‘GeneratePoseTarget’,
sigma=0.6,
use_score=True,
with_kp=True,
with_limb=False
),
dict(type=‘FormatShape’, input_format=‘NCTHW_Heatmap’)
clip_len = 48
NCTHW_Heatmap
因此 PoseC3D 输入中的 T 应该是 48。
也就是类似:
N C T H W
1 17 48 xx xx
训练 pipeline 是:
Resize(-1, 64)
RandomResizedCrop(…)
Resize((56, 56))
所以训练阶段用了 56×56。
但是验证 pipeline 是:
UniformSampleFrames(clip_len=48, num_clips=1)
PoseDecode
PoseCompact
Resize(-1, 64)
CenterCrop(crop_size=64)
GeneratePoseTarget(…)
FormatShape(NCTHW_Heatmap)
而我们真正实时部署时更应该模拟的是 val pipeline,因为:
train pipeline
有随机裁剪/随机翻转
→ 用来做数据增强
val pipeline
单 clip
确定性 CenterCrop
→ 更适合实时推理
因此,导出的 TensorRT 输入应该是:
(1, 17, 48, 64, 64)
而不是:
(1, 17, 1, 56, 56)
推荐的最终输入
input_tensor
shape = (1, 17, 48, 64, 64)
dtype = float32
输出:
cls_score
shape = (1, 2)
TensorRT 内部仍然可以 FP16。
我们最终要做的是
① 正确导出 PoseC3D
[1,17,48,64,64]
↓
② 把 实时推理的代码 的 PoseC3D 前处理
完全改成训练配置对应的方式
PoseCompact
↓
Resize
↓
CenterCrop 64
↓
GeneratePoseTarget sigma=0.6
↓
[1,17,48,64,64]
↓
③ TensorRT PoseC3D
↓
④ 再跑 视频推理 和真值比较
最终链路固定为:
YOLO26n Detect TensorRT
│ 只检测 person bbox,不再算关键点
↓
ByteTrack + Stable ID
↓
RTMPose TensorRT
│ 每人每帧只推理 1 次
↓
Halpe26 → 前17关键点
↓
高召回 Peak Trigger
↓
48帧骨骼窗口
│ 短时缺帧插值
↓
PoseC3D TensorRT
[1,17,48,64,64]
│ 每个候选只推理 1 次
↓
KICK / NO_KICK
确认onnx的输入输出尺寸
``bash
(kicking) orin2@orin2-desktop:/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/mmaction2-main$ python - <<‘PY’
import onnx
path=“/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_t48_64.onnx”
m=onnx.load(path)
for x in m.graph.input:
print(“INPUT”,x.name)
print(x.type.tensor_type.shape)for x in m.graph.output:
print(“OUTPUT”,x.name)
print(x.type.tensor_type.shape)
PY
**输出:**
```bash
INPUT input_tensor
dim {
dim_param: "batch_size"
}
dim {
dim_value: 17
}
dim {
dim_param: "num_frames"
}
dim {
dim_value: 64
}
dim {
dim_value: 64
}
OUTPUT cls_score
dim {
dim_param: "batch_size"
}
dim {
dim_value: 2
}
5.1 转换命令
转之前,建议先检查 ONNX 输入名字。
因为 exporter 写的是:
input_names=['input_tensor']
output_names=['cls_score']
所以 TensorRT 的转换命令:
/usr/src/tensorrt/bin/trtexec \
--onnx=/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_t48_64.onnx \
--saveEngine=/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_agx-orin_jp512_trt85_fp16_t48_64_b1.engine \
--fp16 \
--minShapes=input_tensor:1x17x48x64x64 \
--optShapes=input_tensor:1x17x48x64x64 \
--maxShapes=input_tensor:1x17x48x64x64 \
--workspace=4096
但是转之前建议做一个小检查
因为这个模型是:
ResNet3dSlowOnly + I3DHead
你需要确认 TensorRT 支持所有算子。
如果失败,常见的是:
Resize3D
AdaptiveAvgPool3D
不过你的 exporter 已经处理了:
AdaptiveAvgPool3d
它专门替换成:
AvgPool3d()
避免 ONNX/TensorRT 不支持。
所以成功概率很高。
5.2 测试命令
/usr/src/tensorrt/bin/trtexec \
--loadEngine=/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_fp16.engine \
--duration=10 \
--warmUp=1000
输出:
[08/21/2026-13:03:26] [I] === Performance summary ===
[08/21/2026-13:03:26] [I] Throughput: 150.773 qps
[08/21/2026-13:03:26] [I] Latency: min = 6.03198 ms, max = 12.7129 ms, mean = 7.05929 ms, median = 6.69043 ms, percentile(90%) = 8.7749 ms, percentile(95%) = 9.00476 ms, percentile(99%) = 9.85522 ms
[08/21/2026-13:03:26] [I] Enqueue Time: min = 0.289978 ms, max = 0.979614 ms, mean = 0.575282 ms, median = 0.675598 ms, percentile(90%) = 0.788086 ms, percentile(95%) = 0.823181 ms, percentile(99%) = 0.922729 ms
[08/21/2026-13:03:26] [I] H2D Latency: min = 0.402466 ms, max = 0.511353 ms, mean = 0.43221 ms, median = 0.426086 ms, percentile(90%) = 0.46405 ms, percentile(95%) = 0.475525 ms, percentile(99%) = 0.48584 ms
[08/21/2026-13:03:26] [I] GPU Compute Time: min = 5.60767 ms, max = 12.3062 ms, mean = 6.61961 ms, median = 6.2244 ms, percentile(90%) = 8.32764 ms, percentile(95%) = 8.5752 ms, percentile(99%) = 9.4126 ms
[08/21/2026-13:03:26] [I] D2H Latency: min = 0.00170898 ms, max = 0.0102539 ms, mean = 0.00746995 ms, median = 0.00805664 ms, percentile(90%) = 0.00952148 ms, percentile(95%) = 0.00976562 ms, percentile(99%) = 0.0100098 ms
[08/21/2026-13:03:26] [I] Total Host Walltime: 3.02441 s
[08/21/2026-13:03:26] [I] Total GPU Compute Time: 3.01854 s
[08/21/2026-13:03:26] [W] * GPU compute time is unstable, with coefficient of variance = 16.8995%.
[08/21/2026-13:03:26] [W] If not already in use, locking GPU clock frequency or adding --useSpinWait may improve the stability.
[08/21/2026-13:03:26] [I] Explanations of the performance metrics are printed in the verbose logs.
[08/21/2026-13:03:26] [I]
&&&& PASSED TensorRT.trtexec [TensorRT v8502] # /usr/src/tensorrt/bin/trtexec --onnx=/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_t48_64.onnx --saveEngine=/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_agx-orin_jp512_trt85_fp16_t48_64_b1.engine --fp16 --minShapes=input_tensor:1x17x48x64x64 --optShapes=input_tensor:1x17x48x64x64 --maxShapes=input_tensor:1x17x48x64x64 --workspace=4096
先分析 PoseC3D 性能
结果:
Throughput: 150.773 qps
Latency:
mean = 7.059 ms
GPU Compute:
mean = 6.619 ms
换算:
1 / 0.007059 ≈ 141.7 FPS
所以单独 PoseC3D:
大约 140 FPS 级别
这对于 Orin 非常充裕。
和 RTMPose 对比
目前两个模型:
| 模型 | 输入 | GPU Compute |
|---|---|---|
| RTMPose-m | 1×3×256×192 | 1.63 ms |
| PoseC3D | 1×17×48×64×64 | 6.62 ms |
总:
RTMPose
+
PoseC3D
≈
8.25 ms
也就是:
≈121 FPS
注意:
这只是两个 TensorRT 网络串联推理时间。
还没包括:
- detector
- ByteTrack
- bbox crop
- affine transform
- SimCC decode
- GeneratePoseTarget
- OpenCV
但是网络部分非常健康。
5.3 检查 PoseC3D engine 输入输出
/usr/src/tensorrt/bin/trtexec \
--loadEngine=/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_agx-orin_jp512_trt85_fp16_t48_64_b1.engine \
--dumpLayerInfo
预期应该看到:
input_tensor
1x17x48x64x64
cls_score
1x2
实际输出:
[08/21/2026-13:05:38] [I] === Performance summary ===
[08/21/2026-13:05:38] [I] Throughput: 165.833 qps
[08/21/2026-13:05:38] [I] Latency: min = 6.03638 ms, max = 9.78223 ms, mean = 6.45345 ms, median = 6.08176 ms, percentile(90%) = 7.57346 ms, percentile(95%) = 7.64911 ms, percentile(99%) = 8.19598 ms
[08/21/2026-13:05:38] [I] Enqueue Time: min = 0.292236 ms, max = 0.832275 ms, mean = 0.527159 ms, median = 0.432556 ms, percentile(90%) = 0.751465 ms, percentile(95%) = 0.768066 ms, percentile(99%) = 0.79248 ms
[08/21/2026-13:05:38] [I] H2D Latency: min = 0.4039 ms, max = 0.49353 ms, mean = 0.429407 ms, median = 0.422852 ms, percentile(90%) = 0.466797 ms, percentile(95%) = 0.478271 ms, percentile(99%) = 0.484863 ms
[08/21/2026-13:05:38] [I] GPU Compute Time: min = 5.61084 ms, max = 9.30151 ms, mean = 6.01791 ms, median = 5.65186 ms, percentile(90%) = 7.12866 ms, percentile(95%) = 7.20264 ms, percentile(99%) = 7.76685 ms
[08/21/2026-13:05:38] [I] D2H Latency: min = 0.00195312 ms, max = 0.00842285 ms, mean = 0.00613266 ms, median = 0.00634766 ms, percentile(90%) = 0.00744629 ms, percentile(95%) = 0.00762939 ms, percentile(99%) = 0.00805664 ms
[08/21/2026-13:05:38] [I] Total Host Walltime: 3.02111 s
[08/21/2026-13:05:38] [I] Total GPU Compute Time: 3.01497 s
[08/21/2026-13:05:38] [W] * GPU compute time is unstable, with coefficient of variance = 10.8864%.
[08/21/2026-13:05:38] [W] If not already in use, locking GPU clock frequency or adding --useSpinWait may improve the stability.
[08/21/2026-13:05:38] [I] Explanations of the performance metrics are printed in the verbose logs.
[08/21/2026-13:05:38] [I]
&&&& PASSED TensorRT.trtexec [TensorRT v8502] # /usr/src/tensorrt/bin/trtexec --loadEngine=/media/orin2/45fd09e1-b992-4c00-9a54-232c1fb0e672/kicking/posec3d_orin/posec3d_hardpos_agx-orin_jp512_trt85_fp16_t48_64_b1.engine --dumpLayerInfo
很好,这个结果比刚才那个更稳定,说明第二次 trtexec --dumpLayerInfo 测试时,TensorRT 已经进入比较稳定状态。
现在正式记录 PoseC3D TensorRT FP16 基准性能:
Engine:
posec3d_hardpos_agx-orin_jp512_trt85_fp16_t48_64_b1.engine
输入:
1 × 17 × 48 × 64 × 64
输出:
1 × 2
性能结果
端到端 TensorRT latency
| 指标 | 结果 |
|---|---|
| 平均 Latency | 6.45 ms |
| Median | 6.08 ms |
| P90 | 7.57 ms |
| P95 | 7.65 ms |
| P99 | 8.20 ms |
换算:
1 / 0.00645 ≈ 155 FPS
所以:
PoseC3D 单模型 TensorRT FP16 推理约 155 FPS。
GPU Kernel 时间(更重要)
GPU Compute Time
mean = 6.02 ms
这个才是真正 GPU 计算时间。
因此论文/性能分析里面建议写:
PoseC3D 在 Jetson AGX Orin 平台 TensorRT FP16 加速后,单次推理平均 GPU 计算耗时约 6.02 ms。
和 RTMPose 对比
现在两个模型数据:
| 模块 | 输入 | GPU Compute |
|---|---|---|
| RTMPose-m Halpe26 | 1×3×256×192 | 1.63 ms |
| PoseC3D | 1×17×48×64×64 | 6.02 ms |
如果一个人:
RTMPose
+
PoseC3D
=1.63+6.02
≈7.65 ms
理论:
≈130 FPS
当然实际还要加:
- detector
- tracking
- crop
- affine
- SimCC decode
- heatmap生成
- 数据缓存
但是模型本身已经不是瓶颈。
5.4 PyTorch vs TensorRT 输出一致性验证
这个非常重要。
现在我们需要证明:
posec3d_hardpos_best.pth
↓
PyTorch
↓
概率
≈
TensorRT engine
↓
概率
否则以后出现:
PyTorch:
KICK 0.98
TensorRT:
NO_KICK 0.45
我们不知道哪里错。
验证方法:
准备一个固定输入:
shape:
1,17,48,64,64
随机或者真实样本都可以。
比较:
cls_score
误差。
正常:
max abs error < 1e-3
FP16:
<1e-2
也可以接受。
5.5 当前进度
现在两个核心网络已经都完成正式部署:
RTMPose-m Halpe26
ONNX
↓
TensorRT FP16
↓
rtmpose-m_halpe26_agx-orin_jp512_trt85_fp16_256x192_b1.engine
平均 GPU Compute:
1.63 ms
以及:
PoseC3D hardpos
.pth
↓
ONNX
↓
TensorRT FP16
↓
posec3d_hardpos_agx-orin_jp512_trt85_fp16_t48_64_b1.engine
平均 GPU Compute:
6.62 ms
这一步非常关键,因为之前最大的历史问题:
错误:
[1,17,1,56,56]
现在:
[1,17,48,64,64]
已经彻底解决。
已经完成:
| 模块 | 状态 |
|---|---|
| YOLO Detect TensorRT | ✅ |
| RTMPose-m Halpe26 ONNX | ✅ |
| RTMPose TensorRT FP16 | ✅ |
| PoseC3D .pth→ONNX | ✅ |
| PoseC3D T=48 TensorRT FP16 | ✅ |
| 三模型实时融合 | ❌ |
| 算力 profiling | ❌ |

279

被折叠的 条评论
为什么被折叠?



