一、项目概述
本文总结了一个基于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; // 上角度设为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; // 更新上一次转向/升降状态
}
}
}