QT之键盘控制虚拟遥控系统开发总结

发布于:2025-08-12 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、项目概述

本文总结了一个基于Qt框架开发的键盘控制虚拟遥控系统,通过WSADQE六个按键实现方向控制功能。系统模拟了真实遥控器的操作逻辑,包括前进/后退控制、左右上下转向功能。

二、核心功能实现

1. 键盘事件处理机制

系统通过重写Qt的keyPressEventkeyReleaseEvent方法实现键盘控制:

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    // W键 - 前进
    if(event->key()==Qt::Key_W)
    {
        movementState = FORWARD;
        ui->but_forward->setStyleSheet("background-color:rgb(138,43,226)");
    }
    // ...其他按键处理
}

void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
    // W键释放 - 停止前进
    if(event->key()==Qt::Key_W&&!event->isAutoRepeat())
    {
        movementState = STOPPED;
        mainThrottle = 0;
        ui->but_forward->setStyleSheet("background-color:white");
    }
    // ...其他按键释放处理
}

关键点:

  • 使用Qt::Key枚举识别特定按键

  • !event->isAutoRepeat()防止按键重复触发

  • 按键状态与UI元素(按钮样式)同步更新

2. 状态机设计

系统采用状态机模式管理运动状态:

// 运动状态枚举
enum MovementState {
    STOPPED = 0,   // 停止
    FORWARD = 1,   // 前进
    BACKWARD = 2   // 后退
};

// 转向状态枚举
enum SteeringState {
    NONE = 0,    // 无
    LEFT = 1,    // 左
    RIGHT = 2,   // 右
    UP = 3,      // 上
    DOWN = 4     // 下
};

// 状态变量
quint8 movementState = 0;     // 运动状态
quint8 steeringState = 0;     // 转向/升降状态

优势:

  • 清晰的状态定义和转换逻辑

  • 便于扩展新的控制状态

  • 组合状态处理更直观

3. 组合控制逻辑

系统实现了多种组合控制模式:

// 前进+左转组合
if(movementState == FORWARD && steeringState == LEFT)
{
    if(mainThrottle < maxThrottle) mainThrottle += 100;
    upperRudderAngle = 20;
    lowerRudderAngle = 20;
}

// 后退+右转组合
if(movementState == BACKWARD && steeringState == RIGHT)
{
    if(mainThrottle > minThrottle) mainThrottle -= 50;
    upperRudderAngle = -20;
    lowerRudderAngle = -20;
}

特点:

  • 前进/后退与转向/升降可任意组合

  • 不同方向采用差异化参数调整

  • 设置了合理的油门限制范围

三、关键技术点

1. 油门控制与转向角度管理

// 油门参数
qint16 mainThrottle = 0;         // 主推进器油门值
const qint16 maxThrottle = 700;  // 最大转速限制
const qint16 minThrottle = -350; // 最小转速限制

// 角度
qint8 upperRudderAngle = 0;      // 上角度
qint8 lowerRudderAngle = 0;      // 下角度
qint8 leftRudderAngle = 0;       // 左角度
qint8 rightRudderAngle = 0;      // 右角度

设计考虑:

  • 前进/后退采用非对称速度限制

  • 转向角度固定增量调整(±20度)

2. 指令生成与发送机制

// 构建控制指令
QString list = "$1RC," + throttleStr + "," + upperRudderStr + "," 
             + lowerRudderStr + "," + leftRudderStr + "," + rightRudderStr + "^";

// 发送策略
if((movementState != lastMovementState) || (steeringState != lastSteeringState))
{
    // 状态变化时立即发送
    qDebug() << "list:" << list.toLatin1();
    lastMovementState = movementState;
    lastSteeringState = steeringState;
}
else if((movementState != STOPPED) || (steeringState != NONE))
{
    // 持续状态时定期发送(每20次计数)
    commandCounter += 1;
    if(commandCounter == 20)
    {
        qDebug() << "list:" << list.toLatin1();
        commandCounter = 0;
    }
}

优化点:

  • 状态变化时即时响应

  • 持续操作时降低发送频率

  • 采用标准指令格式($1RC开头,^结尾)

四、UI反馈设计

系统通过按钮样式变化提供操作反馈:

// 按键按下时改变样式
ui->but_forward->setStyleSheet("background-color:rgb(138,43,226)");

// 按键释放时恢复默认
ui->but_forward->setStyleSheet("background-color:white");

视觉设计:

  • 不同功能使用不同颜色标识

  • 前进(紫色)、后退(黄色)、左转(红色)等

  • 即时反馈提升操作体验

五、项目总结

本键盘控制虚拟遥控系统展示了:

  1. Qt事件处理机制的有效应用

  2. 状态机设计在控制系统的实用性

  3. 组合控制逻辑的清晰实现方式

  4. 用户反馈的即时可视化设计

完整项目代码已实现核心控制功能(见文末),通过进一步优化可发展为完善的虚拟遥控平台,适用于机器人控制、无人机模拟等应用场景。

关键收获:

  • Qt键盘事件处理的正确方式

  • 复杂控制逻辑的状态管理技巧

  • 实时系统的人机交互设计要点

  • 控制指令的优化传输策略

完整项目代码

// 基于键盘控制的虚拟遥控---通过WSADQE键控制方向

#include "keyevent.h"

qint16 mainThrottle = 0;         // 主推进器油门值
quint8 commandCounter = 0;        // 指令发送计数器
quint8 movementState = 0;         // 运动状态(0:停止,1:前进,2:后退)
quint8 steeringState = 0;         // 转向/升降状态(0:无,1:左,2:右,3:上,4:下)
quint8 lastMovementState = 0;     // 上一次的运动状态
quint8 lastSteeringState = 0;     // 上一次的转向/升降状态
qint8 upperRudderAngle = 0;      // 上角度(默认0)
qint8 lowerRudderAngle = 0;      // 下角度(默认0)
qint8 leftRudderAngle = 0;       // 左角度(默认0)
qint8 rightRudderAngle = 0;      // 右角度(默认0)

const qint16 maxThrottle = 700; // 最大转速限制
const qint16 minThrottle = -350;  // 最小转速限制

// 运动状态枚举
enum MovementState {
    STOPPED = 0,   // 停止
    FORWARD = 1,   // 前进
    BACKWARD = 2   // 后退
};

// 转向/升降状态枚举
enum SteeringState {
    NONE = 0,    // 无
    LEFT = 1,    // 左
    RIGHT = 2,   // 右
    UP = 3,      // 上
    DOWN = 4     // 下
};


// 按键按下事件处理
void MainWindow::keyPressEvent(QKeyEvent *event)
{
    // W键 - 前进
    if(event->key()==Qt::Key_W)
    {
        movementState = FORWARD; // 设置前进状态
        ui->but_forward->setStyleSheet("background-color:rgb(138,43,226)"); // 改变按钮颜色
    }
    // S键 - 后退
    if(event->key()==Qt::Key_S)
    {
        movementState = BACKWARD; // 设置后退状态
        ui->but_back->setStyleSheet("background-color:rgb(255,255,0)");
    }
    // A键 - 左转
    if(event->key()==Qt::Key_A) 
    {
        steeringState = LEFT;
        ui->but_left->setStyleSheet("background-color:rgb(255,0,0)");
    }
    // D键 - 右转
    if(event->key()==Qt::Key_D) 
    {
        steeringState = RIGHT;
        ui->but_right->setStyleSheet("background-color:rgb(127,255,0)");
    }
    // Q键 - 上升
    if(event->key()==Qt::Key_Q) 
    {
        steeringState = UP;
        ui->but_up->setStyleSheet("background-color:rgb(127,255,0)");
    }
    // E键 - 下潜
    if(event->key()==Qt::Key_E) 
    {
        steeringState = DOWN;
        ui->but_down->setStyleSheet("background-color:rgb(127,255,0)");
    }

    // 以下是组合键处理逻辑
    // 仅左转(无前后运动)
    if(movementState == STOPPED && steeringState == LEFT)   
    {
        upperRudderAngle = 20; // 上角度设为20
        lowerRudderAngle = 20; // 下角度设为20
    }
    // 仅右转(无前后运动)
    if(movementState == STOPPED && steeringState == RIGHT)    
    {
        upperRudderAngle = -20; // 上角度设为-20
        lowerRudderAngle = -20; // 下角度设为-20
    }
    // 仅上升(无前后运动)
    if(movementState == STOPPED && steeringState == UP)    
    {
        leftRudderAngle = 20; // 左角度设为20
        rightRudderAngle = 20; // 右角度设为20
    }
    // 仅下潜(无前后运动)
    if(movementState == STOPPED && steeringState == DOWN)    
    {
        leftRudderAngle = -20; // 左角度设为-20
        rightRudderAngle = -20; // 右角度设为-20
    }
    // 仅前进(无转向/升降)
    if(movementState == FORWARD && steeringState == NONE)   //前进
    {
        mainThrottle += 100; // 增加主推转速
        if(mainThrottle > maxThrottle || mainThrottle < 0) //检查油门值是否超出合理范围,是否超过最大限制,是否小于0(无效值)
        {
            mainThrottle = maxThrottle; // 限制转速范围
        }
    }
    // 前进+左转
    if(movementState == FORWARD && steeringState == LEFT)   
    {
        if(mainThrottle < maxThrottle)
        {
            mainThrottle += 100;  // 增加主推转速
        }
        upperRudderAngle = 20;
        lowerRudderAngle = 20; // 设置角度为左转
    }
    // 前进+右转
    if(movementState == FORWARD && steeringState == RIGHT)    
    {
        if(mainThrottle < maxThrottle)
        {
            mainThrottle += 100;
        }
        upperRudderAngle = -20;
        lowerRudderAngle = -20; // 设置角度为右转
    }
    // 前进+上升
    if(movementState == FORWARD && steeringState == UP)    
    {
        if(mainThrottle < maxThrottle)
        {
            mainThrottle += 100;
        }
        leftRudderAngle = 20;
        rightRudderAngle = 20; // 设置角度为上升
    }
    // 前进+下潜
    if(movementState == FORWARD && steeringState == DOWN)    
    {
        if(mainThrottle < maxThrottle)
        {
            mainThrottle += 100;
        }
        leftRudderAngle = -20;
        rightRudderAngle = -20; // 设置角度为下潜
    }
    // 仅后退
    if(movementState == BACKWARD && steeringState == NONE)   //后退
    {
        if(mainThrottle > minThrottle)
        {
            mainThrottle -= 50; // 减小主推转速
        }
    }
    // 后退+左转
    if(movementState == BACKWARD && steeringState == LEFT)   
    {
        if(mainThrottle > minThrottle)
        {
            mainThrottle -= 50;
        }
        upperRudderAngle = 20;
        lowerRudderAngle = 20;
    }
    // 后退+右转
    if(movementState == BACKWARD && steeringState == RIGHT)    
    {
        if(mainThrottle > minThrottle)
        {
            mainThrottle -= 50;
        }
        upperRudderAngle = -20;
        lowerRudderAngle = -20;
    }
    // 后退+上升
    if(movementState == BACKWARD && steeringState == UP)   
    {
        if(mainThrottle > minThrottle)
        {
            mainThrottle -= 50;
        }
        leftRudderAngle = 20;
        rightRudderAngle = 20;
    }
    // 后退+下潜
    if(movementState == BACKWARD && steeringState == DOWN)   
    {
        if(mainThrottle > minThrottle)
        {
            mainThrottle -= 50;
        }
        leftRudderAngle = -20;
        rightRudderAngle = -20;
    }
}

// 按键释放事件处理
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
    // W键释放 - 停止前进
    if(event->key()==Qt::Key_W&&!event->isAutoRepeat())
    {
        movementState = STOPPED;
        mainThrottle = 0; // 重置油门中位
        ui->but_forward->setStyleSheet("background-color:white"); // 恢复按钮颜色
    }
    // S键释放 - 停止后退
    if(event->key()==Qt::Key_S&&!event->isAutoRepeat())
    {
        movementState = STOPPED;
        mainThrottle = 0; // 重置油门中位
        ui->but_back->setStyleSheet("background-color:white");
    }
    // A键释放 - 停止左转
    if(event->key()==Qt::Key_A&&!event->isAutoRepeat())
    {
        steeringState = NONE;
        upperRudderAngle = 0;
        lowerRudderAngle = 0; // 重置角度到中位
        ui->but_left->setStyleSheet("background-color:white");
    }
    // D键释放 - 停止右转
    if(event->key()==Qt::Key_D&&!event->isAutoRepeat())
    {
        steeringState = NONE;
        upperRudderAngle = 0;
        lowerRudderAngle = 0; // 重置角度到中位
        ui->but_right->setStyleSheet("background-color:white");
    }
    // Q键释放 - 停止上升
    if(event->key()==Qt::Key_Q&&!event->isAutoRepeat())
    {
        steeringState = NONE;
        leftRudderAngle = 0;
        rightRudderAngle = 0; // 重置角度到中位
        ui->but_up->setStyleSheet("background-color:white");
    }
    // E键释放 - 停止下潜
    if(event->key()==Qt::Key_E&&!event->isAutoRepeat())
    {
        steeringState = NONE;
        leftRudderAngle = 0;
        rightRudderAngle = 0;
        ui->but_down->setStyleSheet("background-color:white");
    }

    // 任何控制键释放时发送数据
    if((event->key() == Qt::Key_W) || (event->key() == Qt::Key_S) || (event->key() == Qt::Key_A) || (event->key() == Qt::Key_D) || (event->key() == Qt::Key_Q) || (event->key() == Qt::Key_E)) //发送数据
    {
        // 将各参数转换为字符串
        QString throttleStr = QString::number(mainThrottle);
        QString upperRudderStr = QString::number(upperRudderAngle);
        QString lowerRudderStr = QString::number(lowerRudderAngle);
        QString leftRudderStr = QString::number(leftRudderAngle);
        QString rightRudderStr = QString::number(rightRudderAngle);

        // 构建指令字符串:$1RC,主推转速,上角度,下角度,左角度,右角度^
        QString list = "$1RC," + throttleStr + "," + upperRudderStr + "," + lowerRudderStr + "," + leftRudderStr + "," + rightRudderStr +"^";
        QByteArray list_1 = list.toLatin1();  // 转换为Latin1编码

        // 如果当前有运动状态,则每20次计数发送一次指令
        if((movementState != STOPPED) || (steeringState != NONE))
        {
            // id1=startTimer(1000);
            commandCounter += 1;
            if(commandCounter == 20)
            {
                Radio_SUM = 0;
                //my_serialport->write(list_1); // 实际发送指令(被注释)
                qDebug() << "list:" << list_1;
                commandCounter = 0;
            }
        }
        // 如果状态发生变化,立即发送指令
        if((movementState != lastMovementState) || (steeringState != lastSteeringState))
        {
            Radio_SUM = 0;
            //my_serialport->write(list_1); // 实际发送指令(被注释)
            qDebug() << "list:" << list_1;
            lastMovementState = movementState;  // 更新上一次前后状态
            lastSteeringState = steeringState;  // 更新上一次转向/升降状态
        }
    }
}

UI


网站公告

今日签到

点亮在社区的每一天
去签到