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
相关产品推荐
相关产品推荐

