Skip to content

Latest commit

 

History

History
149 lines (125 loc) · 3.52 KB

File metadata and controls

149 lines (125 loc) · 3.52 KB

ROS1 to ROS2 Migration Guide

This document provides instructions for migrating packages from ROS1 to ROS2 and making them compatible with both versions using CMake's configure_file.

Migration Steps

  1. Update Package Dependencies:
    • Replace ROS1 dependencies with their ROS2 equivalents in package.xml and CMakeLists.txt.
  2. Update Code for ROS2 APIs:
    • Change node initialization, message/service definitions, and callback signatures to ROS2 style.
  3. Refactor Launch Files:
    • Convert ROS1 launch files (.launch) to ROS2 launch files (.py).
  4. Test in ROS2 Environment:
    • Build and run your package in ROS2 to ensure compatibility.

Dual Compatibility Using configure_file

You can use CMake's configure_file to generate version-specific headers or config files. This allows your code to adapt to ROS1 or ROS2 at build time.

Example: CMakeLists.txt

if(DEFINED ENV{ROS_VERSION} AND $ENV{ROS_VERSION} EQUAL 2)
  set(IS_ROS2 TRUE)
else()
  set(IS_ROS2 FALSE)
endif()

configure_file(
  ${CMAKE_CURRENT_SOURCE_DIR}/include/config.h.in
  ${CMAKE_CURRENT_BINARY_DIR}/include/config.h
)

```cpp
#include "config.h"

#if IS_ROS2
  // ROS2-specific code
#else
  // ROS1-specific code
#endif

Example: config.h.in

#define IS_ROS2 @IS_ROS2@

Example: Usage in C++

#include "config.h"
#if IS_ROS2
#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>
using StringMsg = std_msgs::msg::String;
using PublisherPtr = rclcpp::Publisher<StringMsg>::SharedPtr;
#else
#include <ros/ros.h>
#include <std_msgs/String.h>
using StringMsg = std_msgs::String;
using PublisherPtr = ros::Publisher;
#endif

#if IS_ROS2
class ExampleNode : public rclcpp::Node {
public:
  ExampleNode() : rclcpp::Node("example_node") {
    publisher_ = this->create_publisher<StringMsg>("chatter", 10);
    timer_ = this->create_wall_timer(
      std::chrono::seconds(1),
      std::bind(&ExampleNode::publish_message, this));
  }
private:
  void publish_message() {
    auto msg = StringMsg();
    msg.data = "Hello from ROS2";
    publisher_->publish(msg);
  }
  PublisherPtr publisher_;
  rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char **argv) {
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<ExampleNode>());
  rclcpp::shutdown();
  return 0;
}
#else
class ExampleNode {
public:
  ExampleNode() {
    ros::NodeHandle nh;
    publisher_ = nh.advertise<StringMsg>("chatter", 10);
    timer_ = nh.createTimer(ros::Duration(1.0), &ExampleNode::publish_message, this);
  }
  void spin() {
    ros::spin();
  }
private:
  void publish_message(const ros::TimerEvent&) {
    StringMsg msg;
    msg.data = "Hello from ROS1";
    publisher_.publish(msg);
  }
  PublisherPtr publisher_;
  ros::Timer timer_;
};

int main(int argc, char **argv) {
  ros::init(argc, argv, "example_node");
  ExampleNode node;
  node.spin();
  return 0;
}
#endif

Python Migration: Dual Compatibility

For Python packages, you can use try/except imports to support both ROS1 and ROS2 APIs in the same codebase.

Example: Python Node

try:
  import rclpy  # ROS2
  from rclpy.node import Node
  ROS_VERSION = 2
except ImportError:
  import rospy  # ROS1
  ROS_VERSION = 1

class MyNode(Node):
    def __init__(self):
      if ROS_VERSION == 2:
        super().__init__('my_node')
        self.get_logger().info('Running in ROS2')
      else:
        rospy.init_node('my_node')
        rospy.loginfo('Running in ROS1')


if __name__ == "__main__":
  node = MyNode()
  # Add your main loop or spin logic here