丹青识画系统在Keil5开发环境下的交叉编译与部署测试

📅 发布时间:2026/7/5 22:42:40 👁️ 浏览次数:
丹青识画系统在Keil5开发环境下的交叉编译与部署测试
丹青识画系统在Keil5开发环境下的交叉编译与部署测试最近在做一个智能家居的嵌入式项目需要在Cortex-M4内核的MCU上跑一个简单的图像识别功能。客户要求能识别一些简单的家居物品比如水杯、遥控器、书本之类的。一开始想用传统的图像处理算法但发现场景稍微一变效果就大打折扣维护起来也头疼。后来了解到“丹青识画”这个轻量级的AI推理引擎据说专门为资源受限的嵌入式设备优化过。这听起来挺对路的但问题来了它的官方示例大多是在Linux上用GCC编译而我们的硬件开发环境是Keil MDK5这是ARM开发最常用的IDE之一。怎么把这两者打通让AI模型能在Keil工程里跑起来成了我必须解决的实际问题。折腾了几天总算把整套流程跑通了。从在x86电脑上交叉编译出ARM库到集成进Keil工程再到用J-Link把程序烧录到板子上实际测试中间踩了不少坑。这篇文章我就把这次“打通任督二脉”的过程记录下来重点不是讲高深的AI理论而是聚焦在如何一步步完成交叉编译和工程集成这个具体的工程任务上。如果你也在尝试把类似的小型AI推理引擎部署到Keil环境下希望这些经验能帮你省点时间。1. 项目准备与环境搭建在开始动手编译之前我们需要把“战场”清理好把必要的工具和代码都准备好。这个过程有点像做菜前备料东西备齐了后面才能顺利进行。1.1 核心工具清单首先你得确保电脑上安装了以下软件。别担心大部分都是嵌入式开发的常用工具Keil MDK5 (uVision5)这是我们的主战场用于编写、编译和调试ARM芯片的代码。确保你已经安装并激活了对应你芯片型号的Device Family PackDFP比如STM32F4系列或NXP的Kinetis系列。CMake (3.10或更高版本)丹青识画系统使用CMake来管理构建过程。我们需要用它来生成针对ARM架构的Makefile。GNU Arm Embedded Toolchain这是关键。我们需要一套能在你电脑比如Windows或Linux上运行但能生成ARM芯片可执行代码的编译器。可以去ARM官网或芯片厂商的网站下载。我使用的是arm-none-eabi-gcc这个版本。J-Link驱动与软件如果你和我一样使用J-Link仿真器进行程序下载和调试那么需要安装Segger的J-Link驱动和配套的RTT Viewer等工具。Python 3.x一些辅助脚本和模型转换工具可能需要Python环境。1.2 获取丹青识画系统源码接下来是获取我们要部署的“主角”。通常这类轻量级引擎的源码会托管在GitHub或Gitee上。假设我们已经从官方仓库克隆了代码它的目录结构可能看起来像这样danqing_system/ ├── CMakeLists.txt ├── src/ │ ├── core/ # 核心推理引擎代码 │ ├── ops/ # 算子实现 │ └── ... ├── include/ # 头文件 ├── tools/ # 模型转换、量化等工具 ├── examples/ # 示例程序 └── third_party/ # 第三方依赖我们的目标就是把src/目录下的核心代码编译成一个静态库比如libdanqing.a然后把这个库和对应的头文件放进我们的Keil工程里。2. 配置交叉编译工具链这是整个流程中最核心的一步。我们要告诉CMake“请不要用我电脑自带的编译器请用我指定的那个ARM编译器来编译代码。”2.1 创建工具链文件为了让CMake明白我们的意图最好创建一个独立的工具链定义文件。我在丹青源码根目录下新建了一个文件命名为arm-gcc-toolchain.cmake内容如下# 指定目标系统类型 set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) # 指定交叉编译器的路径 # 这里需要替换成你电脑上 arm-none-eabi-gcc 的实际安装路径 set(TOOLCHAIN_PATH C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin) set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH}/arm-none-eabi-gcc.exe) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH}/arm-none-eabi-g.exe) # 指定编译器和链接器的通用 flags # 这些参数非常重要必须和你的目标芯片如Cortex-M4以及Keil工程里的设置匹配 set(CMAKE_C_FLAGS -mcpucortex-m4 -mthumb -mfpufpv4-sp-d16 -mfloat-abihard -ffunction-sections -fdata-sections -Og CACHE STRING C Compiler Flags) set(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS} CACHE STRING C Compiler Flags) set(CMAKE_EXE_LINKER_FLAGS -Wl,--gc-sections -T${LINKER_SCRIPT} -specsnano.specs CACHE STRING Linker Flags) # 禁止在构建目录中搜索本地依赖库 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)关键点解释-mcpucortex-m4指定目标CPU内核一定要和你的芯片一致。-mfloat-abihard使用硬件FPU进行浮点运算如果你的芯片没有FPU或者想用软件模拟需要改为soft。-T${LINKER_SCRIPT}链接脚本路径。这个脚本定义了内存布局Flash, RAM的起始地址和大小必须和你Keil工程里使用的链接脚本一致。通常可以在Keil工程生成的.sct文件基础上修改或者直接从Keil的芯片支持包里复制一个。2.2 执行交叉编译工具链文件准备好后我们就可以在命令行或CMake GUI中执行交叉编译了。我习惯在丹青源码目录下新建一个build_arm文件夹专门用于这次构建。打开命令行切换到丹青源码目录然后执行mkdir build_arm cd build_arm cmake .. -DCMAKE_TOOLCHAIN_FILE../arm-gcc-toolchain.cmake -DCMAKE_BUILD_TYPERelease make -j4如果一切顺利你会在build_arm目录下的某个子文件夹比如src/里找到生成的libdanqing.a静态库文件。同时include/目录下的头文件也是我们需要的。常见问题编译错误找不到头文件检查工具链文件中的编译器路径是否正确以及丹青源码的CMakeLists.txt是否正确定义了头文件包含路径。链接错误未定义的引用这通常发生在后续Keil链接阶段可能是交叉编译时某些模块没有被正确编译进静态库或者Keil工程缺少必要的源文件。3. 集成到Keil MDK5工程拿到编译好的静态库和头文件后下一步就是让Keil认识并使用它们。3.1 将库文件引入Keil工程复制文件在你的Keil工程目录下比如./Libraries/Danqing/创建两个文件夹lib和inc。将上一步生成的libdanqing.a复制到lib文件夹将所有必要的头文件通常是include/目录下的所有.h文件复制到inc文件夹。Keil工程配置打开你的Keil工程.uvprojx文件。在左侧的“Project”窗口中右键点击你的Target选择“Manage Project Items”。在“Groups”标签页新建一个组例如命名为“Danqing”。在“Files”标签页切换到“Danqing”组点击“Add Files”将libdanqing.a添加到工程中。注意Keil添加.a库文件时文件类型过滤器要选“All files (.)”。还是在“Project Items”窗口切换到“Include Paths”标签页添加你刚才创建的inc文件夹的路径。这样编译器就能找到丹青系统的头文件了。3.2 配置Keil编译选项这一步至关重要必须保证Keil的编译选项和之前交叉编译时的选项兼容否则链接时会出各种奇怪错误。点击工具栏的“魔术棒”图标Options for Target。Target 标签“ARM Compiler”选择“Use default compiler version 5”或“Use default compiler version 6”都可以但要和工具链的兼容性保持一致。V5更通用。确保“Operating System”为“None”我们用的是裸机。根据芯片核对“Code Generation”中的ARM架构如Cortex-M4、浮点单元如Single Precision和浮点ABI如Use FPU。C/C 标签在“Misc Controls”里手动添加一些关键参数确保它们和arm-gcc-toolchain.cmake里的一致。例如--cpuCortex-M4 -D__MICROLIB -D__FPU_PRESENT1 -D__FPU_USED1“One ELF Section per Function”建议勾选有利于链接器进行代码段优化。Linker 标签确保使用了正确的链接脚本.sct文件这个脚本定义了芯片的内存映射必须和交叉编译时工具链文件里-T参数指定的脚本一致或兼容。在“Misc controls”里可能需要添加--library_typemicrolib如果你的工程使用了微库MicroLib。完成这些设置后尝试编译一下你的Keil工程。如果之前步骤都正确此时应该能成功编译链接不会报找不到libdanqing.a中函数定义的错误。4. 模型准备与集成推理引擎准备好了还需要一个能在上面运行的、经过优化的模型。4.1 模型量化与转换丹青识画系统这类嵌入式引擎通常不支持直接加载原始的PyTorch或TensorFlow模型。我们需要使用其提供的工具将训练好的模型进行量化将FP32权重转换为INT8等低精度格式以减小体积和加速和转换生成引擎能识别的二进制格式。这个过程一般由Python脚本完成# 示例假设使用丹青系统提供的转换脚本 import danqing_converter as dc # 加载预训练模型 model dc.load_model(my_image_classifier.onnx) # 执行量化校准需要一小部分校准数据 calibration_data ... # 加载一些代表性图片 quantized_model dc.quantize_model(model, calibration_data, quant_typeint8) # 转换为丹青引擎格式 dc.convert_to_runtime_format(quantized_model, output_filemodel_danqing.bin)生成的model_danqing.bin文件就是最终要部署到芯片上的模型文件。4.2 将模型文件嵌入程序对于嵌入式系统模型数据通常有两种集成方式直接编译进代码数组使用xxd或类似的二进制转C数组工具将.bin文件转换为一个const unsigned char数组并放在一个.c文件中。这样模型数据就成为程序只读数据段的一部分烧录到Flash中。优点是加载速度快无需文件系统。// model_data.c const unsigned char g_model_data[] { 0x10, 0x20, 0x30, // ... 模型二进制数据 }; const unsigned int g_model_data_len sizeof(g_model_data);存储在外部存储器并通过文件系统读取如果模型较大或者需要更新可以将.bin文件存放在外部SPI Flash、SD卡中程序运行时通过文件系统接口读取到RAM中。这种方式更灵活但需要额外的驱动和存储空间。在我们的轻量级场景下第一种方式更简单直接。将这个model_data.c文件添加到Keil工程中即可。5. 编写应用代码与调试万事俱备只欠东风。现在可以在Keil工程里编写调用丹青引擎进行推理的应用程序了。5.1 初始化与推理代码示例在main.c或专门的AI任务文件中添加如下代码#include danqing_inference.h // 丹青引擎头文件 #include model_data.h // 包含模型数组的头文件 // 定义输入输出缓冲区 static float input_buffer[224 * 224 * 3]; // 假设输入是224x224 RGB图 static float output_buffer[10]; // 假设输出是10个类别的概率 void danqing_demo_task(void) { // 1. 初始化推理引擎 dq_handle_t handle; dq_status_t status dq_create_handle(handle, g_model_data, g_model_data_len); if (status ! DQ_SUCCESS) { printf(Engine init failed!\n); return; } // 2. 准备输入数据 (这里需要你实现图像采集和预处理如缩放、归一化) // capture_and_preprocess_image(input_buffer); // 3. 执行推理 status dq_run_inference(handle, input_buffer, output_buffer); if (status DQ_SUCCESS) { // 4. 处理输出结果 int predicted_class 0; float max_prob output_buffer[0]; for (int i 1; i 10; i) { if (output_buffer[i] max_prob) { max_prob output_buffer[i]; predicted_class i; } } printf(Predicted class: %d with probability: %.2f\n, predicted_class, max_prob); } else { printf(Inference failed!\n); } // 5. 释放资源 dq_destroy_handle(handle); }5.2 使用J-Link进行片上调试与测试代码写好后就是最激动人心的实测环节了。连接硬件用J-Link连接你的开发板和电脑。Keil调试配置在Keil的“Debug”标签页选择“Use: J-LINK / J-TRACE Cortex”然后点击“Settings”确认连接成功并选择正确的芯片型号。下载与运行编译无误后点击“Load”按钮将程序下载到芯片Flash。实时测试你可以设置断点在推理函数前后观察输入输出缓冲区的内容。更实用的方法是在芯片的UART串口上打印日志如上例中的printf实时查看识别结果。如果结果不对首先检查输入数据的预处理尺寸、颜色顺序、归一化是否与模型训练时完全一致。这是嵌入式AI部署中最常见的错误来源。其次检查内存是否充足。推理过程中的中间张量可能会消耗大量RAM如果芯片RAM较小可能需要调整模型或使用内存复用策略。6. 总结走完这一整套流程从交叉编译、Keil集成到最终板子跑通感觉像是完成了一次小小的“系统集成”。整个过程的核心其实就两点一是让编译环境统一确保在PC上生成库文件的工具链和Keil里的编译选项“说同一种语言”二是细心特别是内存布局、编译参数这些细节错一点就可能导致链接失败或者运行时崩溃。实际用下来丹青这类轻量级引擎在Cortex-M4这类芯片上跑简单的分类任务速度还是可以接受的功耗也符合预期。当然它的能力边界也很明显复杂的模型或者高分辨率输入就别想了。对于智能家居里“识别一下面前有没有水杯”这种级别的需求它是一个挺划算的解决方案。如果你也想尝试我的建议是先别急着搞复杂的模型就用官方提供的最简单的示例模型把整个“交叉编译-集成-下载-运行”的流程跑通。这个流程通了后面换你自己的模型就是水到渠成的事情。过程中遇到链接错误或者运行异常多对比检查两边CMake编译环境和Keil环境的设置问题大概率就出在这些配置的差异上。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。