You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
exercise_2/myTelloProject-master/myScripts/main.py

646 lines
27 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from PyQt5 import QtCore,QtGui
from PyQt5.QtCore import QTimer,QCoreApplication,Qt,pyqtSignal
from PyQt5.QtWidgets import QApplication,QMainWindow,QMessageBox,QLabel,QWidget
from PyQt5.QtGui import QImage,QPixmap,QIcon
from UIfile.MainWindow2 import Ui_MainWindow
from myTello import myTello
import mySpeechRecognition_UI,myGesture_UI
import sys,datetime
import cv2,threading
import TelloCV_ball_people
import AlphaPose_control
class mainWindow(QMainWindow,Ui_MainWindow):
sent_move_distance_per_step = pyqtSignal(int) #向子窗口发送当前设置的步长
def __init__(self):
super(mainWindow, self).__init__()
self.setupUi(self)
#子窗口
self.speechRecognitionUI = mySpeechRecognition_UI.mySpeechRecognition_UI() #语音识别窗口
self.gestureUI = myGesture_UI.myGesture_UI() #手势控制窗口
# #加载中
# self.loadingUI = myLoadingUI.myLoading_UI(self)
# self.loadingUI_thread = loading_thread(self.loadingUI)
# self.loadingUI_thread.setDaemon(True)
# self.loadingUI_thread.start()
self.iniUI()
self.statusBar_init() #初始化状态栏
self.signal_slot_init() #初始化控件触发与反应函数
self.Video_show_init() #有关视频显示的初始化
self.RMTT = False #标记是否连接到RMTT拓展板
self.date_fmt = '%Y-%m-%d_%H%M%S' #日期格式
def iniUI(self):
'''有关控件的初始化'''
self.power = False #标记是否打开了"电源",刚开始电源键是关闭的
self.move_distance_per_step = 20 # 设置初始的每次给出移动命令后移动的步长
self.speed = 25 #设置移动速度'
#同步设置主界面速度与步长的初始值
self.horizontalSlider_setSpeed.setValue(self.speed)
self.horizontalSlider_set_MoveDistancePerStep.setValue(self.move_distance_per_step)
def Video_show_init(self):
'''有关视频显示的初始化'''
self.is_show_initial_frame = True #标记是否显示原始视频
self.Tello_frame = None #存储Tello传回来的图像
self.timer_initial_video_show = QTimer(self) #用于显示原始图像的倒计时
self.timer_initial_video_show.timeout.connect(self.show_initial_frame)
self.channel = 3
def signal_slot_init(self):
'''各信号的初始化'''
self.pushButton_power.clicked.connect(self.powerBtn_clicked) #当电源键被按下时
self.pushButton_emergency_brake.clicked.connect(self.emergancyBtn_clicked) #紧急制动按钮
self.horizontalSlider_set_MoveDistancePerStep.valueChanged.connect(self.update_distance_per_step) #每次移动步长的更新
self.horizontalSlider_setSpeed.valueChanged.connect(self.update_speed) #移动速度的更新
#来自语音控制窗口相关的信号处理
self.radioButton_speech_control.clicked.connect(self.open_speechRecognition_Window) #当按下语音控制按钮时打开语音控制界面
self.speechRecognitionUI.when_closed.connect(self.speechRecognitionUI_closed) #当关闭语音控制界面时的触发函数
self.speechRecognitionUI.send_order_signal.connect(self.speech_control) #订阅来自子窗口返回的语音命令
self.speechRecognitionUI.speech_recognition_thread.send_LED_state.connect(self.update_LED_state) #订阅改变LED状态的消息
#手势控制相关信号
self.actiongestureControl.triggered.connect(self.open_gestureUI) #打开手势控制窗口
self.gestureUI.signal_send_order_to_Tello.connect(self.process_gesture_order) #处理手势控制的发过来的命令
self.sent_move_distance_per_step.connect(self.gestureUI.get_move_distance_per_step) #主窗口步长变化时,更新子窗口的步长
#目标跟踪相关信号
self.actiongreenBall.triggered.connect(self.start_ball_track) #开始球跟踪
self.timer_ball_track = QTimer()
self.timer_ball_track.timeout.connect(self.show_ball_track_img_and_control_tello)
self.timer_face_track = QTimer()
self.actionpeople.triggered.connect(self.start_face_track) #开始人脸跟踪
self.timer_face_track.timeout.connect(self.show_face_track_img_and_control_tello)
#体态控制
self.actionpostureControl.triggered.connect(self.start_pose_control)
self.timer_pose_control = QTimer()
self.timer_pose_control.timeout.connect(self.show_pose_control_result)
self.AlphaPose_control = AlphaPose_control.AlphaPose_control()
#关闭已打开功能
self.actioncloseALL.triggered.connect(self.close_ALL_functions)
# 拍照
self.pushButton_take_photo.clicked.connect(self.take_picture)
# 录像
self.pushButton_video_record.clicked.connect(self.record_video)
self.start_video_record = False
def update_speed(self):
'''速度设置更新函数'''
self.speed = self.horizontalSlider_setSpeed.value()
def update_distance_per_step(self):
'''步长设置更新函数'''
self.move_distance_per_step = self.horizontalSlider_set_MoveDistancePerStep.value()
self.sent_move_distance_per_step.emit(self.move_distance_per_step) #同时向子窗口发送现在的步长
def power_pushed_reply(self):
'''按下按钮时的弹窗确保已经连接到Tello的WiFi'''
reply = QMessageBox(QMessageBox.Warning,'warning','请先确保已连接到Tello的Wi-Fi')
No = reply.addButton(self.tr('未连接'),QMessageBox.NoRole)
Yes = reply.addButton(self.tr('已连接(无拓展板)'),QMessageBox.YesRole)
RMTT = reply.addButton(self.tr('已连接(有拓展板)'),QMessageBox.YesRole)
reply.exec_()
if reply.clickedButton() == No:
return False
elif reply.clickedButton() == Yes:
return True
elif reply.clickedButton() == RMTT:
self.RMTT = True
return True
def powerBtn_clicked(self):
'''电源键按下的出发动作'''
#若此时电源处于关闭状态
if self.power == False:
reply = self.power_pushed_reply()
if not reply:
return
else:
try:
self.Tello = myTello() # 定义一个扩展tello实例里面有更多的功能
self.about_tello_init()
self.connect_status_label_value.setText('连接成功')
self.power = True #标记电源为打开状态
#更新电源图标
self.pushButton_power.setIcon(QIcon(QPixmap("../ico/power_button_442px_1301172_easyicon_red.net.png")))
self.timer_initial_video_show.start(100)
except:
self.connect_status_label_value.setText('连接失败')
#如果此时电源处于打开状态
elif self.power == True:
self.power = False
if self.Tello != None:
self.Tello.land() #确保着陆
self.timer_initial_video_show.stop() #停止显示图像的计时
self.timer_ball_track.stop()
self.timer_face_track.stop()
self.TelloCV.tracking = False
self.label_show_video_frame.clear() #清空显示
self.Tello.sock.close() #关闭socket避免下次打开时冲突
self.Tello.quit() #关闭Tello的各线程
del self.Tello #释放Tello实例
self.pushButton_power.setIcon(QIcon(QPixmap("../ico/power_button_442px_1301172_easyicon.net.png"))) #更新电源图标
self.connect_status_label_value.setText('请按电源键尝试建立连接') #更新状态栏
def emergancyBtn_clicked(self):
'''紧急刹车'''
if self.power:
self.Tello.send('emergency')
else:
self.status.showMessage('请先连接Tello',2000)
def statusBar_init(self):
'''状态栏的初始化函数'''
self.status = self.statusBar()
self.connect_status_label = QLabel('连接状态:')
self.connect_status_label_value = QLabel('请按电源键尝试建立连接')
self.status.addPermanentWidget(self.connect_status_label,stretch=0,)
self.status.addPermanentWidget(self.connect_status_label_value,stretch=0)
def about_tello_init(self):
'''关于tello的初始化函数'''
# self.Tello.send('command') #直接进入手动控制模式
#存储飞行状态数据
self.prev_flight_data = None
#订阅飞行状态数据更新并显示到LCDNumber上
self.Tello.subscribe(self.Tello.EVENT_FLIGHT_DATA,self.flight_data_handler)
#订阅拍照后发回来的照片
self.Tello.subscribe(self.Tello.EVENT_FILE_RECEIVED,self.handle_flight_received)
self.TelloCV = TelloCV_ball_people.TelloCV(self.Tello)
def __radioBtn_speechRecogniton_clicked(self):
'''语音识别按钮被按下的反馈函数'''
pass
def keyPressEvent(self, event):
'''键盘监听函数——当按下键时'''
"""
W S A D:前 后 左 右
Tab:起飞
L:降落
P:悬停
H:在手上落下
U I :向上 向下
J K:顺时针旋转 逆时针旋转
Z:前空翻
X:后空翻
C:左空翻
V:右空翻
"""
if self.power == True:
#判断各事件
if event.key() == Qt.Key_Tab:
self.status.showMessage('起飞')
# self.Tello.send('takeoff')
self.Tello.takeoff()
elif event.key() == Qt.Key_P:
self.status.showMessage('悬停')
self.Tello.forward(0)
self.Tello.backward(0)
self.Tello.left(0)
self.Tello.right(0)
elif event.key() == Qt.Key_H:
self.status.showMessage('在手上着落')
self.Tello.palm_land()
elif event.key() == Qt.Key_W:
self.status.showMessage('向前')
self.Tello.forward(int(self.speed))
elif event.key() == Qt.Key_A:
self.status.showMessage('向左')
self.Tello.left(int(self.speed))
elif event.key() == Qt.Key_S:
self.status.showMessage('向后')
self.Tello.backward(int(self.speed))
elif event.key() == Qt.Key_D:
self.status.showMessage('向右')
self.Tello.right(int(self.speed))
elif event.key() == Qt.Key_U:
self.status.showMessage('向上')
self.Tello.up(self.speed)
elif event.key() == Qt.Key_I:
self.status.showMessage('向下')
self.Tello.down(self.speed)
elif event.key() == Qt.Key_J:
self.status.showMessage('顺时针旋转')
self.Tello.clockwise(self.speed)
elif event.key() == Qt.Key_K:
self.status.showMessage('逆时针旋转')
self.Tello.counter_clockwise(self.speed)
def keyReleaseEvent(self, event: QtGui.QKeyEvent):
'''键盘监听函数——当按键释放时'''
if self.power == True:
if event.key() == Qt.Key_W:
self.Tello.forward(0)
self.status.clearMessage()
elif event.key() == Qt.Key_A:
self.Tello.left(0)
self.status.clearMessage()
elif event.key() == Qt.Key_S:
self.Tello.backward(0)
self.status.clearMessage()
elif event.key() == Qt.Key_D:
self.Tello.right(0)
self.status.clearMessage()
elif event.key() == Qt.Key_U:
self.status.clearMessage()
self.Tello.up(0)
elif event.key() == Qt.Key_I:
self.status.clearMessage()
self.Tello.down(0)
elif event.key() == Qt.Key_J:
self.status.clearMessage()
self.Tello.clockwise(0)
elif event.key() == Qt.Key_K:
self.status.clearMessage()
self.Tello.counter_clockwise(0)
elif event.key() == Qt.Key_Z:
self.status.showMessage('前空翻',1000)
self.Tello.flip_forward()
# self.Tello.send('flip f')
elif event.key() == Qt.Key_X:
self.status.showMessage('后空翻',1000)
self.Tello.flip_back()
elif event.key() == Qt.Key_C:
self.status.showMessage('左空翻',1000)
self.Tello.flip_left()
elif event.key() == Qt.Key_V:
self.status.showMessage('右空翻',1000)
self.Tello.flip_right()
elif event.key() == Qt.Key_L:
self.status.showMessage('降落')
self.Tello.land()
elif event.key() == Qt.Key_G:
self.status.showMessage('抛飞模式')
self.Tello.send('throwfly')
def flight_data_handler(self,event, sender, data):
'''处理飞行状态数据的变化'''
text = str(data)
text_list = text.split('|')
state_data = {}
#如果状态数据发生变化了,更新数据
if text != self.prev_flight_data:
for item in text_list:
key = ''
value = ''
flag = False
for x in item:
if x == ' ':
continue
elif x == ':':
flag = True
else:
if not flag:
key = key + x
else:
value = value + x
state_data[key] = value #添加到字典中
self.update_lcdNumbers(state_data)
# print(state_data)
self.prev_flight_data = text
def update_lcdNumbers(self,data:dict):
'''更新LCD显示飞行状态包括剩余电量、'''
for key in data:
if key == 'ALT':
self.lcdNumber_Height.display(data[key])
elif key == 'SPD':
self.lcdNumber_speed.display(data[key])
elif key == 'BAT':
#设置一下不同的电量对应不同的LCD颜色
if int(data[key]) < 70 and int(data[key]) >= 30:
self.lcdNumber_battery.setStyleSheet("color:rgb(255, 212, 0);") #黄色,中等电量
elif int(data[key]) >= 70:
self.lcdNumber_battery.setStyleSheet("color:rgb(33, 255, 6);") #绿色,充足电量
else:
self.lcdNumber_battery.setStyleSheet("color:rgb(255, 0, 22);") #红色,电量不足
self.lcdNumber_battery.display(data[key])
elif key == 'WIFI':
self.lcdNumber_WiFi.display((data[key]))
def open_speechRecognition_Window(self):
'''语音控制按钮被按下的反应'''
if self.power == False:
QMessageBox.warning(self, 'warning', "请先按电源键建立连接", buttons=QMessageBox.Ok)
self.radioButton_speech_control.setChecked(False)
self.radioButton_keyBoard_control.setChecked(True)
return
self.speechRecognitionUI.show() #显示子窗口
self.speechRecognitionUI.dockWidget.setFloating(True) #把浮动窗口浮动了
def speechRecognitionUI_closed(self):
'''语音窗口关闭时的反应'''
self.radioButton_speech_control.setChecked(False) #更新按钮状态
self.radioButton_keyBoard_control.setChecked(True)
def speech_control(self,is_tellopy_order,order):
'''语音控制Tello'''
print('Tello收到命令')
if is_tellopy_order == False:
_ = self.Tello.send(order)
else:
if order == 'T':
# self.Tello.send('takeoff')
self.Tello.takeoff()
# self.Tello.send('command')
elif order == 'H':
self.Tello.palm_land()
elif order == 'U':
self.Tello.up(self.speed)
elif order == 'L':
self.Tello.left(self.speed)
elif order == 'R':
# self.Tello.send('land')
self.Tello.land()
elif order == 'S':
self.Tello.backward(self.speed)
elif order == 'D':
self.Tello.right(self.speed)
elif order == 'P':
self.Tello.send('stop')
self.Tello.forward(0)
self.Tello.backward(0)
self.Tello.left(0)
self.Tello.right(0)
self.Tello.up(0)
self.Tello.down(0)
self.Tello.clockwise(0)
self.Tello.counter_clockwise(0)
elif order == 'FF':
self.Tello.flip_forward()
# self.Tello.send('flip f')
elif order == 'FS':
self.Tello.flip_back()
# self.Tello.send('flip b')
elif order == 'FL':
self.Tello.flip_left()
# self.Tello.send('flip l')
elif order == 'FR':
self.Tello.flip_right()
# self.Tello.send('flip r')
def update_LED_state(self,state:str):
'''更新顶部LED的状态'''
if self.RMTT == True:
if state == 'recording':
self.Tello.send('EXT led br 1.5 255 0 255')
elif state == 'executing':
self.Tello.send('EXT led 65 105 225')
else:
return
def open_gestureUI(self):
'''打开手势控制窗口'''
if self.power:
self.gestureUI.show()
self.gestureUI.dockWidget_HintHelp.setFloating(True)
self.sent_move_distance_per_step.emit(self.move_distance_per_step)
else:
QMessageBox.warning(self, 'warning', "请先按电源键建立连接", buttons=QMessageBox.Ok)
def process_gesture_order(self,result : str, order : str):
'''处理手势控制命令'''
#无效命令
if order == 'land':
self.Tello.land()
elif order == 'landhand':
self.Tello.palm_land()
else:
if order in ('cw 90','ccw 90','stop','takeoff','command'):
#这些命令不需要得到步长
self.Tello.send(order)
else:
self.Tello.send(order + ' ' + str(self.move_distance_per_step))
def show_initial_frame(self):
'''显示来自无人机的原始视频图像'''
#只有当电源打开以及"显示无人机图像"标记为真的时候才会显示图像
if self.is_show_initial_frame and self.power:
#显示原始无人机相机画面
self.img_w = self.label_show_video_frame.width()
self.img_h = self.label_show_video_frame.height()
self.Tello_frame = self.Tello.get_video_frame()
#检查是不是还没有跳完帧或者是不是没有解码成功
#假如解码失败
if self.Tello_frame == 'decode error':
self.status.showMessage('解码失败')
return
#解码成功但正在跳帧
else:
if self.Tello_frame == '':
return
else:
if self.start_video_record:
frame_BGR = cv2.cvtColor(self.Tello_frame, cv2.COLOR_RGB2BGR)
self.Video_Writer.write(frame_BGR)
self.Tello_frame = cv2.resize(self.Tello_frame, (self.img_w, self.img_h)) #将图像大小设置为显示窗口的大小
pixmap = QPixmap.fromImage(QImage(self.Tello_frame.data,self.img_w,self.img_h,self.channel*self.img_w,QImage.Format_RGB888))
self.label_show_video_frame.setPixmap(pixmap)
def start_ball_track(self):
'''开始绿球跟踪'''
if self.power:
#关闭显示原始图像
self.is_show_initial_frame = False
self.timer_initial_video_show.stop()
#关闭其他功能
self.timer_face_track.stop()
self.timer_pose_control.stop()
#开始跟踪并显示跟踪图像
self.status.showMessage('已开启绿球跟踪功能')
self.TelloCV.tracking = True
self.timer_ball_track.start(100)
else:
QMessageBox.warning(self, 'warning', "请先按电源键建立连接", buttons=QMessageBox.Ok)
def show_ball_track_img_and_control_tello(self):
'''跟踪绿球并显示跟踪图像'''
self.img_w = self.label_show_video_frame.width()
self.img_h = self.label_show_video_frame.height()
frame = self.Tello.get_video_frame()
if frame == '':
return
else:
image = self.TelloCV.ball_process_frame(frame)
image = cv2.resize(image, (self.img_w, self.img_h)) #将图像大小设置为显示窗口的大小
pixmap = QPixmap.fromImage(QImage(image.data, self.img_w, self.img_h, 3 * self.img_w, QImage.Format_BGR888))
self.label_show_video_frame.setPixmap(pixmap)
def close_ALL_functions(self):
'''关闭跟踪、手势控制、体态控制等功能,并开始显示原始图像'''
#关闭
self.timer_ball_track.stop()
self.timer_face_track.stop()
self.timer_pose_control.stop()
#打开原始图像显示
self.is_show_initial_frame = True
self.timer_initial_video_show.start(100)
self.TelloCV.tracking = False
def start_face_track(self):
'''开始人脸跟踪'''
if self.power:
#关闭显示原始图像
self.is_show_initial_frame = False
self.timer_initial_video_show.stop()
#关闭其他功能
self.timer_ball_track.stop()
self.timer_pose_control.stop()
#开始跟踪并显示跟踪图像
self.TelloCV.tracking = True
self.status.showMessage('已开启人脸跟踪功能')
self.timer_face_track.start(100)
else:
QMessageBox.warning(self, 'warning', "请先按电源键建立连接", buttons=QMessageBox.Ok)
def show_face_track_img_and_control_tello(self):
'''跟踪人脸并显示跟踪图像'''
self.img_w = self.label_show_video_frame.width()
self.img_h = self.label_show_video_frame.height()
frame = self.Tello.get_video_frame()
if frame == '':
return
else:
image = self.TelloCV.face_process_frame(frame)
image = cv2.resize(image, (self.img_w, self.img_h)) # 将图像大小设置为显示窗口的大小
pixmap = QPixmap.fromImage(QImage(image.data, self.img_w, self.img_h, 3 * self.img_w, QImage.Format_BGR888))
self.label_show_video_frame.setPixmap(pixmap)
def handle_flight_received(self, event, sender, data):
"""把拍照得到的照片存下来存放在Pictures文件夹里"""
path = '../Pictures/tello-%s.jpeg' % (
datetime.datetime.now().strftime(self.date_fmt))
with open(path, 'wb') as out_file:
out_file.write(data)
print('Saved photo to %s' % path)
def take_picture(self):
'''拍照'''
if self.power:
self.Tello.take_picture()
else:
QMessageBox.warning(self, 'warning', "请先按电源键建立连接", buttons=QMessageBox.Ok)
def record_video(self):
'''录像'''
if self.power:
if not self.start_video_record:
fourcc = cv2.VideoWriter_fourcc(*'XVID')
self.Video_Writer = cv2.VideoWriter('Tello-Video-%s.avi'% (datetime.datetime.now().strftime(self.date_fmt)),fourcc, 20.0, (self.Tello.width,self.Tello.height))
self.start_video_record = True
self.pushButton_video_record.setText('录像中')
else:
self.start_video_record = False
self.pushButton_video_record.setText('')
self.Video_Writer.release()
def start_pose_control(self):
'''开始体态控制'''
if self.power:
#关闭显示原始图像
self.is_show_initial_frame = False
self.timer_initial_video_show.stop()
#关闭其他功能
self.timer_ball_track.stop()
self.timer_face_track.stop()
self.TelloCV.tracking = False
#开始体态控制
self.status.showMessage('已开启体态控制功能')
self.timer_pose_control.start(300)
# 加载模型
self.PoseModel = self.AlphaPose_control.downModel()
else:
QMessageBox.warning(self, 'warning', "请先按电源键建立连接", buttons=QMessageBox.Ok)
def show_pose_control_result(self):
'''显示体态控制图像'''
self.img_w = self.label_show_video_frame.width()
self.img_h = self.label_show_video_frame.height()
img_path = '../AlphaPose/duan_alphapose/photo/'
image = self.Tello.get_video_frame()
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
cv2.imwrite(img_path + 'frame.jpg', image) # 保存图片
# 载入图片
img_names = self.AlphaPose_control.GetImage(self.AlphaPose_control.args)
# alphapose处理
point_results = self.AlphaPose_control.Alphapose(img_names, self.PoseModel)
# 分析动作
if point_results is not None:
try:
pose,result_img = self.AlphaPose_control.PoseFind(point_results)
print(pose)
except:
self.AlphaPose_control.del_files(img_path) # 删除文件夹内图片
return
result_img= cv2.resize(result_img, (self.img_w, self.img_h)) # 将图像大小设置为显示窗口的大小
pixmap = QPixmap.fromImage(QImage(result_img.data, self.img_w, self.img_h, 3 * self.img_w, QImage.Format_BGR888))
self.label_show_video_frame.setPixmap(pixmap)
self.AlphaPose_control.del_files(img_path) # 删除文件夹内图片
else:
self.AlphaPose_control.del_files(img_path) # 删除文件夹内图片
return
if __name__ == '__main__':
QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
mainWin = mainWindow()
mainWin.show()
sys.exit(app.exec())