摘要
在工业现场,昂贵的工控机(x86)往往不是最优解。边缘计算设备(如 RK3588, Jetson Orin/Nano)凭借低功耗、小体积和专用 NPU/GPU,正成为视觉检测的新宠。

但痛点也随之而来:

  • 架构差异:x86 (x64) 代码无法直接在 ARM64 上运行。
  • 推理引擎:ONNX Runtime CPU 太慢,必须调用 NPU/GPU。
  • 部署复杂:交叉编译、依赖库缺失、C# 与 C++ SDK 的互操作性。

本文提供一套完整的 C#+YOLO 边缘部署全链路方案

  1. 模型转换:如何将 PyTorch 模型优化为适配 RKNN/TensorRT 的格式。
  2. 架构选型
    • 方案 A (纯 C#):使用 .NET 6/8 + ONNX Runtime (适合 Jetson GPU)。
    • 方案 B (混合架构):C# UI + C++ 推理服务 (gRPC/Socket) (适合 RK3588 NPU,性能最强)。
  3. 实战部署:RK3588 (RKNN) 与 Jetson (TensorRT) 的具体配置步骤。
  4. 性能对比:实测 FPS 与延迟数据。

一、核心挑战与解决方案

挑战 传统 x86 方案 边缘计算 (ARM64) 方案 解决策略
CPU 架构 x64 ARM64 (aarch64) 使用 .NET 6/8 跨平台特性,发布时指定 -r linux-arm64
推理加速 CUDA / OpenVINO NPU (RKNN) / GPU (TensorRT) 弃用通用 ONNX Runtime,改用厂商专用推理库 (通过 P/Invoke 或 gRPC)
操作系统 Windows 10/11 Ubuntu (Linux) 代码需移除 Windows 特有 API (如 Registry, GDI+),使用跨平台库
内存限制 16GB+ 4GB - 16GB 启用模型量化 (INT8),使用对象池,严格管理内存

二、模型准备与转换 (关键第一步)

在部署前,必须在 PC 上将 PyTorch (.pt) 模型转换为边缘设备专用的格式。

1. 通用步骤:导出 ONNX

无论目标设备是什么,先导出标准 ONNX 模型。

# YOLOv8 示例
yolo export model=yolov8s.pt format=onnx opset=12 simplify=True dynamic=False

2. 针对 RK3588 (Rockchip NPU) -> 转换为 .rknn

Rockchip NPU 不支持直接跑 ONNX,必须使用 rknn-toolkit2 转换并量化。

  • 环境:PC (Ubuntu 20.04) 或 Docker。
  • 工具rknn-toolkit2 (Python)。
  • 流程
    1. 加载 ONNX。
    2. 量化校准:传入 20-50 张现场图片进行 INT8 校准(至关重要,否则精度暴跌)。
    3. 构建模型。
    4. 导出 .rknn 文件。
# convert_rknn.py 简略逻辑
from rknn.api import RKNN

rknn = RKNN()
rknn.config(target_platform='rk3588', quantization=True, do_quantization=True, dataset='./calib_dataset.txt')
rknn.load_onnx(model='yolov8s.onnx')
rknn.build(do_quantization=True, dataset='./calib_dataset.txt')
rknn.export_rknn('yolov8s.rknn')

3. 针对 Jetson (NVIDIA GPU) -> 转换为 .engine (TensorRT)

Jetson 虽然能跑 ONNX,但为了极致性能,必须转为 TensorRT Engine。

  • 环境:直接在 Jetson 设备上操作(因为 TRT 引擎与硬件绑定)。
  • 工具trtexectorch2trt
  • 命令
    trtexec --onnx=yolov8s.onnx --saveEngine=yolov8s.engine --fp16 --workspace=2048
    
    注:Jetson Nano 可能只支持 FP32,Orin/Xavier 推荐 FP16。

三、部署方案选型

方案 A:纯 C# 直连 (适合 Jetson + TensorRT/ONNX)

利用 NVIDIA 提供的 TensorRT C# 绑定或直接调用 libnvinfer.so

  • 优点:架构简单,单进程,调试方便。
  • 缺点:RK3588 的 C# 绑定不完善;C# 调用底层 C++ 库需要大量 P/Invoke 代码。
  • 适用:Jetson 系列,且团队 C# 功底深厚。

方案 B:C# UI + C++ 推理微服务 (推荐 ⭐⭐⭐⭐⭐)

  • 架构
    • C++ Service:运行在边缘设备上,加载 .rknn.engine,暴露 gRPC 或 ZeroMQ 接口。负责最底层的硬件加速。
    • C# Client:运行在同一设备(或局域网另一台),负责 UI、业务逻辑、PLC 通信。通过 IPC 发送图像字节流,接收检测结果。
  • 优点
    • 性能最大化:C++ 直接调用厂商 SDK,无中间层损耗。
    • 解耦:算法升级只需替换 C++ 服务,不影响 C# 主程序。
    • 生态兼容:完美利用 Rockchip/NVIDIA 官方提供的 C++ Demo 代码。
  • 缺点:增加了进程间通信 (IPC) 的复杂度。

下文将以【方案 B】为主轴,详解 RK3588 和 Jetson 的落地流程。


四、实战:RK3588 部署 (C# + C++ gRPC)

1. C++ 推理服务 (Server)

基于 Rockchip 官方 rknn_yolov8_demo 修改,封装为 gRPC 服务。

  • Proto 定义 (detection.proto):
    service DetectionService {
      rpc Detect (ImageRequest) returns (DetectionResponse);
    }
    message ImageRequest {
      bytes image_data = 1; // JPEG 或 RGB 裸数据
      int32 width = 2;
      int32 height = 3;
    }
    message DetectionResponse {
      repeated Box boxes = 1;
      double latency_ms = 2;
    }
    
  • 核心逻辑:
    1. 初始化 rknn_api,加载 .rknn 模型。
    2. 接收 gRPC 请求,解码图像。
    3. 调用 rknn_run 进行推理 (INT8)。
    4. 后处理 (NMS),返回坐标。
  • 编译: 在 RK3588 上使用交叉编译工具链或本地编译。
    mkdir build && cd build
    cmake .. -DCMAKE_BUILD_TYPE=Release
    make -j8
    ./detection_server # 监听端口 50051
    

2. C# 客户端 (Client)

在 .NET 8 (Linux ARM64) 中调用 gRPC。

  • 项目配置 (*.csproj):
    <PropertyGroup>
      <TargetFramework>net8.0</TargetFramework>
      <RuntimeIdentifier>linux-arm64</RuntimeIdentifier>
      <SelfContained>true</SelfContained>
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="Grpc.Net.Client" Version="2.60.0" />
      <PackageReference Include="Google.Protobuf" Version="3.25.0" />
      <PackageReference Include="Grpc.Tools" Version="2.60.0">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      </PackageReference>
    </ItemGroup>
    
  • 调用代码:
    using var channel = GrpcChannel.ForAddress("http://localhost:50051");
    var client = new DetectionService.DetectionServiceClient(channel);
    
    public async Task<List<Detection>> DetectAsync(Mat image)
    {
        // 编码图像 (JPEG 压缩以减少传输开销)
        var buffer = new List<byte>();
        Cv2.ImEncode(".jpg", image, buffer, new ImageEncodingParam(ImwriteFlags.JpegQuality, 80));
        
        var request = new ImageRequest
        {
            ImageData = ByteString.CopyFrom(buffer.ToArray()),
            Width = image.Cols,
            Height = image.Rows
        };
    
        var response = await client.DetectAsync(request);
        
        // 解析结果
        return response.Boxes.Select(b => new Detection 
        { 
            X = b.X, Y = b.Y, Width = b.W, Height = b.H, Confidence = b.Conf 
        }).ToList();
    }
    

3. 性能表现 (RK3588)

  • 模型: YOLOv8s (INT8)
  • 分辨率: 640x640
  • FPS: 45-55 FPS (NPU 满载)
  • 延迟: ~20ms (含通信)
  • 功耗: < 5W (仅 NPU 部分)

五、实战:Jetson Orin/Nano 部署 (C# + TensorRT)

对于 Jetson,由于 NVIDIA 对 C# 支持较好,也可以尝试纯 C# 方案 (使用 NVIDIA TensorRT C# BindingEmgu.CV 的 TRT 支持),但为了稳定性,依然推荐 C++ 服务化

1. C++ 服务 (TensorRT)

基于 tensorrt_samples 中的 sampleOnnxMNIST 或 YOLO 示例修改。

  • 加载 .engine 文件。
  • 使用 CUDA Stream 异步推理。
  • 同样封装 gRPC 或共享内存 (Shared Memory) 接口。
    • 进阶技巧:使用 POSIX Shared Memory 传递图像数据,避免 gRPC 序列化拷贝,零拷贝传输,延迟可降低 2-3ms。

2. C# 客户端

同上,只需改变连接地址。如果是共享内存方案,C# 端需调用 System.Runtime.InteropServices 映射内存区域。

3. 性能表现 (Jetson Orin NX)

  • 模型: YOLOv8m (FP16)
  • 分辨率: 640x640
  • FPS: 80+ FPS
  • 延迟: ~12ms

六、部署流水线与运维

1. 交叉编译与发布

不要试图在 Windows 上编译 Linux ARM 程序。

  • 方法 A (Docker 交叉编译):
    使用 mcr.microsoft.com/dotnet/sdk:8.0 镜像,安装 gcc-aarch64-linux-gnu,在 CI/CD 流水线中构建。
    dotnet publish -c Release -r linux-arm64 --self-contained true -o ./publish
    
  • 方法 B (原生编译):
    直接在 RK3588/Jetson 上安装 .NET SDK,拉取代码编译。
    wget https://dot.net/v1/dotnet-install.sh
    chmod +x dotnet-install.sh
    ./dotnet-install.sh -channel 8.0
    export DOTNET_ROOT=$HOME/.dotnet
    export PATH=$PATH:$DOTNET_ROOT
    dotnet publish ...
    

2. 系统服务化 (Systemd)

将 C++ 服务和 C# 程序都注册为 Linux 服务,实现开机自启、崩溃重启。

  • 创建服务文件 (/etc/systemd/system/yolo-vision.service):
    [Unit]
    Description=YOLO Vision System (C# + C++ Backend)
    After=network.target
    
    [Service]
    Type=simple
    # 先启动 C++ 推理服务
    ExecStartPre=/opt/vision/detection_server
    # 再启动 C# 主程序
    ExecStart=/opt/vision/VisionApp
    WorkingDirectory=/opt/vision
    Restart=always
    User=vision
    Environment=DOTNET_ENVIRONMENT=Production
    
    [Install]
    WantedBy=multi-user.target
    
  • 启用:
    sudo systemctl enable yolo-vision
    sudo systemctl start yolo-vision
    

3. 监控与日志

  • 使用 journalctl -u yolo-vision -f 查看实时日志。
  • C# 内部集成 Serilog 写入文件,C++ 服务使用 spdlog
  • 编写简单的 Shell 脚本监控 FPS,若低于阈值自动重启服务。

七、总结与建议

维度 建议
硬件选型 高算力需求选 Jetson Orin;低成本、低功耗选 RK3588
开发模式 强烈推荐 C# (UI/Logic) + C++ (Inference) 模式。这是平衡开发效率与运行性能的最佳实践。
通信方式 同机部署首选 gRPC (开发快) 或 Shared Memory (极致低延迟)。
模型优化 RK3588 必须 INT8 量化,否则 NPU 优势发挥不出来;Jetson 推荐 FP16
部署工具 熟练使用 Docker 构建环境,使用 Systemd 管理进程。

通过这套方案,你可以将原本运行在厚重工控机上的视觉系统,缩小到信用卡大小的开发板上,成本降低 70%,功耗降低 90%,同时保持工业级的稳定性和实时性。这就是 C#+YOLO 在边缘计算时代的终极形态。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐