環境
この記事は以下の環境で動いています。
項目 | 値 |
---|---|
CPU | Core i5-3470 |
Ubuntu | 22.04 |
GPU | GTX 1050 Ti |
GPU driver | 535.86.05 |
ROS2 | Humble |
Ignition Gazebo | Fortress |
概要
IgnitionではPluginを使用することで、機能を拡張することが出来ます。今回はGUIプラグインを作成・使用をします。
Ignition GazeboのGUIはGazebo Classicから大きく変更されていて、画面上のほぞすべての表示要素をプラグインで制御できるようになっています。
予備知識
QtQuick
QtQuickとはQtの新たなUIフレームワークです。QML(Qt Meta-Object Language)というファイルを使ってUIを実装します。追加のパッケージが必要なため以下のコマンドでインストールします。
extrasのインストール
sudo apt install qml-module-qtquick-extras
GUIプラグインの作成
QML
ignition_plugin_lecture/src/switch_panel/SwitchPanel.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.1
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
import QtQuick.Extras 1.4
import "qrc:/qml"
Rectangle
{
Layout.minimumWidth: 400
Layout.minimumHeight: 110
Row {
spacing: 5
Button {
text: "Forward"
onPressed: { SwitchPanel.OnForwardButton(); }
}
Button {
text: "Stop"
onPressed: { SwitchPanel.OnStopButton(); }
}
Button {
text: "Reverse"
onPressed: { SwitchPanel.OnReverseButton(); }
}
}
}
-
qmlファイルを用いてUIを設計します。
-
トップには
Rectangle
要素を、その下にRow
要素を置きます。-
Layout.minimumWidth
/Layout.minimumHeight
は設定しないとGUIが正しく表示されないことがあります。
-
-
Row
はQtでいうLayoutに近いもので配下の要素を水平に並べます。 -
Button
は押しボタンの要素です。-
onPressed
以下にはボタンを押した時に実行される処理を書きます。この処理は後のccファイルに書きます。
-
c++コード
ignition_plugin_lecture/src/switch_panel/SwitchPanel.hpp
#include <ignition/gui/qt.h>
#include <ignition/transport/Node.hh>
#include <ignition/gui/Plugin.hh>
namespace iginition_plugin_lecture
{
class SwitchPanel : public ignition::gui::Plugin
{
Q_OBJECT
public:
SwitchPanel();
virtual ~SwitchPanel();
void LoadConfig(const tinyxml2::XMLElement * _pluginElem) override;
protected slots:
void OnForwardButton(void);
void OnStopButton(void);
void OnReverseButton(void);
private:
void CreateIgnitionIf(void);
private:
ignition::transport::Node node_;
ignition::transport::Node::Publisher speed_pub_;
float forward_speed_{1.0f};
float reverse_speed_{1.0f};
};
} // namespace iginition_plugin_lecture
ignition_plugin_lecture/src/switch_panel/SwitchPanel.cpp
#include "SwitchPanel.hpp"
#include <ignition/msgs/float.pb.h>
#include <ignition/plugin/Register.hh>
namespace iginition_plugin_lecture
{
SwitchPanel::SwitchPanel() : Plugin()
{
CreateIgnitionIf();
}
SwitchPanel::~SwitchPanel()
{
}
void SwitchPanel::LoadConfig(const tinyxml2::XMLElement * _pluginElem)
{
if (!_pluginElem) {
return;
}
auto forward_speed_elem = _pluginElem->FirstChildElement("forward_speed");
if (nullptr != forward_speed_elem)
{
forward_speed_ = forward_speed_elem->FloatText();
}
auto reverse_speed_elem = _pluginElem->FirstChildElement("reverse_speed");
if (nullptr != reverse_speed_elem)
{
reverse_speed_ = reverse_speed_elem->FloatText();
}
}
void SwitchPanel::OnForwardButton(void) {
ignition::msgs::Float float_msg;
float_msg.set_data(forward_speed_);
speed_pub_.Publish(float_msg);
}
void SwitchPanel::OnStopButton(void) {
ignition::msgs::Float float_msg;
float_msg.set_data(0.0f);
speed_pub_.Publish(float_msg);
}
void SwitchPanel::OnReverseButton(void) {
ignition::msgs::Float float_msg;
float_msg.set_data(reverse_speed_);
speed_pub_.Publish(float_msg);
}
void SwitchPanel::CreateIgnitionIf(void){
this->speed_pub_ = this->node_.Advertise<ignition::msgs::Float>("target_speed");
}
}
// Register this plugin
IGNITION_ADD_PLUGIN(iginition_plugin_lecture::SwitchPanel, ignition::gui::Plugin)
-
ignition::gui::Plugin
クラスを継承したクラスを作成します。 - 起動時に
LoadConfig()
が呼ばれます。SDFファイルで子要素で書いた値が読めます。 -
OnForwardButton()
、OnStopButton()
、OnReverseButton()
UIで作ったボタンの押した時の処理slots関数です。
ビルド系
qrcはリソースファイルで実行に必要なファイルを列挙するファイルです。のちのCMakeで指定します。画像ファイルなどを使うときはこれも記載に追加します。
ignition_plugin_lecture/src/switch_panel/SwitchPanel.qrc
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="SwitchPanel/">
<file>SwitchPanel.qml</file>
</qresource>
</RCC>
CmakeではQt特有の記述が入ります。
ignition_plugin_lecture/CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(ignition_plugin_lecture)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
set(CMAKE_AUTOMOC ON)
find_package(ament_cmake REQUIRED)
find_package(ignition-plugin1 REQUIRED COMPONENTS register)
find_package(ignition-gazebo6 REQUIRED)
include_directories(SYSTEM
${IGNITION-COMMON_INCLUDE_DIRS}
${IGNITION-GUI_INCLUDE_DIRS}
)
# gui plugin
qt5_add_resources(resources_rcc src/switch_panel/SwitchPanel.qrc)
add_library(SwitchPanel SHARED
src/switch_panel/SwitchPanel.cpp
${resources_rcc}
)
target_link_libraries(SwitchPanel
${IGNITION-COMMON_LIBRARY_DIRS}
${IGNITION-GUI_LIBRARIES}
)
# system plugin
add_library(RotateAxis SHARED
src/rotate_axis/rotate_axis.cpp
)
target_link_libraries(RotateAxis
${IGNITION-PLUGIN_LIBRARIES}
${IGNITION-GAZEBO_LIBRARIES}
)
install(
TARGETS SwitchPanel RotateAxis
LIBRARY DESTINATION lib
)
ament_environment_hooks("${CMAKE_CURRENT_SOURCE_DIR}/hooks/${PROJECT_NAME}.dsv.in")
ament_package()
-
set(CMAKE_AUTOMOC ON)
を書いてQt用のMOCを有効にします。 -
qt5_add_resources()
でqrcファイルを指定します。qmlファイルや画像を取り込んだc++ファイルが生成されます。 - ターゲット名とQMLファイル名は一緒にします。
-
ament_environment_hooks()
で環境変数を設定します。
GUIプラグインの使用
ignition_lecture/worlds/gui_example1.sdf
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="visualize_lidar_world">
<gui fullscreen="0">
<plugin filename="GzScene3D" name="3D View">
<camera_pose>10 0 6 0 0.5 3.14</camera_pose>
<ignition-gui>
<title>3D View</title>
<property type="bool" key="showTitleBar">false</property>
<property type="string" key="state">docked</property>
</ignition-gui>
</plugin>
<plugin filename="SwitchPanel" name="iginition_plugin_lecture::SwitchPanel">
<forward_speed>0.3</forward_speed>
<reverse_speed>-0.1</reverse_speed>
<ignition-gui>
<title>SwitchPanel</title>
<property type="bool" key="showTitleBar">true</property>
<property type="string" key="state">docked</property>
<property key="resizable" type="bool">true</property>
</ignition-gui>
</plugin>
<plugin filename="Teleop" name="ignition::gui::plugins::Teleop">
<topic>/cmd_vel</topic>
<ignition-gui>
<property type="bool" key="showTitleBar">true</property>
<property type="string" key="state">docked</property>
<property key="resizable" type="bool">true</property>
</ignition-gui>
</plugin>
</gui>
<physics name="1ms" type="ignored">
<max_step_size>0.001</max_step_size>
<real_time_factor>1.0</real_time_factor>
</physics>
<plugin
filename="libignition-gazebo-physics-system.so"
name="ignition::gazebo::systems::Physics">
</plugin>
<plugin
filename="libignition-gazebo-sensors-system.so"
name="ignition::gazebo::systems::Sensors">
<render_engine>ogre2</render_engine>
</plugin>
<plugin
filename="libignition-gazebo-scene-broadcaster-system.so"
name="ignition::gazebo::systems::SceneBroadcaster">
</plugin>
<light name="sun" type="directional">
<cast_shadows>true</cast_shadows>
<pose>0 0 10 0 0 0</pose>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.2 0.2 0.2 1</specular>
<attenuation>
<range>1000</range>
<constant>0.9</constant>
<linear>0.01</linear>
<quadratic>0.001</quadratic>
</attenuation>
<direction>-0.5 0.1 -0.9</direction>
</light>
</world>
</sdf>
-
<gui>
タグの中でGUIの設定を書きます。このタグがない場合はignitionのデフォルトのGUIが適用されます。-
<ignition-gui>
以下ではパネルの設定を指定します。-
<title>
パネルのタイトルの文字を指定します。 - 他はパネルのサイズの設定です。
-
-
ビルド&実行
ビルド
source /opt/ros/humble/setup.bash
cd ros2_ws
colcon build
実行
source ~/ros2_ws/install/setup.bash
ros2 launch ignition_lecture sim.launch.py
ros2 launch ignition_lecture gazebo.launch.py world:=gui_example1.sdf
ignトピックのecho
ign topic -e -t target_speed
参考
ignition pluginパス
gui pluginリファレンス