STM32N6570-DK: Inference always outputs the same class with the same confidence level

Question/Issue:
The inference result is always fixed to class 3 with a confidence of around ~0,996. When further investigated, I found that the features array is always (mostly) full of zero’s.

Project ID:
971849

Context/Use case:
I am building an algorithm to detect 9 different cylindrical objects. The microphone that’s on the DK picks up the sound, a DMA puts the samples in a uint16_t buffer. I then convert the buffer using q15_arm_to_float(); and after that I use signal_from_buffer() and call run_classifier()

Steps Taken:

  1. I enabled the debug and printed the results to UART, the Features calculation takes 0ms and the output after that shows the array filled with 0’s, before it had some float values, but most where still 0
  2. Confirmed the buffer for the raw microhpone data is not in the Dcache of the CPU.
  3. Confirmed that the float buffer that is being put into the signal_to_buffer function is also correct.

Expected Outcome:
The classifier should output different labels depending on the sound the microphone picks up.

Actual Outcome:
The classifier always outputs class 3

Reproducibility:

  • Always

Environment:

  • Platform: STM32N6570-DK, on-board microphone
  • Build Environment Details: STM32CubeIDE 2.0.0
  • OS Version: Windows 11
  • Edge Impulse Version (Firmware): N/A I am using my own Firmware, this firmware is coded in C
  • Edge Impulse CLI Version: N/A, see above
  • Project Version: Version 3
  • Custom Blocks / Impulse Configuration: [Describe custom blocks used or impulse configuration]
    Logs/Attachments:
    The output of the model in UART:
    Features (0 ms.): a bunch of zero’s here
    Running impulse…
    [0] Drinkbeker_0: 0.000000
    [1] Drinkbeker_100: 0.000000
    [2] Drinkbeker_50: 0.000000
    [3] Glas_0: 0.996094
    [4] Glas_100: 0.000000
    [5] Glas_50: 0.000000
    [6] MetalenBeker_0: 0.000000
    [7] MetalenBeker_100: 0.000000
    [8] MetalenBeker_50: 0.000000
    Segment 0: Glas_0 (99.61%)

Additional Information:
The model works fine when running it on my laptop, it defaults to label 3 when doing nothing, but that is to be expected i think.
Here is the relevant code that calls the run_classifier function:

                //Reset ArrayIndex
				ArrayIndex = 0;


				// Process all 10 segments
				uint32_t valid_results = 0;

				c_run_classifier_init();

				// Process all 10 segments
				for(int i = 0; i < 10; i++)
				{
					// Convert this chunk of uint16_t Q15 to float32
					arm_q15_to_float((q15_t *)&PcmBuffer[ArrayIndex], audio_float_buffer, 3200);

					SignalToBufferResult = c_signal_from_buffer(audio_float_buffer, 3200, Signal);

					if(SignalToBufferResult != 0)
					{
						UART_broadcast("Writing to signal buffer failed (segment %d), error code: %d\n", i, SignalToBufferResult);
						ArrayIndex += 3200;
						continue;
					}

					//Run the AI on the current segment
					AIresult = c_run_classifier(Signal, AI200msResult, true);
                    ArrayIndex += 3200;
                }

Yesterday I found out that you can use C functions directly instead of writing your own wrapper, if you defined some statements (EI_C_LINKAGE=1 and EIDSP_SIGNAL_C_FN_POINTER=1). So I did just that. The code now looks like this:

				//Reset ArrayIndex
				ArrayIndex = 0;

				// Process all 10 segments
				uint32_t valid_results = 0;

				// Process all 10 segments
				for(int i = 0; i < 10; i++)
				{
					// Convert this chunk of uint16_t Q15 to float32
					arm_q15_to_float((q15_t *)&PcmBuffer[ArrayIndex], features, 3200);

					signal_t signal;
					signal.total_length = 3200;
					signal.get_data = &get_feature_data;

					ei_impulse_result_t result;

					//Run the AI with the current buffer
					EI_IMPULSE_ERROR res = run_classifier(&signal, &result, true);

					if(res != EI_IMPULSE_OK)
					{
						UART_broadcast("Executing model failed in segment: %d, error code: %d\n", i, res);
					} else {
						UART_broadcast("Model execution succesfull of segment: %d", i);
						result_confidences[valid_results] = result.classification[0].value; // test code
						result_labels[valid_results] = result.classification[0].label;      // Test code
						valid_results++;
					}
					ArrayIndex += 3200;
				}

It now returns a mostly zero array of features, but most of the time there are at least a couple of values, but most importantly, it now returns:

ERR: Failed to allocate scratch buffer of size 64, reached EI_MAX_SCRATCH_BUFFER_COUNT
Failed to initialize the model (error code 1)
Executing model failed in segment: 0, error code: -6

So I tried increasing the EI_MAX_SCRATCH_BUFFER_COUNT to 8, then 128, and even 256, but that does not really do anything, I debugged the code and found that this ERR message comes from RequestScratchBufferInArenaImpl(); In there, the value of scratch_buffers_ix is way too big, it is always somewhere around 1043296727 or 3191845335. So that is where I am at now, I just can’t seem to find the cause of why scratch_buffers_ix is so big.

Also, the get_feature_data function looks like this:

int get_feature_data(size_t offset, size_t length, float *out_ptr){
	memcpy(out_ptr, features + offset, length * sizeof(float));
	return 0;
}

Hi @Ko1rr1ef

did you tested the model using the example standalone with a static feature ?

in this way you have a much simpler project to test if the model is working as expected.

you can enable some debug print here

setting true instead of false as third parameter.

I’ll have a quick look at your project.

fv

Thanks for the reply fv,

I am trying to compile the standalone example, but during compilation it returns a lot of errors, some of these are:

Model/network.c:57:4: error: #error "Possible mismatch in ll_aton library used"
   57 | #  error "Possible mismatch in ll_aton library used"
      |    ^~~~~
Model/network.c: In function 'LL_ATON_Start_EpochBlock_3':
Model/network.c:348:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  348 |     .vshift = 0,
      |      ^~~~~~
Model/network.c: In function 'LL_ATON_Start_EpochBlock_4':
Model/network.c:716:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  716 |     .vshift = 0,
      |      ^~~~~~
Model/network.c:767:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  767 |     .vshift = 0,
      |      ^~~~~~
Model/network.c:819:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  819 |     .vshift = 0,
      |      ^~~~~~
Model/network.c:871:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  871 |     .vshift = 0,
      |      ^~~~~~
Model/network.c: In function 'LL_ATON_Start_EpochBlock_5':
Model/network.c:1263:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
 1263 |     .vshift = 0,
      |      ^~~~~~
Model/network.c: In function 'LL_ATON_Start_EpochBlock_7':
Model/network.c:1696:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
 1696 |     .vshift = 0,
      |      ^~~~~~
Model/network.c: In function 'LL_ATON_EpochBlockItems_network':

Also I just noticed, all this time I was using the C++ library and not the neural-art library, I also tried integrating that into my project, but the generated code from edge-impulse misses a lot of ll_aton files, and I can’t seem to find that code anywhere

Hi @Ko1rr1ef

I updated the repo for the example-standalone, now should build

fv

Hi @ei_francesco

Thanks! It compiles now, btw the readme should be updated to instruct the user to include stai_network.h/.c now, had to put those in now.

So the features array that is now returned is fixed, it has data now, no full-on 0’s anymore, but it still returns Features (0 ms.): (rest of the data), so that is weird.

Also when it’s done outputting the feature results the last thing it spits out is: Running impulse…
After that nothing else happens. Also the program can’t be reset anymore with the reset button, the board has to be power reset for the program to load again.

This is the part of my ei_main.cpp that is important here:

do {
    ei_sleep(2000);
    ei_printf("Running impulse...\n");
    // invoke the impulse
    EI_IMPULSE_ERROR res = run_classifier(&features_signal, &result, true);

    ei_printf("run_classifier returned: %d\n", res);

    if (res != 0) {
        ei_printf("Error while running classifier: %d\n", res);
        return 1;
    }

    display_results(&ei_default_impulse, &result);
    ei_sleep(2000);
}while(1);

So it never executes ei_printf(“run_classifier returned: %d\n”, res);

I will try debugging the software a bit in the meantime, let me know if you have any further steps I could take.

~Kyan

Hi @Ko1rr1ef ,

Probably the inference get stuck, I need to debug it, but I don’t have the board with me right now, I can have a look next week.
Let me know if you find anything else in the meantime.

regards,
fv

Hi @ei_francesco ,

Ok thanks for taking the time! In the meantime I am trying to use the edge-impulse library as dsp only and creating my own generated code using the up to date STedgeAI generation tool. I am currently debugging that, but please let me know when you find something!

Best regards,
Kyan

Hi @ei_francesco,

I’m having some trouble using the STEdgeAI generation tool. While the generation itself works fine, porting the Edge Impulse DSP functions is proving a bit too challenging for me.
Please let me know if you need anything from my end to help debug the application on the board!

Best regards,
Kyan Vijlbrief

Hi @Ko1rr1ef

I’m debugging to check what’s wrong on our engine.
I’ll keep you updated!

fv

Hi @ei_francesco,

Thanks for taking the time! Did you find anything while debugging the engine last week?

Best regards,
Kyan

Hi @Ko1rr1ef

not yet, I see the NPU is never returning done, but i don’t understand why… I’ll keep you posted

fv

Hi @Ko1rr1ef

there were some issue in the example-standalone, now should be working correctly!

with your project, for this sample Login - Edge Impulse, i get this:

Timing: DSP 12 ms, inference 2 ms, anomaly 0 ms, postprocessing 1 us
#Classification predictions:
AchtergrondRuis: 0.14062
Drinkbeker_0: 0
Drinkbeker_100: 0
Drinkbeker_50: 0
Geklik_muis: 0.85546
Glas_0: 0
Glas_100: 0
Glas_50: 0
Menselijke_spraak: 0.0039
MetalenBeker_0: 0
MetalenBeker_100: 0
MetalenBeker_50: 0

which is correct, so the model and the inference engine are working correctly.

let me know!
fv