【ROS2】中级:RViz-构建自定义 RViz 显示

 背景 

在 RViz 中有许多类型的数据已经有现有的可视化。然而,如果有一种消息类型尚未有插件来显示它,那么有两种选择可以在 RViz 中查看它

  1. 将消息转换为另一种类型,例如 visualization_msgs/Marker 。

  2. 编写自定义 RViz 显示。

使用第一个选项,会有更多的网络流量和数据表示的限制。它也快速且灵活。后一种选择在本教程中进行了说明。虽然需要一些工作,但可以带来更丰富的可视化效果。

本教程的所有代码都可以在此存储库 https://github.com/MetroRobots/rviz_plugin_tutorial 中找到。为了查看本教程中编写的插件的增量进展,存储库有不同的分支( step2 , step3 ……),每个分支都可以在您进行时编译和运行。

93b9388f7b6ad90c18d7a49540ba211d.png

cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select rviz_plugin_tutorial_msgs
Starting >>> rviz_plugin_tutorial_msgs
Finished <<< rviz_plugin_tutorial_msgs [24.6s]                       


Summary: 1 package finished [24.9s]
cxy@ubuntu2404-cxy:~/ros2_ws$ . install/setup.bash
cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select rviz_plugin_tutorial
Starting >>> rviz_plugin_tutorial
Finished <<< rviz_plugin_tutorial [25.8s]                       


Summary: 1 package finished [26.8s]
cxy@ubuntu2404-cxy:~/ros2_ws$ . install/setup.bash
cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 run rviz2 rviz2
[INFO] [1720966890.438943145] [rviz2]: Stereo is NOT SUPPORTED
[INFO] [1720966890.439067206] [rviz2]: OpenGl version: 4.6 (GLSL 4.6)
[INFO] [1720966890.559429080] [rviz2]: Stereo is NOT SUPPORTED

4f07c7cef75a626853d32e590a344d80.png

f15a2ce975353f08f1c4497b3e1456d7.png

Step By Step

 点 2D 消息

我们将使用 rviz_plugin_tutorial_msgs 包中定义的玩具消息: Point2D.msg :

std_msgs/Header header
float64 x
float64 y

基本插件的样板代码

系好安全带,这里有很多代码。您可以使用分支名称 step1 查看此代码的完整版本。

69529cb93af05b89215ce2da5ae940f6.png

头文件 

以下是 point_display.hpp 的内容

// 如果没有定义 RVIZ_PLUGIN_TUTORIAL__POINT_DISPLAY_HPP_,则定义它
#ifndef RVIZ_PLUGIN_TUTORIAL__POINT_DISPLAY_HPP_
#define RVIZ_PLUGIN_TUTORIAL__POINT_DISPLAY_HPP_


// 包含 rviz_common 的消息过滤显示库
#include <rviz_common/message_filter_display.hpp>
// 包含 rviz_plugin_tutorial_msgs 的 Point2D 消息库
#include <rviz_plugin_tutorial_msgs/msg/point2_d.hpp>


// 定义 rviz_plugin_tutorial 命名空间
namespace rviz_plugin_tutorial
{
// 定义 PointDisplay 类,它是 rviz_common::MessageFilterDisplay 类的公共派生类
class PointDisplay
  : public rviz_common::MessageFilterDisplay<rviz_plugin_tutorial_msgs::msg::Point2D>
{
  // 使用 Qt 的元对象系统
  Q_OBJECT


protected:
  // 重写 processMessage 方法,该方法用于处理 Point2D 消息
  void processMessage(const rviz_plugin_tutorial_msgs::msg::Point2D::ConstSharedPtr msg) override;
};
}  // 结束 rviz_plugin_tutorial 命名空间的定义


// 结束 RVIZ_PLUGIN_TUTORIAL__POINT_DISPLAY_HPP_ 的定义
#endif  // RVIZ_PLUGIN_TUTORIAL__POINT_DISPLAY_HPP_
  • 我们正在实现 MessageFilterDisplay 类,该类可用于任何带有 std_msgs/Header 的消息

  • 该类使用我们的 Point2D 消息类型进行模板化。

  • 由于本教程范围之外的原因,您需要在其中使用 Q_OBJECT 宏才能使 GUI 的 QT 部分正常工作。

  • processMessage 是唯一需要实现的方法,我们将在 cpp 文件中实现。

源文件 

point_display.cpp

// 包含 rviz_plugin_tutorial 的 PointDisplay 库
#include <rviz_plugin_tutorial/point_display.hpp>
// 包含 rviz_common 的日志库
#include <rviz_common/logging.hpp>


// 定义 rviz_plugin_tutorial 命名空间
namespace rviz_plugin_tutorial
{
// 定义 PointDisplay 类的 processMessage 方法,该方法用于处理 Point2D 消息
void PointDisplay::processMessage(const rviz_plugin_tutorial_msgs::msg::Point2D::ConstSharedPtr msg)
{
  // 使用 RVIZ_COMMON_LOG_INFO_STREAM 宏打印接收到的消息的帧ID
  RVIZ_COMMON_LOG_INFO_STREAM("We got a message with frame " << msg->header.frame_id);
}
}  // 结束 rviz_plugin_tutorial 命名空间的定义


// 包含 pluginlib 的类列表宏库
#include <pluginlib/class_list_macros.hpp>
// 使用 PLUGINLIB_EXPORT_CLASS 宏导出 PointDisplay 类,使其成为 rviz_common::Display 的插件
PLUGINLIB_EXPORT_CLASS(rviz_plugin_tutorial::PointDisplay, rviz_common::Display)
  • 记录不是严格必要的,但有助于调试。

  • 为了让 RViz 找到我们的插件,我们需要在代码中使用这个 PLUGINLIB 调用(以及下面的其他内容)。

 package.xml

我们需要在我们的 package.xml 中包含以下三个依赖项:

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

rviz_common_plugins.xml

<library path="point_display">
  <class type="rviz_plugin_tutorial::PointDisplay" base_class_type="rviz_common::Display">
    <description></description>
  </class>
</library>
  • 这是标准 pluginlib 代码。

    • 库 path 是我们将在 CMake 中分配的库的名称。

    • 类应与上面的 PLUGINLIB 调用匹配。

  • 我们稍后会回到描述部分,我保证。

 CMakeLists.txt

将以下几行添加到标准样板的顶部。

// 寻找ament_cmake_ros包,这是ROS2的构建系统
find_package(ament_cmake_ros REQUIRED)
// 寻找pluginlib包,这是ROS的插件库
find_package(pluginlib REQUIRED)
// 寻找rviz_common包,这是RViz的通用库
find_package(rviz_common REQUIRED)
// 寻找rviz_plugin_tutorial_msgs包,这是我们自定义的消息类型库
find_package(rviz_plugin_tutorial_msgs REQUIRED)


// 开启CMake的自动MOC功能,用于处理Qt的元对象编译
set(CMAKE_AUTOMOC ON)
// 使用Qt5的MOC预处理器处理头文件,生成MOC文件
qt5_wrap_cpp(MOC_FILES
  include/rviz_plugin_tutorial/point_display.hpp
)


// 添加一个库,名为point_display,源文件包括point_display.cpp和上面生成的MOC文件
add_library(point_display src/point_display.cpp ${MOC_FILES})
// 设置point_display库的公共包含目录
target_include_directories(point_display PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>
)
// 设置point_display库的依赖项
ament_target_dependencies(point_display
  pluginlib
  rviz_common
  rviz_plugin_tutorial_msgs
)
// 安装point_display库,包括静态库、动态库和可执行文件
install(TARGETS point_display
        EXPORT export_rviz_plugin_tutorial
        ARCHIVE DESTINATION lib
        LIBRARY DESTINATION lib
        RUNTIME DESTINATION bin
)
// 安装include目录下的所有头文件
install(DIRECTORY include/
        DESTINATION include
)
// 安装rviz_common_plugins.xml文件
install(FILES rviz_common_plugins.xml
        DESTINATION share/${PROJECT_NAME}
)
// 导出include目录
ament_export_include_directories(include)
// 导出rviz_plugin_tutorial目标
ament_export_targets(export_rviz_plugin_tutorial)
// 导出插件描述文件
pluginlib_export_plugin_description_file(rviz_common rviz_common_plugins.xml)
  • 要生成正确的 Qt 文件,我们需要

    •  打开 CMAKE_AUTOMOC 。

    • 通过调用 qt5_wrap_cpp 包装每个包含 Q_OBJECT 的标题。

    • 将 MOC_FILES 包含在库中,与我们的其他 cpp 文件一起。

  • 请注意,如果您不包装头文件,在尝试在运行时加载插件时,可能会收到类似以下内容的错误消息:

[rviz2]: PluginlibFactory: The plugin for class 'rviz_plugin_tutorial::PointDisplay' failed to load. Error: Failed to load library /home/ros/ros2_ws/install/rviz_plugin_tutorial/lib/libpoint_display.so. Make sure that you are calling the PLUGINLIB_EXPORT_CLASS macro in the library code, and that names are consistent between this macro and your XML. Error string: Could not load library LoadLibrary error: /home/ros/ros2_ws/install/rviz_plugin_tutorial/lib/libpoint_display.so: undefined symbol: _ZTVN20rviz_plugin_tutorial12PointDisplayE, at /tmp/binarydeb/ros-foxy-rcutils-1.1.4/src/shared_library.c:84
  • 许多其他代码确保插件部分正常工作。即,调用 pluginlib_export_plugin_description_file 对于让 RViz 找到您的新插件至关重要。

 测试一下

编译你的代码并运行 rviz2 。你应该能够通过点击左下角的 Add ,然后选择你的包/插件来添加你的新插件。

de44b6aeeb5fbf1e61297fe2b359b8c6.png

最初,显示将处于错误状态,因为您尚未分配主题。

7679fbe52cfb4af6330bb69bb2cac16c.png

如果我们将主题 /point 放入,它应该加载正常但不会显示任何内容。

e1ae54a591d5e8f879d23ecbbe1671e4.png

您可以使用以下命令发布消息:

ros2 topic pub /point rviz_plugin_tutorial_msgs/msg/Point2D "{header: {frame_id: map}, x: 1, y: 2}" -r 0.5

068d2d5dd9cb015bc474961106b0c3b0.png

这应该会导致“我们收到了一条消息”的日志出现在 RViz 的 stdout 中。

 实际可视化

您可以查看此步骤的完整版本,分支名称为 step2 。

首先,您需要在 CMakeLists.txt 和 package.xml 中添加对包 rviz_rendering 的依赖。

我们需要在头文件中添加三行

  • 在 rviz_rendering 包中有很多选项可以用于构建可视化对象。这里我们使用一个简单的形状。

  • 在类里,我们将添加一个新的 protected 虚拟方法: void onInitialize() override;

  • 我们还添加了一个指向我们形状对象的指针: std::unique_ptr<rviz_rendering::Shape> point_shape_;

然后在 cpp 文件中,我们定义 onInitialize 方法:

// 定义PointDisplay类的onInitialize方法
void PointDisplay::onInitialize()
{
  // 调用MFDClass类的onInitialize方法
  MFDClass::onInitialize();


  // 创建一个新的rviz_rendering::Shape对象,类型为Cube
  // 并将其赋值给point_shape_成员变量
  point_shape_ =
    std::make_unique<rviz_rendering::Shape>(rviz_rendering::Shape::Type::Cube, scene_manager_,
      scene_node_);
}
  • MFDClass 被别名化为模板化父类以方便使用。

  • 形状对象必须在 onInitialize 方法中构建,而不是在构造函数中构建,因为否则 scene_manager_ 和 scene_node_ 将无法准备好。

我们还更新了我们的 processMessage 方法:

// 定义PointDisplay类的processMessage方法
void PointDisplay::processMessage(const rviz_plugin_tutorial_msgs::msg::Point2D::ConstSharedPtr msg)
{
  // 打印接收到的消息的frame_id
  RVIZ_COMMON_LOG_INFO_STREAM("We got a message with frame " << msg->header.frame_id);


  // 定义位置和方向变量
  Ogre::Vector3 position;
  Ogre::Quaternion orientation;


  // 如果无法从消息的header获取变换,则打印错误信息
  if (!context_->getFrameManager()->getTransform(msg->header, position, orientation)) {
    RVIZ_COMMON_LOG_DEBUG_STREAM("Error transforming from frame '" << msg->header.frame_id <<
        "' to frame '" << qPrintable(fixed_frame_) << "'");
  }


  // 设置场景节点的位置和方向
  scene_node_->setPosition(position);
  scene_node_->setOrientation(orientation);


  // 定义点的位置
  Ogre::Vector3 point_pos;
  point_pos.x = msg->x;
  point_pos.y = msg->y;


  // 设置点的位置
  point_shape_->setPosition(point_pos);
}
  • 我们需要为我们的消息获取适当的框架,并相应地转换 scene_node_ 。这确保了可视化不会总是相对于固定框架出现。

  • 实际的可视化内容在最后四行:我们将可视化的位置设置为与消息的位置匹配。

结果应如下所示: 

c9c60c749d08d58b8122761f7038f0e1.png

如果该位置没有出现该框,可能是因为:

  • 此时您未发布该主题

  • 消息在过去 2 秒内尚未发布。

  • 您没有正确设置 RViz 中的主题。

有选择真好

如果您想允许用户自定义可视化的不同属性,则需要添加 rviz_common::Property 对象。https://github.com/ros2/rviz/tree/ros2/rviz_common/include/rviz_common/properties

您可以查看此步骤的完整版本,分支名称为 step3 。

 标题更新

包含颜色属性的头文件: #include <rviz_common/properties/color_property.hpp> 。颜色只是您可以设置的众多属性之一。

添加 updateStyle 的原型,每当通过 Qt 的 SIGNAL/SLOT 框架更改 GUI 时调用它

private Q_SLOTS:
  void updateStyle();

添加一个新属性来存储属性本身: std::unique_ptr<rviz_common::properties::ColorProperty> color_property_;

 Cpp 更新 

  • #include <rviz_common/properties/parse_color.hpp> - 包含将属性转换为 OGRE 颜色的辅助函数。

  • 为了我们的 onInitialize ,我们添加

color_property_ = std::make_unique<rviz_common::properties::ColorProperty>(
    "Point Color", QColor(36, 64, 142), "Color to draw the point.", this, SLOT(updateStyle()));
updateStyle();
  • 这将构建具有其名称、默认值、描述和回调的对象。

  • 我们直接调用 updateStyle ,以便在属性更改之前就设置颜色。

  • 然后我们定义回调函数。

void PointDisplay::updateStyle()
{
  Ogre::ColourValue color = rviz_common::properties::qtToOgre(color_property_->getColor());
  point_shape_->setColor(color);
}

结果应如下所示: 

60a2f9b3c1e954d248a007121b8cb539.png

 哦,粉红色的!

8312e0bf633914ccbe464dedafb399a4.png

状态报告

您可以查看此步骤的完整版本,分支名称为 step4 。

您还可以设置显示状态。作为一个任意示例,让我们在 x 坐标为负时显示警告,为什么不呢?在 processMessage :

if (msg->x < 0) {
  setStatus(StatusProperty::Warn, "Message",
      "I will complain about points with negative x values.");
} else {
  setStatus(StatusProperty::Ok, "Message", "OK");
}
  • 我们假设之前的 using rviz_common::properties::StatusProperty; 声明。

  • 将状态视为键/值对,其中键是某个字符串(这里我们使用 "Message" ),值是状态级别(错误/警告/正常)和描述(其他一些字符串)。

37579be5ff0295c646ef52c84ca06c52.png

ad9cc4b85ba7a2f79f1d06338bf1223a.png

清理

现在是时候清理一下了。这使得事情看起来更好看,也更容易使用,但不是严格要求的。您可以查看此步骤的完整版本,分支名称为 step5 。

首先,我们更新插件声明。

<library path="point_display">
  <class name="Point2D" type="rviz_plugin_tutorial::PointDisplay" base_class_type="rviz_common::Display">
    <description>Tutorial to display a point</description>
    <message_type>rviz_plugin_tutorial_msgs/msg/Point2D</message_type>
  </class>
</library>
  • 我们将 name 字段添加到 class 标签。这会更改在 RViz 中显示的名称。在代码中,将其称为 PointDisplay 是有意义的,但在 RViz 中,我们希望简化。

  • 我们将实际文本放入描述中。不要偷懒。

  • 通过在此处声明特定的消息类型,当您尝试按主题添加显示时,它将为该类型的主题建议此插件。

我们还在 icons/classes/Point2D.png 添加了插件的图标。文件夹是硬编码的,文件名应与插件声明中的名称(或未指定时的类名)匹配。[图标来源]

我们需要在 CMake 中安装镜像文件。

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

现在,当您添加显示时,它应该显示一个图标和描述。

c02c2a136592abf0c3fbf13756700ecf.png

以下是按主题添加时的显示:

ac9b694be5a7db6fd69ac324832dbaa5.png

a00d4828a1cd72b7bfd3f605d444af89.png

最后,这是标准界面中的图标:

adc5b2f3cd1bd8f1ceb77223aa1977ad.png

注意,如果更改插件名称,先前的 RViz 配置将不再有效。

附录: 

point_display.hpp

// 防止头文件被重复包含
#ifndef RVIZ_PLUGIN_TUTORIAL__POINT_DISPLAY_HPP_
#define RVIZ_PLUGIN_TUTORIAL__POINT_DISPLAY_HPP_


// 包含所需的头文件
#include <memory>
#include <rviz_common/message_filter_display.hpp>
#include <rviz_common/properties/color_property.hpp>
#include <rviz_plugin_tutorial_msgs/msg/point2_d.hpp>
#include <rviz_rendering/objects/shape.hpp>


// 定义rviz_plugin_tutorial命名空间
namespace rviz_plugin_tutorial
{
// 定义PointDisplay类,继承自MessageFilterDisplay
class PointDisplay
  : public rviz_common::MessageFilterDisplay<rviz_plugin_tutorial_msgs::msg::Point2D>
{
  Q_OBJECT  // 使用Qt的信号和槽机制


private Q_SLOTS:
  // 定义私有槽函数updateStyle
  void updateStyle();


protected:
  // 重写onInitialize函数
  void onInitialize() override;


  // 重写processMessage函数,处理接收到的消息
  void processMessage(const rviz_plugin_tutorial_msgs::msg::Point2D::ConstSharedPtr msg) override;


  // 定义unique_ptr类型的point_shape_和color_property_成员变量
  std::unique_ptr<rviz_rendering::Shape> point_shape_;
  std::unique_ptr<rviz_common::properties::ColorProperty> color_property_;
};
}  // 结束rviz_plugin_tutorial命名空间的定义


// 结束防止头文件被重复包含的预处理器指令
#endif  // RVIZ_PLUGIN_TUTORIAL__POINT_DISPLAY_HPP_

point_display.cpp

// 包含所需的头文件
#include <rviz_plugin_tutorial/point_display.hpp>
#include <rviz_common/properties/parse_color.hpp>
#include <rviz_common/logging.hpp>


// 定义rviz_plugin_tutorial命名空间
namespace rviz_plugin_tutorial
{
// 使用rviz_common::properties::StatusProperty
using rviz_common::properties::StatusProperty;


// 定义PointDisplay类的onInitialize函数
void PointDisplay::onInitialize()
{
  // 调用父类的onInitialize函数
  MFDClass::onInitialize();
  // 创建一个新的Shape对象,类型为Cube
  point_shape_ =
    std::make_unique<rviz_rendering::Shape>(rviz_rendering::Shape::Type::Cube, scene_manager_,
      scene_node_);


  // 创建一个新的ColorProperty对象,用于设置点的颜色
  color_property_ = std::make_unique<rviz_common::properties::ColorProperty>(
      "Point Color", QColor(36, 64, 142), "Color to draw the point.", this, SLOT(updateStyle()));
  // 更新样式
  updateStyle();
}


// 定义PointDisplay类的processMessage函数,处理接收到的消息
void PointDisplay::processMessage(const rviz_plugin_tutorial_msgs::msg::Point2D::ConstSharedPtr msg)
{
  // 打印接收到的消息的帧ID
  RVIZ_COMMON_LOG_INFO_STREAM("We got a message with frame " << msg->header.frame_id);


  // 定义位置和方向变量
  Ogre::Vector3 position;
  Ogre::Quaternion orientation;
  // 获取消息的转换
  if (!context_->getFrameManager()->getTransform(msg->header, position, orientation)) {
    // 如果获取转换失败,打印错误信息
    RVIZ_COMMON_LOG_DEBUG_STREAM("Error transforming from frame '" << msg->header.frame_id <<
        "' to frame '" << qPrintable(fixed_frame_) << "'");
  }


  // 设置场景节点的位置和方向
  scene_node_->setPosition(position);
  scene_node_->setOrientation(orientation);


  // 如果x值小于0,设置状态为警告
  if (msg->x < 0) {
    setStatus(StatusProperty::Warn, "Message",
        "I will complain about points with negative x values.");
  } else {
    // 否则,设置状态为OK
    setStatus(StatusProperty::Ok, "Message", "OK");
  }


  // 设置点的位置
  Ogre::Vector3 point_pos;
  point_pos.x = msg->x;
  point_pos.y = msg->y;
  point_shape_->setPosition(point_pos);
}


// 定义PointDisplay类的updateStyle函数,更新样式
void PointDisplay::updateStyle()
{
  // 获取颜色属性的颜色值,并设置到点的形状上
  Ogre::ColourValue color = rviz_common::properties::qtToOgre(color_property_->getColor());
  point_shape_->setColor(color);
}


}  // 结束rviz_plugin_tutorial命名空间的定义


// 包含pluginlib的宏定义
#include <pluginlib/class_list_macros.hpp>
// 导出PointDisplay类,使其可以作为rviz_common::Display的插件
PLUGINLIB_EXPORT_CLASS(rviz_plugin_tutorial::PointDisplay, rviz_common::Display)

CMakeLists.txt

// 设置CMake的最低版本要求为3.5
cmake_minimum_required(VERSION 3.5)
// 定义项目名称为rviz_plugin_tutorial
project(rviz_plugin_tutorial)


// 默认使用C99标准
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()


// 默认使用C++17标准
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 17)
endif()


// 如果编译器是GNU或Clang,添加编译选项
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()


// 寻找必要的包
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_ros REQUIRED)
find_package(pluginlib REQUIRED)
find_package(rviz_common REQUIRED)
find_package(rviz_plugin_tutorial_msgs REQUIRED)
find_package(rviz_rendering REQUIRED)


// 开启CMake的自动MOC功能
set(CMAKE_AUTOMOC ON)
// 使用Qt的MOC预处理一些头文件
qt5_wrap_cpp(MOC_FILES
  include/rviz_plugin_tutorial/point_display.hpp
)


// 添加一个库
add_library(point_display src/point_display.cpp ${MOC_FILES})
// 设置库的包含目录
target_include_directories(point_display PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>
)
// 设置库的依赖
ament_target_dependencies(point_display
  pluginlib
  rviz_common
  rviz_plugin_tutorial_msgs
  rviz_rendering
)
// 安装库
install(TARGETS point_display
        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}
)
// 安装图标文件
install(FILES icons/classes/Point2D.png
        DESTINATION share/${PROJECT_NAME}/icons/classes
)
// 导出包含目录
ament_export_include_directories(include)
// 导出目标
ament_export_targets(export_rviz_plugin_tutorial)
// 导出插件描述文件
pluginlib_export_plugin_description_file(rviz_common rviz_common_plugins.xml)


// 如果开启了测试,寻找测试依赖
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  ament_lint_auto_find_test_dependencies()
endif()


// 打包
ament_package()

package.xml

<!-- 设置XML版本为1.0 -->
<?xml version="1.0"?>
<!-- 设置XML模式 -->
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<!-- 定义包的格式为3 -->
<package format="3">
  <!-- 设置包的名称为rviz_plugin_tutorial -->
  <name>rviz_plugin_tutorial</name>
  <!-- 设置包的版本为0.0.0 -->
  <version>0.0.0</version>
  <!-- 设置包的描述 -->
  <description>A tutorial how to create a new plugin for rviz</description>
  <!-- 设置包的维护者 -->
  <maintainer email="davidvlu@gmail.com">David V. Lu!!</maintainer>
  <!-- 设置包的许可证为BSD -->
  <license>BSD</license>


  <!-- 设置构建工具依赖为ament_cmake -->
  <buildtool_depend>ament_cmake</buildtool_depend>
  <!-- 设置包的依赖 -->
  <depend>pluginlib</depend>
  <depend>rviz_common</depend>
  <depend>rviz_plugin_tutorial_msgs</depend>
  <depend>rviz_rendering</depend>


  <!-- 设置测试依赖 -->
  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>


  <!-- 导出构建类型 -->
  <export>
    <build_type>ament_cmake</build_type>
  </export>
<!-- 结束包的定义 -->
</package>

rviz_common_plugins.xml

<!-- 定义库的路径为point_display -->
<library path="point_display">
  <!-- 定义一个类,名称为Point2D,类型为rviz_plugin_tutorial::PointDisplay,基类类型为rviz_common::Display -->
  <class name="Point2D" type="rviz_plugin_tutorial::PointDisplay" base_class_type="rviz_common::Display">
    <!-- 设置类的描述 -->
    <description>Tutorial to display a point</description>
    <!-- 设置类的消息类型 -->
    <message_type>rviz_plugin_tutorial_msgs/msg/Point2D</message_type>
  <!-- 结束类的定义 -->
  </class>
<!-- 结束库的定义 -->
</library>
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个关于ROS Noetic软件包依赖关系的问题。其中,下列软件包的依赖关系尚不足够满足要求,无法安装: ros-noetic-desktop-full: 依赖于 ros-noetic-desktop,但它不会被安装。 依赖于 ros-noetic-perception,但它不会被安装。 依赖于 ros-noetic-simulators,但它不会被安装。 依赖于 ros-noetic-urdf-sim-tu,但它不会被安装。 ### 回答2: 这个错误提示是说明在安装 ros-noetic-desktop-full 软件包时,发现它需要依赖一些其他的软件包,但是这些软件包未被安装。其中,ros-noetic-desktop、ros-noetic-perception、ros-noetic-simulators 和 ros-noetic-urdf-sim-tu 是四个未满足依赖关系的软件包。 这个错误提示一般是由于软件源的问题所导致的。在安装软件包时,系统会从软件源中查找该软件包以及它所需的依赖关系。如果软件源中不存在某个软件包的依赖关系,则会提示这个错误信息。 要解决这个问题,可以尝试以下几个方法: 1. 更新软件源:可通过修改软件源配置文件或使用软件源管理工具来更新软件源。更新后再次尝试安装软件包,看是否能够解决依赖关系问题。 2. 手动安装依赖关系:如果更新软件源后仍然无法解决依赖关系问题,可以尝试手动安装依赖关系。按照依赖关系的提示,逐个安装这四个软件包。安装完成后再次尝试安装 ros-noetic-desktop-full 软件包,看是否能够正常安装。 3. 使用 aptitude 命令安装:aptitude 命令可以自动处理依赖关系,可能会更好地解决这个问题。可以通过运行以下命令安装 ros-noetic-desktop-full 软件包: sudo aptitude install ros-noetic-desktop-full 以上是我的回答,希望能对你有所帮助。如果你还有其他问题,请随时回复。 ### 回答3: 这个问题意味着在安装 ros-noetic-desktop-full 软件包时,计算机无法满足所有需要的依赖关系。这些依赖关系包括 ros-noetic-desktop、ros-noetic-perception、ros-noetic-simulators 和 ros-noetic-urdf-sim-tu。 在解决这个问题之前,我们需要了解什么是依赖关系。在软件工程中,依赖关系指的是一个软件包需要另一个软件包才能正常运行的情况。例如,在 ROS 中,ros-noetic-desktop-full 需要依赖其他的软件包才能提供完整的功能。 为了解决这个问题,我们可以使用以下方法: 1. 更新软件包源列表。我们可以更新软件包源列表,这有助于计算机查找所需的软件包。在 Ubuntu 系统中,我们可以使用以下命令更新软件包源列表:sudo apt-get update。 2. 安装依赖关系。我们可以尝试单独安装缺失的依赖关系。在 ROS 中,我们可以使用以下命令安装缺失的软件包:sudo apt-get install ros-noetic-desktop ros-noetic-perception ros-noetic-simulators ros-noetic-urdf-sim-tu。 3. 检查软件包仓库。某些情况下,软件包源可能已经过时或不再受支持。我们可以检查软件包仓库,查看软件包是否可用。在 Ubuntu 系统中,我们可以使用以下命令查看软件包仓库:apt-cache search ros-noetic-desktop-full。 总之,无法满足依赖关系的问题是常见的,在解决这个问题之前,我们需要了解依赖关系的概念,并掌握一些解决方法。在 ROS 中,我们可以使用更新软件包源列表、安装依赖关系和检查软件包仓库等方法解决问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值