Robot controlado por voz basado en ROS (2): la realización de la computadora host

Directorio de artículos

Tabla de contenido

Directorio de artículos

prefacio

1. Preparación

1. Entorno de trabajo Python

2.ros medio ambiente

diseñador 3.QT

2. Programación de la interfaz

1. Diseño de interfaz

2. Convertir archivo ui a archivo py

 3. Programación de PC

1. Ideas específicas

2. Implementación concreta

3. Problemas encontrados

(1) Problema de Huaping

(2) problema rospy.spin()

 4. Ejecución de resultados

 1. Enciende la cámara

2. Activa el reconocimiento facial 

 3. Activa el control por voz

4. Control de teclado abierto

 5. Control de la computadora anfitriona

epílogo



prefacio

Basado en la realización de funciones básicas, este documento escribe una computadora host para controlar robots ros basada en PyQT5.

Compartir código fuente: https://gitee.com/sy_run/myroscar


Consejo: El siguiente es el texto de este artículo, y los siguientes casos son para referencia

1. Preparación

1. Entorno de trabajo Python

El entorno de python seleccionado en este artículo es pycharm2021, el tutorial de instalación no dirá más aquí

Las bibliotecas principales requeridas son PyQt5, rospy y opencv

2.ros medio ambiente

Desde que cambié a una computadora nueva e instalé la versión de ubuntu20.04, la versión de ros se cambió a noetic, pero el programa específico no cambió mucho.

diseñador 3.QT

Instalar QT designer puede facilitarnos un mejor diseño de la interfaz, el proceso de instalación específico no quiere decir más

2. Programación de la interfaz

1. Diseño de interfaz

Abra el diseñador QT, seleccione la ventana principal y luego diseñe una interfaz que le guste a través del control deseado.

La interfaz diseñada es la siguiente:

Con 8 botones y un control QLabel, haga clic en Guardar para generar el archivo ui.

2. Convertir archivo ui a archivo py

El pyuic que viene con pyqt5 puede convertir el archivo ui en un archivo py, el método específico es el siguiente

(1) Después de importar el archivo ui a pycharm, haga clic en Archivo -> Configuración

Seleccione Herramientas -> Herramientas externas

(2) Ingrese pyuic para el nombre y python3 para el programa, porque ros-noetic ejecuta el programa python a través de python 3. Si es la versión ros-kinetic, es posible que deba ingresar python aquí.

Introduzca el siguiente contenido en la columna de parámetros

-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py

Ingrese $FileDirs para el directorio de trabajo, es decir, el archivo py generado y el archivo ui están en el mismo directorio

(3) Haga clic en Aceptar, busque el archivo ui en el directorio del proyecto a la izquierda, haga clic con el botón derecho en el archivo ui, seleccione Herramientas externas y haga clic en pyuic

 En este punto, el directorio actual generará un archivo py

 Regrese al directorio de nivel superior, cree una nueva carpeta de scripts y mueva el archivo py generado a este directorio. El contenido específico del archivo py es el siguiente. Los amigos que no tienen QT Designer pueden copiar directamente el siguiente código.

# -*- 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", "打开摄像头"))

 3. Programación de PC

Cree un nuevo archivo app.py, listo para comenzar a escribir en la computadora host.

1. Ideas específicas

        Como se mencionó en el artículo anterior, la comunicación entre la PC y la Raspberry Pi se realiza publicando y suscribiendo temas, por lo que podemos enviar la señal de clic presionando el botón y soltar el comando en la ranura asociada para pasar la computadora superior realiza la comunicación.

        Pero si desea iniciar el control de botones y el control de voz a través de la computadora host, es muy problemático escribir en el programa.Podemos usar el programa ya escrito para usar la función os.fork() en python para crear un proceso secundario, y luego use os.execl() para llamar al archivo ejecutable relevante para realizar la función.

2. Implementación concreta

#!/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. Problemas encontrados

(1) Problema de Huaping

Cuando la computadora host de QT recibió la imagen, había una pantalla borrosa. Después de consultar al jefe, llegué a la raíz del problema. La imagen que obtuve fue de 640x480, y el tamaño que configuré QtGui.QImage fue de hecho 480x320, que era más pequeño. que mi foto. La solución es que el tamaño de la imagen mostrada debe ser consistente con el tamaño mostrado por la configuración QT.

(2) problema rospy.spin()

Agregar la función rospy.spin () al programa causará una pantalla negra en la interfaz QT. Se especula que puede haber ingresado a la función de devolución de llamada y no puede mostrar la interfaz qt. Simplemente elimine esta oración.

 4. Ejecución de resultados

 1. Enciende la cámara

2. Activa el reconocimiento facial 

 3. Activa el control por voz

4. Control de teclado abierto

 5. Control de la computadora anfitriona


epílogo

El aprendizaje de qt es bastante interesante, y también es la primera vez que diseño una computadora host, por lo que esto debe quedar registrado.

Supongo que te gusta

Origin blog.csdn.net/deku_desi/article/details/118878715
Recomendado
Clasificación