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

GLUT中idle函数导致程序窗口冻结问题求助

问题分析与修复方案

核心问题

  1. 串口重复操作+阻塞读取:GLUT的idle()会被高频调用,每次都执行串口打开、参数配置,不仅效率极低,更致命的是read()默认是阻塞模式——当Arduino无数据发送时,read()会卡住GLUT主线程,导致窗口完全无法响应,直接冻结。
  2. 语法错误:tcsetattr的判断代码块缺失闭合},导致后续逻辑混乱。
  3. 冗余操作:代码中重复调用close(serial_port),属于无效操作。
  4. 内存泄漏:charArrayToFloatArray中用new创建的float数组未释放,长期运行会耗尽内存。

修复步骤

1. 串口初始化移至主线程,仅执行一次

把串口的打开、配置逻辑放到main()中,避免在idle()里反复操作:

// 全局变量保存串口句柄
int serial_port = -1;

int main(int argc, char **argv) { 
    // 先初始化串口
    serial_port = open("/dev/ttyUSB0", O_RDWR | O_NONBLOCK); // 开启非阻塞模式
    if(serial_port < 0) {
        std::cerr << "打开串口失败: " << strerror(errno) << std::endl;
        return 1;
    }

    struct termios tty;
    if(tcgetattr(serial_port, &tty) != 0) {
        std::cerr << "获取串口属性错误 " << errno << ": " << strerror(errno) << std::endl;
        close(serial_port);
        return 1;
    }
    cfsetispeed(&tty, B115200);
    cfsetospeed(&tty, B115200);

    tty.c_cflag &= ~PARENB; 
    tty.c_cflag &= ~CSTOPB; 
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;
    tty.c_cflag &= ~CRTSCTS;
    tty.c_cflag |= CREAD | CLOCAL;

    // 配置非阻塞读取参数
    tty.c_cc[VMIN] = 0;
    tty.c_cc[VTIME] = 0;

    if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
        std::cerr << "设置串口属性错误 " << errno << ": " << strerror(errno) << std::endl;
        close(serial_port);
        return 1;
    }

    // 初始化GLUT
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Robot Simulation");
    initOpenGL();
    glutDisplayFunc(display);
    glutIdleFunc(idle);
    glutMainLoop();

    // 程序退出前关闭串口
    close(serial_port);
    return 0;
}

2. 修改idle()函数,仅做非阻塞读取

去掉idle()里的串口初始化逻辑,只处理数据读取,避免阻塞主线程:

void idle() {
    if(serial_port < 0) return; // 串口未初始化直接返回

    unsigned char read_buf[64];
    int num_bytes = read(serial_port, read_buf, sizeof(read_buf));
    
    // 非阻塞模式下,无数据时read返回-1且errno为EAGAIN,直接跳过
    if(num_bytes < 0) {
        if(errno != EAGAIN) {
            std::cerr << "读取串口数据错误: " << strerror(errno) << std::endl;
        }
        return;
    }

    if(num_bytes == 0) return; // 无数据可读

    float *a = charArrayToFloatArray(read_buf, num_bytes);
    if(a == nullptr) return;

    static float x = 0, y = 0, z = 0;
    x += a[0]*0.1*0.1;
    y += a[1]*0.1*0.1;
    z += a[2]*0.1*0.1;
    
    updateRobotPosition(x, y, z);

    // 释放内存,避免泄漏
    delete[] a;
}

3. 修复语法错误

在原代码的tcsetattr判断块后补上缺失的闭合}:

if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
    std::cerr << "Error " << errno << " from tcsetattr: " << strerror(errno) << std::endl;
    exit(1);
} // 补上这个大括号

4. 可选:优化数据包解析逻辑

原代码未处理不完整数据包(如只收到半条数据),可添加缓存机制,基于换行符(假设Arduino每行发送完整数据包)解析:

// 全局缓存串口数据
char serial_buffer[256] = {0};
int buffer_pos = 0;

void idle() {
    if(serial_port < 0) return;

    unsigned char read_buf[64];
    int num_bytes = read(serial_port, read_buf, sizeof(read_buf));
    
    if(num_bytes < 0) {
        if(errno != EAGAIN) {
            std::cerr << "读取串口数据错误: " << strerror(errno) << std::endl;
        }
        return;
    }

    if(num_bytes == 0) return;

    // 将数据追加到缓存,直到收到完整行
    for(int i=0; i<num_bytes; i++) {
        if(read_buf[i] == '\n') {
            serial_buffer[buffer_pos] = '\0';
            // 解析完整数据包
            float *a = charArrayToFloatArray((unsigned char*)serial_buffer, buffer_pos);
            if(a != nullptr) {
                static float x = 0, y = 0, z = 0;
                x += a[0]*0.1*0.1;
                y += a[1]*0.1*0.1;
                z += a[2]*0.1*0.1;
                updateRobotPosition(x, y, z);
                delete[] a;
            }
            buffer_pos = 0; // 重置缓存位置
        } else {
            if(buffer_pos < sizeof(serial_buffer)-1) {
                serial_buffer[buffer_pos++] = read_buf[i];
            }
        }
    }
}

关键原理说明

  • 非阻塞读取:通过O_NONBLOCK标志和串口参数配置,让read()在无数据时立即返回,不会阻塞GLUT主线程,保证窗口能响应事件。
  • 串口单次初始化:避免重复打开/配置串口带来的资源冲突和性能损耗。
  • 内存管理:及时释放动态分配的数组,防止内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 04:25:56