QSerialPort读取串口频繁收到空数据如何解决
问题:Qt串口读取出现大量空数据的原因与解决方法
问题复现
使用Qt读取传感器串口数据时,无法稳定获取有效数据,日志中存在大量空内容输出。
初始读取实现代码
QObject::connect(this->serialPort,SIGNAL(readyRead()),this,SLOT(pollSerialPort())); QObject::connect(serialPort, &::QSerialPort::errorOccurred,this,&Imu::handleError); QByteArray hexData = d.append(serialPort->readAll().toHex()); qDebug() << "Serial received " << hexData;
调试输出样例
Serial received "555541321e7ff3029f61da00000000ffff00d2fffa0cbe24b124b124b1007b13d00000f4d8" Serial received ""
已尝试的无效方案
在readyRead信号触发后延迟5ms再调用读取函数,未解决空数据问题,对应代码:
QObject::connect(this->serialPort, &QSerialPort::readyRead, this, [this](){QTimer::singleShot(5,this,&Imu::pollSerialPort);});
根本原因
readyRead信号的触发逻辑是串口IO缓冲区状态发生变化即触发,并非只有收到完整有效数据才触发:操作系统缓冲区刷新、串口线路电平波动、前一次读取后的状态标记重置都可能触发信号,此时调用readAll()会直接返回空字节数组。- 串口本身是流传输设备,不存在天然的数据包边界,
readAll()仅会读取当前缓冲区已存在的所有字节,既可能一次读到半包数据,也可能一次读到多包拼接的数据。固定延迟读取的方案本质是赌延迟时间内数据能全部到达,受硬件、系统负载、波特率影响,延迟时间不可能固定,无法保证稳定性。 - 原有逻辑未做可读字节数校验,空读取结果也会进入后续打印、处理流程,直接放大了空消息问题。
可行解决步骤
1. 前置空读过滤
在读取逻辑入口增加可读字节数判断,无可读数据直接返回,避免无效处理:
void Imu::pollSerialPort() { // 无有效可读数据直接退出 if(serialPort->bytesAvailable() == 0) { return; } QByteArray rawData = serialPort->readAll(); // 原始数据追加到全局接收缓冲区,不要直接转hex做单次处理 receiveBuffer.append(rawData);
2. 按传感器协议做帧边界切分
从给出的有效数据样例判断,传感器数据包以0x55 0x55为帧头,先对照传感器官方手册确认单包固定长度(样例中有效hex共70字符,对应35字节原始数据,可直接核对手册修正),在缓冲区中循环提取完整帧:
// 追加数据后循环校验完整帧 while (receiveBuffer.size() >= 2) { // 查找有效帧头位置 int frameStart = receiveBuffer.indexOf("\x55\x55"); if (frameStart == -1) { // 无有效帧头,清空缓冲区脏数据 receiveBuffer.clear(); break; } // 丢弃帧头前的乱码数据 if (frameStart > 0) { receiveBuffer.remove(0, frameStart); } // 替换为传感器实际单包字节长度 const int FRAME_FIXED_LENGTH = 35; if (receiveBuffer.size() < FRAME_FIXED_LENGTH) { // 数据未到齐,等待下次触发再处理 break; } // 提取完整一帧数据 QByteArray validFrame = receiveBuffer.left(FRAME_FIXED_LENGTH); // 移除已处理的帧数据 receiveBuffer.remove(0, FRAME_FIXED_LENGTH); // 此处补充校验和验证、数据解析逻辑 qDebug() << "Received valid frame: " << validFrame.toHex(); } }
3. 配置校验注意事项
- 打开串口时必须保证参数和传感器完全匹配:波特率、数据位、停止位、校验位、流控参数,任意参数不匹配都会导致乱码、空读、丢包问题。
- Windows平台下部分USB转串口驱动会将
readyRead信号触发粒度设置得极碎,打开串口后可增加serialPort->setReadBufferSize(4096);设置足够大的读取缓冲区,避免数据被截断。 - 不要使用固定延迟读取的方案,该方案在负载波动、硬件更换后会直接失效。
内容的提问来源于stack exchange,提问作者sam
相关产品推荐
相关产品推荐

