文章目錄
目錄
文章目錄
前言
一、準備作業
1.python作業環境
2.ros環境
3.QT designer
二、界面程式設計
1.界面設計
2.ui檔案轉py檔案
三、上位機程式撰寫
1.具體思路
2.具體實作
3.遇到的問題
(1)花屏問題
(2)rospy.spin()問題
四、運行結果
1.打開攝像頭
2.打開人臉識別
3.打開語音控制
4.打開鍵盤控制
5.上位機控制
結語
前言
本文在基本功能實作的基礎上,基于PyQT5撰寫了一個用來控制ros機器人的上位機
原始碼分享:https://gitee.com/sy_run/myroscar
提示:以下是本篇文章正文內容,下面案例可供參考
一、準備作業
1.python作業環境
本文選擇的python環境為pycharm2021,安裝教程在這里不再多說
所需要的核心庫有PyQt5,rospy和opencv
2.ros環境
由于換了新電腦,安裝了ubuntu20.04的版本,因此ros的版本更換為了noetic,但具體程式并沒有太大變化,
3.QT designer
安裝QT designer可以方便我們更好的設計界面,具體安裝流程不在多說
二、界面程式設計
1.界面設計
打開QT designer,選擇mainwindow,然后通過想要的控制元件,設計一個自己喜歡的界面即可,本文
設計的界面如下所示:

具有8個按鈕和一個QLabel控制元件,點擊保存即可生成ui檔案,
2.ui檔案轉py檔案
通過pyqt5自帶的pyuic能夠將ui檔案轉換成py檔案,具體方式如下
(1)將ui檔案匯入至pycharm之后,然后點擊檔案->設定

選擇工具->外部工具

(2)名稱輸入pyuic,然后程式輸入python3,因為ros-noetic是通過python3運行python程式,如果是ros-kinetic版本的話,這里可能需要輸入python,
引數這一欄輸入下面內容
-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py
作業目錄輸入$FileDirs即可,也就是生成的py檔案和ui檔案在同一目錄下
(3)點擊確定,在左側專案目錄中找到ui檔案,右鍵點擊ui檔案,選擇External Tools,點擊pyuic

此時當前目錄會生成一個py檔案

回傳上一級目錄,新建檔案夾scripts,將生成的py檔案移動到該目錄下,具體的py檔案內容如下,沒有QT Designer的朋友可以直接復制下面這個代碼,
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'MyRobotApp.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(689, 529)
MainWindow.setToolTipDuration(1)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.ButtonGo = QtWidgets.QPushButton(self.centralwidget)
self.ButtonGo.setGeometry(QtCore.QRect(210, 350, 100, 50))
self.ButtonGo.setObjectName("ButtonGo")
self.ButtonLeft = QtWidgets.QPushButton(self.centralwidget)
self.ButtonLeft.setGeometry(QtCore.QRect(110, 400, 100, 50))
self.ButtonLeft.setObjectName("ButtonLeft")
self.ButtonRight = QtWidgets.QPushButton(self.centralwidget)
self.ButtonRight.setGeometry(QtCore.QRect(310, 400, 100, 50))
self.ButtonRight.setObjectName("ButtonRight")
self.ButtonBack = QtWidgets.QPushButton(self.centralwidget)
self.ButtonBack.setGeometry(QtCore.QRect(210, 450, 100, 50))
self.ButtonBack.setObjectName("ButtonBack")
self.OpenFace = QtWidgets.QPushButton(self.centralwidget)
self.OpenFace.setGeometry(QtCore.QRect(550, 100, 100, 50))
self.OpenFace.setObjectName("OpenFace")
self.OpenVoice = QtWidgets.QPushButton(self.centralwidget)
self.OpenVoice.setGeometry(QtCore.QRect(550, 180, 100, 50))
self.OpenVoice.setObjectName("OpenVoice")
self.OpenKey = QtWidgets.QPushButton(self.centralwidget)
self.OpenKey.setGeometry(QtCore.QRect(550, 260, 100, 50))
self.OpenKey.setObjectName("OpenKey")
self.OpenCamera = QtWidgets.QPushButton(self.centralwidget)
self.OpenCamera.setGeometry(QtCore.QRect(550, 20, 100, 50))
self.OpenCamera.setObjectName("OpenCamera")
self.VeidoLabel = QtWidgets.QLabel(self.centralwidget)
self.VeidoLabel.setGeometry(QtCore.QRect(20, 20, 480, 320))
self.VeidoLabel.setText("攝像頭未打開!")
self.VeidoLabel.setWordWrap(False)
self.VeidoLabel.setObjectName("VeidoLabel")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.retranslateUi(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "My RobotCar"))
self.ButtonGo.setText(_translate("MainWindow", "前進"))
self.ButtonLeft.setText(_translate("MainWindow", "左轉"))
self.ButtonRight.setText(_translate("MainWindow", "右轉"))
self.ButtonBack.setText(_translate("MainWindow", "后退"))
self.OpenFace.setText(_translate("MainWindow", "打開人臉識別"))
self.OpenVoice.setText(_translate("MainWindow", "打開語音控制"))
self.OpenKey.setText(_translate("MainWindow", "打開鍵盤控制"))
self.OpenCamera.setText(_translate("MainWindow", "打開攝像頭"))
三、上位機程式撰寫
新建app.py檔案,準備開始撰寫上位機,
1.具體思路
上一篇內容提到過,pc和樹莓派之間的通訊是通過發布和訂閱主題實作的,因此我們可以通過按下按鈕發送clicked信號,在相關聯的槽去實作命令的發布即可通過上位機實作通信,
而想要通過上位機啟動按鍵控制和語音控制,在程式里撰寫是十分麻煩的,我們可以利用已經撰寫好的程式,利用python中os.fork()函式創建子行程,然后利用os.execl()去呼叫相關可執行檔案,即可實作功能,
2.具體實作
#!/usr/bin/python3
import signal
import sys
import os
import rospy
import cv2
from cv_bridge import CvBridge
from sensor_msgs.msg import CompressedImage
from PyQt5 import QtCore, QtGui, QtWidgets
from std_msgs.msg import String
from MyRobotApp import Ui_MainWindow
class MyCallback(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyCallback, self).__init__(parent)
rospy.init_node("qtcmd") # 初始化結點
self.qtkeypub = rospy.Publisher("keycmd", String, queue_size=1000) # 鍵盤指令發布者
self.bridge = CvBridge()
# 訂閱壓縮影像主題,提高幀率
self.comimgsub = rospy.Subscriber("image_compressed/compressed", CompressedImage, self.compressedimagecallback)
self.msg = String("") # 發送的訊息
self.setupUi(self) # 設定ui
# 人臉模型
self.face_cascade = cv2.CascadeClassifier(r'/usr/share/opencv4/haarcascades/haarcascade_frontalface_alt.xml')
# 槽
self.OpenCamera.clicked.connect(self.OpenCameraCallback)
self.OpenFace.clicked.connect(self.OpenFaceCallback)
self.OpenKey.clicked.connect(self.OpenKeyCallback)
self.OpenVoice.clicked.connect(self.OpenVoiceCallback)
self.ButtonGo.clicked.connect(self.ButtonGoCallback)
self.ButtonLeft.clicked.connect(self.ButtonLeftCallback)
self.ButtonBack.clicked.connect(self.ButtonBackCallback)
self.ButtonRight.clicked.connect(self.ButtonRightCallback)
# 宣告
self._translate = QtCore.QCoreApplication.translate
self.compressed_image = None # 壓縮影像
self.opencameraflag = False # 攝像頭開啟標志
self.openfaceflag = False # 人臉識別開啟標志
self.openkeyflag = False # 鍵盤開啟標志
self.openvoiceflag = False # 語音開啟標志
self.keypid = -1 # 鍵盤行程號
self.voicepubpid = -1 # 語音上報行程
self.voicesubpid = -1 # 語音識別行程
self.camerapid = -1 # 攝像頭行程
# 人臉識別函式
def facedetect(self, data):
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
faces = self.face_cascade.detectMultiScale(
gray,
scaleFactor=1.15,
minNeighbors=5,
minSize=(5, 5),
flags=cv2.CASCADE_SCALE_IMAGE)
for face in faces:
x,y,w,h = face
# 畫矩形
cv2.rectangle(data, (x, y), (x+w, y+h), (50, 255, 50), 2)
# QT顯示圖片函式
def showimg(self, data):
pixmap = QtGui.QImage(data, 480, 320, QtGui.QImage.Format_RGB888)
pixmap = QtGui.QPixmap.fromImage(pixmap)
self.VeidoLabel.setPixmap(pixmap)
self.show()
# ros圖片主題回呼函式,引數data為圖片資料
def compressedimagecallback(self, data):
bridge = CvBridge()
self.compressed_image = bridge.compressed_imgmsg_to_cv2(data, "bgr8")
if self.openfaceflag:
self.facedetect(self.compressed_image)
self.compressed_image = cv2.cvtColor(self.compressed_image, cv2.COLOR_BGR2RGB)
self.showimg(self.compressed_image)
# 打開攝像頭
def OpenCameraCallback(self): # 攝像頭
if not self.opencameraflag: # 如果攝像頭未打開
self.opencameraflag = True # 標志為打開
self.OpenCamera.setText(self._translate("MainWindow", "關閉攝像頭")) # 修改按鍵文本
self.msg = String("opencam")
self.qtkeypub.publish(self.msg) # 發布打開命令
else: # 如果已經打開
self.opencameraflag = False # 標志關閉
self.OpenCamera.setText(self._translate("MainWindow", "打開攝像頭")) # 修改文本
self.msg = String("closecam")
self.qtkeypub.publish(self.msg) # 發布關閉命令
rospy.sleep(0.5) # 延時0.5s,否則會出現無法clear
self.VeidoLabel.clear() # 清屏
self.VeidoLabel.setText("攝像頭未打開!") # 設定Qlabel文本
self.openfaceflag = False # 關閉人臉識別,無論是否打開
self.OpenFace.setText(self._translate("MainWindow", "打開人臉識別")) # 修改文本
rospy.loginfo("send %s", self.msg) # 發布除錯資訊
# 人臉識別函式
def OpenFaceCallback(self):
if not self.openfaceflag: # 如果沒有打開人臉識別
self.openfaceflag = True # 打開
self.OpenFace.setText(self._translate("MainWindow", "關閉人臉識別")) # 修改文本
if not self.opencameraflag: # 如果沒有打開攝像頭
self.opencameraflag = True # 打開攝像頭
self.OpenCamera.setText(self._translate("MainWindow", "關閉攝像頭")) # 修改文本
self.msg = String("opencam")
self.qtkeypub.publish(self.msg) # 發布打開命令
rospy.loginfo("send %s", self.msg) # 除錯資訊
else: # 如果已經打開
self.openfaceflag = False # 關閉人臉識別
self.OpenFace.setText(self._translate("MainWindow", "打開人臉識別")) # 修改文本
# 鍵盤打開
def OpenKeyCallback(self): # 按鍵控制
if self.openkeyflag: # 如果鍵盤是打開的,則關閉鍵盤
self.openkeyflag = False
self.OpenKey.setText(self._translate("MainWindow", "打開鍵盤控制"))
self.msg = String("openkey")
if self.keypid > 0: # 鍵盤控制父行程
os.kill(self.keypid, signal.SIGKILL) # 殺死子行程
os.wait()
else:
self.openkeyflag = True
self.OpenKey.setText(self._translate("MainWindow", "關閉鍵盤控制"))
self.msg = String("closekey")
self.keypid = os.fork() # 創建子行程
if self.keypid == 0: # 鍵盤控制子行程
os.execl('/opt/ros/noetic/bin/rosrun', 'rosrun', 'myrobot', 'keycontrol')
rospy.loginfo("send %s", self.msg)
# 語音控制
def OpenVoiceCallback(self):
if self.openvoiceflag: # 如果語音已經打開,則關閉
self.openvoiceflag = False
self.OpenVoice.setText(self._translate("MainWindow", "打開語音控制"))
self.msg = String("openvoice")
if self.voicepubpid > 0: # 父行程
os.kill(self.voicepubpid, signal.SIGKILL)
os.wait()
if self.voicesubpid > 0: # 父行程
os.kill(self.voicesubpid, signal.SIGKILL)
os.wait()
else:
self.openvoiceflag = True
self.OpenVoice.setText(self._translate("MainWindow", "關閉語音控制"))
self.msg = String("closevoice")
self.voicepubpid = os.fork() # 創建子行程發布語音命令
self.voicesubpid = os.fork() # 創建子行程接受語音命令
if self.voicepubpid == 0:
os.execl('/opt/ros/noetic/bin/rosrun', 'rosrun', 'myrobot', 'voicepub') # 打開語音識別
if self.voicesubpid == 0:
os.execl('/opt/ros/noetic/bin/rosrun', 'rosrun', 'myrobot', 'voicesub') # 打開命令接收
rospy.loginfo("send %s", self.msg)
# 前進
def ButtonGoCallback(self):
self.msg = String("go")
self.qtkeypub.publish(self.msg)
rospy.loginfo("send %s", self.msg)
# 后退
def ButtonBackCallback(self):
self.msg = String("back")
self.qtkeypub.publish(self.msg)
rospy.loginfo("send %s", self.msg)
# 左轉
def ButtonLeftCallback(self):
self.msg = String("left")
self.qtkeypub.publish(self.msg)
rospy.loginfo("send %s", self.msg)
# 右轉
def ButtonRightCallback(self):
self.msg = String("right")
self.qtkeypub.publish(self.msg)
rospy.loginfo("send %s", self.msg)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ui = MyCallback()
ui.show()
sys.exit(app.exec_())
3.遇到的問題
(1)花屏問題
QT上位機在接收圖片時,產生了花屏現象,在請教了大佬之后,得到問題的根源,我得到的圖片是640x480的,而我設定QtGui.QImage的大小確為480x320,小于我的圖片,解決方法就是顯示的大小得到的圖片大小應當和QT設定顯示的大小保持一致,
(2)rospy.spin()問題
在程式中加入rospy.spin()函式會導致QT界面黑屏,推測可能是一直進入回呼函式中,無法顯示qt界面,洗掉該句即可,
四、運行結果
1.打開攝像頭

2.打開人臉識別

3.打開語音控制

4.打開鍵盤控制

5.上位機控制

結語
qt的學習還是比較有意思的,也是我第一次設計上位機,這必須得記錄下來,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/289412.html
標籤:其他
