Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rviz Panel Tutorial #4869

Merged
merged 22 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
dbfd3eb
Rviz Panel Tutorial
DLu Nov 15, 2024
99703f8
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
DLu Nov 18, 2024
0a11ad5
Add link to code, images, edits
DLu Nov 18, 2024
1234a9d
PR Feedack response for Kat
DLu Nov 21, 2024
be64144
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
fa8d2d3
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
c68f06e
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
3ebfde1
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
3173b6f
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
e26fd4a
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
c18a452
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
21fad13
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
ef1ae71
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
f78d5d3
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 21, 2024
affc0ab
Merge branch 'rolling' into rviz_panel
kscottz Nov 22, 2024
efd2b3b
Apply suggestions from code review
kscottz Nov 22, 2024
a6d233f
Apply suggestions from code review
kscottz Nov 22, 2024
97a3a35
Update source/Tutorials/Intermediate/RViz/RViz-Custom-Panel/RViz-Cust…
kscottz Nov 23, 2024
ed25f68
Update source/Tutorials/Intermediate/RViz/RViz-Main.rst
kscottz Nov 23, 2024
4b39fed
Update source/Tutorials/Intermediate/RViz/RViz-Main.rst
kscottz Nov 25, 2024
d5756dd
Update source/Tutorials/Intermediate/RViz/RViz-Main.rst
kscottz Nov 25, 2024
c30ef60
De-duplicate content
DLu Jan 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
Building a Custom RViz Panel
============================

This tutorial is for people who would like to work within the RViz environment to either display or interact with some data in a two-dimensional environment.

Boilerplate Code
----------------

Header File
^^^^^^^^^^^

Here are the contents of ``demo_panel.hpp``

.. code-block:: c++

#ifndef RVIZ_PLUGIN_TUTORIAL__DEMO_PANEL_HPP_
#define RVIZ_PLUGIN_TUTORIAL__DEMO_PANEL_HPP_

#include <rviz_common/panel.hpp>

namespace rviz_plugin_tutorial
{
class DemoPanel
: public rviz_common::Panel
{
Q_OBJECT
public:
explicit DemoPanel(QWidget * parent = 0);
~DemoPanel() override;
};
} // namespace rviz_plugin_tutorial

#endif // RVIZ_PLUGIN_TUTORIAL__DEMO_PANEL_HPP_

* We're extending the `rviz_common::Panel <https://github.com/ros2/rviz/blob/9a94bdf2f5f92ccdac4037c9268b95940845d609/rviz_common/include/rviz_common/panel.hpp#L46>`__ class.
* `For reasons outside the scope of this tutorial <https://doc.qt.io/qt-5/moc.html>`__, you need the ``Q_OBJECT`` macro in there to get the QT parts of the GUI to work.
* We start by declaring just a constructor and destructor, implemented in the cpp file.

Source File
^^^^^^^^^^^

``demo_panel.cpp``

.. code-block:: c++

#include <rviz_plugin_tutorial/demo_panel.hpp>

namespace rviz_plugin_tutorial
{
DemoPanel::DemoPanel(QWidget* parent) : Panel(parent)
{
}

DemoPanel::~DemoPanel() = default;
} // namespace rviz_plugin_tutorial

#include <pluginlib/class_list_macros.hpp>
PLUGINLIB_EXPORT_CLASS(rviz_plugin_tutorial::DemoPanel, rviz_common::Panel)

* Overriding the constructor and deconstructor are not strictly necessary, but we can do more with them later.
* In order for RViz to find our plugin, we need this ``PLUGINLIB`` invocation in our code (as well as other things below).

package.xml
^^^^^^^^^^^

We need the following dependencies in our package.xml:

.. code-block:: xml

<depend>pluginlib</depend>
<depend>rviz_common</depend>

rviz_common_plugins.xml
^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: xml

<library path="demo_panel">
<class type="rviz_plugin_tutorial::DemoPanel" base_class_type="rviz_common::Panel">
<description></description>
</class>
</library>

* This is standard ``pluginlib`` code.

* The library ``path`` is the name of the library we'll assign in the CMake.
* The class should match the ``PLUGINLIB`` invocation from above.

* We'll come back to the description later, I promise.

CMakeLists.txt
^^^^^^^^^^^^^^

Add the following lines to the top of the standard boilerplate.

.. code-block:: cmake

find_package(ament_cmake_ros REQUIRED)
find_package(pluginlib REQUIRED)
find_package(rviz_common REQUIRED)

set(CMAKE_AUTOMOC ON)
qt5_wrap_cpp(MOC_FILES
include/rviz_plugin_tutorial/demo_panel.hpp
)

add_library(demo_panel src/demo_panel.cpp ${MOC_FILES})
target_include_directories(demo_panel PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
ament_target_dependencies(demo_panel
pluginlib
rviz_common
)
install(TARGETS demo_panel
EXPORT export_rviz_plugin_tutorial
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(DIRECTORY include/
DESTINATION include
)
install(FILES rviz_common_plugins.xml
DESTINATION share/${PROJECT_NAME}
)
ament_export_include_directories(include)
ament_export_targets(export_rviz_plugin_tutorial)
pluginlib_export_plugin_description_file(rviz_common rviz_common_plugins.xml)


* To generate the proper Qt files, we need to

* Turn ``CMAKE_AUTOMOC`` on.
* Wrap the headers by calling ``qt5_wrap_cpp`` with each header that has ``Q_OBJECT`` in it.
* Include the ``MOC_FILES`` in the library alongside our other cpp files.

* A lot of the other code ensures that the plugin portion works.
Namely, calling ``pluginlib_export_plugin_description_file`` is essential to getting RViz to find your new plugin.

Testing it out
^^^^^^^^^^^^^^

Compile your code, source your workspace and run ``rviz2``.

In the top Menu bar, there should be a "Panels" menu. Select "Add New Panel" from that menu.
A dialog will pop up showing all the panels accessible in your ROS environment, grouped into folders based on their ROS package.
Create a new instance of your panel by either double clicking on its name, or selecting it and clicking OK.

This should create a new panel in your RViz window, albeit with nothing but a title bar with the name of your panel.

Filling in the Panel
--------------------
We're going to update our panel with some very basic ROS/QT interaction.

Updated Header File
^^^^^^^^^^^^^^^^^^^

Update ``demo_panel.hpp`` to include the following includes and class Body.

.. code-block:: c++

#include <rviz_common/panel.hpp>
#include <rviz_common/ros_integration/ros_node_abstraction_iface.hpp>
#include <std_msgs/msg/string.hpp>
#include <QLabel>
#include <QPushButton>

namespace rviz_plugin_tutorial
{
class DemoPanel : public rviz_common::Panel
{
Q_OBJECT
public:
explicit DemoPanel(QWidget * parent = 0);
~DemoPanel() override;

void onInitialize() override;

protected:
std::shared_ptr<rviz_common::ros_integration::RosNodeAbstractionIface> node_ptr_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;

void topicCallback(const std_msgs::msg::String & msg);

QLabel* label_;
QPushButton* button_;

private Q_SLOTS:
void buttonActivated();
};
} // namespace rviz_plugin_tutorial

* On the ROS side, we declare an abstract node pointer, publisher and subscriber.
We also have methods to initialize them and a callback for the subscriber.
* On the QT side, we declare a label and a button, as well as a callback for the button.

Updated Source File
^^^^^^^^^^^^^^^^^^^

Update ``demo_panel.cpp`` to have the following contents:

.. code-block:: c++

#include <rviz_plugin_tutorial/site_data_panel.hpp>
#include <QVBoxLayout>
#include <rviz_common/display_context.hpp>

namespace rviz_plugin_tutorial
{

DemoPanel::DemoPanel(QWidget* parent) : Panel(parent)
{
const auto layout = new QVBoxLayout(this);
label_ = new QLabel("[no data]");
button_ = new QPushButton("GO!");
layout->addWidget(label_);
layout->addWidget(button_);

QObject::connect(button_, &QPushButton::released, this, &DemoPanel::buttonActivated);
}

DemoPanel::~DemoPanel() = default;

void DemoPanel::onInitialize()
{
node_ptr_ = getDisplayContext()->getRosNodeAbstraction().lock();
rclcpp::Node::SharedPtr node = node_ptr_->get_raw_node();
publisher_ = node->create_publisher<std_msgs::msg::String>("/output", 10);
subscription_ = node->create_subscription<std_msgs::msg::String>("/input", 10, std::bind(&DemoPanel::topicCallback, this, std::placeholders::_1));
}

void DemoPanel::topicCallback(const std_msgs::msg::String & msg)
{
label_->setText(QString(msg.data.c_str()));
}

void DemoPanel::buttonActivated()
{
auto message = std_msgs::msg::String();
message.data = "Button clicked!";
publisher_->publish(message);
}

} // namespace rviz_plugin_tutorial

#include <pluginlib/class_list_macros.hpp>

PLUGINLIB_EXPORT_CLASS(rviz_plugin_tutorial::DemoPanel, rviz_common::Panel)

* In the constructor, we add the two widgets to a layout object, and connect the callback to the button object.
* In the ``onInitialize`` method, we can access the ``DisplayContext`` which allows us access to the ROS interfaces.
We get a pointer to the actual node interface, and then create a standard publisher/subscriber.
* When we get a message from the subscription, we update the label.
* When the button is clicked, we publish a new message.

Testing with ROS
^^^^^^^^^^^^^^^^
Compile and launch RViz2 with your panel again. You should see your label and button in the panel now.

To change the label, run

.. code-block:: bash

ros2 topic pub /input std_msgs/msg/String "{data: 'Please be kind.'}"

You can also see the results of pushing the button:

.. code-block:: bash

ros2 topic echo /output


Cleanup
-------

Now its time to clean it up a bit.
This makes things look nicer and be a little easier to use, but aren't strictly required.

First, you should update the description of your plugin in ``rviz_common_plugins.xml``

We also add an icon for the plugin at ``icons/classes/DemoPanel.png``.
The folder is hardcoded, and the filename should match the name from the plugin declaration (or the name of the class if not specified).

We need to install the image file in the CMake.

.. code-block:: cmake

install(FILES icons/classes/DemoPanel.png
DESTINATION share/${PROJECT_NAME}/icons/classes
)

Now when you add the panel, it should show up with an icon and description.
1 change: 1 addition & 0 deletions source/Tutorials/Intermediate/RViz/RViz-Main.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ RViz is a 3D visualizer for the Robot Operating System (ROS) framework.

RViz-User-Guide/RViz-User-Guide
RViz-Custom-Display/RViz-Custom-Display
RViz-Custom-Panel/RViz-Custom-Panel