はじめに
ロボコンをしていると、強豪校は自作のコントロールUIを作ったりしているのをよく目にします。
今回私がUIを書こうと思ったのは、それが単純にかっこいいと思ったからです。しかし、いざ書こうとすると、どのように書いたらいいかわかんなくなると思います。そんな人のためにこの記事を書きました。少しでも役に立つと幸いです。思ったより長くなりそうなのでpartを分けます。今回はROS2とQt5の連携の仕方です。
今回の学ロボで作ったUIの機能(ざっくり)
今回のUIに求めた機能は以下のようになっています。
- ロボットに乗せてあるカメラの画像の表示
- ボタンによってロボットに司令を送信する
- ロボットが今どこにいるのかの可視化
- ロボットの状態の表示
完成品はもう少しタイマー等の機能が増えて以下のようになりました。
この記事ではこのUIで使った基本的な部分のみ抜粋して解説していきます。
コードの全体はgithubに上げておきます。
ROS2とQt5の連携
最初の一歩はROS2とQt5の連携です。ボタンを押したら、ボタンを押した回数をpubするUIをつくります。実際に動かすと以下のようになります。
実際のコードは以下のようです。
ui_test.pyは以下のようになっておりこのコードはボタンの配置とかを指定しています。
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui_test.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_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(Dialog)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.pushButton = QtWidgets.QPushButton(Dialog)
self.pushButton.setObjectName("pushButton")
self.horizontalLayout.addWidget(self.pushButton)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.label.setText(_translate("Dialog", "TextLabel"))
self.pushButton.setText(_translate("Dialog", "PushButton"))
ROSとかの処理を書いている方(上のui_test.pyをimportして使ってます)
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from PyQt5.QtGui import QPixmap,QIcon
from ui_test import *
import rclpy
from rclpy.node import Node
from std_msgs.msg import Int32
class MyForm(QDialog):
def __init__(self,parent=None):
super(MyForm,self).__init__(parent)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.count =0
self.ui.pushButton.clicked.connect(self.button_update)
self.show()
rclpy.init(args=None)
self.node = Node('button_main')
self.pub = self.node.create_publisher(Int32,'button_topic',10)
def __del__(self):
self.node.destroy_node()
def pub_int32(self,num: int):
msg = Int32()
msg.data=num
self.pub.publish(msg)
def button_update(self):
self.count +=1
self.pub_int32(self.count)
print(f'{self.count}回目のボタンが押されました\n')
def main():
app = QApplication(sys.argv)
window = MyForm()
window.show()
sys.exit(app.exec_())
if __name__=="__main__":
main()
解説
Qtの解説は長くなるので今回はrosに関するところを説明します。
- コンストラクタでnodeを初期化してインスタンス化しています。
その中にpablisherを宣言しています。
rclpy.init(args=None)
self.node = Node('button_main')
self.pub = self.node.create_publisher(Int32,'button_topic',10)
- デイストラクタではnodeを破棄しています。
def __del__(self):
self.node.destroy_node()
- ボタンが押されたときのcallbackを宣言
self.ui.pushButton.clicked.connect(self.button_update)
- ボタンが押されたときのcallback関数でbutton_updateがあります。
この関数では押された回数を数えてそれをpublishしています。
def button_update(self):
self.count +=1
self.pub_int32(self.count)
print(f'{self.count}回目のボタンが押されました\n')
終わりに
今回は主にros2側を解説しました。
この記事を書いていてQtの説明を入れるのが難しかったのでまたの機会にQtだけの記事も書いたほうがいいと感じました。