QT之键盘控制虚拟遥控系统开发总结
一、项目概述
本文总结了一个基于Qt框架开发的键盘控制虚拟遥控系统,通过WSADQE六个按键实现方向控制功能。系统模拟了真实遥控器的操作逻辑,包括前进/后退控制、左右上下转向功能。
二、核心功能实现
1. 键盘事件处理机制
系统通过重写Qt的keyPressEvent
和keyReleaseEvent
方法实现键盘控制:
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");
视觉设计:
不同功能使用不同颜色标识
前进(紫色)、后退(黄色)、左转(红色)等
即时反馈提升操作体验
五、项目总结
本键盘控制虚拟遥控系统展示了:
Qt事件处理机制的有效应用
状态机设计在控制系统的实用性
组合控制逻辑的清晰实现方式
用户反馈的即时可视化设计
完整项目代码已实现核心控制功能(见文末),通过进一步优化可发展为完善的虚拟遥控平台,适用于机器人控制、无人机模拟等应用场景。
关键收获:
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; // 上角度设为20lowerRudderAngle = 20; // 下角度设为20}// 仅右转(无前后运动)if(movementState == STOPPED && steeringState == RIGHT) {upperRudderAngle = -20; // 上角度设为-20lowerRudderAngle = -20; // 下角度设为-20}// 仅上升(无前后运动)if(movementState == STOPPED && steeringState == UP) {leftRudderAngle = 20; // 左角度设为20rightRudderAngle = 20; // 右角度设为20}// 仅下潜(无前后运动)if(movementState == STOPPED && steeringState == DOWN) {leftRudderAngle = -20; // 左角度设为-20rightRudderAngle = -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; // 更新上一次转向/升降状态}}
}