環境
この記事は以下の環境で動いています。
項目 | 値 |
---|---|
CPU | Core i5-8250U |
Ubuntu | 20.04 |
ROS | Noetic |
Qt | 5.12.8 |
インストールについてはROS講座02 インストールを参照してください。
またこの記事のプログラムはgithubにアップロードされています。ROS講座11 gitリポジトリを参照してください。
概要
QtのGUIを使ってpublishするROSノードであるtalker、subscribeするROSノードであるlistenerを製作します。talkerではQwidgetのSIGNALを自作関数で受ける必要があります。またlistenerではqtのmainスレッド処理とrosのメインスレッド処理を両立することが必要です。特にtalkerではコーディングでは仕様によりc++ライクの別クラス実装でライブラリの宣言と実装は別ファイルにする必要があります。
冗長な書き方に見えますが両立の都合上このような書き方になります。
talkerのソースコード
ノード本体
#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();
}
}
ライブラリ
#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:
を付けます。 - また
#include <ros/ros.h>
はQtのMOCでエラーになるために#ifndef Q_MOC_RUN
~#endif
で囲みます。
#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_, &QPushButton::clicked, this, &MainDialog::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のソースコード
ノード本体
#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を共存させることはできません。そこで今回は両者ともノンブロッキングな関数を使います。
ライブラリ
#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);
};
#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
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 build
実行
各ターミナルごとに実行前にsource ~/catkin_ws/devel/setup.bashを実行する必要があります。
roscore
rosrun qt_lecture qt_talker
rosrun qt_lecture qt_listener