Jetpack 4.5.1及以前的版本4.x里使用的是TensorRT 7.1.3,JetPack 4.6开始则是使用的TensorRT 8,由于我们在模型加速上多是建立在TensorRT的基础上的,而TensorRT 8没有完全保证对TensorRT 7.x的兼容,所以受TensorRT这个版本的变化对封装调用模型的部分代码是有影响的,在Jetpack 4.5 + Deepstream 5开发的程序涉及到AI模型的部分在Jetpack 4.6 + Deepstream 6下很可能编译都会出错,更不要说能跑起来。
近期在Jetson Nano上对我们基于Jetpack 4.5 + Deepstream 5开发的程序做了针对Jetpack 4.6 + Deepstream 6的migration实验,发现受影响最大的自然是nvinfer plugin调用的我们自己的目标检测模型的部分以及nvtracker plugin部分。
1. nvinfer plugin调用我们定制模型的部分有些改动:
1) 用来创建engine的接口函数增加了nvinfer1::IBuilderConfig * const builderConfig这个参数,以前Deepstream 5.0中的接口是类似这样的
extern "C"
bool NvDsInferYoloCudaEngineGet(nvinfer1::IBuilder * const builder,
const NvDsInferContextInitParams * const initParams,
nvinfer1::DataType dataType,
nvinfer1::ICudaEngine *& cudaEngine);
Deepstream 6.0中则是类似这样:
extern "C"
bool NvDsInferYoloCudaEngineGet(nvinfer1::IBuilder * const builder,
nvinfer1::IBuilderConfig * const builderConfig,
const NvDsInferContextInitParams * const initParams,
nvinfer1::DataType dataType,
nvinfer1::ICudaEngine *& cudaEngine);
可能是有人提了意见,增加了这个buildConfig参数后确实便于统一从外部在创建build的地方配置好config后传入,而不是在创建engine函数的内部再写硬代码设置。这个参数变化问题有点坑人,因为这个创建engine的函数是动态调用的:
std::unique_ptr<TrtEngine>
TrtModelBuilder::buildModel(const NvDsInferContextInitParams& initParams,
std::string& suggestedPathName)
{
if (m_DlLib && !string_empty(initParams.customEngineCreateFuncName))
{
cudaEngineGetFcn = m_DlLib->symbol<NvDsInferEngineCreateCustomFunc>(
initParams.customEngineCreateFuncName);
...
engine = getCudaEngineFromCustomLib (cudaEngineGetDeprecatedFcn,
cudaEngineGetFcn, initParams, networkMode);
如果使用原来DeepStream5下的参数,没有buildConfig参数的话,这个调用本身检查不出来参数的不匹配因而不会报错,到创建engine时才会出错,报的错也不直接,费了时间发现出错是因为initParams里的customNetworkConfigFilePath之类的值都成了乱码(例如: initParams->customNetworkConfigFilePath的值打出来是2EEEEEvNS_12LaunchParamsEN8nvinfer121VirtualMachineProgramE),导致找不到模型的配置文件和权重文件,但是initParams的数据怎么被搞乱了也费了点时间最后才追踪到原因是DeepStream创建engine的接口函数的参数发生了变化,const NvDsInferContextInitParams * const initParams,的前面增加了nvinfer1::IBuilderConfig * const builderConfig这个参数,导致使用Deepstream5版的老接口函数总是得不到正确的initParams数据,里面全是乱七八糟的值,把builderConfig参数加上后一切正常了。
2) Deepstream6里IPluginV2IOExt、IPluginV2Ext、IPluginV2、ILogger这些接口或类的定义有点变化,需要在涉及到这些类的继承实现的地方把成员函数的声明和实现的地方的override都改成noexcept (生命的地方还可以写成noexcept override),否则编译报错。另外老版本的
virtual int enqueue(int batchSize, const void*const * inputs, void** outputs, void* workspace, cudaStream_t stream) override;
需要改成
virtual int32_t enqueue(int32_t batchSize, void const* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept;
不该会报下面的类似这些错误:
yololayer.h(54): warning: function "nvinfer1::IPluginV2::enqueue(int32_t, const void *const *, void *const *, void *, cudaStream_t)" is hidden by "nvinfer1::YoloLayerPlugin::enqueue" -- virtual function override intended?
yololayer.cu(156): error: object of abstract class type "nvinfer1::YoloLayerPlugin" is not allowed:
pure virtual function "nvinfer1::IPluginV2::enqueue" has no overrider
yololayer.cu(299): error: object of abstract class type "nvinfer1::YoloLayerPlugin" is not allowed:
pure virtual function "nvinfer1::IPluginV2::enqueue" has no overrider
yololayer.cu(308): error: object of abstract class type "nvinfer1::YoloLayerPlugin" is not allowed:
pure virtual function "nvinfer1::IPluginV2::enqueue" has no overrider
2. nvtracker plugin这部分变化挺大的,NVIDIA把所有的跟踪算法实现代码都编译到了/opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so这个so文件里,而不是像Deepstream5那样iou、mot_klt、nvdcf分成三个so,并且Deepstream6的nvtracker plugin新增了DeepSORT+ReId这种跟踪算法实现,去掉了mot_klt,也就是支持iou、nvdcf、DeepSORT+ReId三种跟踪算法实现,切换时通过给nvtracker设置ll-config-file指定不同的配置文件即可(相关配置文件在/opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/下,可以拷贝到自己的程序下对参数做调整修改),而不是Deepstream5那样通过给nvtracker element设置ll-lib-file不同的值指向不同的so库来切换,Deepstream6下需要给ll-lib-file设置为nvmultiobjecttracker或者你自己实现的跟踪算法库的名字(算法库的名字当然得遵从libnvds_xxx.so这个命名规则)。
另外, nvtracker algorithm library指定要求实现的这些API有变化:
1) NvMOTStatus NvMOT_Query(uint16_t customConfigFilePathSize, char* pCustomConfigFilePath, NvMOTQuery *pQuery)里的NvMOTQuery *pQuery的定义有变化,所以需要把原来的
pQuery->supportBatchProcessing = true;
改成类似这样:
pQuery-> batchMode = NvMOTBatchMode_Batch;
2) void NvMOT_RemoveStreams(NvMOTContextHandle contextHandle, NvMOTStreamId streamIdMask); 改成
NvMOTStatus NvMOT_RemoveStreams(NvMOTContextHandle contextHandle, NvMOTStreamId streamIdMask);
然后对程序做全编译,将Jetpack4.5下生成的模型的engine删掉,在Jetpack4.6里重新生成engine文件,然后就可以在Jetson Nano上跑起来了!