一、项目概述

1.1 核心目标

  • 使用C++与TensorRT部署YOLOv11目标检测模型
  • 实现高性能的图片和视频推理系统
  • 提供完整的端到端解决方案

1.2 技术栈

  • 推理框架:TensorRT 8.x+
  • 计算机视觉库:OpenCV 4.x
  • CUDA版本:11.x+
  • cuDNN版本:8.x+
  • 编程语言:C++17

二、TensorRT部署解决方案

2.1 部署流程架构

原始模型(ONNX/PyTorch) → 模型转换 → TensorRT优化 → C++推理部署

2.2 关键步骤详解

步骤1:模型准备与转换
# Python端模型导出示例
import torch
from yolov11.models import YOLOv11

# 加载预训练模型
model = YOLOv11(weights='yolov11.pt')
model.eval()

# 转换为ONNX格式
dummy_input = torch.randn(1, 3, 640, 640)
torch.onnx.export(
    model, 
    dummy_input, 
    "yolov11.onnx",
    opset_version=11,
    input_names=['images'],
    output_names=['output']
)
步骤2:TensorRT引擎生成
// C++端引擎生成
bool buildEngineFromOnnx(const std::string& onnxPath, 
                        const std::string& enginePath,
                        int batchSize = 1) {
    // 创建Builder
    auto builder = std::unique_ptr<nvinfer1::IBuilder>(
        nvinfer1::createInferBuilder(gLogger));
    
    // 创建网络定义
    const auto explicitBatch = 1U << static_cast<uint32_t>(
        nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);
    auto network = std::unique_ptr<nvinfer1::INetworkDefinition>(
        builder->createNetworkV2(explicitBatch));
    
    // 创建ONNX解析器
    auto parser = std::unique_ptr<nvonnxparser::IParser>(
        nvonnxparser::createParser(*network, gLogger));
    parser->parseFromFile(onnxPath.c_str(), 
                         static_cast<int>(nvinfer1::ILogger::Severity::kWARNING));
    
    // 配置Builder
    auto config = std::unique_ptr<nvinfer1::IBuilderConfig>(
        builder->createBuilderConfig());
    config->setMaxWorkspaceSize(1 << 30);  // 1GB
    
    // 设置精度模式(可调整)
    if (builder->platformHasFastFp16()) {
        config->setFlag(nvinfer1::BuilderFlag::kFP16);
    }
    
    // 构建引擎
    auto engine = std::unique_ptr<nvinfer1::IHostMemory>(
        builder->buildSerializedNetwork(*network, *config));
    
    // 保存引擎文件
    std::ofstream engineFile(enginePath, std::ios::binary);
    engineFile.write(static_cast<const char*>(engine->data()), engine->size());
    
    return true;
}

2.3 性能优化策略

2.3.1 精度优化
// 混合精度配置
config->setFlag(nvinfer1::BuilderFlag::kFP16);
config->setFlag(nvinfer1::BuilderFlag::kTF32);

// INT8量化(需校准数据集)
config->setFlag(nvinfer1::BuilderFlag::kINT8);
config->setInt8Calibrator(calibrator); // 自定义校准器
2.3.2 内存优化
// 动态形状支持(批处理优化)
auto profile = builder->createOptimizationProfile();
profile->setDimensions("input", 
    nvinfer1::OptProfileSelector::kMIN, 
    nvinfer1::Dims4{1, 3, 640, 640});
profile->setDimensions("input", 
    nvinfer1::OptProfileSelector::kOPT, 
    nvinfer1::Dims4{4, 3, 640, 640});
profile->setDimensions("input", 
    nvinfer1::OptProfileSelector::kMAX, 
    nvinfer1::Dims4{8, 3, 640, 640});
config->addOptimizationProfile(profile);

三、核心代码模块详解

3.1 推理引擎类设计

class YOLOv11Detector {
public:
    // 初始化
    YOLOv11Detector(const std::string& enginePath, 
                   float confThreshold = 0.5,
                   float nmsThreshold = 0.4);
    
    // 推理接口
    std::vector<Detection> detect(const cv::Mat& image);
    std::vector<std::vector<Detection>> detectBatch(
        const std::vector<cv::Mat>& images);
    
    // 辅助功能
    void setConfidenceThreshold(float threshold);
    void setNMSThreshold(float threshold);
    void enableProfiling(bool enable);
    
private:
    // TensorRT组件
    nvinfer1::IRuntime* runtime_;
    nvinfer1::ICudaEngine* engine_;
    nvinfer1::IExecutionContext* context_;
    
    // 缓冲区管理
    std::vector<void*> deviceBuffers_;
    std::vector<void*> hostBuffers_;
    
    // 参数配置
    float confThreshold_;
    float nmsThreshold_;
    int inputWidth_;
    int inputHeight_;
    int batchSize_;
    
    // 私有方法
    void preprocess(const cv::Mat& image, float* blob);
    void postprocess(float* output, 
                    std::vector<Detection>& detections,
                    int imgWidth, int imgHeight);
    void allocateBuffers();
    void freeBuffers();
};

3.2 预处理优化

void YOLOv11Detector::preprocess(const cv::Mat& image, float* blob) {
    // 使用CUDA加速的预处理
    cv::Mat resized;
    cv::resize(image, resized, cv::Size(inputWidth_, inputHeight_));
    
    // 转换为浮点并归一化
    cv::Mat floatImg;
    resized.convertTo(floatImg, CV_32FC3, 1.0 / 255.0);
    
    // BGR到RGB转换
    cv::cvtColor(floatImg, floatImg, cv::COLOR_BGR2RGB);
    
    // 使用CUDA内存拷贝和通道分离
    std::vector<cv::Mat> channels(3);
    cv::split(floatImg, channels);
    
    // 填充到blob(NHWC到NCHW转换)
    size_t channelSize = inputWidth_ * inputHeight_;
    for (int c = 0; c < 3; ++c) {
        memcpy(blob + c * channelSize, 
               channels[c].data, 
               channelSize * sizeof(float));
    }
}

3.3 后处理优化

void YOLOv11Detector::postprocess(float* output, 
                                 std::vector<Detection>& detections,
                                 int imgWidth, int imgHeight) {
    // 解析YOLOv11输出格式
    // 假设输出形状为 [batch, num_anchors, 85]
    // 85 = 4(bbox) + 1(conf) + 80(classes)
    
    int numAnchors = engine_->getBindingDimensions(1).d[1];
    const float* ptr = output;
    
    for (int i = 0; i < numAnchors; ++i) {
        float conf = ptr[4];
        if (conf < confThreshold_) {
            ptr += 85;
            continue;
        }
        
        // 找到最大类别得分
        float* classScores = const_cast<float*>(ptr + 5);
        int classId = std::max_element(classScores, classScores + 80) - classScores;
        float maxClassScore = classScores[classId];
        float totalScore = conf * maxClassScore;
        
        if (totalScore < confThreshold_) {
            ptr += 85;
            continue;
        }
        
        // 解码边界框
        float centerX = ptr[0] * imgWidth;
        float centerY = ptr[1] * imgHeight;
        float width = ptr[2] * imgWidth;
        float height = ptr[3] * imgHeight;
        
        cv::Rect box(
            static_cast<int>(centerX - width / 2),
            static_cast<int>(centerY - height / 2),
            static_cast<int>(width),
            static_cast<int>(height)
        );
        
        detections.emplace_back(Detection{
            classId, totalScore, box
        });
        
        ptr += 85;
    }
    
    // 应用NMS
    applyNMS(detections);
}

3.4 NMS优化实现

void YOLOv11Detector::applyNMS(std::vector<Detection>& detections) {
    std::sort(detections.begin(), detections.end(),
        [](const Detection& a, const Detection& b) {
            return a.confidence > b.confidence;
        });
    
    std::vector<Detection> filtered;
    std::vector<bool> suppressed(detections.size(), false);
    
    for (size_t i = 0; i < detections.size(); ++i) {
        if (suppressed[i]) continue;
        filtered.push_back(detections[i]);
        
        for (size_t j = i + 1; j < detections.size(); ++j) {
            if (suppressed[j]) continue;
            
            // 计算IoU
            cv::Rect intersection = detections[i].box & detections[j].box;
            float intersectionArea = intersection.area();
            float unionArea = detections[i].box.area() + 
                            detections[j].box.area() - 
                            intersectionArea;
            float iou = intersectionArea / unionArea;
            
            if (iou > nmsThreshold_) {
                suppressed[j] = true;
            }
        }
    }
    
    detections = std::move(filtered);
}

四、业务场景应用

4.1 安防监控系统

class SecurityMonitor {
private:
    YOLOv11Detector detector_;
    cv::VideoCapture cap_;
    std::mutex mtx_;
    bool running_;
    
public:
    SecurityMonitor(const std::string& enginePath, 
                   const std::string& rtspUrl)
        : detector_(enginePath), running_(false) {
        cap_.open(rtspUrl);
    }
    
    void start() {
        running_ = true;
        std::thread processThread(&SecurityMonitor::processStream, this);
        processThread.detach();
    }
    
    void stop() { running_ = false; }
    
private:
    void processStream() {
        cv::Mat frame;
        int frameCount = 0;
        
        while (running_) {
            {
                std::lock_guard<std::mutex> lock(mtx_);
                cap_ >> frame;
            }
            
            if (frame.empty()) {
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
                continue;
            }
            
            // 目标检测
            auto detections = detector_.detect(frame);
            
            // 告警逻辑
            checkAlarms(detections);
            
            // 日志记录
            logDetections(detections, frameCount++);
            
            // 显示(可选)
            drawDetections(frame, detections);
        }
    }
    
    void checkAlarms(const std::vector<Detection>& detections) {
        // 检测特定类别(如人、车辆)
        for (const auto& det : detections) {
            if (det.classId == 0) {  // person
                if (det.box.area() > 10000) {  // 面积阈值
                    triggerAlarm("Large person detected");
                }
            }
        }
    }
};

4.2 工业质检系统

class QualityInspector {
public:
    struct Defect {
        int type;           // 缺陷类型
        cv::Rect location;  // 缺陷位置
        float confidence;   // 置信度
        cv::Mat roi;        // 缺陷区域图像
    };
    
    QualityInspector(const std::string& enginePath) 
        : detector_(enginePath) {}
    
    std::vector<Defect> inspect(const cv::Mat& productImage) {
        // 1. 目标检测
        auto detections = detector_.detect(productImage);
        
        std::vector<Defect> defects;
        for (const auto& det : detections) {
            if (det.classId >= 80) {  // 自定义缺陷类别
                Defect defect;
                defect.type = det.classId - 80;
                defect.location = det.box;
                defect.confidence = det.confidence;
                defect.roi = productImage(det.box).clone();
                
                defects.push_back(defect);
                
                // 额外分析
                analyzeDefect(defect);
            }
        }
        
        // 2. 生成质检报告
        generateReport(defects);
        
        return defects;
    }
    
private:
    YOLOv11Detector detector_;
    
    void analyzeDefect(Defect& defect) {
        // 使用OpenCV进行进一步分析
        cv::Mat gray;
        cv::cvtColor(defect.roi, gray, cv::COLOR_BGR2GRAY);
        
        // 计算缺陷特征
        cv::Scalar mean, stddev;
        cv::meanStdDev(gray, mean, stddev);
        
        defect.severity = stddev[0];  // 使用标准差作为严重程度指标
    }
};

4.3 自动驾驶感知模块

class PerceptionModule {
public:
    struct TrafficObject {
        int classId;        // 0:person, 1:bicycle, 2:car, ...
        cv::Rect2f bbox;    // 归一化坐标 [0,1]
        float confidence;
        float speed;        // 估计速度
        cv::Point2f direction;  // 运动方向
    };
    
    PerceptionModule(const std::string& enginePath)
        : detector_(enginePath) {}
    
    std::vector<TrafficObject> processFrame(const cv::Mat& frame,
                                           const cv::Mat& prevFrame,
                                           float timestamp) {
        // 目标检测
        auto detections = detector_.detect(frame);
        
        std::vector<TrafficObject> objects;
        for (const auto& det : detections) {
            TrafficObject obj;
            obj.classId = det.classId;
            obj.confidence = det.confidence;
            
            // 转换为归一化坐标
            obj.bbox = cv::Rect2f(
                det.box.x / static_cast<float>(frame.cols),
                det.box.y / static_cast<float>(frame.rows),
                det.box.width / static_cast<float>(frame.cols),
                det.box.height / static_cast<float>(frame.rows)
            );
            
            // 跟踪与速度估计
            if (!prevFrame.empty()) {
                estimateMotion(obj, frame, prevFrame);
            }
            
            objects.push_back(obj);
        }
        
        // 多目标跟踪
        trackObjects(objects, timestamp);
        
        return objects;
    }
    
private:
    YOLOv11Detector detector_;
    std::map<int, TrafficObject> trackedObjects_;  // 跟踪ID到对象的映射
    
    void estimateMotion(TrafficObject& obj, 
                       const cv::Mat& currFrame,
                       const cv::Mat& prevFrame) {
        // 使用光流法估计运动
        cv::Mat currGray, prevGray;
        cv::cvtColor(currFrame, currGray, cv::COLOR_BGR2GRAY);
        cv::cvtColor(prevFrame, prevGray, cv::COLOR_BGR2GRAY);
        
        // 在目标区域内计算光流
        cv::Rect roi = cv::Rect(
            static_cast<int>(obj.bbox.x * currFrame.cols),
            static_cast<int>(obj.bbox.y * currFrame.rows),
            static_cast<int>(obj.bbox.width * currFrame.cols),
            static_cast<int>(obj.bbox.height * currFrame.rows)
        );
        
        // ... 光流计算逻辑
    }
};

五、性能优化技巧

5.1 异步推理流水线

class AsyncInferencePipeline {
private:
    struct FrameData {
        cv::Mat frame;
        int64_t frameId;
        cudaEvent_t gpuEvent;
    };
    
    YOLOv11Detector detector_;
    std::queue<FrameData> inputQueue_;
    std::queue<std::pair<int64_t, std::vector<Detection>>> outputQueue_;
    std::mutex inputMutex_, outputMutex_;
    std::condition_variable inputCV_, outputCV_;
    std::vector<std::thread> workerThreads_;
    bool stop_;
    
public:
    AsyncInferencePipeline(const std::string& enginePath, int numWorkers = 2)
        : detector_(enginePath), stop_(false) {
        // 创建工作线程
        for (int i = 0; i < numWorkers; ++i) {
            workerThreads_.emplace_back(
                &AsyncInferencePipeline::workerThread, this);
        }
    }
    
    ~AsyncInferencePipeline() {
        stop_ = true;
        inputCV_.notify_all();
        for (auto& thread : workerThreads_) {
            if (thread.joinable()) thread.join();
        }
    }
    
    void submitFrame(const cv::Mat& frame, int64_t frameId) {
        std::lock_guard<std::mutex> lock(inputMutex_);
        FrameData data{frame.clone(), frameId};
        cudaEventCreate(&data.gpuEvent);
        inputQueue_.push(std::move(data));
        inputCV_.notify_one();
    }
    
    bool getResult(int64_t& frameId, std::vector<Detection>& detections) {
        std::unique_lock<std::mutex> lock(outputMutex_);
        outputCV_.wait(lock, [this]() { 
            return !outputQueue_.empty() || stop_; 
        });
        
        if (outputQueue_.empty()) return false;
        
        auto result = std::move(outputQueue_.front());
        outputQueue_.pop();
        
        frameId = result.first;
        detections = std::move(result.second);
        return true;
    }
    
private:
    void workerThread() {
        while (!stop_) {
            FrameData data;
            
            {
                std::unique_lock<std::mutex> lock(inputMutex_);
                inputCV_.wait(lock, [this]() { 
                    return !inputQueue_.empty() || stop_; 
                });
                
                if (stop_) break;
                
                data = std::move(inputQueue_.front());
                inputQueue_.pop();
            }
            
            // 执行推理
            auto detections = detector_.detect(data.frame);
            
            // 记录GPU时间
            cudaEventRecord(data.gpuEvent);
            cudaEventSynchronize(data.gpuEvent);
            
            // 推送结果
            {
                std::lock_guard<std::mutex> lock(outputMutex_);
                outputQueue_.emplace(data.frameId, std::move(detections));
                outputCV_.notify_one();
            }
            
            cudaEventDestroy(data.gpuEvent);
        }
    }
};

5.2 批处理优化

class BatchProcessor {
public:
    BatchProcessor(const std::string& enginePath, int maxBatchSize = 8)
        : detector_(enginePath), maxBatchSize_(maxBatchSize) {}
    
    std::vector<std::vector<Detection>> processBatch(
        const std::vector<cv::Mat>& images) {
        
        // 动态批处理
        std::vector<std::vector<Detection>> allDetections;
        
        for (size_t i = 0; i < images.size(); i += maxBatchSize_) {
            size_t end = std::min(i + maxBatchSize_, images.size());
            std::vector<cv::Mat> batch(images.begin() + i, images.begin() + end);
            
            // 填充不足的批次
            while (batch.size() < maxBatchSize_) {
                batch.push_back(cv::Mat::zeros(
                    images[0].size(), images[0].type()));
            }
            
            auto batchDetections = detector_.detectBatch(batch);
            
            // 只取有效结果
            allDetections.insert(allDetections.end(),
                batchDetections.begin(),
                batchDetections.begin() + (end - i));
        }
        
        return allDetections;
    }
    
private:
    YOLOv11Detector detector_;
    int maxBatchSize_;
};

六、部署与运维

6.1 Docker容器化部署

# Dockerfile
FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    git \
    libopencv-dev \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

# 安装TensorRT
ARG TENSORRT_VERSION=8.6.1
COPY TensorRT-${TENSORRT_VERSION}.Linux.x86_64-gnu.cuda-11.8.tar.gz /tmp/
RUN cd /tmp && \
    tar -xzf TensorRT-${TENSORRT_VERSION}.Linux.x86_64-gnu.cuda-11.8.tar.gz && \
    cd TensorRT-${TENSORRT_VERSION} && \
    cp -r lib/* /usr/lib/x86_64-linux-gnu/ && \
    cp -r include/* /usr/include/ && \
    cp -r bin/* /usr/bin/ && \
    cp -r python/* /usr/lib/python3.10/dist-packages/ && \
    rm -rf /tmp/TensorRT*

# 构建应用
WORKDIR /app
COPY . .
RUN mkdir build && cd build && \
    cmake .. -DCMAKE_BUILD_TYPE=Release && \
    make -j$(nproc)

# 运行
CMD ["./build/yolov11_inference"]

6.2 性能监控

class PerformanceMonitor {
private:
    struct Metrics {
        float fps;
        float inferenceTime;
        float preprocessTime;
        float postprocessTime;
        size_t memoryUsage;
        int numDetections;
    };
    
    std::deque<Metrics> history_;
    size_t maxHistorySize_;
    std::chrono::high_resolution_clock::time_point lastUpdate_;
    
public:
    PerformanceMonitor(size_t historySize = 100) 
        : maxHistorySize_(historySize) {}
    
    void recordInference(float inferenceTime, 
                        float preprocessTime,
                        float postprocessTime,
                        int numDetections) {
        
        Metrics metrics;
        metrics.inferenceTime = inferenceTime;
        metrics.preprocessTime = preprocessTime;
        metrics.postprocessTime = postprocessTime;
        metrics.numDetections = numDetections;
        
        // 计算FPS
        auto now = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
            now - lastUpdate_).count();
        metrics.fps = (duration > 0) ? 1000.0f / duration : 0;
        lastUpdate_ = now;
        
        // 内存使用
        metrics.memoryUsage = getGPUMemoryUsage();
        
        // 保存历史
        history_.push_back(metrics);
        if (history_.size() > maxHistorySize_) {
            history_.pop_front();
        }
    }
    
    Metrics getAverageMetrics() const {
        if (history_.empty()) return {};
        
        Metrics avg{};
        for (const auto& m : history_) {
            avg.fps += m.fps;
            avg.inferenceTime += m.inferenceTime;
            avg.preprocessTime += m.preprocessTime;
            avg.postprocessTime += m.postprocessTime;
            avg.numDetections += m.numDetections;
            avg.memoryUsage += m.memoryUsage;
        }
        
        float size = static_cast<float>(history_.size());
        avg.fps /= size;
        avg.inferenceTime /= size;
        avg.preprocessTime /= size;
        avg.postprocessTime /= size;
        avg.numDetections /= static_cast<int>(size);
        avg.memoryUsage /= size;
        
        return avg;
    }
    
    void printReport() const {
        auto avg = getAverageMetrics();
        std::cout << "=== 性能报告 ===" << std::endl;
        std::cout << "平均FPS: " << avg.fps << std::endl;
        std::cout << "推理时间: " << avg.inferenceTime << "ms" << std::endl;
        std::cout << "预处理时间: " << avg.preprocessTime << "ms" << std::endl;
        std::cout << "后处理时间: " << avg.postprocessTime << "ms" << std::endl;
        std::cout << "平均检测数量: " << avg.numDetections << std::endl;
        std::cout << "GPU内存使用: " << avg.memoryUsage / 1024 / 1024 << "MB" << std::endl;
    }
    
private:
    size_t getGPUMemoryUsage() const {
        size_t free, total;
        cudaMemGetInfo(&free, &total);
        return total - free;
    }
};

七、项目总结与最佳实践

7.1 成功关键因素

  1. 模型优化充分

    • 使用TensorRT的FP16/INT8量化
    • 实现动态形状支持
    • 优化预处理/后处理流水线
  2. 内存管理严谨

    • 使用智能指针管理TensorRT对象
    • 实现GPU内存池
    • 避免内存泄漏和碎片化
  3. 错误处理完善

    • 检查所有CUDA API返回值
    • 实现异常安全设计
    • 提供详细的日志信息

7.2 性能指标参考

优化级别FPS(1080p)内存占用延迟
FP32原始25-302.5GB40ms
FP16优化45-501.5GB22ms
INT8量化60-701.0GB15ms
批处理(4)80-902.0GB12ms

7.3 推荐配置

# config.yaml
model:
  engine_path: "models/yolov11_fp16.engine"
  input_size: [640, 640]
  confidence_threshold: 0.5
  nms_threshold: 0.4

performance:
  use_fp16: true
  max_batch_size: 4
  enable_async: true
  num_worker_threads: 2

deployment:
  gpu_id: 0
  max_memory_usage: 2048  # MB
  enable_profiling: false

monitoring:
  log_level: "INFO"
  save_detections: true
  output_dir: "results/"

7.4 常见问题解决方案

  1. 内存不足

    // 解决方案:实现内存池
    class GPUMemoryPool {
    public:
        void* allocate(size_t size) {
            // 复用现有内存块
            auto it = std::find_if(pool_.begin(), pool_.end(),
                [size](const MemBlock& block) {
                    return block.size >= size && !block.used;
                });
            
            if (it != pool_.end()) {
                it->used = true;
                return it->ptr;
            }
            
            // 分配新内存
            void* ptr;
            cudaMalloc(&ptr, size);
            pool_.push_back({ptr, size, true});
            return ptr;
        }
    };
    
  2. 推理速度慢

    // 解决方案:流水线优化
    - 使用CUDA流实现异步传输
    - 重叠计算和数据传输
    - 批处理推理
    
  3. 精度下降

    // 解决方案:校准优化
    - 使用代表性校准数据集
    - 选择合适的校准方法
    - 验证量化后精度
    

这个项目展示了如何使用TensorRT在C++环境中高效部署YOLOv11模型,涵盖了从模型转换到生产部署的全流程,并针对不同业务场景提供了优化方案。

更多推荐