MSPM0G5187: Edge AI Studio for Time Series: classification result is shown as “Other” instead of “Normal.”

Part Number: MSPM0G5187
Other Parts Discussed in Thread: , C2000WARE

Hi experts,

We are evaluating the system under the following environment; however, the results displayed on the Live Preview screen are not as expected.

Even though no modifications have been made to the motor, the classification result is shown as “Other” instead of “Normal.”

image.png

Could you please point out if there are any mistakes in our procedure?
Also, is this behavior already reported in the errata mentioned below?
:Edge AI Studio for Time Series - Quick Start Guide

■ Evaluation Environment

  • Hardware: LP-MSPM0G5187
  • Motor: SUNON EF80251S1-1000U-A99 (powered either by an external 12 V supply or by 5 V from the EVM)
  • Sensor: ADXL355
  • CCS: v20.5.1
  • M0 SDK: v2.10.00.04
  • Sample project: motor_fault_live_preview_LP_MSPM0G5187_nortos_ticlang
  • Compiler: 4.00.04.00
  • Edge AI Studio for Time Series: v1.6.1
  • AI sample project: Fan Imbalance Classification

■ Observation

The result does not change regardless of which of the following methods is used:

Pattern 1:
Build motor_fault_live_preview_LP_MSPM0G5187_nortos_ticlang in CCS and flash the generated .out file

Pattern 2:
Train the model based on Fan Imbalance Classification with default settings, and then program the device using “Load Program”

In addition, we captured our own data using motor_fault_data_capture_LP_MSPM0G5187_nortos_ticlang, and the data appears consistent with the default “Normal” dataset.
When the fan blades are touched with a finger, “Imbalance” is detected, which confirms that the sensor is operating properly.
The result remains the same even when using a different PC.
We also tested several different types of motors. 

■ Additional Findings (from comparison using F28P550)

The following issues were identified during comparative investigation with F28P550:

Best regards,
O.H

  • Hi O.H,

    I will do some test after I back office next Tuesday.

    B.R.

    Sal

  • Hi O.H.

    The result does not change regardless of which of the following methods is used:

    According to your capture behavior, looks like the sensor and capture demo works. But the live preview demo does not work.

    Unfortunately, I have no hardware in my side to test it; I will forward to internal team to see if it is a known issue.

    The following issues were identified during comparative investigation with F28P550:

    Thanks for sharing the findings for C2000 device. While I see you mentioned the data capture demo works, I think there should have no issues on the hardware connection.

    B.R.

    Sal

  • Additionally, when you run the demo, could you please help check the data from ADXL355 to M0 launchpad to see whether the live preview demo has corrected hardware setting?

    B.R.

    Sal

  • Hi Sal,

    Sorry for late reply. Thnak your for you supports.

    Additionally, when you run the demo, could you please help check the data from ADXL355 to M0 launchpad to see whether the live preview demo has corrected hardware setting?

    The sensor is communicating, and the values ​​for all axes seem to be changing in the direction of tilt. However, as shown in the diagram below, the values ​​when the device is placed on a flat surface seem incorrect.

    Best regards,
    O.H

  • Hi Sal,

    First, I apologize that the measurement results are difficult to read.
    I added buffers to “motor_fault_data_capture_LP_MSPM0G5187_nortos_ticlang” and visualized the measured values for the X, Y, and Z axes.
    The sensor was mounted on top of the fan, and measurements were taken under the following three patterns. 
    As a result, the sensor itself appears to be operating, but I cannot determine whether the measured values are valid or reasonable. When the sensor is not mounted on the fan, each axis shows changes in the expected direction according to the direction in which the sensor is tilted.

    Pattern 1: Normal condition. The values are stable on all axes.

    Pattern 2: Touching the blade with a finger. The values fluctuate significantly on all axes.

    Pattern 3: Attaching tape to the blade. The values fluctuate on all axes, but not as much as in Pattern 2.

    Best regards,
    O.H

  • Hi Sal,

    Sorry for rush you, is there any update?

    Best regards,
    O.H

  • Hi O.H.

    I have forwarded to thread to the experts within my team, I am also waiting the comments. Let me send another message to the team.

    B.R.

    Sal

  • Hi O.H.

    Please see the team feedback below:

    In our setup, the sensor is mounted on top of the BLDC motor and not on the blade for all 4 classes. This is different from what you are doing. Additionally, when you test the normal case, does the motor is spinning or keep static?

    For the demo set up if you put the sensor on the blade, then you need using capture demo to get enough data base for all classes and then re-train the model to deploy and test it in MCU side.

    B.R.

    Sal

  • Hi Sal,

    Thank you for your reply. Sorry for the insufficient explanation.

    In our setup, the sensor is mounted on top of the BLDC motor and not on the blade for all 4 classes. This is different from what you are doing.

    The setup is as shown in the photo. The sensor is mounted above the BLDC.

    Additionally, when you test the normal case, does the motor is spinning or keep static?

    The BLDC used is “SUNON EF80251S1-1000U-A99,” and it is always spinning.
    Since we only have one “SUNON EF80251S1-1000U-A99,” we have not damaged any of its blades. For another type of BLDC, we prepared both a normal unit and a unit with a damaged blade, and we have already tried the full process from custom data capture to re-train and compilation using “motor_fault_data_capture_LP_MSPM0G5187_nortos_ticlang.”

    For the demo set up if you put the sensor on the blade, then you need using capture demo to get enough data base for all classes and then re-train the model to deploy and test it in MCU side.

    Using “SUNON EF80251S1-1000U-A99,” we captured data with “motor_fault_data_capture_LP_MSPM0G5187_nortos_ticlang,” and we have confirmed the annotation, re-train, and compilation processes. As a result, “Normal” not appears at the top. Since there is a possibility that the annotation or training parameters may be incorrect, we tried to start by checking the operation with the sample project. This is the background.

    Best regards,
    O.H

  • Hi O.H.

    and we have confirmed the annotation, re-train, and compilation processes

    Have you compared the data when you do annotation and when you do live preview? I think if the re-train works, then you should get correct live preview in normal class.

    Could you share your re-trained model output (confusion matrix), and the data you captured and annotated for the normal/other class:

    B.R.

    Sal

  • Hi Sal,

    I've attached a project using the sample project as the default, and then another project based on that default using data I captured myself as the custom.

    The following graph summarizes the differences between the default and custom .csv files.

    Best regards,
    O.H

    Fan Imbalance Cllasification_default_project.zipFan Imbalance Cllasification_custom_project.zip20260521-143813_MotorFault_model_2_t_custom.zip20260520-175758_MotorFault_model_2_t_default.zip

  • Hi Sal,

    Sorry for rush you, is there any update? Please let us know if you need more information.

    Best regards,
    O.H

  • Hi Sal,

    Sorry for rush you, is there any update? Please let us know if you need more information.

    Best regards,
    O.H

  • Hi Sal,

    I managed to resolve the issue, albeit through a workaround. There was a discrepancy between the logic displayed in the GUI and the logic implemented in the source code. I modified the source code to align the UART output sent to the GUI with the LED display.

    • M0 SDK: v2.11.00.07
    • Sample project: motor_fan_blade_fault_live_preview

    /*
     * Copyright (c) 2025, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include "model/tvmgen_default.h"
    #include "ADXL355.h"
    #include <feature_extract.h>
    
    /* UART communication and DAP (Debug Access Port) core headers */
    #include "uart_comm_target.h"
    #include "dap_core.h"
    
    /* ARM CMSIS-DSP Header files */
    #include "arm_const_structs.h"
    #include "arm_math.h"
    #include "stdlib.h"
    
    /** \brief Size of the class buffer */
    #define SAMPLE_BUFFER_SIZE (10)
    
    /** \brief Number of input channels */
    #define INPUT_CHANNELS     (3)
    
    /** \brief Uart handler for DAP operations */
    UART_Instance gUART;
    
    /** \brief Output buffer for live preview data */
    uint8_t livePrev_Output[5];
    
    /** \brief Detected class for live preview */
    volatile uint8_t livePrev_detClass = 0;
    
    /** \brief Model input buffer */
    int8_t if_map[1][3][128][1];
    
    /** \brief Model output buffer */
    int8_t of_map[1][4];
    
    /** \brief Data points of a single frame */
    q15_t input_data_q15[FE_FRAME_SIZE];
    
    /** \brief Extracted features of a single frame */
    int8_t featuresPerFrame[FE_FEATURE_SIZE_PER_FRAME];
    
    /** \brief Complete feature set for model input */
    int8_t totalFeatures[FE_FEATURE_SIZE_PER_FRAME*FE_NUM_FRAME_CONCAT*INPUT_CHANNELS];
    
    /** \brief Circular class buffer to determine which class LED has to glow */
    uint8_t classBuffer[SAMPLE_BUFFER_SIZE];
    /** \brief Index for the class buffer */
    uint8_t classBufferIndex = 0;
    
    /** \brief Sensor configuration structure */
    sensorStruct sensor;
    
    /** \brief Initialises the sensor */
    void sensor_init()
    {
        sensor.spiReg      = SPI_0_INST;      /* SPI instance for sensor */
        sensor.csPort      = SPI_PORT;        /* Chip select port */
        sensor.csPin       = SPI_CS_0_PIN;    /* Chip select pin */
        sensor.readyPort   = SENSOR_PORT;     /* Ready port for sensor */
        sensor.readyPin    = SENSOR_RDY_1_PIN;/* Ready pin for sensor */
        sensor.sensorMode  = SENSOR_MODE_IDLE;/* Set sensor to idle mode */
    
        DL_GPIO_disableInterrupt(SENSOR_PORT, SENSOR_RDY_1_PIN); /* Disable sensor interrupt initially */
    }
    
    /** \brief Feature extraction from the vibration data */
    void motor_FE()
    {
        for(int i=0;i< FE_NUM_FRAME_CONCAT ;i++)
        {
            for(int j = 0 ; j < FE_FRAME_SIZE ; j++)
            {
                input_data_q15[j] = dataBuff.xData[i*FE_FRAME_SIZE + j];
            }
            FE_process(input_data_q15, featuresPerFrame, 0);
    
            /* Append Extracted features */
            memcpy(&totalFeatures[0*(FE_NUM_FRAME_CONCAT*FE_FEATURE_SIZE_PER_FRAME) + (i*FE_FEATURE_SIZE_PER_FRAME)], featuresPerFrame, sizeof(featuresPerFrame));
        }
    
        for(int i=0;i< FE_NUM_FRAME_CONCAT ;i++)
        {
            for(int j = 0 ; j < FE_FRAME_SIZE ; j++)
            {
                input_data_q15[j] = dataBuff.yData[i*FE_FRAME_SIZE + j];
            }
            FE_process(input_data_q15, featuresPerFrame, 1);
    
            /* Append Extracted features */
            memcpy(&totalFeatures[1*(FE_NUM_FRAME_CONCAT*FE_FEATURE_SIZE_PER_FRAME) + (i*FE_FEATURE_SIZE_PER_FRAME)], featuresPerFrame, sizeof(featuresPerFrame));
        }
    
        for(int i=0;i< FE_NUM_FRAME_CONCAT ;i++)
        {
            for(int j = 0 ; j < FE_FRAME_SIZE ; j++)
            {
                input_data_q15[j] = dataBuff.zData[i*FE_FRAME_SIZE + j];
            }
            FE_process(input_data_q15, featuresPerFrame, 2);
    
            /* Append Extracted features */
            memcpy(&totalFeatures[2*(FE_NUM_FRAME_CONCAT*FE_FEATURE_SIZE_PER_FRAME) + (i*FE_FEATURE_SIZE_PER_FRAME)], featuresPerFrame, sizeof(featuresPerFrame));
        }
    
        int index=0;
        /* Store the extracted features in the if_map*/
        for(int i=0;i<INPUT_CHANNELS;i++)
        {
            for(int j=0;j<FE_NUM_FRAME_CONCAT*FE_FEATURE_SIZE_PER_FRAME;j++)
            {
                if_map[0][i][j][0] = totalFeatures[index];
                index++;
            }
        }
    
    }
    
    /** \brief Glow LED based on which class has the maximum frequency */
    void glow_led()
    {
        uint8_t freq[4] = {0, 0, 0, 0};
    
        /* Calculate the frequency of the classes */
        for(int i=0;i<SAMPLE_BUFFER_SIZE;i++)
        {
            if(classBuffer[i])
            {
                freq[classBuffer[i]-1]++;
            }
        }
    
        /* Find class with maximum frequency */
        uint8_t maximum_freq_class = 0;
        for(int i=1;i<4;i++){
            if(freq[maximum_freq_class]<freq[i]){
                maximum_freq_class = i;
            }
        }
    
        /* Store detected class */
        livePrev_detClass = maximum_freq_class;
    
        /* Run only if the buffer is completely filled */
        if(freq[0]+freq[1]+freq[2]+freq[3] == SAMPLE_BUFFER_SIZE)
        {
            /* Clear all LEDs first */
            DL_GPIO_clearPins(GPIO_LEDS_LED1_PORT, GPIO_LEDS_LED1_PIN);
            DL_GPIO_clearPins(GPIO_LEDS_LED2_PORT, GPIO_LEDS_LED2_PIN);
            DL_GPIO_clearPins(GPIO_LEDS_LED3_PORT, GPIO_LEDS_LED3_PIN);
    
            /* Set LED(s) based on detected class */
            // switch(maximum_freq_class)
            // {
            //     case 0:
            //         DL_GPIO_setPins(GPIO_LEDS_LED1_PORT, GPIO_LEDS_LED1_PIN);
            //         break;
            //     case 1:
            //         DL_GPIO_setPins(GPIO_LEDS_LED2_PORT, GPIO_LEDS_LED2_PIN);
            //         break;
            //     case 2:
            //         DL_GPIO_setPins(GPIO_LEDS_LED3_PORT, GPIO_LEDS_LED3_PIN);
            //         break;
            //     case 3:
            //         DL_GPIO_setPins(GPIO_LEDS_LED1_PORT, GPIO_LEDS_LED1_PIN);
            //         DL_GPIO_setPins(GPIO_LEDS_LED2_PORT, GPIO_LEDS_LED2_PIN);
            //         DL_GPIO_setPins(GPIO_LEDS_LED3_PORT, GPIO_LEDS_LED3_PIN);
            //         break;
            //     default:
            //         break;
            // }
            switch(maximum_freq_class)
            {
                case 0:
                    DL_GPIO_setPins(GPIO_LEDS_LED1_PORT, GPIO_LEDS_LED1_PIN);
                    DL_GPIO_setPins(GPIO_LEDS_LED2_PORT, GPIO_LEDS_LED2_PIN);
                    DL_GPIO_setPins(GPIO_LEDS_LED3_PORT, GPIO_LEDS_LED3_PIN);
                    break;
                case 1:
                    DL_GPIO_setPins(GPIO_LEDS_LED2_PORT, GPIO_LEDS_LED2_PIN);
                    break;
                case 2:
                    DL_GPIO_setPins(GPIO_LEDS_LED3_PORT, GPIO_LEDS_LED3_PIN);
                    break;
                case 3:
                    DL_GPIO_setPins(GPIO_LEDS_LED1_PORT, GPIO_LEDS_LED1_PIN);
                    break;
                default:
                    break;
            }
        }
    }
    
    /** \brief Append output class with maximum frequency to the buffer */
    void add_output_class()
    {
        /* Find class with maximum +ve value */
        uint8_t output_class = 0;
        for(int i=1;i<4;i++){
            if(of_map[0][output_class]<of_map[0][i]){
                output_class = i;
            }
        }
    
        classBuffer[classBufferIndex] = output_class + 1;
        classBufferIndex = (classBufferIndex + 1)%SAMPLE_BUFFER_SIZE;
    }
    
    /** \brief Main function */
    int main(void)
    {
        SYSCFG_DL_init();
    
        NVIC_EnableIRQ(DMA_INT_IRQn);
    
        UART_init(&gUART);
    
        DL_DMA_clearInterruptStatus(DMA, DL_DMA_FULL_CH_INTERRUPT_EARLY_CHANNEL1);
        DL_DMA_enableInterrupt(DMA, DL_DMA_FULL_CH_INTERRUPT_EARLY_CHANNEL1);
        DL_DMA_Full_Ch_setEarlyInterruptThreshold(DMA, DMA_CH0_CHAN_ID, DL_DMA_EARLY_INTERRUPT_THRESHOLD_64);
    
        DL_GPIO_clearInterruptStatus(SENSOR_PORT, SENSOR_RDY_1_PIN);
        DL_GPIO_disableInterrupt(SENSOR_PORT, SENSOR_RDY_1_PIN);
    
        DL_GPIO_clearPins(GPIO_LEDS_LED1_PORT, GPIO_LEDS_LED1_PIN);
        DL_GPIO_clearPins(GPIO_LEDS_LED2_PORT, GPIO_LEDS_LED2_PIN);
        DL_GPIO_clearPins(GPIO_LEDS_LED3_PORT, GPIO_LEDS_LED3_PIN);
    
        /* Power up the neural processing unit (NPU) module.
         * Clear and enable the NPU interrupt.
         * NPU interrupts will be handled inside the NPU
         * library created during model compilation (outside of this project).
         * NOTE: In future releases, the NPU initialization calls made below
         * may be transitioned to SysConfig generated calls placed in
         * the auto-generatead ti_msp_dl_config.h/.c files, at which point
         * they will be handled inside of the SYSCFG_DL_init() call.
         */
        DL_NPU_reset(NPU);
        DL_NPU_enablePower(NPU);
        while (!(DL_SYSCTL_getStatus() & DL_SYSCTL_STATUS_NPU_READY));
        DL_NPU_clearInterruptStatus(NPU, DL_NPU_INTERRUPT_DONE);
        DL_NPU_enableInterrupt(NPU, DL_NPU_INTERRUPT_DONE);
        NVIC_EnableIRQ(NPU_INT_IRQn);
    
        struct tvmgen_default_inputs tvm_if_map = {(void*) &if_map[0]};
        struct tvmgen_default_outputs tvm_of_map = {(void*) &of_map[0]};
    
        /* Initialize ARM library for real FFT */
        FE_init();
    
        /* Sensor initialization */
        sensor_init();
        ADXL355_config(sensor);
        ADXL355_enableInterrupts(&sensor);
        NVIC_EnableIRQ(SENSOR_INT_IRQN);
    
        /* If write and read were successful, toggle LED */
        while (1)
        {
            /* DAP: Check for incoming UART commands */
            UART_checkForCommand(&gUART);
    
            if(gUART.RxStatus == UART_STATUS_PROCESS_FRAME)
            {
                DAP_processBuffer(&gUART);
                gUART.RxStatus = UART_STATUS_IDLE;
                DMA_RX_init(&gUART);
            }
    
            if(gStartStream)
            {
                if(gPipelineConfig.mode == PIPELINE_MODE_SENSOR_INFERENCE)
                {
                    /* Prepare and send live preview output over UART */
                    // livePrev_Output[0] = livePrev_detClass;
                    // livePrev_Output[1] = (int8_t) of_map[0][0] ;
                    // livePrev_Output[2] = (int8_t) of_map[0][1] ;
                    // livePrev_Output[3] = (int8_t) of_map[0][2] ;
                    // livePrev_Output[4] = (int8_t) of_map[0][3] ;
    
                    //Add for GUI
                    livePrev_Output[0] = livePrev_detClass;
                    livePrev_Output[1] = (int8_t) of_map[0][3] ;
                    livePrev_Output[2] = (int8_t) of_map[0][1] ;
                    livePrev_Output[3] = (int8_t) of_map[0][2] ;
                    livePrev_Output[4] = (int8_t) of_map[0][0] ;
    
                    DAP_receivedDataResponse(&gUART, Resp_CMD_Channel_inferencing_value, 5, &livePrev_Output[0]);
                }
            }
    
            if(dataReady)
            {
                ADXL355_disableInterrupts(&sensor);
    
                /* Give data to feature extraction and model */
                motor_FE();
    
                /* Run Inference */
                tvmgen_default_run(&tvm_if_map, &tvm_of_map);
    
                while(!tvmgen_default_finished);
    
                /* Add the output class with maximum value to the classBuffer */
                add_output_class();
    
                /* Glow the respective led for the class with maximum frequency */
                glow_led();
    
                ADXL355_enableInterrupts(&sensor);
    
                NVIC_EnableIRQ(SENSOR_INT_IRQN);
                buffCount = 0;
                dataReady = 0;
            }
        }
    }
    
    /** \brief Interrupt handler for reading SPI data from the sensor */
    void GROUP1_IRQHandler(void)
    {
        switch (DL_GPIO_getPendingInterrupt(SENSOR_PORT))
        {
        case SENSOR_RDY_1_IIDX:
            if(sensor.sensorMode == SENSOR_MODE_COLLECT_DATA)
            {
                SPI_readByte(sensor, ADXL355_STATUS_ADDRESS);
                ADXL355_getData(sensor);
            }
            else
            {
                /* Incorrect interrupt  */
            }
            break;
    
        default:
    
            break;
        }
    }
    
    /** \brief DMA interrupt handler for UART/DAP data transfer */
    void DMA_IRQHandler(void)
    {
        switch (DL_DMA_getPendingInterrupt(DMA))
        {
            case DL_DMA_FULL_CH_EVENT_IIDX_EARLY_IRQ_DMACH1:
                DAP_getFrameLength(&gUART);
                gUART.RxStatus = UART_STATUS_RX_BUFFERING;
                break;
    
            case DL_DMA_EVENT_IIDX_DMACH0:
                gUART.TxStatus = UART_STATUS_IDLE;
                break;
    
            default:
                break;
        }
    }
    

    Best regards,
    O.H