环境:ubuntu20.04 ,ros-foxy(ros2),vscode

背景:项目需要,一直折腾把ros1下面的包升级到ros2版本.以下纯属个人查找资料摸索,自我理解所得,有错误的地方,望大佬们不吝赐教.

1.消失的句柄

ros1里面有成员句柄,里面还分各种命名空间啥的,然后跟话题,参数息息相关,而在ros2里面就没有句柄成员了,个人感觉是可采用节点指针来取代其功能.

ros1句柄

ros::NodeHandle nh;

ros2可改写:

std::shared_ptr<rclcpp::Node> nh;
参考链接:

2.接口文件(msg/srv)编写规则

1)ros2接口(自定义消息)名称首字母必须大写;并且需要在packages.xml文件里面写:

<member_of_group>rosidl_interface_packages</member_of_group>

2)编译接口(自定义数据类型)包时,注意:

Header header			#ros1,but error in ros2
std_msgs/Header header	#ros2 witten

3)包名字,能有连续下划线,能在最后下划线;第一个字符必须是字母,后面字符可以是字母数字下划线,但保证不能下划线是最后.错误例子,如:

custom__msg
custom_msg_
2custom_msg

正确案例:

custom_msg

4)ros2中,自定义消息类型(接口)时,类型和变量名称只能一个空格隔开:

sensor_msgs/PointCloud2              border_area		#ros1 define style,but error in ros2

sensor_msgs/PointCloud2 border_area		#ros2 style

5)ros2中,自定义消息类型(接口)时,名称只能由小写字母单个下划线数字组成:

float32       WB_Angle_Feedback   #ros1,error in ros2
float32 wb_angle_feedback  	#ros2
float32 wb_angle_feedback_1  #ros2

6)ros2,自定义接口文件名称(注意和包名称区别),首字母一定要大写,字母之间不能有下划线,名称组成不含数字(不知道能不能使用数字),建议使用驼峰法来命名.正确样式如:

LidarObjectArray.msg
Num.msg
AddThreeInts.srv

3.创建发布者

ros1:
声明:

ros::Publisher objects_info_pub_;

构造函数里面创建publisher:

objects_info_pub_ = node_handle_.advertise<sensor_msgs::msg::PointCloud2>( "/objects_info_pub_topic", 10);

node_handle_句柄ros::handle node_handle_

ros2:
声明:

rclcpp::Publisher<sensor_msgs::msg::PointCloud2> objects_info_pub_;

构造函数里面创建publisher:

objects_info_pub_= this->create_publisher<sensor_msgs::msg::PointCloud2>("/objects_info_pub_topic", 10);

参考链接:https://blog.csdn.net/qq_45701501/article/details/118577437

4.创建订阅者

ros1:
声明:

ros::NodeHandle n;
ros::Subscriber objects_info_sub_;

在类构造函数里面创建subscriber,并启动回调函数:

//--------------------create_subscription-----------------------------
objects_info_sub_= n.subscribe("objects_info_pub_topic", 1, Callback);
//callback function
void Callback(const sensor_msgs::msg::PointCloud2::ConstPtr msg){
//do stuff
}

ros2:
声明:

rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr objects_info_sub_;

在类构造函数中创建subscriber,并启动回调函数:

//--------------------create_subscription-----------------------------
objects_info_sub_ = this->create_subscription<sensor_msgs::msg::PointCloud2>(
                    objects_info_pub_topic, 1, 
                    std::bind(&Callback, 
                              this, std::placeholders::_1));
//或者
objects_info_sub_ = this->create_subscription<sensor_msgs::msg::PointCloud2>(
                    objects_info_pub_topic, 1, 
                    [this](const sensor_msgs::msg::PointCloud2::ConstPtr msg)
    				{Callback(msg)};
//--------------------/create_subscription-----------------------------
//callback function
void Callback(const sensor_msgs::msg::PointCloud2::ConstPtr msg){
//do stuff
}

参考链接:https://blog.csdn.net/qq_45701501/article/details/118577437

5.spin()和shutdown()系列

ros1   		->   	ros2
ros::spin();              rclcpp::spin(node);//std::shared_ptr<rclcpp::Node> node
ros::spinOnce();	      rclcpp::spinOnce(node);//std::shared_ptr<rclcpp::Node> node
ros::shutdown();    	 rclcpp::shutdown();

spin()表示循环并监听回调函数;spinOnce()表示仅监听回调函数,并不循环

参考链接:

https://roboticsbackend.com/ros2-rclcpp-parameter-callback/
https://www.huaweicloud.com/articles/13536904.html

6.类

对于ros包类,下面是个人的基本思考采用的方式,仅供参考:
ros1:

class A
{
	....
};

ros2:

class A : public rclcpp:Node 
{
	....
};

为啥我这么考虑呢?个人理解,在ros1时,我们想打印点消息到终端,只要头文件包含了"ros/ros.h",直接使用 ROS_INFO("...")即可输出,但是ros2里面输出,并不单单是"rclcpp/rclcpp.h",而且需要有节点指针指向get_logger()函数才行输出的,如RCLCPP_INFO(this->get_logger(),"...");,为了方便使用this,所以直接继承rclcpp::Node类,来得方便.否则,目前我能想到的方法是全局构造一个节点指针来搞.

参考链接:

7.时间系列

ros1:
时间点声明:

ros::Time timestamp ;

使用例子:

ros::Time timestamp = ros::Time::now();

时间段声明:

定时器声明:

定时器回调:

ros2:
时间点声明:

rclcpp::Time timestamp ;

使用例子:

rclcpp::Time timestamp = np->now();//std::shared_ptr<rclcpp::Node> np

时间段声明:

定时器声明:

rclcpp::TimerBase::SharedPtr timer1;

定时器回调:

std::shared_ptr<rclcpp::Node> nh;
//创建定时器
rclcpp::TimerBase::SharedPtr timer1;
//定时器timer1初始化,250ms中断,回调函数timer1_callback
timer1 = nh->create_wall_timer(250ms,timer1_callback);

参考链接:

https://blog.csdn.net/yang434584531/article/details/115749810#commentBox

8.创建包以及编译指令

ros1:
创建包:

catkin_create_pkg <package_name>

编译包:

catkin_make

ros2:

创建包:

ros2 pkg create --build-type ament_cmake <package_name>

编译包:

colcon build

参考链接:https://blog.csdn.net/qq_45701501/article/details/118574569#t6

9.cmakelists.txt部分细节参考

ros2:
launch文件夹查找标签:

参考链接:

10.parameter参数系列

ros1:

ros::handle node_handle;
node_handle_.param("/listen_freq_param", listen_freq_, 1000);

ros2:

rclcpp::Node::SharedPtr nh;
nh->get_parameter_or("/listen_freq_param", listen_freq_, 1000);

这里有个小疑惑,参考材料说,"/listen_freq_param"这个参数,需要声明才能使用使用,形式如

this->declare_parameter<bool>("b_use_", true);
this->get_parameter_or<bool>("b_use_", b_use_, true);//无法获取时用默认值

参考链接:

https://blog.csdn.net/yang434584531/article/details/115749810#commentBox

11.创建服务器端

ros1:
ros2:
参考链接:

12.创建客户端

ros1:
ros2:
参考链接:

13.创建action

ros1:
ros2:
参考链接:

14.初始化init()

ros1:

ros::init(argc, argv, "listener");

ros2:

rclcpp::init(argc, argv);

ros1的话,初始化时候,随便给了节点名了,但在ros2里面,只是初始化,节点名在其他地方搞的,如:

 auto node = rclcpp::Node::make_shared("listener");

参考链接:

16.vscode的cpp开发时路径配置经验参考

1)编译器空间,尽量保证只放一个包,目地是防止路径污染导致疏忽错误;

2)路径添加,第一种是安装在系统上面的,如"/opt/ros/foxy/include/",第二种是自定义的,如自定义包位置,"/home/ylh/xxx_ws/install/custom_pkg_xxx/include",还有自己当前包的头文件引用,如"${workspaceFolder}/include"

3)路径添加方式,第一次加载包时,头文件会有很多下划波浪线报错,光标停留报错行,会出现 小灯泡 ,单击小灯泡,打开界面;此时会在编译器工作空间的目录,生成 .vscode文件夹,选择文件c_cpp_properties.json,添加上述路径,效果如:

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/home/ylh/xxx_ws/install/custom_pkg_xxx/include",
                "/opt/ros/foxy/include/",
                "${workspaceFolder}/include"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "gnu17",
            "cppStandard": "gnu++14",
            "intelliSenseMode": "linux-gcc-x64"
        }
    ],
    "version": 4
}

注意,自定义包的头文件路径,一般都是在所在工作空间编译生成的include文件夹下面的 包名/include

如果担心手写路径写错的话,可终端cd进去目标文件夹,pwd输出全路径,在复制过来即可.

17.打印终端输出

ros1版本:

 ROS_INFO_ONCE("Waiting for yqy...");

ros2版本:

RCLCPP_INFO_ONCE(nh->get_logger(),"Waiting for yqy...");

解析:nh为(rclcpp::Node)节点指针,如果是在rclcpp::Node子类里面,可使用this->get_logger().

其他输出类似,把ROS修改成RCLCPP,然后括号里面添加xx->get_logger(),,如:

 ROS_INFO  ->    RCLCPP_INFO
 ROS_DEBUG  ->        RCLCPP_DEBUG 
 ......

18.头文件引用自定义接口

ros1版本:

 #include <std_msgs/Int16MultiArray.h> //ros1写法

ros2版本:

#include <std_msgs/msg/int16_multi_array.h>//ros2写法,增加了详细路径msg以及将大写改为小写,
											//并添加以大写字母为界添加下划线

19.回调函数的形参

//ros1:
void callback(const std_msgs::String::ConstPtr & msg);

//ros2:
void callback(std_msgs::msg::String::ConstSharedPtr msg);

注意形式参数写法的变化(ros1写法可能还在支持,但是推荐使用ros2方式编写)

其他

奈何笔记过于凌乱,后续继续更新上面内容....

原文链接:https://blog.csdn.net/qq_45701501/article/details/120143845