cancel
Showing results for 
Search instead for 
Did you mean: 

How to Feed Features and Get Predictions with X-CUBE-AI on STM32?

Stone_chan
Associate II

Hi everyone,
I'm working on deploying an MLP model on STM32F746 using X-CUBE-AI. The model was converted from ONNX format, and I’m running into trouble feeding the input features and retrieving the prediction result

I want to run inference with the following flow:

float input_data[60] = { /* preprocessed features */ };
float output_data[1]; // expecting softmax probabilities
int output_label; // expecting predicted class label

My main confusion:

I'm unclear on how to actually use the interface generated by Cube.AI:

  • What are the correct steps to initialize and fill the ai_buffer structs?

  • Do I need to manually align my input/output arrays, or use the data_ins[] / data_outs[] generated by default?

  • What is the purpose of macros like AI_HANDLE_PTR(...) and AI_MNETWORK_IN?

  • I tried calling ai_mlp_run(...) like this:

     
    ai_mlp_run(network, ai_input, ai_output);

    but I get build errors or invalid pointer types

 



model information :

problem.png

app_X-cube-ai.h:
problem2.png

1 ACCEPTED SOLUTION

Accepted Solutions
Julian E.
ST Employee

Hello @Stone_chan,

 

When you create your project in CubeMX, select the last version of X Cube AI and the application Template.

 

Then in the generated code, you will find app_x-cube-ai.c in /X-Cube-AI-App.

The main functions here are the MX_X_CUBE_AI_Init and MX_X_CUBE_AI_Process.

 

In the MX_X_CUBE_AI_Process you will find 3 parts:

  • acquire_and_process_data(data_ins)
  • ai_run()
  • post_process(data_outs)

 

The way to pass data is through the function acquire_and_process_data(data_ins). By default, it looks like this:

int acquire_and_process_data(ai_i8* data[])
{
  /* fill the inputs of the c-model
  for (int idx=0; idx < AI_NETWORK_IN_NUM; idx++ )
  {
      data[idx] = ....
  }

  */
  return 0;
}

The for loop here is used if you have multiple input to give your model. In most case you have one, so you will only pass 1 time and fill data[0].

 

Same for the post_process(data_outs) function.

 

The data that you have to pass are 1D array of size of your input/output and be careful, by default the data_ins and data_outs are in int8 (ai_i8). So if you are using float32 for example, make sure to adapt the code.

 

For your board in particular, the MX_X_CUBE_AI_Process is not called in the main.c.

You can add it in the while(1) in the main function:

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* Enable the CPU Cache */

  /* Enable I-Cache---------------------------------------------------------*/
  SCB_EnableICache();

  /* Enable D-Cache---------------------------------------------------------*/
  SCB_EnableDCache();

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* Configure the peripherals common clocks */
  PeriphCommonClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC3_Init();
  MX_CRC_Init();
  MX_DCMI_Init();
  MX_DMA2D_Init();
  MX_ETH_Init();
  MX_FMC_Init();
  MX_I2C1_Init();
  MX_I2C3_Init();
  MX_LTDC_Init();
  MX_QUADSPI_Init();
  MX_RTC_Init();
  MX_SAI2_Init();
  MX_SDMMC1_SD_Init();
  MX_SPDIFRX_Init();
  MX_SPI2_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
  MX_TIM5_Init();
  MX_TIM8_Init();
  MX_TIM12_Init();
  MX_USART1_UART_Init();
  MX_USART6_UART_Init();
  MX_FATFS_Init();
  MX_X_CUBE_AI_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of defaultTask */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 4096);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */

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

 

Have a good day,

Julian


In order to give better visibility on the answered topics, please click on 'Accept as Solution' on the reply which solved your issue or answered your question.

View solution in original post

1 REPLY 1
Julian E.
ST Employee

Hello @Stone_chan,

 

When you create your project in CubeMX, select the last version of X Cube AI and the application Template.

 

Then in the generated code, you will find app_x-cube-ai.c in /X-Cube-AI-App.

The main functions here are the MX_X_CUBE_AI_Init and MX_X_CUBE_AI_Process.

 

In the MX_X_CUBE_AI_Process you will find 3 parts:

  • acquire_and_process_data(data_ins)
  • ai_run()
  • post_process(data_outs)

 

The way to pass data is through the function acquire_and_process_data(data_ins). By default, it looks like this:

int acquire_and_process_data(ai_i8* data[])
{
  /* fill the inputs of the c-model
  for (int idx=0; idx < AI_NETWORK_IN_NUM; idx++ )
  {
      data[idx] = ....
  }

  */
  return 0;
}

The for loop here is used if you have multiple input to give your model. In most case you have one, so you will only pass 1 time and fill data[0].

 

Same for the post_process(data_outs) function.

 

The data that you have to pass are 1D array of size of your input/output and be careful, by default the data_ins and data_outs are in int8 (ai_i8). So if you are using float32 for example, make sure to adapt the code.

 

For your board in particular, the MX_X_CUBE_AI_Process is not called in the main.c.

You can add it in the while(1) in the main function:

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* Enable the CPU Cache */

  /* Enable I-Cache---------------------------------------------------------*/
  SCB_EnableICache();

  /* Enable D-Cache---------------------------------------------------------*/
  SCB_EnableDCache();

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* Configure the peripherals common clocks */
  PeriphCommonClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC3_Init();
  MX_CRC_Init();
  MX_DCMI_Init();
  MX_DMA2D_Init();
  MX_ETH_Init();
  MX_FMC_Init();
  MX_I2C1_Init();
  MX_I2C3_Init();
  MX_LTDC_Init();
  MX_QUADSPI_Init();
  MX_RTC_Init();
  MX_SAI2_Init();
  MX_SDMMC1_SD_Init();
  MX_SPDIFRX_Init();
  MX_SPI2_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
  MX_TIM5_Init();
  MX_TIM8_Init();
  MX_TIM12_Init();
  MX_USART1_UART_Init();
  MX_USART6_UART_Init();
  MX_FATFS_Init();
  MX_X_CUBE_AI_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of defaultTask */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 4096);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */

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

 

Have a good day,

Julian


In order to give better visibility on the answered topics, please click on 'Accept as Solution' on the reply which solved your issue or answered your question.