You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32与Matlab串口通信问题及多运动捕捉单元方案咨询

问题与解决方案

硬件基础

使用NodeMCU-32S(ESP32)连接MPU9250模块,采用标准I2C连接方式:SDA接GPIO21、SCL接GPIO22、VCC接3.3V、GND共地。


一、Arduino代码优化:输出纯净数据

当前问题

  • 串口输出开头存在初始化无关信息,Matlab无法直接读取有效加速度数据
  • 尝试以浮点数组形式发送数据时,串口仅显示0.00

优化后代码

#include <MPU9250_WE.h>
#include <Wire.h>
#define MPU9250_ADDR 0x68

MPU9250_WE myMPU9250 = MPU9250_WE(MPU9250_ADDR);

void setup() {
  Serial.begin(115200);
  Wire.begin();
  delay(200);
  
  // 传感器校准与配置
  myMPU9250.autoOffsets();
  myMPU9250.setAccOffsets(-14240.0, 18220.0, -17280.0, 15590.0, -20930.0, 12080.0);
  myMPU9250.setGyrOffsets(45.0, 145.0, -105.0);
  myMPU9250.enableGyrDLPF();
  myMPU9250.setGyrDLPF(MPU9250_DLPF_6);
  myMPU9250.setSampleRateDivider(5);
  myMPU9250.setGyrRange(MPU9250_GYRO_RANGE_250);
  myMPU9250.setAccRange(MPU9250_ACC_RANGE_2G);
  myMPU9250.enableAccDLPF(true);
  myMPU9250.setAccDLPF(MPU9250_DLPF_6);
  myMPU9250.enableAccAxes(MPU9250_ENABLE_XYZ);
  myMPU9250.enableGyrAxes(MPU9250_ENABLE_XYZ);
  myMPU9250.setMagOpMode(AK8963_CONT_MODE_100HZ);
  
  delay(200);
  // 发送初始化完成标记,告知Matlab开始输出有效数据
  Serial.println("DATA_START");
}

void loop() {
  xyzFloat gValue = myMPU9250.getGValues();
  
  // 用逗号分隔浮点数据,避免字符串拼接的类型转换错误
  Serial.print(gValue.x, 4); // 保留4位小数保证精度
  Serial.print(",");
  Serial.print(gValue.y, 4);
  Serial.print(",");
  Serial.println(gValue.z, 4);
  
  delay(200);
}

关键改进点

  • 初始化完成后发送DATA_START标记,Matlab可据此过滤开头无关信息
  • 采用Serial.print分开发送浮点数据,避免原代码中字符串拼接导致的隐式类型转换异常
  • 保留4位小数,确保数据精度

二、Matlab实时数据处理与绘图

优化代码(过滤无效数据+实时绘图+超限判断)

% 初始化串口
s = serialport("COM3", 115200);
configureTerminator(s, "CR/LF");
fid = fopen("serialread.txt", 'w'); % 用'w'清空旧数据,避免追加混乱

% 实时绘图初始化
figure;
hold on;
grid on;
xlabel('采样次数');
ylabel('加速度值(g)');
legend('X轴','Y轴','Z轴');
title('实时加速度数据');

sampleCount = 0;
startFlag = false;

while true
    % 读取一行数据
    dataLine = readline(s);
    
    % 等待有效数据标记
    if ~startFlag
        if strcmp(dataLine, "DATA_START")
            startFlag = true;
            disp("开始接收有效数据");
        end
        continue;
    end
    
    % 解析数据
    data = str2num(dataLine);
    if length(data) == 3
        sampleCount = sampleCount + 1;
        
        % 写入文件
        fprintf(fid, "%.4f,%.4f,%.4f\n", data(1), data(2), data(3));
        
        % 实时更新绘图
        plot(sampleCount, data(1), 'r.');
        plot(sampleCount, data(2), 'g.');
        plot(sampleCount, data(3), 'b.');
        drawnow;
        
        % 超限判断(示例:加速度绝对值超过1.5g触发报警)
        if abs(data(1)) > 1.5 || abs(data(2)) > 1.5 || abs(data(3)) > 1.5
            disp("运动超限!");
            % 发送指令给ESP32触发振动电机
            writeline(s, "VIBRATE_ON");
        else
            writeline(s, "VIBRATE_OFF");
        end
    end
end

fclose(fid);
delete(s);

关键改进点

  • 等待DATA_START标记后才开始处理数据,彻底过滤开头无关信息
  • 以逗号分隔解析数据,确保浮点值正确读取
  • 用drawnow刷新绘图界面,保证实时性
  • 增加超限判断逻辑,并通过串口向ESP32下发振动控制指令

三、多ESP32单元组网与数据汇总

方案:主ESP32作为WiFi AP,从节点连接并上传数据

1. 主ESP32代码(WiFi AP + 串口转发)

#include <WiFi.h>
#include <WiFiClient.h>

// WiFi AP配置
const char* ssid = "MotionCapture_AP";
const char* password = "12345678";
WiFiServer server(80);
WiFiClient clients[7]; // 最多支持7个从节点
int clientCount = 0;

void setup() {
  Serial.begin(115200);
  
  // 启动WiFi AP
  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP地址: ");
  Serial.println(IP);
  server.begin();
}

void loop() {
  // 接受新的从节点连接
  if (clientCount < 7) {
    WiFiClient newClient = server.available();
    if (newClient) {
      clients[clientCount] = newClient;
      Serial.print("新节点连接,编号:");
      Serial.println(clientCount+1);
      clientCount++;
    }
  }
  
  // 读取从节点数据并转发到串口(给Matlab)
  for (int i=0; i<clientCount; i++) {
    if (clients[i].connected()) {
      while (clients[i].available()) {
        String data = clients[i].readStringUntil('\n');
        // 添加节点编号前缀,方便Matlab识别
        Serial.print("NODE_");
        Serial.print(i+1);
        Serial.print(",");
        Serial.println(data);
      }
    }
  }
  
  // 读取Matlab串口指令并转发到对应从节点
  while (Serial.available()) {
    String cmd = Serial.readStringUntil('\n');
    // 指令格式:NODE_X,VIBRATE_ON/OFF
    int commaIndex = cmd.indexOf(',');
    if (commaIndex != -1) {
      String nodeStr = cmd.substring(0, commaIndex);
      String action = cmd.substring(commaIndex+1);
      int nodeNum = nodeStr.substring(5).toInt() - 1; // 转成数组索引
      if (nodeNum >=0 && nodeNum < clientCount && clients[nodeNum].connected()) {
        clients[nodeNum].println(action);
      }
    }
  }
}

2. 从ESP32代码(WiFi客户端 + MPU数据上传 + 振动控制)

#include <MPU9250_WE.h>
#include <Wire.h>
#include <WiFi.h>

// WiFi配置
const char* ssid = "MotionCapture_AP";
const char* password = "12345678";
const char* serverIP = "192.168.4.1"; // 主ESP32的AP默认IP
WiFiClient client;

// MPU配置
#define MPU9250_ADDR 0x68
MPU9250_WE myMPU9250 = MPU9250_WE(MPU9250_ADDR);

// 振动电机引脚
#define VIB_PIN 23

void setup() {
  Serial.begin(115200);
  pinMode(VIB_PIN, OUTPUT);
  digitalWrite(VIB_PIN, LOW);
  
  // 连接WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi连接成功");
  
  // 连接主节点
  if (!client.connect(serverIP, 80)) {
    Serial.println("连接主节点失败");
    while(1);
  }
  
  // MPU初始化(同之前的优化代码)
  Wire.begin();
  delay(200);
  myMPU9250.autoOffsets();
  myMPU9250.setAccOffsets(-14240.0, 18220.0, -17280.0, 15590.0, -20930.0, 12080.0);
  myMPU9250.setGyrOffsets(45.0, 145.0, -105.0);
  myMPU9250.enableGyrDLPF();
  myMPU9250.setGyrDLPF(MPU9250_DLPF_6);
  myMPU9250.setSampleRateDivider(5);
  myMPU9250.setGyrRange(MPU9250_GYRO_RANGE_250);
  myMPU9250.setAccRange(MPU9250_ACC_RANGE_2G);
  myMPU9250.enableAccDLPF(true);
  myMPU9250.setAccDLPF(MPU9250_DLPF_6);
  myMPU9250.enableAccAxes(MPU9250_ENABLE_XYZ);
  myMPU9250.enableGyrAxes(MPU9250_ENABLE_XYZ);
  myMPU9250.setMagOpMode(AK8963_CONT_MODE_100HZ);
  delay(200);
}

void loop() {
  // 上传MPU数据到主节点
  xyzFloat gValue = myMPU9250.getGValues();
  client.print(gValue.x, 4);
  client.print(",");
  client.print(gValue.y, 4);
  client.print(",");
  client.println(gValue.z, 4);
  
  // 接收主节点指令控制振动电机
  while (client.available()) {
    String cmd = client.readStringUntil('\n');
    if (cmd == "VIBRATE_ON") {
      digitalWrite(VIB_PIN, HIGH);
    } else if (cmd == "VIBRATE_OFF") {
      digitalWrite(VIB_PIN, LOW);
    }
  }
  
  delay(200);
}

方案说明

  • 主ESP32开启WiFi AP,从节点连接到该AP,通过TCP协议上传数据
  • 主ESP32为各从节点数据添加编号前缀后,通过串口转发给Matlab
  • Matlab处理数据后,发送带节点编号的指令(如NODE_3,VIBRATE_ON)到主ESP32,主ESP32再转发给对应从节点控制振动电机
  • 全程无需使用Matlab的WiFi功能,仅通过串口完成与主ESP32的通信

内容的提问来源于stack exchange,提问作者Rufaida Hussain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 18:15:53