Rvizz中有不同类型的插件,每个插件都必须具有相应的基本类型,才能被RViz识别。
plugin type | base type |
---|---|
Display | rviz_common::Display |
Panel | rviz_common::Panel |
Tool | rviz_common::Tool |
Frames transformation library | rviz_common::transformation::FrameTransformer |
View Controller | rviz_common::ViewController |
创建一个工作空间
您需要将你的程序与rviz_common链接,可能还需要与rviz_rendering和rviz_default_plugins链接。
要让插件加载程序找到您的插件,你需要调用PLUGINILIB_EXPORT_CLASS宏。例如,如果你的插件名为“myplugin::plugin”,基类为rviz_common::Display,则需要在.cpp文件中包含以下内容:
#include
您需要针对pluginlib包进行链接和编译。其他可能相关的CMake选项(请参阅rviz_default_plugins CMakeLists):
#Causes the visibility macros to use dllexport rather than dllimport (for Windows, when your plugin should be used as library)
target_compile_definitions(rviz_default_plugins PRIVATE "RVIZ_DEFAULT_PLUGINS_BUILDING_LIBRARY")
你需要编写一个plugindescription.xml文件,其中包含pluginlib在运行时加载插件所需的信息。有关示例,请参见rviz_default_plugins/plugins_description.xml(语法与旧rviz相同)
导出插件描述文件
pluginlib_export_plugin_description_file(rviz_common plugins_description.xml)
这个描述文件能够确保你的插件运行时能够被rviz_common找到。
为了编写一个display插件,需要从rviz_common::Display或rviz_common::RosTopicDisplay 派生。
在不订阅ros的情况下编写display时,从rviz_common::display
派生.
Ogre::SceneNode
,可用于添加视觉对象。onInitialize()
方法。当你编写一个topic类型的display,从rviz_common::RosTopicDisplay派生。RosTopicDisplay以该显示器的消息类型为模板:
可以将自己的meshes、scripts等添加到RViz中。要使它们在运行时可用,请使用在rviz_rendering中定义的cmake宏register_rviz_ogre_media_exports。例如,如果您想注册位于项目源文件夹中的文件夹test_folder/scripts,请编写
register_rviz_ogre_media_exports(DIRECTORIES "test_folder/scripts")
注意:如果要导出文件夹层次结构,则需要分别导出每个文件夹。导出文件夹中的文件夹不会自动可用。
一些默认显示器(如TF显示器或LaserScan显示器)只能使用tf2作为转换框架。同样,作为一名开发人员,你可能已经编写了自己的转换插件,无法与每个显示器一起工作,或者您自己的显示器无法与每个转换插件一起工作。类rviz_default_plugins::transformation::TransformerGuard可以帮助您处理这些可能性。
如下所述,每个转换插件都实现基类rviz_common::transformation::FrameTransformer。TransformerGuard类是基于此实现的类型模板化的,它将确保如果当前使用的转换器是不同的类型,则其所属的display将被禁用。
在编写应该只与特定转换插件一起使用的display时,可以添加TransformerGuard的模板实例作为display成员。这个类的构造函数有两个参数:一个指向拥有它的显示器的原始指针,以及它应该使用的转换器的名称(正确设置错误状态所需的名称)。理想情况下,TransformerGuard对象在显示器的构造函数中初始化。
除此之外,TransformerGuard类还提供了checkTransformer()方法,如果当前usd转换器是允许的类型,则返回true,否则返回false。每当您希望显示器根据当前使用的转换插件的类型表现不同时,显示器都可以使用此方法。
关于如何完成这一切的具体示例,你可以在rviz_default_plugins中查看TF或LaserScan显示器,如前所述,该显示器仅适用于tf2。
要编写自定义面板,从rviz_common::panel派生。
rviz_rendering包应该包含所有涉及渲染的功能:
rviz_common包包含大量可用于插件开发的rviz:
rviz_default_plugins包含rviz附带的所有插件(view controllers, tools, displays and in the future, panels) 其中大多数是从rviz的default_plugin斯文件夹移植的)。
rviz_visual_testing_framework包含编写插件可视化测试的主干。如果您想编写自动屏幕截图测试,那么只需要将此包用作测试依赖项。有关进一步的帮助,请参阅软件包中的文档。
这个头文件是ROS中的一个视图管理器,主要用于管理rviz中的视图。
它包含了一些重要的类和函数,如ViewManager、ViewController、ViewManager::getCurrent()等。
ViewManager是视图管理器的主要类,它负责管理所有的视图,并提供了一些方法来添加、删除、获取视图等。
ViewController是视图控制器的基类,它定义了一些基本的方法和属性,如getName()、getType()等。
ViewManager::getCurrent()是一个静态方法,用于获取当前的视图管理器。除此之外,rviz_common/view_manager还包含了一些其他的类和函数,
如ViewManager::getViewController()、ViewManager::getViews()等,它们都是视图管理器中非常重要的组成部分。
要将一个QT工程项目编写成rviz的Panel插件,需要按照以下步骤进行操作:
创建一个QT工程项目,并在其中添加一个QT界面,用于显示rviz的Panel插件。
在QT工程项目中添加rviz的依赖库,以便能够使用rviz的API。
在QT工程项目中创建一个继承自rviz::Panel的类,用于实现rviz的Panel插件的功能。
在该类中实现rviz::Panel的虚函数,包括onInitialize()、load()、save()和refresh()等函数,以便能够正确地加载和保存插件的状态,并在需要时刷新插件的显示。
在QT界面中添加一个QVBoxLayout布局,并将rviz::Panel的实例添加到该布局中,以便能够正确地显示rviz的Panel插件。
在QT工程项目中添加一个CMakeLists.txt文件,并在其中添加必要的编译选项和链接选项,以便能够正确地编译和链接rviz的Panel插件。
在QT工程项目中添加一个package.xml文件,并在其中添加必要的依赖项和描述信息,以便能够正确地打包和安装rviz的Panel插件。
以下是一个示例代码,展示了如何将一个QT工程项目编写成rviz的Panel插件:
#include
#include
class MyPanel : public rviz::Panel
{
public:
MyPanel(QWidget* parent = 0) : rviz::Panel(parent)
{
QVBoxLayout* layout = new QVBoxLayout;
setLayout(layout);
}
virtual void onInitialize()
{
// TODO: Initialize the panel here.
}
virtual void load(const rviz::Config& config)
{
// TODO: Load the panel's state from the given config object.
}
virtual void save(rviz::Config config) const
{
// TODO: Save the panel's state to the given config object.
}
virtual void refresh()
{
// TODO: Refresh the panel's display here.
}
};
#include
PLUGINLIB_EXPORT_CLASS(MyPanel, rviz::Panel)
在CMakeLists.txt文件中,需要添加以下内容:
find_package(rviz REQUIRED)
include_directories(${rviz_INCLUDE_DIRS})
link_directories(${rviz_LIBRARY_DIRS})
add_library(my_panel src/my_panel.cpp)
target_link_libraries(my_panel ${rviz_LIBRARIES})
install(TARGETS my_panel
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION})
install(FILES plugin_description.xml
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
在package.xml文件中,需要添加以下内容:
<build_depend>rvizbuild_depend>
<run_depend>rvizrun_depend>
<export>
<rviz plugin="${prefix}/plugin_description.xml"/>
export>
最后,使用catkin_make命令编译和安装rviz的Panel插件即可。
要实现同一个type的rviz的panel插件通过按钮控件实现跳转,可以按照以下步骤进行:
var button = new ROSLIB.Button({
divID : 'my-button',
class : 'my-button-class',
text : 'My Button'
});
button.on('click', function() {
// 跳转到其他panel
});
var panelManager = new ROSLIB.Rviz.PanelManager({
ros : ros,
rootObject : rootObject,
loader : loader
});
panelManager.loadPanel('my_panel_type', {
my_panel_options : 'my_panel_options_value'
});
其中,'my_panel_type’是要跳转到的panel的type,'my_panel_options’是要传递给panel的参数。
<div id="my-button" class="my-button-class">div>
这样就可以实现同一个type的rviz的panel插件通过按钮控件实现跳转了。
可能有很多,以下是一些可能的解决方法:
add_library(my_panel_plugin src/my_panel_plugin.cpp)
target_link_libraries(my_panel_plugin ${catkin_LIBRARIES} ${QT_LIBRARIES} ${Boost_LIBRARIES})
#include
#include
PLUGINLIB_EXPORT_CLASS(my_panel_plugin::MyPanelPlugin, rviz::Panel)
install(TARGETS my_panel_plugin
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
export RVIZ_PLUGIN_PATH=$RVIZ_PLUGIN_PATH:/path/to/my_panel_plugin
如果以上方法都无法解决问题,可以尝试使用调试工具(例如gdb)来查看链接库生成的过程中是否出现了错误。
要设置rviz的panel插件弹出的大小,可以通过在插件的构造函数中设置固定大小或者使用布局管理器来自适应大小。以下是两种方法的示例代码:
MyPanel::MyPanel(QWidget* parent) : rviz::Panel(parent)
{
// 设置固定大小为300x200
setFixedSize(300, 200);
}
MyPanel::MyPanel(QWidget* parent) : rviz::Panel(parent)
{
// 创建一个垂直布局管理器
QVBoxLayout* layout = new QVBoxLayout(this);
// 添加需要显示的控件
QLabel* label = new QLabel("Hello World", this);
layout->addWidget(label);
// 设置布局管理器
setLayout(layout);
}
在第二种方法中,布局管理器会根据添加的控件自动调整大小,从而实现自适应大小的效果。你可以根据实际需求选择其中一种方法来设置rviz的panel插件弹出的大小。
您可以通过以下步骤在rviz的自定义panel中调用另一个panel:
#include
MyOtherPanel* other_panel_;
other_panel_ = new MyOtherPanel();
QPushButton* button = new QPushButton("Call Other Panel");
connect(button, SIGNAL(clicked()), this, SLOT(callOtherPanel()));
void MyCustomPanel::callOtherPanel()
{
other_panel_->show();
}
这将显示另一个panel,并允许用户与其交互。请注意,您需要确保在您的自定义panel中正确管理另一个panel的生命周期,以避免内存泄漏或其他问题。
您可以通过以下步骤在rviz的自定义panel中调用另一个panel并在rviz页面中显示:
首先,您需要在您的rviz插件包中创建两个自定义panel,例如PanelA和PanelB。
在PanelA的头文件中,包含PanelB的头文件,并创建一个指向PanelB的指针:
#include "panel_b.h"
...
PanelB* panel_b_;
panel_b_ = new PanelB();
layout->addWidget(panel_b_);
void PanelA::onButtonClicked()
{
panel_b_->show();
}
setLayout(layout);
setVisible(false);
void PanelB::show()
{
setVisible(true);
rviz::Panel::show();
}
通过以上步骤,您可以在rviz的自定义panel中调用另一个panel并在rviz页面中显示。
具体实现步骤如下:
QVBoxLayout* layout = new QVBoxLayout;
QWidget* widget = new QWidget;
widget->setLayout(layout);
sub_window_ = new rviz_common::PanelDockWidget(tr("Sub Window"), this);
sub_window_->setWidget(widget);
rviz_common::PanelFactory* factory = context_->getPanelFactory("AnotherPlugin");
if (factory)
{
rviz_common::Panel* panel = factory->createPanel();
layout->addWidget(panel);
}
delete sub_window_;
完整的代码示例如下:
#include
#include
#include
#include
#include
class MyPanel : public rviz_common::Panel
{
public:
MyPanel(QWidget* parent = nullptr) : rviz_common::Panel(parent)
{
QVBoxLayout* layout = new QVBoxLayout;
QWidget* widget = new QWidget;
widget->setLayout(layout);
sub_window_ = new rviz_common::PanelDockWidget(tr("Sub Window"), this);
sub_window_->setWidget(widget);
rviz_common::PanelFactory* factory = context_->getPanelFactory("AnotherPlugin");
if (factory)
{
rviz_common::Panel* panel = factory->createPanel();
layout->addWidget(panel);
}
}
~MyPanel()
{
delete sub_window_;
}
private:
rviz_common::PanelDockWidget* sub_window_;
};
需要注意的是,要在panel插件中使用另外一个插件的内容,需要先在CMakeLists.txt中添加对该插件的依赖,例如:
find_package(rviz_common REQUIRED)
rviz_common_plugin(AnotherPlugin)
同时,在package.xml中也需要添加对该插件的依赖,例如:
<depend>rviz_plugin_tutorialsdepend>
rviz2是ROS2中的可视化工具,提供了一些API供开发者使用。以下是使用rviz2 API的步骤:
在你的ROS2包中,需要导入rviz2 API。在CMakeLists.txt文件中添加以下代码:
find_package(rviz2 REQUIRED)
在package.xml文件中添加以下代码:
<depend>rviz2depend>
在你的ROS2节点中,需要创建一个rviz2节点。以下是一个示例代码:
#include "rclcpp/rclcpp.hpp"
#include "rviz_common/node.hpp"
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<rviz_common::Node>("my_rviz2_node");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
在rviz2节点中,需要创建一个可视化组件。以下是一个示例代码:
#include "rclcpp/rclcpp.hpp"
#include "rviz_common/node.hpp"
#include "rviz_common/display.hpp"
#include "rviz_common/display_context.hpp"
#include "rviz_common/display_factory.hpp"
#include "rviz_common/ros_integration/ros_node_abstraction.hpp"
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<rviz_common::Node>("my_rviz2_node");
auto display_context = std::make_shared<rviz_common::DisplayContext>(node);
auto display_factory = std::make_shared<rviz_common::DisplayFactory>();
auto ros_node_abstraction = std::make_shared<rviz_common::RosNodeAbstraction>(node);
auto display = display_factory->make("rviz_common/PointCloud2Display");
display->initialize(display_context);
display->setNode(ros_node_abstraction);
display_context->addDisplay(display);
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
在终端中运行rviz2节点:
ros2 run my_package my_rviz2_node
打开rviz2,点击“Add”按钮,选择你创建的可视化组件,即可在rviz2中显示可视化组件。
以上是使用rviz2 API的基本步骤。你可以根据自己的需求,使用rviz2 API创建更复杂的可视化组件。
在rviz的panel插件调用close后,留下状态条的问题可能是由于插件没有正确释放资源导致的。解决方法可以尝试在插件的close函数中添加释放资源的代码,例如:
void MyPanel::close()
{
// 释放资源
delete my_resource_;
// 调用父类的close函数
Panel::close();
}
如果仍然存在状态条的问题,可以尝试在插件的destructor中添加释放资源的代码,例如:
MyPanel::~MyPanel()
{
// 释放资源
delete my_resource_;
}
另外,也可以尝试在rviz的配置文件中将该插件的auto_save属性设置为false,以避免状态条的出现。具体方法是在配置文件中找到该插件的配置项,添加auto_save: false属性,例如:
MyPanel:
Type: my_package/MyPanel
Name: My Panel
auto_save: false
以上是一些可能的解决方法,具体需要根据实际情况进行调试和优化。
如果您需要彻底关闭当前panel页面,可以考虑使用rviz的removePanel函数。该函数可以将指定的panel从rviz中彻底删除,包括释放资源和销毁QWidget对象。具体方法是在插件的代码中调用rviz::PanelFactory::removePanel函数,例如:
void MyPanel::close()
{
// 调用rviz::PanelFactory::removePanel函数
rviz::PanelFactory::removePanel(this);
// 调用父类的close函数
Panel::close();
}
需要注意的是,使用removePanel函数会彻底删除该panel,因此需要确保在调用该函数之前已经保存了该panel的状态和数据,以便下次需要时可以恢复。另外,如果您的插件需要在关闭时执行一些其他的操作,例如保存数据或发送信号,可以在调用removePanel函数之前添加相应的代码。
rviz2中的自定义Panel在调用close后需要手动点击Panels状态栏才能彻底关闭Panel插件,有什么方法可以在调用close函数后自动彻底关闭当前插件?不要使用hide方法,那个是不可行的。C++代码 请详细回答
可以在自定义Panel的构造函数中添加一个回调函数,用于监听Panel的关闭事件。当Panel关闭时,回调函数会被触发,可以在回调函数中添加代码来彻底关闭Panel插件。
以下是示例代码:
#include
class MyPanel : public rviz_common::Panel
{
public:
MyPanel(QWidget* parent = nullptr) : rviz_common::Panel(parent)
{
// 添加关闭事件监听器
connect(this, &MyPanel::closeRequested, this, &MyPanel::onClose);
}
private:
void onClose()
{
// 在这里添加关闭Panel插件的代码
// ...
}
};
在上面的代码中,我们在MyPanel的构造函数中添加了一个关闭事件监听器,当Panel关闭时,onClose函数会被触发。在onClose函数中,可以添加代码来彻底关闭Panel插件。
如何添加关闭Panel插件的代码 ?
在onClose函数中,可以调用rviz_common::Panel::close()函数来关闭Panel插件。但是,这个函数只会隐藏Panel,而不会彻底关闭它。为了彻底关闭Panel插件,我们需要手动删除Panel对象。
以下是示例代码:
void onClose()
{
// 关闭Panel
rviz_common::Panel::close();
// 删除Panel对象
delete this;
}
在上面的代码中,我们先调用rviz_common::Panel::close()函数来关闭Panel,然后再调用delete this来删除Panel对象。这样就可以彻底关闭Panel插件了。