X-CUBE-AI(TinyML) + Keil

到现在已经完成两个实验了,但是都是在编译和测试别人的东西,那如何移植到我们熟悉的IDE,比如古董Keil(其实我也不喜欢,但是Team里都用这个,我也没办法!),所幸X-CUBE-AI能给到很大的帮助.他是STM32CubeMX内的一个组件.但是Cube只能支持M4/M7/M23/M33/M35P/M55等等(最低推荐架构:ARMv7E-M),大尺寸低配MCU不常见,比如STM32F030RB,手动移植即可.

注意:目前支持依然有点问题,模型文件不知道被他转换到哪里去了,不过他能帮忙添加目录结构也是挺好的.

在新建CubeMX工程后选择顶部的Software Pack并添加X-CUBE-AI组件,选择Core以及应用为Not Selected.

从软件中Add Network.

模型还是从这里获取:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/hello_world/train/train_hello_world_model.ipynb,最终要的文件是这个.

从左侧文件浏览中下载他,然后直接在Cube加载并分析.

  • 如果选的是CubeAI框架,那么可以节约很多编译时间.
  • 如果选的是Tensorflow Micro框架,则可以有和TinyML一样的原生开发体验.

然后可以进行验证测试,我是NUCLEO-H723ZG,PD8/PD9作为USART3的Bootloader下载接口,其他的要自行看原理图,编译工程需要不少时间,耐心等等,由于没有配备验证解雇集,所以能跑就不错了.

然后其他按照自己喜欢配置,最后生成工程,最后编译还缺了个文件:

缺了的东西要补上(主要是ST软件开小差忘了复制了吧,如果要提交到Git,最好是包含官方工程的submodule而不是全部打包.),然后重新添加Keil工程里Application/User/X-CUBE-AI/App缺失的syscall文件.

然后micro_error_reporter没有定义,从前面的学习知道,这个其实就应该映射为串口输出.暂时不想实现他就可以在主函数写个dummy函数.

void DebugLog(const char* s){
	__nop();
}

然后C文件还要CPP化,最后把代码移植过去.(完整版请查看:https://gist.github.com/nickfox-taterli/f21dba55a2118a95406547253ff7141a 完成工程:https://github.com/nickfox-taterli/KeilCube_TfMciro)

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define TFLITE_SCHEMA_VERSION (3)

const int kInferencesPerCycle = 1000;
const float kXrange = 2.f * 3.14159265359f;

// Globals, used for compatibility with Arduino-style sketches.
namespace {
    tflite::ErrorReporter* error_reporter = nullptr;
    const tflite::Model* model = nullptr;
    tflite::MicroInterpreter* interpreter = nullptr;
    TfLiteTensor* input = nullptr;
    TfLiteTensor* output = nullptr;
    int inference_count = 0;

    constexpr int kTensorArenaSize = 2000;
    uint8_t tensor_arena[kTensorArenaSize];
} 
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
alignas(8) const unsigned char g_model[] = {
    0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x14, 0x00, 0x20, 0x00,
    ....
    0x00, 0x00, 0x00, 0x09};
const int g_model_len = 2488;
/* USER CODE END PV */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void DebugLog(const char* s){
	__nop();
}

void HandleOutput(tflite::ErrorReporter* error_reporter, float x_value,
                  float y_value) {
  // 从例子中挪过来
}

void setup() {
  // 从例子中挪过来
}

void loop() {
  // 从例子中挪过来
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 2 */
  setup();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		loop();
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

查看输出.

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注