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

Arduino向Python TCP服务器发送含负数值字节数组的解码问询

解决Arduino向Python TCP服务器发送含负数的传感器数据问题

核心问题分析

  1. UTF-8解码错误根源:你发送的是二进制传感器数据,并非UTF-8文本。UTF-8仅接受符合编码规则的字节序列,传感器原始字节(如0xFF、0xCF)不属于合法UTF-8范围,必然触发解码错误。
  2. 负数存储丢失:Arduino的byte类型是无符号8位整数(0-255),直接将负数转换为byte会被截断为无符号值(例如-1会变成0xFF),完全丢失负数信息。
  3. 精度丢失:roll和pitch是浮点数,直接转换为单字节会丢失绝大部分精度,导致接收端无法还原真实数值。

解决方案:二进制数据打包+结构化解析

无需尝试将二进制数据当作文本解码,而是采用固定格式的二进制打包传输,在Python端用struct模块解析原始字节,完整还原含负数的传感器数值。

1. Arduino端修改:正确打包带负数的数值

将浮点数角度转换为带精度的16位整数(int16_t,支持-32768到32767的范围,足够存储角度值),再拆分为字节存入数组:

void loop()
{
  // 读取传感器数据(原代码不变)
  acc_x = icm20600.getAccelerationX();
  acc_y = icm20600.getAccelerationY();
  acc_z = icm20600.getAccelerationZ();
  roll = atan2((float)acc_y, (float)acc_z) * 57.3;
  pitch = atan2(-(float)acc_x, sqrt((float)acc_y * acc_y + (float)acc_z * acc_z)) * 57.3;

  // 关键修改:将浮点数转为带两位小数的int16_t,保留精度并支持负数
  int16_t roll_int = (int16_t)(roll * 100);
  int16_t pitch_int = (int16_t)(pitch * 100);

  // 将int16_t拆分为两个字节存入数组(小端模式,匹配Arduino和Python的默认字节序)
  array[0] = roll_int & 0xFF;       // 低字节
  array[1] = roll_int >> 8;         // 高字节
  array[2] = pitch_int & 0xFF;      // 低字节
  array[3] = pitch_int >> 8;        // 高字节
  // 剩余4个字节可留空或存储其他传感器数据

  // 测试输出(原代码不变)
  Serial.write(array, 8);
  Serial.println();
  delay(500); 

  // 以太网发送(原代码不变)
  if (client.available()) {
      client.write(array, 8);
      delay(3000); 
  }
  if (!client.connected()) {
    Serial.println("disconnecting.");
    client.stop();
    for(;;);
  }
}

2. Python端修改:用struct解析二进制数据

不再使用decode("utf-8"),而是用struct模块直接解析原始字节,还原出带负数的数值:

# echo-server.py
import socket
import struct

HOST = "192.168.X.X"
PORT = 65432

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    conn, addr = s.accept()
    with conn:
        print(f"Connected by {addr}")
        while True:
            # 固定接收8字节,和Arduino发送的数组长度一致
            data = conn.recv(8)
            if not data:
                break
            # 解析前4字节为两个int16_t:<表示小端模式,hh表示两个短整数
            roll_int, pitch_int = struct.unpack('<hh', data[:4])
            # 转回浮点数,恢复两位小数精度
            roll = roll_int / 100.0
            pitch = pitch_int / 100.0
            print(f"Roll: {roll:.2f}°, Pitch: {pitch:.2f}°")
            conn.send(data)

关键说明

  • 字节序一致性:用<指定小端模式,匹配Arduino的AVR架构和大多数x86/ARM设备的默认字节序,避免数值解析错误。
  • 精度控制:乘以100是为了保留两位小数,若需要更高精度可改为乘以1000,同时确保数值不超过int16_t的范围。
  • 避免粘包:如果后续发送更长数据,可在数组开头添加固定帧头(比如0xAA),Python端先校验帧头再解析,防止数据错位。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 05:15:46