这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【let's_do|2026年第3期】边缘AI步进电机

共2条 1/1 1 跳转至页

【let's_do|2026年第3期】边缘AI步进电机

高工
2026-11-09 14:38:25     打赏

【let's do|2026年第3期】边缘 AI 步进电机

本文介绍了 Let's Do|2026年第3期 活动,使用 NUCLEO-C562RE 开发板结合 LIS3DH 加速度传感器采集步进电机振动数据,训练 AI 模型,实现运行状态推理的项目设计。


项目介绍

NUCLEO-C562RE 开发板通过采集 LIS3DH 传感器数据、模型训练、AI 推理,判断步进电机运行状态的项目设计。

  • 准备工作:硬件连接、环境搭建、软件获取;

  • 数据采集:电机串口控制、加速度数据打印、AI 模型创建;

  • 模型训练:类别数据加载、Benchmark训练、效果验证;

  • 状态识别:模型函数调用、关键代码、效果演示。

cover_neai.jpg

环境搭建

nanoedge_ai_studio_installed.jpg

收到电子邮件,复制注册码;

nanoedge_ai_studio_email_key.jpg

运行软件,输入注册码激活,进入主页面;

nanoedge_ai_studio_start_page.jpg

硬件连接

使用纳米双面胶将 LIS3DH 模块固定在 28BYJ-48 步进电机表面,以探测振动加速度变化;

lis3dh_tape.jpg

粘贴时尽量使接触面积最大,以提升检测精度;

lis3dh_paste_stepper.jpg


最好使用没有弹性的双面胶,避免吸收振动,提高不同电机状态下的信号差异;

将步进电机驱动板、LIS3DH 模块和开发板对应引脚连接;

connect_lis3dh_stepper.jpg

接线方式详见:【let's_do|2026年第3期】步进电机的串口控制 、【let's_do|2026年第3期】加速度计 。

准备工作

串口打印加速度数据,同时串口中断控制步进电机转速,以便后期通过串口采集不同状态类别的训练数据。

步进电机控制

使用串口中断控制步进电机转速。

打开 STM32CubeMX2 软件,新建工程并配置步进电机所需 GPIO、串口通信 USART2、LIS3DH 模块对应 I2C1;

详见:【let's_do|2026年第3期】步进电机的串口控制 、【let's_do|2026年第3期】加速度计 。


代码

修改 main.c 文件,添加如下代码

 #include "main.h"
 #include "stepmotor.h"
 
 /* 档位 -> 相位间延时(ms):0=停 1=慢 2=快 3=最快 */
 static const uint16_t speed_delay_tbl[4] = {0, 5, 2, 0};
 static int speed_level = 0;      /* 0=停止 1=慢 2=快 3=最快 */
 static int motor_dir   = 1;      /* 1=顺时针 -1=逆时针 */
 
 int __io_getchar(void) {}
 int __io_putchar(int ch) {}
 
 void HAL_UART_RxCpltCallback(hal_uart_handle_t *huart, uint32_t size_byte, hal_uart_rx_event_types_t rx_event){}
 
 /**
   * @brief  解析 {"speed": N},N ∈ [0,3]
   * @retval 0 成功,-1 失败
   */
 static int parse_speed_cmd(int *level)
 {
     char *p = strstr((char *)uart_rx_buf, "\"speed\"");
     int v;
 
     if (p == NULL) return -1;
     p = strchr(p, ':');
     if (p == NULL) return -1;
 
     v = atoi(p + 1);
     if (v < 0 || v > 3) return -1;
 
     *level = v;
     return 0;
 }
 
 /**
   * @brief  电机轮询:处理串口速度指令 + 走一个完整周期(8 相位)
   * @note   主循环反复调用即可;先处理指令再走步,新指令当圈立即生效
   */
 static void motor_poll(void)
 {
     /* ---------- 1. 收指令:随时可打断转动 ---------- */
     if (uart_rx_ready)
     {
         int lvl = speed_level;
         int ok  = parse_speed_cmd(&lvl);
 
         uart_rx_len   = 0;
         memset(uart_rx_buf, 0, sizeof(uart_rx_buf));
         uart_rx_ready = false;
 
         if (ok == 0)
         {
             speed_level = lvl;
             if (speed_level == 0)
             {
                 stepper_stop();          /* 0 档:四相断电 */
             }
             printf("OK: speed=%d\r\n", lvl);
         }
         else
         {
             printf("ERR: {\"speed\":0|1|2|3}\r\n");
         }
     }
 
     /* ---------- 2. 走一个完整周期(8 相位)后立即返回 ---------- */
     if (speed_level != 0)
     {
         stepper_step(motor_dir, speed_delay_tbl[speed_level]);
     }
 }
 
 int main(void)
 {
    /* 初始化步进电机 */
    stepper_init();
    printf("\r\n=== Stepper Motor Speed Control ===\r\n");
    printf("Usage: {\"speed\": 0|1|2|3}\r\n");
    printf("  0 = stop\r\n");
    printf("  1 = slow   (5 ms/phase)\r\n");
    printf("  2 = fast   (2 ms/phase)\r\n");
    printf("  3 = max    (0 ms/phase -> real 1ms)\r\n\r\n");
 
    /* 启动 UART 中断接收 */
    HAL_UART_Receive_IT(mx_usart2_uart_gethandle(), &rx_temp, 1);
 
    printf("\r\nSystem Ready!\r\n");
    printf("Waiting for JSON command: {\"speed\": 1}\r\n");
 
     while (1)
     {
         motor_poll();
     }
 } /* end main */

保存代码。


效果

编译并上传工程,打开串口调试助手;

发送速度指令,控制步进电机转速;

stepper_speed_uart_control.jpg

加速度打印

串口打印 LIS3DH 传感器三轴加速度值,同时串口中断控制步进电机转速切换。


代码

修改 main.c 文件,添加如下代码

 #include "main.h"
 #include "stepmotor.h"
 #include <math.h>
 #include "lis3dh_reg.h"
 
 #define LIS3DH_I2C_ADDR    (LIS3DH_I2C_ADD_H & 0xFEU)
 
 static stmdev_ctx_t lis3dh_ctx;
 uint32_t last_acc_tick = 0;   // 数据打印的时间戳
 
 static int32_t platform_write(void *handle, uint8_t reg, const uint8_t *buf, uint16_t len);
 static int32_t platform_read(void *handle, uint8_t reg, uint8_t *buf, uint16_t len);
 static void platform_delay(uint32_t millisec);
 
 /**
   * @brief  加速度打印轮询(非阻塞):每 100ms 读一次 LIS3DH 并打印
   * @note   主循环里反复调用即可,未到时间立即返回
   */
 static void acc_print_poll(void)
 {
     static uint32_t last_acc_tick = 0;
     int16_t acc_raw[3];
     float   acc_mg[3];
     float   acc_g[3];
 
     if ((uint32_t)(HAL_GetTick() - last_acc_tick) < 100U)
         return;
 
     last_acc_tick = HAL_GetTick();
 
     if (lis3dh_acceleration_raw_get(&lis3dh_ctx, acc_raw) == 0)
     {
         /* 已配置为高分辨率(HR_12bit)模式,±2g 用 hr 换算 */
         acc_mg[0] = lis3dh_from_fs2_hr_to_mg(acc_raw[0]);
         acc_mg[1] = lis3dh_from_fs2_hr_to_mg(acc_raw[1]);
         acc_mg[2] = lis3dh_from_fs2_hr_to_mg(acc_raw[2]);
 
         acc_g[0] = acc_mg[0] / 1000.0f;
         acc_g[1] = acc_mg[1] / 1000.0f;
         acc_g[2] = acc_mg[2] / 1000.0f;
 
         printf("%.3f,%.3f,%.3f\r\n",
                (double)acc_g[0], (double)acc_g[1], (double)acc_g[2]);
     }
     else
     {
         printf("Read LIS3DH data failed!\r\n");
     }
 }
 
 int main(void)
 {
     // 初始化 LIS3DH 和步进电机
     while (1)
     {
   acc_print_poll();
   motor_poll();
     }
 } /* end main */

保存代码。


效果

  • 编译并上传工程至板端,打开串口调试助手;

  • 连续打印三轴加速度值;

stepper_ctrl_lis3dh_value_print.jpg

发送 JSON 指令控制电机转速,观察加速度值的变化;


数据采集

使用 NanoEdge AI Studio 软件创建工程并串口采集 LIS3DH 数据。

工程创建

  • 点击左上角 Home 键进入主页,点击 New Project 按钮,选择信号类型为 Classify,点击 Create 按钮;

nanoedge_ai_studio_project_create.jpg

  • 进入工程设置页面,配置工程名称,选择目标设备 NUCLEO-C562RE ;

nanoedge_ai_studio_target_select.jpg

  • 根据芯片型号设置存储空间,设置传感器为 Accelerometer ,轴数为 3,点击 Save 进入下一步;

nanoedge_ai_studio_project_setting.jpg

  • 进入 Signals 标签页,点击 New Class 按钮,可选择文件导入、串口采集等方式(数据格式见右侧说明);

nanoedge_ai_studio_signal_add.jpg


串口打印

通过串口发送指令 {"speed": 0|1|2|3} 实现静止、低速、中速、高速旋转控制;

点击 Add signals 按钮,配置端口、波特率、采集组数,点击 Start 开始接收串口数据;

nanoedge_ai_studio_add_signals.jpg

采集完成后点击 Next ,对数据集进行分类命名,点击 Import 导入;

nanoedge_ai_studio_add_signals_import.jpg

重复上述过程,添加多个分类信号;

nanoedge_ai_studio_signals_class.jpg


模型训练

分类信号采集添加完成后,进入 Benchmark 标签页,点击新建,项目命名后选择 Start;

nanoedge_ai_studio_benchmark_name.jpg

自动开始训练模型;

nanoedge_ai_studio_benchmark_start.jpg

由于模型和算法种类较多,训练过程需耗费2小时以上;

nanoedge_ai_studio_benchmark_analyse.jpg

训练完成,耗时 3 个多小时;

nanoedge_ai_studio_benchmark_complete.jpg

验证导出

待训练完成后,点击 Valiation 标签页进行模型验证;

在量化准确度最高的模型(第1个)点击右侧的 Serial Emulator 按钮,对串口采集的数据进行实时分析汇总,最后给出置信度最高的分类状态;

nanoedge_ai_studio_validation_run.jpg

验证模型需固定步进电机,使其所处环境与训练时一致。

勾选目标模型,点击左侧 New Experiment 按钮,添加不同分类数据,对识别准确度进行估计和验证;

nanoedge_ai_studio_validation_experiment.jpg

验证通过后,进入 Deployment 标签页,设置构建工具链等,点击 Deploy Library 生成 C 代码文件;

nanoedge_ai_studio_deploy.jpg

右侧显示模型的使用示例代码,可根据需要进行修改;

nanoedge_ai_studio_deploy_compile.jpg

状态识别

将采集的 LIS3DH 加速度数据传递给 AI 模型识别函数,串口打印预测结果。

流程图

flowchart_neai.jpg

代码

修改 main.c 文件,添加如下代码

 #include "main.h"
 #include "NanoEdgeAI.h"
 
 #define NEAI_SAMPLE_PERIOD_MS   100U  /* 等于训练时的采样周期:10Hz→100 */
 float input_signal[NEAI_INPUT_SIGNAL_LENGTH * NEAI_INPUT_AXIS_NUMBER];  /* 3 个 */
 float probabilities[NEAI_NUMBER_OF_CLASSES];                            /* 4 个 */
 static uint32_t neai_last_tick = 0;
 
 /**
   * @brief  加速度打印轮询(非阻塞):每 100ms 读一次 LIS3DH 并打印
   * @note   主循环里反复调用即可,未到时间立即返回
   */
 static void acc_print_poll(void){}
 
 /**
   * @brief  电机轮询:处理串口速度指令 + 走一个完整周期(8 相位)
   * @note   主循环反复调用即可;先处理指令再走步,新指令当圈立即生效
   */
 static void motor_poll(void){}
 
 /**
   * @brief  NEAI 轮询:每 100ms 读一次 LIS3DH 并调用 neai_classification(),打印分类结果
   * @note   主循环里反复调用即可,未到时间立即返回
   *         需要在 main() 里先调用 neai_classification_init() 初始化
   */
 static void neai_poll(void)
 {
     int16_t raw[3];
     int id_class = 0;
 
     if ((uint32_t)(HAL_GetTick() - neai_last_tick) < NEAI_SAMPLE_PERIOD_MS)
         return;
 
     neai_last_tick = HAL_GetTick();
 
     if (lis3dh_acceleration_raw_get(&lis3dh_ctx, raw) != 0)
         return;                          /* 读失败跳过这次 */
 
     /* 长度=1,填 X Y Z 值 */
     input_signal[0] = lis3dh_from_fs2_hr_to_mg(raw[0]) / 1000.0f;   /* g 单位 */
     input_signal[1] = lis3dh_from_fs2_hr_to_mg(raw[1]) / 1000.0f;
     input_signal[2] = lis3dh_from_fs2_hr_to_mg(raw[2]) / 1000.0f;
 
     if (neai_classification(input_signal, probabilities, &id_class) != NEAI_OK)
     {
         printf("NEAI ERR\r\n");
         return;
     }
 
     printf("id=%d %s p=%d%%/%d%%/%d%%/%d%%\r\n",
            id_class,
            neai_get_class_name(id_class),
            (int)(probabilities[0] * 100.0f),
            (int)(probabilities[1] * 100.0f),
            (int)(probabilities[2] * 100.0f),
            (int)(probabilities[3] * 100.0f));
 }
 
 int main(void)
 {
   uint8_t whoami = 0;
   if (mx_system_init() != SYSTEM_OK)
   {
     return (-1);
   }
   else
   {
    /* 配置 LIS3DH */
    printf("LIS3DH initialized OK!\r\n");
     
     // 初始化模型
     if (neai_classification_init() != NEAI_OK)
     {
       printf("NEAI init failed!\r\n");
       while (1) { }
     }
     printf("NEAI ready: id=%s len=%d axis=%d classes=%d\r\n",
       neai_get_id(),
       neai_get_input_signal_size(),
       neai_get_axis_number(),
       neai_get_number_of_classes()
     );
     printf("=== NEAI classes (%d) ===\r\n", NEAI_NUMBER_OF_CLASSES);
     for (int i = 0; i < NEAI_NUMBER_OF_CLASSES; i++)
     {
       printf("  id=%d  name=%s\r\n", i, neai_get_class_name(i));
     }
     /* 初始化步进电机 */
    stepper_init();
    printf("\r\nSystem Ready!\r\n");
    printf("Waiting for JSON command: {\"speed\": 2}\r\n");
 
     while (1)
     {
       //acc_print_poll();  /* 加速度打印 */
       motor_poll();      /* 串口控制 */
       neai_poll();    /* 状态预测 */
     }
   }
 } /* end main */

保存代码。

在 CMakeLists.txt 文件添加模型文件路径;

 target_sources(${CMAKE_PROJECT_NAME} PRIVATE
   # Add additional source files here
   stepmotor.c  # 步进电机驱动
   lis3dh_reg.c  # 加速度传感器驱动
 )
 
 target_link_options(${CMAKE_PROJECT_NAME} PUBLIC
   # Add additional linker flags here
   -u _printf_float # 浮点运算
 )
 
 target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC
   # Add additional linker libraries here
   "${CMAKE_CURRENT_LIST_DIR}/libneai.a"  # ai 模型
 )

保存文件。


效果

  • 编译并上传工程至板端,打开串口调试助手;

  • 接收连续打印状态预测结果和 4 个分类的置信度;

neai_print_initial.jpg

输出的各个字段的含义如下


字段含义来源
id = x模型预测的类别编号 xid_class
stop/speed1|2|3电机转速预测结果,与类别 id 对应neai_get_class_name(id_class)
p=95%/1%/1%/1%类别置信度 stop / speed3|2|1probabilities[0..3]


发送 JSON 指令控制电机转速,观察状态预测结果和置信度;

状态 Stop 的置信度约为 90%

neai_print_stop.jpg


对于状态 speed1 进行预测,置信度约为 60%

neai_print_speed1.jpg

状态 speed2 预测的置信度约为 70% ;

neai_print_speed2.jpg

状态 speed3 预测的置信度为 80%

neai_print_speed3.jpg

总结

本文介绍了 Let's Do|2026年第3期 活动,使用 NUCLEO-C562RE 开发板结合 LIS3DH 加速度传感器采集步进电机振动数据,训练 AI 模型,实现运行状态推理的项目设计,为边缘 AI 等相关项目的开发和应用提供了参考。





关键词: 边缘AI     步进电机     训练     模型     串口中断     控制    

高工
2026-11-09 17:12:09     打赏
2楼

写的十分详细,感谢楼主分享


共2条 1/1 1 跳转至页

回复

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