This document provides instructions for migrating packages from ROS1 to ROS2 and making them compatible with both versions using CMake's configure_file.
- Update Package Dependencies:
- Replace ROS1 dependencies with their ROS2 equivalents in
package.xmlandCMakeLists.txt.
- Replace ROS1 dependencies with their ROS2 equivalents in
- Update Code for ROS2 APIs:
- Change node initialization, message/service definitions, and callback signatures to ROS2 style.
- Refactor Launch Files:
- Convert ROS1 launch files (
.launch) to ROS2 launch files (.py).
- Convert ROS1 launch files (
- Test in ROS2 Environment:
- Build and run your package in ROS2 to ensure compatibility.
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.
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#define IS_ROS2 @IS_ROS2@#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;
}
#endifFor Python packages, you can use try/except imports to support both ROS1 and ROS2 APIs in the same codebase.
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