1. 仿真概述
1.1 什么仿真?
注意:这里的系统是个抽象的概念,可以是软件,也可以是硬件,还可以是复杂的综合事件。在J6开发平台中,仿真的对象为“开发板及其上的系统”。
1.2 为什么要仿真?
仿真在不同行业不同背景下的的目的是不同的,这些目的包括但不限于:
设计组装自动控制系统。
评价目标系统性能和功能。
为目标系统设计扩展功能和组件。
在J6开发平台中,仿真系统可以为我们提供下列便捷:
缓解开发工作对开发板硬件的依赖
2. 避免多人共享同一物理开发板时带来的环境冲突问题
物理开发板属于临界资源,其上的系统版本在某一时刻是确定而且是唯一的。如果不同人在开发时所依赖的系统版本不同,那么得不到系统版本满足的开发人员便无法进行开发工作。而仿真环境是通对通过docker image的版本加以区分的,其独立且可以通过DOCKER容器构建多个相同或不同的仿真环境,如此一来,不同开发者对系统版本的依赖便可得到解决。
3. 数据回灌仿真
4. 为代码调试提供便利
在开发环境中同时存在了业务源代码、编译的目标程序以及X86平台调试工具gdb。可以对业务代码逐行进行裆部调试,加速功能bug的排查。
1.3 仿真系统的优缺点
性能一致性
2. 功能一致性
因为仿真系统和被仿真系统在计算架构、操作系统等方面存在差异,同样的数据输入在经过处理后得到的输出结果可能存在些许差异,但差距一般很小。
3. 开发投入
因为业务代码的最终运行环境(arm架构)和 仿真环境间的差异,需要在开发阶段构建两套编译脚本: X86平台编译脚本 For 仿真; arm平台编译脚本 For 最终的板端部署。
4. 系统特性约束
仿真的本质是X86平台上“模仿”出接口以实现在ARM平台上的功能。在X86仿真平台下无法使用ARM平台下特有的功能和指令,如Neon。因此,功能仿真过后在最后的实车部署阶段,还是需要依赖工程的深度优化(Neon 加速等),还会涉及对齐等工作。
2. 仿真在J6计算平台的实现
2.1 仿真框架概述
在J6开发平台中,仿真功能是通过地平线统一异构计算框架HUCP(Horizon Unified Computing Platform)来实现的。相对于J5 的 DNN 预测库,HUCP支持自定义算子(Plugin)并 新增了数学计算库(FFT、BLAS等)、CV 库等的封装, 进行了功能和边界的扩展以提供计算图全图(视频通路 + 前/后处理 + 多模型串街 + 自定义计算)表达的能力,支持将全图一起送入下游编译。
注:为支持全图表达能力,在Torch 开发环境新增 FLAP 和 LEAP 两套自定义计算组件,支持通过 DSL、Numba、Triton 等算法友好的 Python 编程方式,添加模型前/中/后的自定义计算,包括:
在 Pyramid、GDC 等硬件 IP 功能上拆分封装出的各种 OP(LEAP)
CPU/ VPU 上实现的自定义计算(前/后处理,自定义算子等)(FLAP)
2.2 仿真实现原理

上图 是J6开发平台感知部署框架的构成,可以看出HUCP介于应用程序和运行平台之间,是应用程序和运行平台之间的“中间件”。对上,HUCP为上层应用开发设计和提供了一套统一的应用程序编程接口(API);对下,隐蔽不同CPU架构在接口实现上带来的差异,不同架构的运算平台调用各自的系统接口对HUCP提供的编程接口进行实现和编译以生成不同架构的动态链接库供编译器连接时调用。
3. 仿真哪些东西
仿真功能依赖于统一异构计算框架HUCP,而HUCP所提供的应用程序编程接口在实现时不仅调用了BPU、CPU、DSP等常规硬件资源,同时也包含对Pyramid、GDC、Stitch、Codec 等视频通路上的硬件 IP(不包含非计算的 camera 接入部分)资源的利用。在 J6 SoC 上这些硬件是真实存在的,在X86平台下,通过接口的x86平台实现对这些硬件进行了仿真,从而透明化了不同平台间接口调用的差异。对于应用程序开发者而言,在面向接口编程的思想指导下仅需在编译阶段选择对应平台的编译工具和编译库即可。
4. 功能如何使用
4.1 基础使用环境准备
仿真功能作为算法工具链发布套件的一部分,其所依赖的环境已经集成在工具链发布(v3.0.12之后)的docker镜像中,仿真功能基础示例(horizon_j6_open_explorer_xxx-py38_20240430/samples/ucp_tutorial/dnn/basic_samples/code/00_quick_start)也包含在工具链每次发版的OE包中。因此跟使用AI工具链其他功能一样,在开始使用仿真功能之前,需要先参考工具链使用手册load算法工具链的docker镜像,然后根据镜像构建映射了OE包的用户容器。 用户容器启动后便可参考基础示例体验仿真功能了.
4.2 业务代码编写
注:如果想在基于ARM架构的开发板平台上通过ARM平台下特有的功能和指令(如Neon)加速业务处理过程,同时基于仿真调试业务逻辑,可通过编译宏区别平台分别进行编程,由此带来的工作量和两个环境下数据对齐问题需要用户自己评估。
4.3 编译脚本构成
仿真平台和被仿真平台的CPU架构和指令集是不同的,所以说使用的编译器和运行时库也会存在差异。业务代码构建完成后,进行编译时,需要针对不同的目标运行平台选择正确的编译器和运行时库,下下述为X86仿真平台和ARM目标开发板平台在部署工程实现时依赖库和脚本间的差异(来自OE包基础实例程序horizon_j6_open_explorer_xxx-py38_20240430/samples/ucp_tutorial/dnn/basic_samples/code/00_quick_start)。
- 依赖库差异

- SHELL编译脚本差异

- CMakeLists.txt脚本中ARM&X86差异

5. 仿真案例
5.1 快速入门

5.2 扩展工程案例
OE包原ai_benchmark示例为方便向开发者提供参考模型后处理逻辑,构建了插件式“数据处理+模型推理+推理结果处理与展示”业务流框架。为方便开发者使用仿真功能进行自有模型的后处理代码调试,最大限度复用已有的插件式雨雾流程框架,我们对原有的ai_benchmark工程进行了部分修改并添加了仿真编译脚本,开发者可以参考扩展工程中fcos的后处理代码逻辑(类继承关系)构建适配自己模型的后处理文件并在配置中加以引用。因扩展工程中编译脚本是自动进行新增文件索引和编译的,用户只需要将自己扩展的后处理代码文件保存至与fcos的后处理代码文件统计的method目录(注意头文件和源文件分别存放)下进行编译即可,无需进行他们脚本代码的开发。
扩展工程相对于OE包中原ai_benchmark示例代码有如下差异:
保留了原ai_benchmark示例代码中所有的插件式框架代码,包括推理数据准备、模型推理代码以及与后处理、输出四者间的调度逻辑。
删除了源代码中大部分模型的后处理逻辑,仅保留了fcos的部分以作示例。
添加仿真编译脚本并对原有的CMakeLists.txt进行变动同时对测试运行脚本env.sh微调以适应仿真场景需要。
用户可以参考fcos的后处理逻辑可针对自己的模型构建后处理进行功能仿真验证。
6. 其他说明
6.1 与J5仿真对比



