当前位置:   article > 正文

基于ROS的语音控制机器人(二):上位机的实现_ros机器人上位机软件开发

ros机器人上位机软件开发

文章目录

目录

文章目录

前言

一、准备工作

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的朋友可以直接复制下面这个代码。

  1. # -*- coding: utf-8 -*-
  2. # Form implementation generated from reading ui file 'MyRobotApp.ui'
  3. #
  4. # Created by: PyQt5 UI code generator 5.14.1
  5. #
  6. # WARNING! All changes made in this file will be lost!
  7. from PyQt5 import QtCore, QtGui, QtWidgets
  8. class Ui_MainWindow(object):
  9. def setupUi(self, MainWindow):
  10. MainWindow.setObjectName("MainWindow")
  11. MainWindow.resize(689, 529)
  12. MainWindow.setToolTipDuration(1)
  13. self.centralwidget = QtWidgets.QWidget(MainWindow)
  14. self.centralwidget.setObjectName("centralwidget")
  15. self.ButtonGo = QtWidgets.QPushButton(self.centralwidget)
  16. self.ButtonGo.setGeometry(QtCore.QRect(210, 350, 100, 50))
  17. self.ButtonGo.setObjectName("ButtonGo")
  18. self.ButtonLeft = QtWidgets.QPushButton(self.centralwidget)
  19. self.ButtonLeft.setGeometry(QtCore.QRect(110, 400, 100, 50))
  20. self.ButtonLeft.setObjectName("ButtonLeft")
  21. self.ButtonRight = QtWidgets.QPushButton(self.centralwidget)
  22. self.ButtonRight.setGeometry(QtCore.QRect(310, 400, 100, 50))
  23. self.ButtonRight.setObjectName("ButtonRight")
  24. self.ButtonBack = QtWidgets.QPushButton(self.centralwidget)
  25. self.ButtonBack.setGeometry(QtCore.QRect(210, 450, 100, 50))
  26. self.ButtonBack.setObjectName("ButtonBack")
  27. self.OpenFace = QtWidgets.QPushButton(self.centralwidget)
  28. self.OpenFace.setGeometry(QtCore.QRect(550, 100, 100, 50))
  29. self.OpenFace.setObjectName("OpenFace")
  30. self.OpenVoice = QtWidgets.QPushButton(self.centralwidget)
  31. self.OpenVoice.setGeometry(QtCore.QRect(550, 180, 100, 50))
  32. self.OpenVoice.setObjectName("OpenVoice")
  33. self.OpenKey = QtWidgets.QPushButton(self.centralwidget)
  34. self.OpenKey.setGeometry(QtCore.QRect(550, 260, 100, 50))
  35. self.OpenKey.setObjectName("OpenKey")
  36. self.OpenCamera = QtWidgets.QPushButton(self.centralwidget)
  37. self.OpenCamera.setGeometry(QtCore.QRect(550, 20, 100, 50))
  38. self.OpenCamera.setObjectName("OpenCamera")
  39. self.VeidoLabel = QtWidgets.QLabel(self.centralwidget)
  40. self.VeidoLabel.setGeometry(QtCore.QRect(20, 20, 480, 320))
  41. self.VeidoLabel.setText("摄像头未打开!")
  42. self.VeidoLabel.setWordWrap(False)
  43. self.VeidoLabel.setObjectName("VeidoLabel")
  44. MainWindow.setCentralWidget(self.centralwidget)
  45. self.statusbar = QtWidgets.QStatusBar(MainWindow)
  46. self.statusbar.setObjectName("statusbar")
  47. MainWindow.setStatusBar(self.statusbar)
  48. QtCore.QMetaObject.connectSlotsByName(MainWindow)
  49. self.retranslateUi(MainWindow)
  50. def retranslateUi(self, MainWindow):
  51. _translate = QtCore.QCoreApplication.translate
  52. MainWindow.setWindowTitle(_translate("MainWindow", "My RobotCar"))
  53. self.ButtonGo.setText(_translate("MainWindow", "前进"))
  54. self.ButtonLeft.setText(_translate("MainWindow", "左转"))
  55. self.ButtonRight.setText(_translate("MainWindow", "右转"))
  56. self.ButtonBack.setText(_translate("MainWindow", "后退"))
  57. self.OpenFace.setText(_translate("MainWindow", "打开人脸识别"))
  58. self.OpenVoice.setText(_translate("MainWindow", "打开语音控制"))
  59. self.OpenKey.setText(_translate("MainWindow", "打开键盘控制"))
  60. self.OpenCamera.setText(_translate("MainWindow", "打开摄像头"))

 三、上位机程序编写

新建app.py文件,准备开始编写上位机。

1.具体思路

        上一篇内容提到过,pc和树莓派之间的通讯是通过发布和订阅主题实现的,因此我们可以通过按下按钮发送clicked信号,在相关联的槽去实现命令的发布即可通过上位机实现通信。

        而想要通过上位机启动按键控制和语音控制,在程序里编写是十分麻烦的,我们可以利用已经编写好的程序,利用python中os.fork()函数创建子进程,然后利用os.execl()去调用相关可执行文件,即可实现功能。

2.具体实现

  1. #!/usr/bin/python3
  2. import signal
  3. import sys
  4. import os
  5. import rospy
  6. import cv2
  7. from cv_bridge import CvBridge
  8. from sensor_msgs.msg import CompressedImage
  9. from PyQt5 import QtCore, QtGui, QtWidgets
  10. from std_msgs.msg import String
  11. from MyRobotApp import Ui_MainWindow
  12. class MyCallback(QtWidgets.QMainWindow, Ui_MainWindow):
  13. def __init__(self, parent=None):
  14. super(MyCallback, self).__init__(parent)
  15. rospy.init_node("qtcmd") # 初始化结点
  16. self.qtkeypub = rospy.Publisher("keycmd", String, queue_size=1000) # 键盘指令发布者
  17. self.bridge = CvBridge()
  18. # 订阅压缩图像主题,提高帧率
  19. self.comimgsub = rospy.Subscriber("image_compressed/compressed", CompressedImage, self.compressedimagecallback)
  20. self.msg = String("") # 发送的消息
  21. self.setupUi(self) # 设置ui
  22. # 人脸模型
  23. self.face_cascade = cv2.CascadeClassifier(r'/usr/share/opencv4/haarcascades/haarcascade_frontalface_alt.xml')
  24. # 槽
  25. self.OpenCamera.clicked.connect(self.OpenCameraCallback)
  26. self.OpenFace.clicked.connect(self.OpenFaceCallback)
  27. self.OpenKey.clicked.connect(self.OpenKeyCallback)
  28. self.OpenVoice.clicked.connect(self.OpenVoiceCallback)
  29. self.ButtonGo.clicked.connect(self.ButtonGoCallback)
  30. self.ButtonLeft.clicked.connect(self.ButtonLeftCallback)
  31. self.ButtonBack.clicked.connect(self.ButtonBackCallback)
  32. self.ButtonRight.clicked.connect(self.ButtonRightCallback)
  33. # 声明
  34. self._translate = QtCore.QCoreApplication.translate
  35. self.compressed_image = None # 压缩图像
  36. self.opencameraflag = False # 摄像头开启标志
  37. self.openfaceflag = False # 人脸识别开启标志
  38. self.openkeyflag = False # 键盘开启标志
  39. self.openvoiceflag = False # 语音开启标志
  40. self.keypid = -1 # 键盘进程号
  41. self.voicepubpid = -1 # 语音上报进程
  42. self.voicesubpid = -1 # 语音识别进程
  43. self.camerapid = -1 # 摄像头进程
  44. # 人脸识别函数
  45. def facedetect(self, data):
  46. gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
  47. faces = self.face_cascade.detectMultiScale(
  48. gray,
  49. scaleFactor=1.15,
  50. minNeighbors=5,
  51. minSize=(5, 5),
  52. flags=cv2.CASCADE_SCALE_IMAGE)
  53. for face in faces:
  54. x,y,w,h = face
  55. # 画矩形
  56. cv2.rectangle(data, (x, y), (x+w, y+h), (50, 255, 50), 2)
  57. # QT显示图片函数
  58. def showimg(self, data):
  59. pixmap = QtGui.QImage(data, 480, 320, QtGui.QImage.Format_RGB888)
  60. pixmap = QtGui.QPixmap.fromImage(pixmap)
  61. self.VeidoLabel.setPixmap(pixmap)
  62. self.show()
  63. # ros图片主题回调函数,参数data为图片数据
  64. def compressedimagecallback(self, data):
  65. bridge = CvBridge()
  66. self.compressed_image = bridge.compressed_imgmsg_to_cv2(data, "bgr8")
  67. if self.openfaceflag:
  68. self.facedetect(self.compressed_image)
  69. self.compressed_image = cv2.cvtColor(self.compressed_image, cv2.COLOR_BGR2RGB)
  70. self.showimg(self.compressed_image)
  71. # 打开摄像头
  72. def OpenCameraCallback(self): # 摄像头
  73. if not self.opencameraflag: # 如果摄像头未打开
  74. self.opencameraflag = True # 标志为打开
  75. self.OpenCamera.setText(self._translate("MainWindow", "关闭摄像头")) # 修改按键文本
  76. self.msg = String("opencam")
  77. self.qtkeypub.publish(self.msg) # 发布打开命令
  78. else: # 如果已经打开
  79. self.opencameraflag = False # 标志关闭
  80. self.OpenCamera.setText(self._translate("MainWindow", "打开摄像头")) # 修改文本
  81. self.msg = String("closecam")
  82. self.qtkeypub.publish(self.msg) # 发布关闭命令
  83. rospy.sleep(0.5) # 延时0.5s,否则会出现无法clear
  84. self.VeidoLabel.clear() # 清屏
  85. self.VeidoLabel.setText("摄像头未打开!") # 设置Qlabel文本
  86. self.openfaceflag = False # 关闭人脸识别,无论是否打开
  87. self.OpenFace.setText(self._translate("MainWindow", "打开人脸识别")) # 修改文本
  88. rospy.loginfo("send %s", self.msg) # 发布调试信息
  89. # 人脸识别函数
  90. def OpenFaceCallback(self):
  91. if not self.openfaceflag: # 如果没有打开人脸识别
  92. self.openfaceflag = True # 打开
  93. self.OpenFace.setText(self._translate("MainWindow", "关闭人脸识别")) # 修改文本
  94. if not self.opencameraflag: # 如果没有打开摄像头
  95. self.opencameraflag = True # 打开摄像头
  96. self.OpenCamera.setText(self._translate("MainWindow", "关闭摄像头")) # 修改文本
  97. self.msg = String("opencam")
  98. self.qtkeypub.publish(self.msg) # 发布打开命令
  99. rospy.loginfo("send %s", self.msg) # 调试信息
  100. else: # 如果已经打开
  101. self.openfaceflag = False # 关闭人脸识别
  102. self.OpenFace.setText(self._translate("MainWindow", "打开人脸识别")) # 修改文本
  103. # 键盘打开
  104. def OpenKeyCallback(self): # 按键控制
  105. if self.openkeyflag: # 如果键盘是打开的,则关闭键盘
  106. self.openkeyflag = False
  107. self.OpenKey.setText(self._translate("MainWindow", "打开键盘控制"))
  108. self.msg = String("openkey")
  109. if self.keypid > 0: # 键盘控制父进程
  110. os.kill(self.keypid, signal.SIGKILL) # 杀死子进程
  111. os.wait()
  112. else:
  113. self.openkeyflag = True
  114. self.OpenKey.setText(self._translate("MainWindow", "关闭键盘控制"))
  115. self.msg = String("closekey")
  116. self.keypid = os.fork() # 创建子进程
  117. if self.keypid == 0: # 键盘控制子进程
  118. os.execl('/opt/ros/noetic/bin/rosrun', 'rosrun', 'myrobot', 'keycontrol')
  119. rospy.loginfo("send %s", self.msg)
  120. # 语音控制
  121. def OpenVoiceCallback(self):
  122. if self.openvoiceflag: # 如果语音已经打开,则关闭
  123. self.openvoiceflag = False
  124. self.OpenVoice.setText(self._translate("MainWindow", "打开语音控制"))
  125. self.msg = String("openvoice")
  126. if self.voicepubpid > 0: # 父进程
  127. os.kill(self.voicepubpid, signal.SIGKILL)
  128. os.wait()
  129. if self.voicesubpid > 0: # 父进程
  130. os.kill(self.voicesubpid, signal.SIGKILL)
  131. os.wait()
  132. else:
  133. self.openvoiceflag = True
  134. self.OpenVoice.setText(self._translate("MainWindow", "关闭语音控制"))
  135. self.msg = String("closevoice")
  136. self.voicepubpid = os.fork() # 创建子进程发布语音命令
  137. self.voicesubpid = os.fork() # 创建子进程接受语音命令
  138. if self.voicepubpid == 0:
  139. os.execl('/opt/ros/noetic/bin/rosrun', 'rosrun', 'myrobot', 'voicepub') # 打开语音识别
  140. if self.voicesubpid == 0:
  141. os.execl('/opt/ros/noetic/bin/rosrun', 'rosrun', 'myrobot', 'voicesub') # 打开命令接收
  142. rospy.loginfo("send %s", self.msg)
  143. # 前进
  144. def ButtonGoCallback(self):
  145. self.msg = String("go")
  146. self.qtkeypub.publish(self.msg)
  147. rospy.loginfo("send %s", self.msg)
  148. # 后退
  149. def ButtonBackCallback(self):
  150. self.msg = String("back")
  151. self.qtkeypub.publish(self.msg)
  152. rospy.loginfo("send %s", self.msg)
  153. # 左转
  154. def ButtonLeftCallback(self):
  155. self.msg = String("left")
  156. self.qtkeypub.publish(self.msg)
  157. rospy.loginfo("send %s", self.msg)
  158. # 右转
  159. def ButtonRightCallback(self):
  160. self.msg = String("right")
  161. self.qtkeypub.publish(self.msg)
  162. rospy.loginfo("send %s", self.msg)
  163. if __name__ == '__main__':
  164. app = QtWidgets.QApplication(sys.argv)
  165. ui = MyCallback()
  166. ui.show()
  167. 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的学习还是比较有意思的,也是我第一次设计上位机,这必须得记录下来。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/689377
推荐阅读
相关标签
  

闽ICP备14008679号