Edited at

ROS講座94 Qtでpub・subする


環境

この記事は以下の環境で動いています。

項目

CPU
Core i5-8250U

Ubuntu
16.04

ROS
Kinetic

Qt
5.5.1

インストールについてはROS講座02 インストールを参照してください。

またこの記事のプログラムはgithubにアップロードされています。ROS講座11 gitリポジトリを参照してください。


概要

QtのGUIを使ってpublishするROSノードであるtalker、subscribeするROSノードであるlistenerを製作します。talkerではQwidgetのSIGNALを自作関数で受ける必要があります。またlistenerではqtのmainスレッド処理とrosのメインスレッド処理を両立することが必要です。特にtalkerではコーディングでは仕様によりc++ライクの別クラス実装でライブラリの宣言と実装は別ファイルにする必要があります。

冗長な書き方に見えますがこれがほぼ唯一の書き方です。


talkerのソースコード


ノード本体


qt_lecture/src/qt_talker.cpp

#include <ros/ros.h>


#include <QApplication>
#include <QDialog>

#include "qt_talker_class.h"

int main(int argc, char** argv){
ros::init(argc, argv, "qt_talker");
QApplication app(argc,argv);
QWidget* window = new QWidget;
MainDialog* dialog = new MainDialog(window);
dialog->show();

ros::Rate loop_rate(20);
while (ros::ok()){
ros::spinOnce();
app.processEvents();
loop_rate.sleep();
}
}



ライブラリ


qt_lecture/src/qt_talker_class.h

#ifndef Q_MOC_RUN

#include <ros/ros.h>
#endif

#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>

class MainDialog : public QDialog
{
Q_OBJECT
public:
MainDialog(QWidget* parent);

private Q_SLOTS:
void publishString();
private:
QPushButton* setButton;
ros::NodeHandle nh_;
ros::Publisher string_pub_;
};


一番大事なのはcallback関数のvoid publishString();です。これを宣言する前の行にprivate Q_SLOTS:を付けます。


qt_lecture/src/qt_talker_class.cpp

#include <ros/ros.h>

#include <std_msgs/String.h>

#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QVBoxLayout>

#include "qt_talker_class.h"

MainDialog::MainDialog(QWidget* parent): QDialog(parent),nh_(){
setButton = new QPushButton("publish");

connect(setButton,SIGNAL(clicked()),this,SLOT(publishString()));

QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(setButton);
setLayout(layout);

string_pub_ = nh_.advertise<std_msgs::String>("chatter", 10);
}

void MainDialog::publishString(){
std_msgs::String string_msg;
string_msg.data="string";
string_pub_.publish(string_msg);
ROS_INFO("pub: %s", string_msg.data.c_str());
}



listenerのソースコード


ノード本体


qt_lecture/src/qt_listener.cpp

#include <ros/ros.h>


#include <QApplication>
#include <QDialog>

#include "qt_listener_class.h"

int main(int argc, char** argv)
{
ros::init(argc, argv, "qt_listener");
QApplication app(argc,argv);
QWidget* window = new QWidget;
MainDialog* dialog = new MainDialog(window);
dialog->show();

ros::Rate loop_rate(20);
while (ros::ok()){
ros::spinOnce();
app.processEvents();
loop_rate.sleep();
}
}


最後のループの部分がいつもと違います。ここでは普段はROSではros::spin()を入れますし、qtではapp.exec()を入れます。ただし両方の関数ともにブロッキングなのでROSとQtを共存させることはできません。そこで今回はノンブロッキングな関数を使います。


ライブラリ


qt_lecture/src/qt_listener_class.h

#ifndef Q_MOC_RUN

#include <ros/ros.h>
#include <std_msgs/String.h>
#endif

#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>

class MainDialog : public QDialog
{
Q_OBJECT
public:
MainDialog(QWidget* parent);

private:
QLineEdit* lineEdit;
ros::NodeHandle nh_;
ros::Subscriber string_sub_;
void stringCallback(const std_msgs::String& msg);
};



qt_lecture/src/qt_listener_class.cpp

#include <ros/ros.h>

#include <std_msgs/String.h>
#include <functional>

#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QVBoxLayout>

#include "qt_listener_class.h"

MainDialog::MainDialog(QWidget* parent): QDialog(parent),nh_(){
lineEdit = new QLineEdit;

QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(lineEdit);
setLayout(layout);

string_sub_ = nh_.subscribe("chatter", 10, &MainDialog::stringCallback, this);
printf("register\n");
}

void MainDialog::stringCallback(const std_msgs::String& string_msg){
QString text = QString::fromStdString(string_msg.data);
lineEdit->setText(text);
ROS_INFO("sub: %s", string_msg.data.c_str());
}


クラスの中で自らのクラスのメソッドをcallbackとして登録するときに

×string_sub_ = nh_.subscribe("chatter", 10, stringCallback);

と書きたくなりますが、

string_sub_ = nh_.subscribe("chatter", 10, &MainDialog::stringCallback, this);

と書くのが正しいです。


cmake


qt_lecture/CMakeLists.txtの追加

add_executable(qt_talker src/qt_talker.cpp src/qt_talker_class.cpp)

add_executable(qt_listener src/qt_listener.cpp src/qt_listener_class.cpp)

target_link_libraries(qt_listener
${catkin_LIBRARIES}
${QT_LIBRARIES}
)
target_link_libraries(qt_touch_app
${catkin_LIBRARIES}
${QT_LIBRARIES}
)


別途Qtのビルド設定も忘れずに


ビルド

cd ~/catkin_ws

catkin_make


実行


1つ目のターミナル

roscore



2つ目のターミナル

rosrun qt_lecture qt_talker 



3つ目のターミナル

rosrun qt_lecture qt_listener



参考

Qtをノンブロッキングに使う


目次ページへのリンク

ROS講座の目次へのリンク