这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【Let'sdo2026第3期】成果基于边缘AI的电机状态检测数据采集,训练和部

共1条 1/1 1 跳转至页

【Let'sdo2026第3期】成果基于边缘AI的电机状态检测数据采集,训练和部署

高工
2026-11-01 18:05:30     打赏

        这一步应该算是这个项目的核心了,需要依赖工具NanoEdge AI Studio,这个工具需要去st官网上下载,下载链接为:NanoEdgeAIStudio | 软件 - 意法半导体 ,下载时会要求输入邮箱信息,用于接收激活码。

环境安装

        下载到的软件叫NanoEdge-AI-Studio-Setup-xxx.exe,其中xxx是版本号,我下载的版本是5.2.0。双击这个软件便可执行安装步骤,安装过程中会弹出输入序列号激活的内容,此时需要去邮箱中找到对应的邮件复制序列号贴进来继续安装。

数据采集

创建工程

        安装完工具后,便是双击安装好的软件NanoEdge AI Studio,此时会弹出主界面,在界面中选择 “+ NEW PROJECT”,在弹出的界面中选择分类(Classify variations of your signal)模式,此时项目已创建。

        在新建的项目中,把Target选择为NUCLEO-C562RE,同时在sensor里选择三轴加速度传感器(感觉可能不需要选也行,默认就是三轴,问题是也没看到有开关),之后点击SAVE,工程创建下来了

数据提供

数据格式确认

        保存完工程后,界面就自动弹到了signal页面了,这个我的理解就是采集原始数据的。在正常数据页面点击 ADD SIGNAL按钮,就弹出了添加数据的界面:

图片1.jpg

        这个页面很直接地说明了可以在线获取数据,也可以直接按照右侧的格式提供数据,这里我选择了右侧的方法(自己控制电机,同时读取加速度传感器数据并打印)

硬件准备

        到这一步,就是需要把所有模块都接上测试了,最终的接线如下(其实就是打通驱动时的接线方法,唯一新增的是用胶布把加速度传感器贴到步进电机上了):

图片1.jpg

数据获取

        这里还是交给AI实现了,由于前面加驱动和验证驱动效果的实现时,有要求驱动写法是通过注册方式实现的,因此在这一步的采集的修改就变得很简单了,要求AI加宏注释掉测试的业务代码,再增加一个采集数据的任务就行了。

        这个任务设定为:先启动测试环境,待测试环境启动后,启动加速度传感器采集并打印数据,打印够256个后,关闭加速度传感器并终止测试环境。测试环境有三个:静止、转动、堵转(最后验证还是转,最早调试驱动时遇到的问题,给了波形,但电机只有内部齿轮声,不转且发热。但自己理解并记录的原因是错误的)。

        最终核心的采集代码如下:

app_data_acq

/** Self-contained state capture; no dependency on the legacy application apps. */
#include "app_data_acq.h"
#include "drv_lis3dh.h"
#include "drv_gear_stepper.h"
#include "mx_tim1.h"
#include "mx_usart2.h"
#include <inttypes.h>
#include <stdio.h>
#include <string.h>

static drv_lis3dh_t s_sensor;
static drv_gear_stepper_t s_motor;
static app_data_acq_result_t s_acq_result;
static volatile drv_gear_stepper_status_t s_irq_error;
static uint32_t s_previous_sample_ms;
static uint32_t s_wait_started_ms;

static int app_data_acq_marker(uint32_t begin)
{
    static const char *const names[] = {"static", "normal", "abnormal"};
    char line[96];
    int length;
    if (begin != 0U)
    {
        length = snprintf(line, sizeof(line),
                          "# ACQ BEGIN mode=%s samples=%u\r\n",
                          names[s_acq_result.mode], (unsigned)APP_ACQ_SAMPLE_COUNT);
    }
    else
    {
        length = snprintf(line, sizeof(line),
                          "# ACQ END mode=%s count=%" PRIu32 " status=%d\r\n",
                          names[s_acq_result.mode], s_acq_result.sample_count,
                          (int)s_acq_result.status);
    }
    return (length > 0) && ((size_t)length < sizeof(line)) &&
           (HAL_UART_Transmit(mx_usart2_uart_gethandle(), line,
                              (uint32_t)length, 100U) == HAL_OK);
}

/* Mode completion is dispatched in the foreground after motor cleanup. */
static void app_data_acq_mode_done(void)
{
    drv_lis3dh_status_t cleanup = drv_lis3dh_deinit(&s_sensor);
    s_acq_result.mode_done_count++;
    if (cleanup != DRV_LIS3DH_OK)
    {
        s_acq_result.sensor_status = cleanup;
        if (s_acq_result.status == APP_DATA_ACQ_OK)
        {
            s_acq_result.status = APP_DATA_ACQ_SENSOR_ERROR;
        }
    }
    s_acq_result.finished_ms = HAL_GetTick();
    if (!app_data_acq_marker(0U))
    {
        s_acq_result.status = APP_DATA_ACQ_UART_ERROR;
    }
    if (s_acq_result.status != APP_DATA_ACQ_OK)
    {
        s_acq_result.state = APP_DATA_ACQ_FAILED;
    }
    else if (s_acq_result.mode == APP_ACQ_MODE_ABNORMAL)
    {
        s_acq_result.state = APP_DATA_ACQ_DONE;
    }
    else
    {
        s_wait_started_ms = HAL_GetTick();
        s_acq_result.state = APP_DATA_ACQ_WAITING;
    }
}

static void app_data_acq_finish(void)
{
    drv_gear_stepper_status_t status = drv_gear_stepper_deinit(&s_motor);
    if (status != DRV_GEAR_STEPPER_OK)
    {
        s_acq_result.motor_status = status;
        if (s_acq_result.status == APP_DATA_ACQ_OK)
        {
            s_acq_result.status = APP_DATA_ACQ_MOTOR_ERROR;
        }
    }
    app_data_acq_mode_done();
}

void app_data_acq_init(void)
{
    memset(&s_acq_result, 0, sizeof(s_acq_result));
    s_wait_started_ms = HAL_GetTick();
    s_acq_result.state = APP_DATA_ACQ_WAITING;
}

static void app_data_acq_start(uint32_t mode)
{
    drv_lis3dh_config_t sensor_config;
    drv_gear_stepper_config_t motor_config;
    drv_lis3dh_data_t discarded;
    uint32_t done_count = s_acq_result.mode_done_count;
    memset(&s_acq_result, 0, sizeof(s_acq_result));
    s_acq_result.mode_done_count = done_count;
    s_acq_result.mode = mode;
    s_acq_result.state = APP_DATA_ACQ_RUNNING;
    s_irq_error = DRV_GEAR_STEPPER_OK;
    if (!app_data_acq_marker(1U))
    {
        s_acq_result.status = APP_DATA_ACQ_UART_ERROR;
        app_data_acq_finish();
        return;
    }
    /* Also retries a retained binding after an earlier failed motor cleanup. */
    s_acq_result.motor_status = drv_gear_stepper_deinit(&s_motor);
    if (s_acq_result.motor_status != DRV_GEAR_STEPPER_OK)
    {
        s_acq_result.status = APP_DATA_ACQ_MOTOR_ERROR;
        app_data_acq_finish();
        return;
    }
    drv_lis3dh_get_default_config(&sensor_config);
    sensor_config.address_7bit = DRV_LIS3DH_ADDRESS_HIGH;
#if APP_ACQ_SAMPLE_RATE_HZ == 50U
    sensor_config.odr = LIS3DH_ODR_50_HZ;
#elif APP_ACQ_SAMPLE_RATE_HZ == 100U
    sensor_config.odr = LIS3DH_ODR_100_HZ;
#elif APP_ACQ_SAMPLE_RATE_HZ == 200U
    sensor_config.odr = LIS3DH_ODR_200_HZ;
#else
    sensor_config.odr = LIS3DH_ODR_400_HZ;
#endif
    s_acq_result.sensor_status = drv_lis3dh_init(&s_sensor, &sensor_config);
    if (s_acq_result.sensor_status == DRV_LIS3DH_OK)
    {
        /* Do not count the sample produced before the selected mode starts. */
        s_acq_result.sensor_status = drv_lis3dh_read(&s_sensor, &discarded);
    }
    if ((s_acq_result.sensor_status != DRV_LIS3DH_OK) &&
        (s_acq_result.sensor_status != DRV_LIS3DH_NO_DATA))
    {
        s_acq_result.status = APP_DATA_ACQ_SENSOR_ERROR;
        app_data_acq_finish();
        return;
    }
    if (mode != APP_ACQ_MODE_STATIC)
    {
        drv_gear_stepper_get_default_config(&motor_config);
        motor_config.step_interval_ms = mode == APP_ACQ_MODE_NORMAL ?
            APP_ACQ_NORMAL_STEP_MS : APP_ACQ_ABNORMAL_STEP_MS;
        s_acq_result.motor_status = drv_gear_stepper_init(&s_motor, &motor_config);
        if (s_acq_result.motor_status == DRV_GEAR_STEPPER_OK)
        {
            /* Foreground stops this move once the sample target is reached. */
            s_acq_result.motor_status = drv_gear_stepper_move(
                &s_motor, GEAR_STEPPER_FORWARD, 1000000U);
        }
        if (s_acq_result.motor_status != DRV_GEAR_STEPPER_OK)
        {
            s_acq_result.status = APP_DATA_ACQ_MOTOR_ERROR;
            app_data_acq_finish();
            return;
        }
    }
    s_acq_result.started_ms = HAL_GetTick();
}

void app_data_acq_loop(void)
{
    static const uint32_t mode_order[] = {
        APP_ACQ_MODE_NORMAL, APP_ACQ_MODE_STATIC, APP_ACQ_MODE_ABNORMAL
    };
    drv_lis3dh_data_t sample;
    char line[48];
    int length;
    uint32_t now;
    if (s_acq_result.state == APP_DATA_ACQ_WAITING)
    {
        if ((uint32_t)(HAL_GetTick() - s_wait_started_ms) < APP_ACQ_SETTLE_MS)
        {
            return;
        }
        app_data_acq_start(mode_order[s_acq_result.mode_done_count]);
    }
    if (s_acq_result.state != APP_DATA_ACQ_RUNNING)
    {
        return;
    }
    if (s_irq_error != DRV_GEAR_STEPPER_OK)
    {
        s_acq_result.motor_status = s_irq_error;
        s_acq_result.status = APP_DATA_ACQ_MOTOR_ERROR;
        app_data_acq_finish();
        return;
    }
    now = HAL_GetTick();
    if ((uint32_t)(now - s_acq_result.started_ms) >= APP_ACQ_TIMEOUT_MS)
    {
        s_acq_result.status = APP_DATA_ACQ_TIMEOUT;
        app_data_acq_finish();
        return;
    }
    s_acq_result.sensor_status = drv_lis3dh_read(&s_sensor, &sample);
    if (s_acq_result.sensor_status == DRV_LIS3DH_NO_DATA)
    {
        return;
    }
    if (s_acq_result.sensor_status != DRV_LIS3DH_OK)
    {
        s_acq_result.status = APP_DATA_ACQ_SENSOR_ERROR;
        app_data_acq_finish();
        return;
    }
    now = HAL_GetTick();
    length = snprintf(line, sizeof(line), "%" PRId32 " %" PRId32 " %" PRId32 "\r\n",
                      sample.x_mg, sample.y_mg, sample.z_mg);
    if ((length <= 0) || ((size_t)length >= sizeof(line)) ||
        (HAL_UART_Transmit(mx_usart2_uart_gethandle(), line, (uint32_t)length, 100U) != HAL_OK))
    {
        s_acq_result.status = APP_DATA_ACQ_UART_ERROR;
        app_data_acq_finish();
        return;
    }
    if (s_acq_result.sample_count > 0U)
    {
        uint32_t gap = now - s_previous_sample_ms;
        if (gap > s_acq_result.max_sample_gap_ms)
        {
            s_acq_result.max_sample_gap_ms = gap;
        }
    }
    s_previous_sample_ms = now;
    if (++s_acq_result.sample_count == APP_ACQ_SAMPLE_COUNT)
    {
        app_data_acq_finish();
    }
}

void HAL_TIM_UpdateCallback(hal_tim_handle_t *timer)
{
    if (timer == mx_tim1_gethandle())
    {
        drv_gear_stepper_status_t status = drv_gear_stepper_on_timer_update(&s_motor);
        if ((status != DRV_GEAR_STEPPER_OK) && (status != DRV_GEAR_STEPPER_RUNNING))
        {
            s_irq_error = status;
        }
    }
}

const app_data_acq_result_t *app_data_acq_get_result(void)
{
    return &s_acq_result;
}

main

int main(void)
{
  /** System Init: this code placed in targets folder initializes your system.
    * It calls the initialization (and sets the initial configuration) of the peripherals.
    * You can use STM32CubeMX to generate and call this code or not in this project.
    * It also contains the HAL initialization and the initial clock configuration.
    */
  if (mx_system_init() != SYSTEM_OK)
  {
    return (-1);
  }
  else
  {
    /*
      * You can start your application code here
      */
    app_data_acq_init();

    while (1) {
      app_data_acq_loop();
      HAL_Delay(1U);
    }
  }
} /* end main */

采集到的数据

        程序运行后,串口输出了以下内容(其中,abnormal是无效数据,我对之前遇到的只发热不转问题的根因的理解错误,实际按照理解去复现时,还是转动的)

# ACQ BEGIN mode=normal samples=256
-108 -384 876
288 -408 876
-336 -264 852
108 -324 900
252 -240 996
36 -144 1104
-228 -132 1080
-240 -132 1068
-84 -144 996
312 -144 996
336 -156 1044
-48 -12 1080
-156 -48 1176
-72 -72 1164
-48 -72 1284
264 -108 1392
228 -96 1464
-60 -36 1476
-132 -96 1452
-180 -24 1428
48 -72 1356
360 -72 1308
324 -72 1200
-60 24 1176
-12 -24 1152
108 -12 1104
240 0 1092
600 0 1092
312 96 1068
84 96 1080
132 108 1020
-24 132 1008
276 24 936
432 -24 936
156 0 948
-48 0 960
-108 48 1008
84 48 972
384 24 984
480 -12 948
-24 72 864
-96 -24 804
-48 -24 708
0 36 684
288 -12 696
108 -48 720
-216 -48 804
-228 -60 888
-420 -84 948
-144 -84 1056
216 -96 1164
36 -48 1236
-396 -48 1236
-456 -72 1260
-360 -72 1188
-96 -60 1128
36 -84 1092
-456 -96 996
-588 -108 888
-552 -144 804
-660 -156 804
-168 -84 852
-192 -108 816
-576 -144 876
-720 -120 852
-1032 -180 852
-744 -132 996
-276 0 1056
-480 -60 1080
-936 -84 1104
-960 -96 1032
-948 -156 1104
-732 -96 1236
-384 -108 1308
-720 -204 1272
-900 -168 1368
-828 -180 1296
-1044 -216 1284
-528 -156 1332
-204 -84 1212
-444 -132 1176
-624 -108 1056
-732 -192 864
-564 -192 864
-120 -108 888
-24 -132 804
-456 -144 792
-516 -108 768
-504 -144 732
-600 -144 852
-204 -120 840
-240 -192 696
-636 -252 756
-732 -252 732
-1080 -396 672
-780 -336 732
-360 -264 720
-588 -408 624
-1020 -456 600
-1020 -444 552
-1008 -504 480
-756 -396 492
-408 -348 492
-660 -444 432
-744 -348 492
-552 -312 480
-696 -348 468
-264 -228 552
-24 -216 456
-432 -384 384
-672 -432 324
-828 -564 264
-636 -564 252
-192 -372 264
-180 -432 216
-600 -564 120
-696 -468 156
-708 -564 96
-744 -600 96
-312 -516 132
-360 -624 132
-660 -720 156
-696 -648 204
-900 -684 204
-528 -576 204
-96 -420 216
-276 -516 156
-624 -540 132
-624 -528 144
-588 -600 96
-312 -468 156
-48 -444 180
-432 -612 120
-660 -588 276
-684 -636 240
-864 -672 288
-348 -588 396
-120 -552 432
-432 -600 492
-612 -564 468
-780 -600 432
-552 -576 540
-60 -408 660
-12 -468 684
-396 -468 804
-420 -396 828
-396 -468 840
-360 -384 900
108 -312 912
48 -312 924
-336 -288 888
-336 -348 792
-408 -228 780
-156 -168 768
312 -84 816
288 -96 876
-12 12 912
12 -48 948
108 12 900
252 48 948
648 84 1068
336 72 1044
-24 24 1200
-60 -60 1368
-132 -48 1488
144 -60 1620
468 -60 1704
336 0 1692
-72 60 1584
-48 0 1416
24 48 1152
300 24 972
456 12 936
108 144 792
-60 168 756
72 180 660
60 192 648
408 168 672
516 168 684
132 192 768
0 144 792
-216 108 792
-96 72 780
264 48 840
120 12 924
-408 -48 1020
-384 -216 1188
-228 -216 1320
-24 -132 1512
324 -120 1668
144 0 1680
-96 60 1632
-48 60 1488
-228 72 1332
96 48 1212
360 96 1128
84 120 1044
-144 120 960
-240 108 912
-144 108 888
252 108 828
276 24 852
-228 -36 840
-300 0 804
-372 -12 804
-480 -48 840
-60 -60 840
-240 -132 864
-660 -168 960
-696 -156 972
-1008 -216 1044
-636 -180 1164
-156 -72 1164
-408 -132 1188
-828 -108 1188
-792 -96 1104
-732 -60 1104
-360 96 1176
-36 144 1092
-444 132 1068
-576 144 1032
-564 84 912
-744 48 936
-276 84 960
-144 84 900
-552 -36 984
-708 -60 936
-936 -156 912
-684 -144 1008
-216 -48 1056
-276 -108 1032
-756 -192 1044
-720 -168 996
-744 -192 996
-672 -156 1092
-240 -108 1056
-384 -180 948
-636 -156 996
-660 -156 912
-936 -228 948
-540 -120 984
-132 -48 900
-408 -120 840
-732 -132 804
-816 -204 708
-696 -204 708
-324 -60 804
-144 -120 696
-588 -300 600
-720 -252 636
-744 -348 576
-912 -408 576
-432 -312 576
-372 -348 504
-744 -444 504
-840 -408 492
# ACQ END mode=normal count=256 status=0
# ACQ BEGIN mode=static samples=256
-96 -72 1212
-96 -84 1236
-72 -84 1248
-36 -84 1236
-12 -84 1260
12 -72 1236
-24 -72 1224
-12 -48 1188
0 -24 1104
-24 -24 1020
-72 -12 996
-96 -36 960
-144 -48 960
-168 -72 936
-228 -120 936
-264 -132 948
-300 -156 960
-336 -120 960
-336 -144 984
-360 -144 1056
-384 -156 1116
-396 -156 1152
-372 -156 1188
-372 -132 1212
-360 -108 1212
-372 -36 1188
-348 12 1140
-372 0 1080
-372 0 1020
-360 -24 996
-372 -48 984
-372 -72 996
-384 -84 1032
-372 -84 1116
-372 -84 1200
-372 -84 1200
-336 -108 1224
-324 -132 1212
-312 -96 1188
-312 -132 1140
-324 -132 1080
-288 -120 1020
-252 -132 960
-276 -132 876
-276 -144 816
-312 -168 756
-348 -192 756
-360 -204 756
-372 -228 792
-384 -252 792
-384 -228 768
-432 -276 804
-480 -300 804
-528 -312 828
-552 -324 840
-504 -348 840
-492 -348 828
-492 -348 804
-504 -360 792
-480 -372 732
-480 -372 660
-492 -372 612
-516 -396 588
-504 -432 516
-516 -456 492
-504 -456 492
-468 -492 480
-396 -444 504
-348 -408 456
-264 -372 408
-240 -276 360
-216 -240 276
-192 -228 216
-192 -264 168
-264 -312 156
-396 -348 168
-480 -384 204
-540 -468 228
-612 -528 264
-600 -588 300
-588 -564 384
-600 -564 504
-588 -576 564
-576 -588 600
-552 -564 600
-480 -540 612
-432 -480 636
-408 -420 588
-360 -384 540
-348 -348 540
-324 -336 528
-264 -276 516
-228 -252 696
-168 -204 696
-108 -204 684
-132 -180 756
-96 -132 816
-48 -108 828
-12 -84 864
-12 -72 912
-24 -60 1008
-60 -72 1080
-96 -96 1152
-120 -120 1260
-60 -108 1380
24 -60 1452
12 0 1560
-24 -12 1572
-72 -12 1524
-132 -48 1416
-144 -72 1332
-120 -84 1296
-72 -36 1272
-24 48 1224
48 84 1176
84 96 1056
144 144 1032
96 168 960
60 168 864
48 132 768
36 132 708
60 108 732
72 96 744
48 72 816
60 24 864
24 -24 984
24 -60 1128
0 -84 1224
-12 -96 1272
0 -144 1308
-24 -120 1344
-24 -60 1296
12 -36 1260
12 -36 1164
-12 -24 1080
0 -12 1020
0 -24 948
-36 -60 936
-108 -60 912
-156 -60 900
-180 -72 888
-216 -108 936
-252 -120 948
-264 -132 1020
-276 -144 1092
-300 -132 1164
-300 -120 1212
-312 -108 1260
-324 -108 1260
-288 -96 1272
-324 -84 1212
-324 -72 1176
-360 -36 1116
-384 0 1068
-396 -24 1056
-432 -48 1008
-492 -84 996
-516 -108 1032
-540 -144 1032
-528 -156 1044
-540 -144 1068
-540 -156 1068
-516 -144 1056
-456 -144 1056
-420 -132 1032
-396 -144 1008
-384 -156 984
-396 -156 972
-396 -180 936
-384 -192 888
-372 -192 888
-324 -192 888
-348 -204 852
-372 -192 864
-384 -204 828
-408 -192 828
-420 -252 840
-492 -264 792
-468 -288 744
-504 -300 744
-552 -300 732
-564 -288 720
-576 -312 672
-552 -348 600
-540 -336 588
-516 -348 588
-492 -360 576
-480 -372 516
-480 -360 516
-468 -348 528
-444 -336 528
-432 -300 540
-408 -288 552
-420 -312 576
-456 -360 612
-516 -396 624
-528 -396 612
-552 -420 600
-552 -432 588
-552 -456 600
-564 -492 612
-564 -492 588
-540 -468 576
-540 -456 552
-480 -420 528
-432 -396 516
-408 -348 528
-384 -300 564
-360 -252 576
-372 -276 552
-384 -276 528
-384 -300 528
-372 -312 516
-348 -288 504
-336 -264 492
-360 -264 492
-360 -300 504
-336 -264 492
-360 -228 588
-348 -204 588
-348 -228 636
-324 -216 732
-300 -204 792
-252 -168 852
-216 -144 936
-204 -108 972
-168 -84 1044
-156 -72 1056
-168 -84 1032
-180 -96 1020
-168 -96 1032
-168 -96 1056
-168 -72 1068
-144 -48 1056
-168 0 996
-168 12 1008
-180 0 1020
-156 12 1032
-168 -24 1080
-156 48 1164
-144 48 1284
-168 -24 1392
-180 -60 1464
-192 -72 1548
-180 -84 1560
-156 -60 1536
-72 -60 1536
-36 -36 1428
-12 -24 1368
-24 -12 1260
-36 -12 1140
-24 -36 1092
-72 -48 1032
-48 -72 996
-48 -84 1044
-60 -84 1140
# ACQ END mode=static count=256 status=0
# ACQ BEGIN mode=abnormal samples=256
216 180 888
360 144 876
312 120 888
48 96 840
156 48 864
120 -12 948
324 48 972
144 0 1008
-264 12 1020
180 -12 1116
288 36 1224
192 96 1236
156 144 1308
108 108 1296
396 108 1284
336 60 1284
-156 60 1248
216 72 1248
348 120 1188
288 132 1140
24 120 1140
-300 96 1116
108 36 1092
84 -12 1092
-408 -48 1092
-228 -72 1092
-144 -84 1080
-48 -60 1080
-204 -48 1068
-672 -108 1068
-216 -96 1044
-132 -96 1032
-432 -108 1008
-420 -120 1032
-396 -132 1056
-192 -108 1044
-300 -96 1044
-840 -156 1068
-396 -108 1056
-192 -72 1044
-396 -96 1080
-552 -108 1056
-732 -108 972
-276 -36 984
-276 -60 1068
-840 -156 984
-576 -192 936
-408 -144 948
-324 -144 1008
-552 -204 1020
-912 -288 936
-372 -204 948
-240 -144 1068
-696 -216 1008
-612 -264 924
-480 -216 888
-204 -168 960
-348 -144 960
-828 -264 840
-312 -204 792
-156 -144 888
-432 -192 864
-528 -276 792
-576 -300 672
-120 -192 780
-180 -168 888
-840 -348 780
-480 -336 732
-300 -264 768
-324 -276 792
-576 -348 756
-888 -396 684
-324 -276 720
-312 -252 648
-912 -360 540
-672 -432 468
-528 -396 468
-372 -384 456
-576 -468 396
-996 -588 360
-408 -492 408
-312 -420 432
-756 -504 444
-648 -576 480
-576 -492 456
-288 -444 456
-444 -480 396
-1056 -624 300
-492 -552 264
-324 -480 228
-612 -528 192
-660 -624 192
-696 -624 180
-288 -528 192
-348 -528 192
-960 -624 120
-540 -588 108
-300 -468 156
-396 -468 192
-588 -588 192
-864 -684 96
-312 -528 204
-192 -468 336
-768 -564 348
-528 -636 324
-336 -528 372
-144 -456 552
-312 -504 588
-804 -612 504
-216 -480 540
-24 -348 720
-468 -396 744
-444 -492 636
-432 -456 564
-72 -372 744
-204 -372 864
-828 -516 816
-276 -492 828
-36 -348 1020
-204 -384 1116
-240 -324 1200
-336 -348 1224
48 -264 1200
0 -288 1152
-552 -300 1176
-120 -324 1188
12 -240 1128
-24 -252 1080
-60 -180 1008
-144 -168 1008
288 -144 1008
252 -156 984
-156 -132 1044
96 -180 1080
132 -192 1056
252 -252 1056
156 -240 1128
-36 -180 1200
444 -156 1308
480 -120 1308
96 -72 1260
288 -48 1200
252 -48 1092
516 -48 948
360 -72 840
-12 -72 756
408 -120 720
408 -108 744
168 -144 792
276 -108 840
264 -72 876
588 -72 924
552 -72 984
144 24 996
516 -36 1056
552 -12 1116
420 -24 1080
384 0 1044
288 48 1008
624 12 972
624 -24 960
180 24 972
552 -48 1020
564 -24 1056
624 -48 1008
528 24 972
288 108 852
696 120 780
756 132 720
348 168 672
636 132 648
648 132 636
756 96 612
660 48 636
276 72 612
696 24 672
708 0 696
456 -24 708
540 -36 780
468 -12 852
708 -48 960
672 -84 1056
180 -84 1020
636 -84 1056
684 -48 1080
660 -60 1044
588 -24 1116
312 48 1056
624 24 1044
648 0 1020
168 -48 912
492 -72 864
552 -108 828
564 -96 744
468 -108 720
-132 -156 720
336 -276 816
480 -276 852
48 -312 816
-24 -444 984
-72 -480 1092
288 -408 1260
276 -396 1440
-480 -468 1416
-60 -468 1332
264 -336 1356
108 -288 1320
-96 -444 1236
-492 -480 1128
24 -360 1128
180 -348 1152
-672 -516 1104
-480 -672 1056
-168 -528 1056
-156 -456 1068
-264 -528 1008
-756 -612 804
-228 -468 756
108 -396 660
-456 -408 492
-252 -480 348
-84 -408 168
-36 -408 12
-48 -480 -84
-660 -612 -228
-192 -576 -216
0 -552 -240
-480 -564 -312
-444 -684 -300
-480 -648 -324
-228 -600 -312
-252 -636 -252
-936 -756 -384
-504 -744 -288
-384 -708 -192
-612 -744 -132
-672 -864 0
-864 -888 12
-408 -744 84
-372 -720 144
-936 -816 0
-552 -744 0
-432 -648 -48
-420 -636 -108
-528 -696 -120
-912 -792 -240
-456 -684 -216
-348 -648 -204
-816 -696 -264
-612 -732 -276
-576 -660 -276
-408 -660 -216
-480 -672 -108
-984 -804 -168
-456 -696 -84
-336 -648 -24
# ACQ END mode=abnormal count=256 status=0

        把这份log中的static(静止)和normal(正常转动)部分的log保存成txt文件即可。

数据训练

        把采集到的数据分别提交到数据并命名,之后点击save便会跳到Benchmark界面,在此界面中点击 New benchmark,分别选中刚刚添加的两份数据后保存,便自动进行了数据训练了(可能电脑性能不行,导致训练过程有点长,一轮训练下来3小时没了)。这里我试着把带噪数据(除了电机是否运转,手还拿着电机各个角度摆)和不带噪数据分别喂进去,发现如果是不带噪数据,训练准确度上升非常快。

图片1.jpg

        训练完成后,需要去Validation界面去选择一个认为OK的结果生成一个库,在此界面选择一个库,点击NEW EXPERIMENT,之后选择训练时提供的数据,便可以等待库的生成。

图片1.jpg

        之后在Deployment目录下,点击 DEPLOY LIBRARY按钮,就可以生成并下载训练好的库,右侧也贴出了参考实现代码,代码如下:

/* =============
    Copyright (c) 2026, STMicroelectronics

    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 the copyright holders 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 HOLDER / 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.*
*/
/**
  **************************************************************************
  * Demo: NanoEdge AI process to include in main program body
  *
  * @note  This program must be completed and customized by the user
  **************************************************************************
  */

/* Includes --------------------------------------------------------------------*/
#include "NanoEdgeAI.h"
/* Private define --------------------------------------------------------------*/
/* Private variables defined by user -------------------------------------------*/
float input_signal[NEAI_INPUT_SIGNAL_LENGTH * NEAI_INPUT_AXIS_NUMBER]; // Buffer of input values
float probabilities[NEAI_NUMBER_OF_CLASSES]; // Buffer of class probabilities
/* Private function prototypes defined by user ---------------------------------*/
/*
 * @brief Collect data process
 *
 * This function is defined by user, depends on applications and sensors
 *
 * @param input_signal: [in, out] buffer of sample values
 * @retval None
 * @note   If NEAI_INPUT_AXIS_NUMBER = 3 (cf NanoEdgeAI.h), the buffer must be
 *         ordered as follow:
 *         [x0 y0 z0 x1 y1 z1 ... xn yn zn], where xi, yi and zi
 *         are the values for x, y and z axes, n is equal to
 *         NEAI_INPUT_SIGNAL_LENGTH (cf NanoEdgeAI.h)
 */
void fill_buffer(float *input_signal)
{
	/* USER BEGIN */
	/* USER END */
}
/* -----------------------------------------------------------------------------*/
int main(void)
{
	/* Initialization ------------------------------------------------------------*/
	enum neai_state error_code = neai_classification_init();
	if (error_code != NEAI_OK) {
		/* Check the returned error code (cf NanoEdgeAI.h). */
	}

	/* Classification ------------------------------------------------------------*/
	int id_class = 0;
	while (1) {
		fill_buffer(input_signal);
		neai_classification(input_signal, probabilities, &id_class);
		/* USER BEGIN */
		/*
		* e.g.: Trigger functions depending on id_class
		* (print output class probabilities using probabilities[id_class],
		* print the name of the associated class using the getter function defined 
		* in NanoEdgeAI.h, neai_get_class_name(id_class),
		* blink LED, ring alarm, etc.).
		*/
		/* USER END */
	}
}

效果集成

        拿到了库,以及算法效果,那最后一步就是集成了,还是老方案,直接用一个测试app,这个app干两个事,一个是一直采集加速度传感器数据并喂给神经网络库,另一个是不定期(间隔时间至少1s,最多5s)地启动一两秒的电机,最终输出判定结果。

代码编写

        还是交给ai写的代码,这套代码的逻辑是通过硬件定时器驱动电机,主循环随机设置电机的启停,并一直读取三轴加速度传感器数据喂给大模型,并在大模型数据输出时做统计并输出结果(由于大模型训练不是100%的质量,因此在这里做了一个统计,只有连续20次读出状态变更了,才认为是状态变了,这样就能降低偶发性错误数据干扰判断)

/** Continuous mg sampling and pretrained classification with independent motor timing. */
#include "app_neai.h"
#include "NanoEdgeAI.h"
#include "drv_lis3dh.h"
#include "drv_gear_stepper.h"
#include "mx_tim1.h"
#include "mx_usart2.h"
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

#if (NEAI_INPUT_SIGNAL_LENGTH != 1) || (NEAI_INPUT_AXIS_NUMBER != 3) || \
    (NEAI_NUMBER_OF_CLASSES != APP_NEAI_CLASS_COUNT)
#error "This application requires the exported single-XYZ, two-class model"
#endif

#define APP_NEAI_TX_DEPTH 8U
#define APP_NEAI_TX_LENGTH 256U
/* A finite guard beyond the 1 s deadline; the IRQ normally cancels first. */
#define APP_NEAI_MOVE_STEPS (APP_NEAI_MOTOR_RUN_MS / APP_NEAI_NORMAL_STEP_MS + 2U)

static drv_lis3dh_t s_sensor;
static drv_gear_stepper_t s_motor;
static app_neai_result_t s_result;
static float s_input[NEAI_INPUT_SIGNAL_LENGTH * NEAI_INPUT_AXIS_NUMBER];
static const char *s_class_names[NEAI_NUMBER_OF_CLASSES];
static uint32_t s_initialized;
static uint32_t s_random;
static uint32_t s_off_started_ms;
static volatile uint32_t s_motor_on;
static volatile uint32_t s_motor_started_ms;
static volatile uint32_t s_motor_stopped_ms;
static volatile uint32_t s_stop_pending;
static volatile drv_gear_stepper_status_t s_irq_error;
static char s_tx_lines[APP_NEAI_TX_DEPTH][APP_NEAI_TX_LENGTH];
static uint32_t s_tx_lengths[APP_NEAI_TX_DEPTH];
static uint32_t s_tx_head;
static uint32_t s_tx_count;
static uint32_t s_tx_active;
static uint32_t s_tx_started_ms;
static uint32_t s_uart_failed;

/* Only the foreground formats/queues lines. An active slot is retained until
 * HAL reports TX idle, so the UART IRQ never reads a modified buffer. */
static int app_neai_log_state(uint32_t timestamp_ms, int id_class)
{
    /* UTF-8: class 0 = startup/running, class 1 = stationary. */
    static const char *const states[] = {
        "startup/running", "stationary"
    };
    uint32_t slot;
    int length;
    if ((s_uart_failed != 0U) || (s_tx_count == APP_NEAI_TX_DEPTH))
    {
        return 0;
    }
    slot = (s_tx_head + s_tx_count) % APP_NEAI_TX_DEPTH;
    length = snprintf(s_tx_lines[slot], APP_NEAI_TX_LENGTH,
                      "%" PRIu32 " %s\r\n", timestamp_ms, states[id_class]);
    if ((length <= 0) || ((uint32_t)length >= APP_NEAI_TX_LENGTH))
    {
        return 0;
    }
    s_tx_lengths[slot] = (uint32_t)length;
    s_tx_count++;
    return 1;
}

static int app_neai_tx_service(void)
{
    hal_uart_handle_t *uart = mx_usart2_uart_gethandle();
    if (s_uart_failed != 0U)
    {
        return 0;
    }
    if (s_tx_active != 0U)
    {
        if (HAL_UART_GetTxState(uart) != HAL_UART_TX_STATE_IDLE)
        {
            if ((uint32_t)(HAL_GetTick() - s_tx_started_ms) < APP_NEAI_UART_TIMEOUT_MS)
            {
                return 1;
            }
            s_uart_failed = 1U;
            (void)HAL_UART_AbortTransmit(uart);
            return 0;
        }
        s_tx_active = 0U;
        s_tx_head = (s_tx_head + 1U) % APP_NEAI_TX_DEPTH;
        s_tx_count--;
    }
    if (s_tx_count != 0U)
    {
        if (HAL_UART_Transmit_IT(uart, s_tx_lines[s_tx_head], s_tx_lengths[s_tx_head]) != HAL_OK)
        {
            s_uart_failed = 1U;
            return 0;
        }
        s_tx_active = 1U;
        s_tx_started_ms = HAL_GetTick();
    }
    return 1;
}

static void app_neai_fail(app_neai_status_t status)
{
    if (s_result.state == APP_NEAI_FAILED)
    {
        return;
    }
    s_result.state = APP_NEAI_FAILED;
    s_result.status = status;
    /* Deinit stops TIM1 before releasing coils/restoring high-Z. Preserve the
     * original error and retain separate cleanup status on failure. */
    s_result.motor_cleanup_status = drv_gear_stepper_deinit(&s_motor);
    s_motor_on = 0U;
    s_result.motor_on = 0U;
    s_result.sensor_cleanup_status = drv_lis3dh_deinit(&s_sensor);
}

static uint32_t app_neai_random_wait(void)
{
    /* Non-cryptographic xorshift. Sampling also mixes changing sensor bits into
     * the state; repeated resets are not guaranteed to produce different waits. */
    s_random ^= s_random << 13;
    s_random ^= s_random >> 17;
    s_random ^= s_random << 5;
    return APP_NEAI_MOTOR_OFF_MIN_MS +
           s_random % (APP_NEAI_MOTOR_OFF_MAX_MS - APP_NEAI_MOTOR_OFF_MIN_MS + 1U);
}

static int app_neai_motor_service(void)
{
    if (s_irq_error != DRV_GEAR_STEPPER_OK)
    {
        s_result.motor_status = s_irq_error;
        app_neai_fail(APP_NEAI_MOTOR_ERROR);
        return 0;
    }
    if (s_stop_pending != 0U)
    {
        /* TIM1 is stopped; these values cannot change until the next move. */
        s_off_started_ms = s_motor_stopped_ms;
        s_stop_pending = 0U;
        s_result.last_motor_run_ms = s_motor_stopped_ms - s_motor_started_ms;
        s_result.motor_off_wait_ms = app_neai_random_wait();
    }
    if ((s_motor_on == 0U) &&
        ((uint32_t)(HAL_GetTick() - s_off_started_ms) >= s_result.motor_off_wait_ms))
    {
        s_motor_started_ms = HAL_GetTick();
        s_motor_on = 1U;
        s_result.motor_status = drv_gear_stepper_move(
            &s_motor, GEAR_STEPPER_FORWARD, APP_NEAI_MOVE_STEPS);
        if (s_result.motor_status != DRV_GEAR_STEPPER_OK)
        {
            app_neai_fail(APP_NEAI_MOTOR_ERROR);
            return 0;
        }
        s_result.motor_run_count++;
    }
    s_result.motor_on = s_motor_on;
    return 1;
}

void app_neai_init(void)
{
    drv_lis3dh_config_t sensor_config;
    drv_gear_stepper_config_t motor_config;
    drv_lis3dh_data_t discarded;
    if (s_initialized != 0U)
    {
        return;
    }
    s_initialized = 1U;
    s_result.id_class = -1;
    s_result.confirmed_class = -1;
    s_result.candidate_class = -1;
    s_random = 0x9E3779B9U ^ HAL_GetTick();
    if (s_random == 0U)
    {
        s_random = 1U;
    }
    /* Export uses short enums. The only enum at this library boundary is a
     * return value (0..6), extended in r0 by AAPCS32; no enum pointer, struct or
     * enum parameter crosses the boundary. Keep the HAL/driver enum ABI intact. */
    s_result.model_status = neai_classification_init();
    if (s_result.model_status != NEAI_OK)
    {
        app_neai_fail(APP_NEAI_MODEL_ERROR);
        return;
    }
    s_class_names[0] = neai_get_class_name(0);
    s_class_names[1] = neai_get_class_name(1);
    if ((neai_get_input_signal_size() != NEAI_INPUT_SIGNAL_LENGTH) ||
        (neai_get_axis_number() != NEAI_INPUT_AXIS_NUMBER) ||
        (neai_get_number_of_classes() != NEAI_NUMBER_OF_CLASSES) ||
        (s_class_names[0] == NULL) || (s_class_names[1] == NULL) ||
        (strcmp(s_class_names[0], "run_1") != 0) ||
        (strcmp(s_class_names[1], "not_run1") != 0))
    {
        app_neai_fail(APP_NEAI_MODEL_MISMATCH);
        return;
    }
    drv_gear_stepper_get_default_config(&motor_config);
    motor_config.step_interval_ms = APP_NEAI_NORMAL_STEP_MS;
    s_result.motor_status = drv_gear_stepper_init(&s_motor, &motor_config);
    if (s_result.motor_status != DRV_GEAR_STEPPER_OK)
    {
        app_neai_fail(APP_NEAI_MOTOR_ERROR);
        return;
    }
    drv_lis3dh_get_default_config(&sensor_config);
    sensor_config.address_7bit = DRV_LIS3DH_ADDRESS_HIGH;
    sensor_config.range = LIS3DH_RANGE_16G;
    sensor_config.mode = LIS3DH_MODE_HIGH_RESOLUTION;
#if APP_NEAI_SAMPLE_RATE_HZ == 50U
    sensor_config.odr = LIS3DH_ODR_50_HZ;
#elif APP_NEAI_SAMPLE_RATE_HZ == 100U
    sensor_config.odr = LIS3DH_ODR_100_HZ;
#elif APP_NEAI_SAMPLE_RATE_HZ == 200U
    sensor_config.odr = LIS3DH_ODR_200_HZ;
#else
    sensor_config.odr = LIS3DH_ODR_400_HZ;
#endif
    s_result.sensor_status = drv_lis3dh_init(&s_sensor, &sensor_config);
    if (s_result.sensor_status == DRV_LIS3DH_OK)
    {
        s_result.sensor_status = drv_lis3dh_read(&s_sensor, &discarded);
    }
    if ((s_result.sensor_status != DRV_LIS3DH_OK) &&
        (s_result.sensor_status != DRV_LIS3DH_NO_DATA))
    {
        app_neai_fail(APP_NEAI_SENSOR_ERROR);
        return;
    }
    s_off_started_ms = HAL_GetTick();
    s_result.last_sample_ms = s_off_started_ms;
    s_result.motor_off_wait_ms = app_neai_random_wait();
    s_result.state = APP_NEAI_RUNNING;
}

/* Count only successful fresh classifications. Returning to the confirmed
 * state cancels a pending transition; changing the candidate restarts at one.
 * Initial state acquisition uses the same threshold as later transitions. */
static int app_neai_confirm_state(int id_class)
{
    if (id_class == s_result.confirmed_class)
    {
        s_result.candidate_class = -1;
        s_result.consecutive_count = 0U;
        return 0;
    }
    if (id_class != s_result.candidate_class)
    {
        s_result.candidate_class = id_class;
        s_result.consecutive_count = 1U;
    }
    else if (s_result.consecutive_count < APP_NEAI_STATE_CONFIRM_COUNT)
    {
        s_result.consecutive_count++;
    }
    if (s_result.consecutive_count < APP_NEAI_STATE_CONFIRM_COUNT)
    {
        return 0;
    }
    s_result.confirmed_class = id_class;
    s_result.candidate_class = -1;
    s_result.consecutive_count = 0U;
    return 1;
}

void app_neai_loop(void)
{
    drv_lis3dh_data_t sample;
    uint32_t now;
    uint32_t started_ms;
    uint32_t duration_ms;
    int id_class = -1;
    float probabilities[NEAI_NUMBER_OF_CLASSES];
    if (s_initialized == 0U)
    {
        return;
    }
    if (!app_neai_tx_service())
    {
        app_neai_fail(APP_NEAI_UART_ERROR);
        return;
    }
    if ((s_result.state != APP_NEAI_RUNNING) || !app_neai_motor_service())
    {
        return;
    }
    now = HAL_GetTick();
    if ((uint32_t)(now - s_result.last_sample_ms) >= APP_NEAI_SENSOR_TIMEOUT_MS)
    {
        app_neai_fail(APP_NEAI_SENSOR_TIMEOUT);
        return;
    }
    s_result.sensor_status = drv_lis3dh_read(&s_sensor, &sample);
    if (s_result.sensor_status == DRV_LIS3DH_NO_DATA)
    {
        return;
    }
    if (s_result.sensor_status != DRV_LIS3DH_OK)
    {
        app_neai_fail(APP_NEAI_SENSOR_ERROR);
        return;
    }
    now = HAL_GetTick();
    if (s_result.sample_count != 0U)
    {
        uint32_t gap = now - s_result.last_sample_ms;
        if (gap > s_result.max_sample_gap_ms)
        {
            s_result.max_sample_gap_ms = gap;
        }
        if (gap > ((1000U + APP_NEAI_SAMPLE_RATE_HZ - 1U) / APP_NEAI_SAMPLE_RATE_HZ + 1U))
        {
            s_result.delayed_sample_count++;
        }
    }
    s_result.last_sample_ms = now;
    s_result.sample_count++;
    s_input[0] = (float)sample.x_mg;
    s_input[1] = (float)sample.y_mg;
    s_input[2] = (float)sample.z_mg;
    s_random ^= (uint32_t)sample.x_mg ^ ((uint32_t)sample.y_mg << 11) ^
                ((uint32_t)sample.z_mg << 22) ^ now;
    if (s_random == 0U)
    {
        s_random = 1U;
    }
    started_ms = HAL_GetTick();
    s_result.model_status = neai_classification(s_input, probabilities, &id_class);
    duration_ms = HAL_GetTick() - started_ms;
    if (duration_ms > s_result.max_inference_ms)
    {
        s_result.max_inference_ms = duration_ms;
    }
    if (s_result.model_status != NEAI_OK)
    {
        app_neai_fail(APP_NEAI_MODEL_ERROR);
        return;
    }
    if ((id_class < 0) || (id_class >= NEAI_NUMBER_OF_CLASSES) ||
        !isfinite(probabilities[0]) || !isfinite(probabilities[1]) ||
        (probabilities[0] < 0.0f) || (probabilities[0] > 1.0f) ||
        (probabilities[1] < 0.0f) || (probabilities[1] > 1.0f))
    {
        s_result.model_status = NEAI_INVALID_PARAM;
        app_neai_fail(APP_NEAI_MODEL_ERROR);
        return;
    }
    s_result.id_class = id_class;
    memcpy(s_result.probabilities, probabilities, sizeof(probabilities));
    s_result.inference_count++;
    /* A timer fault/stop may have happened during I2C or classification. */
    if (!app_neai_motor_service())
    {
        return;
    }
    now = HAL_GetTick();
    if (app_neai_confirm_state(id_class))
    {
        if (!app_neai_log_state(now, s_result.confirmed_class))
        {
            app_neai_fail(APP_NEAI_UART_ERROR);
            return;
        }
    }
    if (!app_neai_tx_service())
    {
        app_neai_fail(APP_NEAI_UART_ERROR);
    }
}

void HAL_TIM_UpdateCallback(hal_tim_handle_t *timer)
{
    drv_gear_stepper_status_t status;
    if (timer != mx_tim1_gethandle())
    {
        return;
    }
    if ((s_motor_on != 0U) &&
        ((uint32_t)(HAL_GetTick() - s_motor_started_ms) >= APP_NEAI_MOTOR_RUN_MS))
    {
        /* Stop independently of foreground inference/I2C/UART latency. This is
         * evaluated every normal step (3 ms); no formatting or I2C in IRQ. */
        status = drv_gear_stepper_stop(&s_motor, GEAR_STEPPER_RELEASE);
        s_motor_stopped_ms = HAL_GetTick();
        s_motor_on = 0U;
        s_stop_pending = 1U;
    }
    else
    {
        status = drv_gear_stepper_on_timer_update(&s_motor);
    }
    if ((status != DRV_GEAR_STEPPER_OK) && (status != DRV_GEAR_STEPPER_RUNNING))
    {
        s_irq_error = status;
    }
}

const app_neai_result_t *app_neai_get_result(void)
{
    return &s_result;
}

最终效果

1.gif


        实际测试发现,也就采集数据的方式一样的场景,才能正常区分转动和静止,也就是说,实际工程落地的路会更长,采集的数据量会更大,要穷举所有可能的原因(或者把采集到的数据转换成这种坐标后再去喂给大模型)。





关键词: 基于     边缘     电机     检测     数据采集     训练     部署    

共1条 1/1 1 跳转至页

回复

匿名不能发帖!请先 [ 登陆 注册 ]