【scene_manager_msgs】ROS2 自定义消息、服务的包

时间:2024-10-17 08:47:34

scene_manager_msgs
在ROS 1向ROS 2迁移的过程中,有些依赖项发生了变化,这是因为ROS 2的通信框架和工具链与ROS 1不同,尤其在消息、服务和动作生成方面有了一些新的方法和库。

动作库

如果你的ROS 1包依赖于actionlibactionlib_msgs,在迁移到ROS 2时,需要使用ROS 2的原生动作机制来代替。这是因为在ROS 2中,动作库(actionlib)被重新实现,集成进了ROS 2核心的通信框架中,不再需要像ROS 1那样单独依赖actionlib_msgsactionlib。以下是详细的替换和迁移步骤:

1. ROS 1中的actionlibactionlib_msgs

在ROS 1中,actionlib用于处理复杂的长时间操作,特别是那些需要反馈和预先终止的操作。actionlib_msgs包含了动作通信的标准消息类型,如:

  • Goal:客户端发送到服务器的目标。
  • Feedback:服务器定期发送给客户端的反馈。
  • Result:服务器完成任务后发送的结果。

2. ROS 2中的原生动作机制

在ROS 2中,动作接口被直接集成进了ROS 2通信架构中,通过rclcpp_action(C++)或rclpy_action(Python)来处理。动作在ROS 2中作为一种独立的接口,与消息和服务一起被支持。ROS 2的动作机制改进了性能、简化了开发流程,并且通过rosidl接口定义语言来生成动作接口。

主要区别:
  • 内置支持:在ROS 2中,动作系统是核心架构的一部分,直接使用与消息和服务相同的rosidl工具链。
  • 生成方式:动作文件(.action)可以直接与消息和服务一起通过rosidl_generate_interfaces生成。

3. 迁移步骤

(1) 定义动作文件(.action

在ROS 1中,你可能有一个.action文件。迁移到ROS 2时,这些文件不需要改变语法,但是要确保它们遵循ROS 2的生成规则。一个标准的.action文件如下:

# 定义目标(Goal)
int32 goal_value
---
# 定义结果(Result)
int32 result_value
---
# 定义反馈(Feedback)
float32 feedback_value
(2) 在CMakeLists.txt中生成动作接口

在ROS 2中,你需要使用rosidl_generate_interfaces来生成动作、消息和服务的接口。将以下内容添加到CMakeLists.txt中:

find_package(rosidl_default_generators REQUIRED)

set(ACTION_FILES
  "action/MyAction.action"
)

rosidl_generate_interfaces(${PROJECT_NAME}
  ${ACTION_FILES}
  DEPENDENCIES std_msgs
)

ACTION_FILES变量中列出了你的.action文件。rosidl_generate_interfaces会生成C++和Python代码,供你的动作服务器和客户端使用。

(3) 修改代码以使用ROS 2的动作API

在ROS 1中,你可能用过actionlib::SimpleActionClientactionlib::SimpleActionServer。在ROS 2中,需要改用rclcpp_action::Clientrclcpp_action::Server(C++),或者用rclpy.action.ActionClientrclpy.action.ActionServer(Python)。

C++动作客户端(ROS 2)示例:

#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "my_package/action/my_action.hpp"

class MyActionClient : public rclcpp::Node {
public:
  using MyAction = my_package::action::MyAction;
  using GoalHandleMyAction = rclcpp_action::ClientGoalHandle<MyAction>;

  MyActionClient() : Node("my_action_client") {
    this->client_ = rclcpp_action::create_client<MyAction>(this, "my_action");

    // 检查服务器是否可用
    if (!this->client_->wait_for_action_server(std::chrono::seconds(10))) {
      RCLCPP_ERROR(this->get_logger(), "Action server not available after waiting");
      return;
    }

    // 发送目标
    auto goal_msg = MyAction::Goal();
    goal_msg.goal_value = 42;

    auto goal_options = rclcpp_action::Client<MyAction>::SendGoalOptions();
    goal_options.result_callback = std::bind(&MyActionClient::result_callback, this, std::placeholders::_1);
    this->client_->async_send_goal(goal_msg, goal_options);
  }

private:
  rclcpp_action::Client<MyAction>::SharedPtr client_;

  void result_callback(const GoalHandleMyAction::WrappedResult & result) {
    RCLCPP_INFO(this->get_logger(), "Result received: %d", result.result->result_value);
  }
};

int main(int argc, char ** argv) {
  rclcpp::init(argc, argv);
  auto node = std::make_shared<MyActionClient>();
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}

C++动作服务器(ROS 2)示例:

#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "my_package/action/my_action.hpp"

class MyActionServer : public rclcpp::Node {
public:
  using MyAction = my_package::action::MyAction;
  using GoalHandleMyAction = rclcpp_action::ServerGoalHandle<MyAction>;

  MyActionServer() : Node("my_action_server") {
    this->server_ = rclcpp_action::create_server<MyAction>(
      this,
      "my_action",
      std::bind(&MyActionServer::handle_goal, this, std::placeholders::_1, std::placeholders::_2),
      std::bind(&MyActionServer::handle_cancel, this, std::placeholders::_1),
      std::bind(&MyActionServer::handle_accepted, this, std::placeholders::_1)
    );
  }

private:
  rclcpp_action::Server<MyAction>::SharedPtr server_;

  rclcpp_action::GoalResponse handle_goal(
    const rclcpp_action::GoalUUID & uuid,
    std::shared_ptr<const MyAction::Goal> goal) {
    RCLCPP_INFO(this->get_logger(), "Received goal request with value %d", goal->goal_value);
    return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
  }

  rclcpp_action::CancelResponse handle_cancel(
    const std::shared_ptr<GoalHandleMyAction> goal_handle) {
    RCLCPP_INFO(this->get_logger(), "Received request to cancel goal");
    return rclcpp_action::CancelResponse::ACCEPT;
  }

  void handle_accepted(const std::shared_ptr<GoalHandleMyAction> goal_handle) {
    std::thread{std::bind(&MyActionServer::execute, this, goal_handle)}.detach();
  }

  void execute(const std::shared_ptr<GoalHandleMyAction> goal_handle) {
    RCLCPP_INFO(this->get_logger(), "Executing goal");
    // 在这里执行动作,并发送反馈或结果
    auto result = std::make_shared<MyAction::Result>();
    result->result_value = 42;
    goal_handle->succeed(result);
  }
};

int main(int argc, char ** argv) {
  rclcpp::init(argc, argv);
  auto node = std::make_shared<MyActionServer>();
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}

4. 更新依赖项

package.xml中,确保你已经包含了ROS 2的相关依赖项,例如:

<depend>rclcpp_action</depend>
<depend>rosidl_default_generators</depend>

错误信息:

使用 colcon build 构建过程中遇到的错误提示表明了 scene_manager_msgs 这个 ROS 2 包的 package.xml 文件存在一个特定问题。错误信息指出你的包缺少一个必需的声明,这是定义自定义消息、服务的包所必需的。

Packages installing interfaces must include
'<member_of_group>rosidl_interface_packages</member_of_group>' in their
package.xml

告诉我们需要在 package.xml 中声明该包是 rosidl_interface_packages 组的一部分。因为在ROS 2中,负责定义消息、服务和动作接口的包必须向构建系统声明它们是接口包的一部分。这是为了让构建工具正确处理这些自定义接口,确保生成消息、服务和动作所需的代码。

如何修复

你需要在 package.xml 中添加一个 member_of_group 标签,将你的包包含在 rosidl_interface_packages 组中。这样可以告诉 ROS 构建工具你的包提供了需要处理的自定义接口。

  1. 打开你的 package.xml 文件。
  2. <package> 标签内添加以下行:
<member_of_group>rosidl_interface_packages</member_of_group>

这一行应该添加在* <package> 标签内部,但在任何特定依赖声明之外。

更新后的 package.xml 示例

在修改后,你的 package.xml 部分内容可能如下所示:

<?xml version="1.0"?>
<package format="3">
  <name>scene_manager_msgs</name>
  <version>0.0.1</version>
  <description>用于场景管理的消息和服务的包</description>
  <maintainer email="your-email@example.com">你的名字</maintainer>
  <license>BSD</license>

  <!-- 将此包包含在 rosidl_interface_packages 组中 -->
  <member_of_group>rosidl_interface_packages</member_of_group>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <depend>std_msgs</depend>
  <depend>geometry_msgs</depend>
  <depend>builtin_interfaces</depend>
  <depend>rosidl_default_generators</depend>
  <exec_depend>rosidl_default_runtime</exec_depend>
</package>

修改后的步骤

一旦你添加了这一行,应该:

  • 保存 package.xml 文件。
  • 重新运行构建命令:
colcon build --packages-select scene_manager_msgs

这应该会解决构建错误,允许 colcon 构建工具正确识别并处理你包的自定义接口。

详细解释:

  1. rosidl_interface_packages 组的作用
    在ROS 2中,rosidl(ROS Interface Definition Language)负责处理消息、服务和动作的定义和生成。所有涉及定义这些接口的包,都需要被归类为rosidl_interface_packages组的成员。通过将包声明为该组的一部分,ROS 2的构建系统可以识别该包包含自定义接口,并生成必要的消息、服务或动作文件。

  2. 生成自定义接口的流程
    当你在包中定义了消息(.msg)、服务(.srv)或动作(.action)文件时,构建工具会通过rosidl_generate_interfaces命令生成这些接口所需的C++或Python代码。然而,为了让构建系统知道你的包中包含这些接口文件,必须在package.xml中通过<member_of_group>标签将该包添加到rosidl_interface_packages组。

  3. 缺少声明的后果
    如果不将包加入到rosidl_interface_packages组,构建系统在处理这些消息和服务文件时将无法识别它们,进而无法生成必要的代码。这就会导致构建失败或在运行时无法找到相应的消息和服务接口。因此,缺少这个声明会导致类似于你之前遇到的错误。

包含消息和服务的包

从你提供的 CMakeLists.txt 文件来看,scene_manager_msgs 包定义了消息(msg/Layout.msg)和服务(如 srv/ModifyObject.srv 等),但并没有定义动作文件(.action 文件)。因此,这个包不是一个动作包,而是一个包含消息和服务的包。

动作包的定义:

  • 动作包(Action Package)通常会定义 .action 文件,这些文件用于定义长时间运行的任务,通常需要反馈机制。动作在ROS 2中可以使用 rclcpp_actionrclpy_action 库进行操作。

  • 如果这个包是一个动作包,你的 CMakeLists.txt 文件中应该有对 .action 文件的定义,比如:

    set(ACTION_FILES
      "action/MyAction.action"
    )
    
    rosidl_generate_interfaces(${PROJECT_NAME}
      ${MSG_FILES}
      ${SRV_FILES}
      ${ACTION_FILES}
      DEPENDENCIES std_msgs geometry_msgs builtin_interfaces
    )
    

当前的 CMakeLists.txt 说明:

  • 你定义了消息文件 msg/Layout.msg 和服务文件 srv/ModifyObject.srvsrv/SelectObjects.srvsrv/MoveTo.srv
  • 没有提到 .action 文件,因此该包没有动作接口。

总结:

  • 不是动作包:由于没有定义动作文件(.action),这个包目前是一个消息和服务的包,而不是动作包。
  • 如何添加动作:如果你希望将其扩展为一个动作包,你需要定义 .action 文件,并在 CMakeLists.txt 中增加动作生成的配置。

消息(.msg)和服务(.srv)文件本身通常不需要修改其定义结构,因为ROS 2仍然使用相同的格式来定义这些文件

在将ROS 1软件包迁移到ROS 2时,消息(.msg)和服务(.srv)文件本身通常不需要修改其定义结构,因为ROS 2仍然使用相同的格式来定义这些文件。然而,你可能需要进行一些更新以确保这些定义与ROS 2的接口兼容和一致。

需要考虑的修改点:

  1. 依赖检查

    • 确保消息和服务文件中使用的类型在ROS 2中是有效的。例如,如果使用了geometry_msgs/PoseStamped,确保ROS 2中包含此类型。
    • 检查是否所有依赖的包都已经被迁移到ROS 2并在package.xmlCMakeLists.txt中正确引用。
  2. 包和类型的更新

    • 在ROS 2中,某些消息类型可能已经更新或替换。尽管大部分标准消息类型如std_msgsgeometry_msgs在ROS 2中仍然可用,但一些特定类型或属性可能已经有所改变。
    • 确保使用的类型与ROS 2的命名和结构标准一致。
  3. 服务定义的格式

    • 服务文件在ROS 1和ROS 2中的定义方式保持一致,格式为请求部分,一个---分割符,然后是响应部分
    • 确认服务文件的请求和响应部分是否符合你的应用需求,并考虑是否需要进行更新以利用ROS 2提供的新特性或改进。