Features buffer filling up at 258?

Hello community! Hoping someone can help me figure this one out…

Background:
I’m working on a project that uses an ODrive brushless motor controller with Edge Impulse (on an Arduino Nano 33 BLE Sense). The main code is based on this guide for classifying data on the Arduino. The main difference is that instead of 3 accelerometer axes I’m using input velocity, measured velocity, and current draw from the ODrive.

The problem:
My code is getting stuck when the features buffer length reaches 258. Every time. I used the Arduino’s LED_BUILTIN to see if the code is entering the classification if block, and it’s not! The EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE is 300 (confirmed with Serial.print()), so the fact that it’s stopping at 258 has me stumped!

Here is the code, with a comment where it is getting stuck:

/* Includes ---------------------------------------------------------------- */
#include <odrive_inferencing.h>
#include <HardwareSerial.h>
#include <ODriveArduino.h>
#include <Wire.h>
#include <Arduino.h>

// Printing with stream operator helper functions
template<class T> inline Print& operator <<(Print &obj,     T arg) { obj.print(arg);    return obj; }
template<>        inline Print& operator <<(Print &obj, float arg) { obj.print(arg, 4); return obj; }

HardwareSerial& odrive_serial = Serial1;
ODriveArduino odrive(odrive_serial);

#define FREQUENCY_HZ        EI_CLASSIFIER_FREQUENCY
#define INTERVAL_MS         (1000 / (FREQUENCY_HZ + 1))

static unsigned long last_interval_ms = 0;
size_t feature_ix = 0;

static float features[] = {};

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

void ei_printf(const char *format, ...) {
    static char print_buf[1024] = { 0 };

    va_list args;
    va_start(args, format);
    int r = vsnprintf(print_buf, sizeof(print_buf), format, args);
    va_end(args);

    if (r > 0) {
        Serial.write(print_buf);
    }
}

void setup()
{
    // Debug LED
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);
    
    Serial.begin(115200);
    while (!Serial);
    
    odrive_serial.begin(115200);
    while (!odrive_serial);
}

void loop() {
    // Initialize parameter variables
    float input_vel, meas_vel, current;

    if (millis() > last_interval_ms + INTERVAL_MS) {
        last_interval_ms = millis();

        // Get parameter values
        input_vel = odrive.GetInputVel(0);
        meas_vel  = odrive.GetSensorlessVelocityEstimate(0);
        current   = odrive.GetIbus();

        // Fill the features buffer
        features[feature_ix++] = input_vel;
        features[feature_ix++] = meas_vel;
        features[feature_ix++] = current;

        // Check length of feature buffer
        Serial.println(feature_ix);

        // CODE GETTING STUCK HERE WHEN feature_ix REACHES 258

        // Features buffer full? then classify!
        if (feature_ix == EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
            // Toggle LED to see if entering 'if' block
            digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
            
            ei_impulse_result_t result;

            // Create signal from features frame
            signal_t signal;
            numpy::signal_from_buffer(features, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);

            // Run classifier
            EI_IMPULSE_ERROR res = run_classifier(&signal, &result, false);
            ei_printf("run_classifier returned: %d\n", res);
            if (res != 0) return;

            // Print predictions
            ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.): \n",
                result.timing.dsp, result.timing.classification, result.timing.anomaly);

            for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
                ei_printf("%s:\t%.5f\n", result.classification[ix].label, result.classification[ix].value);
            }
      
        #if EI_CLASSIFIER_HAS_ANOMALY == 1
            ei_printf("anomaly:\t%.3f\n", result.anomaly);
        #endif

            // reset features frame
            feature_ix = 0;
        }
    }
}

Here is the Edge Impulse project for reference (am still adding more data so ignore the accuracy ;)).

Thanks in advance for any ideas!

Edit: Perhaps worth noting that I had originally designed the project to use two axes (input / measured velocity) and eventually changed it to three. The odrive_inferencing.h library being imported into the project is definitely the most up-to-date version, but maybe there’s some vestigal parameter that snuck in from the two-axis version of the project?

Update:

To add to the weirdness - I added some debug prints and after adding the prints the code runs in a loop, but features_xi is resetting to 0 after reaching 255! For example:

.
.
.
14:41:02.748 -> Inside first IF block.
14:41:02.748 -> Reset last_interval_ms.
14:41:02.748 -> Got input vel.
14:41:02.748 -> Got measured vel.
14:41:02.748 -> Got current.
14:41:02.748 -> Added to buffer.
14:41:02.748 -> Features buffer length: 249
14:41:02.748 -> Inside first IF block.
14:41:02.748 -> Reset last_interval_ms.
14:41:02.748 -> Got input vel.
14:41:02.748 -> Got measured vel.
14:41:02.748 -> Got current.
14:41:02.748 -> Added to buffer.
14:41:02.781 -> Features buffer length: 252
14:41:02.781 -> Inside first IF block.
14:41:02.781 -> Reset last_interval_ms.
14:41:02.781 -> Got input vel.
14:41:02.781 -> Got measured vel.
14:41:02.781 -> Got current.
14:41:02.781 -> Added to buffer.
14:41:02.781 -> Features buffer length: 255
14:41:02.781 -> Inside first IF block.
14:41:02.781 -> Reset last_interval_ms.
14:41:02.781 -> Got input vel.
14:41:02.781 -> Got measured vel.
14:41:02.781 -> Got current.
14:41:02.781 -> Added to buffer.
14:41:02.781 -> Features buffer length: 0 // <----- Weirdness.
14:41:02.781 -> Inside first IF block.
14:41:02.781 -> Reset last_interval_ms.
14:41:02.781 -> Got input vel.
14:41:02.781 -> Got measured vel.
14:41:02.815 -> Got current.
14:41:02.815 -> Added to buffer.
14:41:02.815 -> Features buffer length: 3
14:41:02.815 -> Inside first IF block.
14:41:02.815 -> Reset last_interval_ms.
14:41:02.815 -> Got input vel.
14:41:02.815 -> Got measured vel.
14:41:02.815 -> Got current.
14:41:02.815 -> Added to buffer.
14:41:02.815 -> Features buffer length: 6
14:41:02.815 -> Inside first IF block.
14:41:02.815 -> Reset last_interval_ms.
14:41:02.815 -> Got input vel.
14:41:02.815 -> Got measured vel.
14:41:02.815 -> Got current.
14:41:02.815 -> Added to buffer.
.
.
.

Here is the code with the debug prints for reference:

/* Includes ---------------------------------------------------------------- */
#include <odrive_inferencing.h>
#include <HardwareSerial.h>
#include <ODriveArduino.h>
#include <Wire.h>
#include <Arduino.h>

// Printing with stream operator helper functions
template<class T> inline Print& operator <<(Print &obj,     T arg) { obj.print(arg);    return obj; }
template<>        inline Print& operator <<(Print &obj, float arg) { obj.print(arg, 4); return obj; }

HardwareSerial& odrive_serial = Serial1;
ODriveArduino odrive(odrive_serial);

#define FREQUENCY_HZ        EI_CLASSIFIER_FREQUENCY
#define INTERVAL_MS         (1000 / (FREQUENCY_HZ + 1))

    // Initialize parameter variables
    float input_vel, meas_vel, current;

static unsigned long last_interval_ms = 0;
size_t feature_ix = 0;

static float features[] = {};

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

void ei_printf(const char *format, ...) {
    static char print_buf[1024] = { 0 };

    va_list args;
    va_start(args, format);
    int r = vsnprintf(print_buf, sizeof(print_buf), format, args);
    va_end(args);

    if (r > 0) {
        Serial.write(print_buf);
    }
}

void setup()
{
    // Debug LED
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);
    
    Serial.begin(115200);
    while (!Serial);
    
    odrive_serial.begin(115200);
    while (!odrive_serial);
}

void loop() {
    if (millis() > last_interval_ms + INTERVAL_MS) {
        Serial.println("Inside first IF block.");
        
        last_interval_ms = millis();
        Serial.println("Reset last_interval_ms.");

        // Get parameter values
        input_vel = odrive.GetInputVel(0);
        Serial.println("Got input vel.");
        meas_vel  = odrive.GetSensorlessVelocityEstimate(0);
        Serial.println("Got measured vel.");
        current   = odrive.GetIbus();
        Serial.println("Got current.");

        // Fill the features buffer
        features[feature_ix++] = input_vel;
        features[feature_ix++] = meas_vel;
        features[feature_ix++] = current;
        Serial.println("Added to buffer.");

        // Check length of feature buffer
        Serial.print("Features buffer length: "); Serial.println(feature_ix);

        // Features buffer full? then classify!
        if (feature_ix == EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
            
            // Toggle LED to see if entering 'if' block
            digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
            
            ei_impulse_result_t result;

            // Create signal from features frame
            signal_t signal;
            numpy::signal_from_buffer(features, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);

            // Run classifier
            EI_IMPULSE_ERROR res = run_classifier(&signal, &result, false);
            ei_printf("run_classifier returned: %d\n", res);
            if (res != 0) return;

            // Print predictions
            ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.): \n",
                result.timing.dsp, result.timing.classification, result.timing.anomaly);

            for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
                ei_printf("%s:\t%.5f\n", result.classification[ix].label, result.classification[ix].value);
            }
      
        #if EI_CLASSIFIER_HAS_ANOMALY == 1
            ei_printf("anomaly:\t%.3f\n", result.anomaly);
        #endif
            // reset features frame
            feature_ix = 0;
        }
    }
}

@avielbr,

It looks like size_t (differs per implementation) might be a 8-bit and hence rolls over after 255. Can you instead use define feature_x as:

uint32_t feature_ix = 0;
// or unsigned int feature_ix = 0;

To test the the size of size_t can you try printing the sizeof(size_t) and see how much bytes it occupies.

UPDATE:
On second thought this shouldn’t be the case size_t should be guaranteed to be at least 16 bits.
Are you sure it’s not resetting back to 0 in your if block?

I think you may have a buffer overflow.

You should define your features array as follows:

static float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = {};
1 Like

@rjames YES! Thanks. That did it.

1 Like