ROS入门21讲笔记(总结自用)

简述

一周看完了古月老师的ROS入门21讲,理清了ROS工作空间和各个功能包是如何相互通讯的,还有如何自建功能包编写内部的代码等。本文跳过Linux命令行、ROS安装等环境搭建问题,重点记录如何写ROS的程序。

创建工作空间和功能包

在home中创建项目文件夹,在项目文件夹中再创建src文件夹,随后进入src文件夹中进行工作空间的初始化,和功能包的创建,最后到工作空间文件夹中进行编译,最后还需要设置环境变量。具体的命令行代码如下:

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
catkin_pkg_create pkg_name depend1 depend2  ....
cd ..
catkin_make
source ~/catkin_ws/devel/setup.bash
echo "source /WORKSPACE/devel/setup.bash">>~/.bashrc //添加到系统环境变量中

后续的所有代码编写都会在此工作空间下和功能包内进行。

话题(Topic)

话题是发布者Publisher和订阅者Subscriber相互之间传输消息Messages的通道,是一种单向的通讯,一个话题可以包括多个msg对象,需要掌握消息,发布者和订阅者的编写方法。可以用C++或者Python编写发布者和订阅者,C++需要在CMakelist文件中进行修改,使得编译成功;而Python无需编译但需要改执行权限。以下代码使用C++编写,下面做整体的总结。

消息(Messages)

在功能包内创建msg文件夹,并在其中创建.msg文件,文件名即对象名,如asoul.msg即创建asoul这个新对象。 随后需要在功能包内的CMakelist.txt和package.xml文件中配置依赖和编译规则。

每个消息都是一种严格的数据结构,支持标准数据类型(整型、浮点型、布尔型等),也支持嵌套结构和数组(类似于C语言的结构体struct),还可以根据需求由开发者自主定义。——《ROS机器人开发实践》

MESSAGE.msg

string name
uint8 age
………… 

CMakelist.txt

find_package(..... message_generation)

add_message_files(FILES MESSAGE.msg)
generate_messages(DEPENDENCIES std_msgs)

catkin_package(... message_runtime)

package.xml

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

发布者(Publisher)

需要在功能包的src文件下创建.cpp文件,文件名最好与发布名相同,以便后续的使用。不同于message,发布者仅需要对CMakelist.txt文件进行配置。
PUBLISHER.cpp

#include <ros/ros.h>
#include <package_name/msgs_name.h>  //发布某个功能包下的某个消息

int main(int argc,char *argv){
	// 节点初始化
	ros::init(argc,argv,"node_name");
	//创建节点句柄
	ros::NodeHandle n;
	//创建Publisher对象,发布名为/topic_name的topic,此处topic可以选择已存在的topic,消息类型是msgs定义的类,队列长度为10
	ros::Publisher publisher_name = n.advertise<msgs的类型>("/topic_name",10);
	//设置循环频率,10ms
	ros::Rate loop_rate(10);
	while(ros::ok()){
		//初始化msgs下类型的消息,及新建msg对象并给对象变量赋值
		msgs::asoul  idol;
		idol.name = "";
		idol.age = "";
		//发布消息
		publisher_name.publish(idol);
		ROS_INFO()	//日志内容
		//按照循环频率延时
		loop_rate.sleep();
	}
	return 0;
}

CMakelist.txt

add_executable(PUBLISHER src/PUBLISHER.cpp)
target_link_libraries(PUBLISHER ${catkin_LIBRARIES})

订阅者(Subscriber)

与发布者相同,需要在功能包的src文件夹下创建.cpp文件,也需要对CMakelist.txt文件进行配置。
SUBSCRIBER.cpp

#include <ros/ros.h>
#include <package_name/msgs_name.h>	//订阅某个功能包下的某个消息

// 接收到订阅的消息类型后,会进入回调函数
void callback(const package::msg_name::ConstPtr& msg){
	//回调函数下的任务不可以过于复杂,否则会在处理数据的时候丢失发布者的实时数据
	ROS_INFO();
}

int main(int argc,char *argv){
	// 节点初始化
	ros::init(argc,argv,"node_name");
	//创建节点句柄
	ros::NodeHandle n;
	//创建Subscriber对象,订阅/topic_name的topic,注册回调函数
	ros::Subscriber subscriber_name = n.subscribe("/topic_name",10,callback);
	//循环等待回调函数
	ros::spin();
	return 0;
}

CMakelist.txt

add_executable(SUBSCRIBER src/SUBSCRIBER.cpp)
target_link_libraries(SUBSCRIBER ${catkin_LIBRARIES})

服务(Service)

服务不同于话题,是一种同步通信的方式,允许客户端(Client)向服务端发送请求(Request),同时服务端(Server)处理后反馈应答(Response)。与话题相似也需要掌握服务数据,客户端和服务端的编写方法,编写完成也需要进行文件配置。

服务数据(srv)

在功能包内创建srv文件夹,并在其中创建.srv文件,srv文件包括请求和应答两个数据域,数据类型与消息msg类似,但需要使用---进行数据域的分割。同样也需要在功能包内的package.xml和CMakelist.txt文件中配置依赖和编译规则。
Test.srv

int64 id
...............	//客户端发送请求的数据类型
---
String name
...............	//服务端发送应答的数据类型

CMakelist.txt

find_package(..... message_generation)

add_message_files(FILES Test.srv)
generate_messages(DEPENDENCIES std_msgs)

catkin_package(... message_runtime)

package.xml

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

客户端

同样需要在功能包下的src文件夹内创建.cpp文件,
CLIENT.cpp

#include <ros/ros.h>
#include <package_name/srv_name.h>  //发布某个功能包下的某个服务数据

int main(int argc, char** argv){
	//初始化ROS节点
	ros::init(argc, argv, "Node_NAME");
	//创建节点句柄
	ros::NodeHandle n;
	//等待SERVER服务端启动,创建客户端并连接
	ros::service::waitForService("/SERVER_NAME");
	ros::ServiceClient client = n.serviceClient<package_name::srv_name>("/SERVER_NAME");
	
	//初始化请求数据,即给srv请求数据赋值
	package_name::srv_name srv;
	srv.request.a = 2;
	.....
	
	//请求服务调用	call	waitForService	spin均为阻塞型函数,若没有反馈后续代码不会运行
	client.call(srv);

	//如果得到服务器的反馈,则可以运行查看是否调用成功srv.response
	ROS_INFO("%s",srv.response.name.c_str());
	
	return 0;
}

CMakelist.txt

add_executable(CLIENT src/CLIENT.cpp)
target_link_libraries(CLIENT ${catkin_LIBRARIES})

服务端

古月老师在21讲的服务端加入了发布者,来实现小乌龟的绕圈运动。服务和话题可以同时写入一个文件中使用,从而实现我们想要的功能。由于是总结性笔记,此处仅仅编写如何接受客户端发送的请求,和如何将回应发出,具体代码如下。
SERVER.cpp

#include <ros/ros.h>
#include <package_name/srv_name.h>  //发布某个功能包下的某个服务数据

//回调函数,输入参数req,输出参数res
void callback(package_name::srv_name::Request &req, package_name::srv_name::Response &res){
	//与订阅者的回调函数相似,区别在于有两个数据进入函数,可以获取请求数据进行查询或计算,还可以设置反馈数据
	ROS_INFO("%d", req.id);
	res.name = "JR";
	.......
}

int main(int argc, char **argv){
	//ROS节点初始化
	ros::init(argc, argv, "Node_Name");
	//创建节点句柄
	ros::NodeHandle n;
	//创建名为/Server_name的server,注册回调函数
	ros::ServiceServer server = n.advertiseService("/Server_name", callback);
	//循环等待回调函数
	ROS_INFO("");
	ros::spin();   
	//如果在while循环内查询队列,则需要使用ros::spinOnce()方法,使得while正常运行
	return 0;
}

CMakelist.txt

add_executable(SERVER src/SERVER.cpp)
target_link_libraries(SERVER ${catkin_LIBRARIES})

坐标管理系统(TF)

在机器人设计和机器人应用过程中,都会涉及到不同组件的位置和姿态,这就需要引入坐标系以及坐标变换的概念。TF功能包是一个让用户随时间跟踪多个坐标系的功能包,使用树形数据结构,根据时间缓冲并维护多个坐标系之间的坐标变换关系,可以帮助我们在任意时间完成坐标系之间的点、向量等坐标变换。而使用TF功能包需要编写广播(Broadcaster)和监听(Listener)两个部分。

广播(Broadcaster)

古月老师使用了小乌龟示例,更容易理解,此处仅编写如何将坐标系和坐标变换广播出去。同样的需要在功能包的src下建立.cpp文件,对功能包内的CMakelist.txt配置编译规则。
Broadcaster.cpp

#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <package_name/pose_msg.h>	//此处需要添加需要广播的坐标数据

//回调函数中的msg可能包含x,y,z和r,p,y,存在则用msg->赋值
void poseCallback(const package_name::pose_msgConstPtr& msg){
	//创建tf的广播器
	static tf::TransformBroadcaster br;

	//初始化tf数据,即创建坐标变换值
	tf::Transform transform;
	transform.setOrigin(tf::Vector3(msg->x,msg->y,msg->z));	//设置坐标变换平移
	tf::Quaternion q;	//此处采用RPY设置坐标变换旋转,即xyz三轴旋转的方式,还可以直接使用四元数
	q.setRPY(roll, pitch, yaw);
	transform.setRotation(q)	//transform.setRotation(tf::Quaternion( , , , ));四元数方法
	//广播两个坐标系之间的tf数据
	br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "frame1", "frame2"));
}

int main(int argc, char** argv){
	//初始化ROS节点
	ros::init(argc, argv, "node_name");
	//创建节点句柄
	ros::NodeHandle n;
	//订阅存有坐标信息的位姿话题
	ros::Subscriber sub = n.subscribe("",10.&poseCallback);
	//循环等待回调函数
	ros::spin();
	
	return 0;
}

CMakelist.txt

add_executable(Broadcaster src/Broadcaster.cpp)
target_link_libraries(Broadcaster ${catkin_LIBRARIES})

监听(Listener)

监听器可以用于监听和计算tf数据,在此同样仅仅写出监听相关的代码,不对具体的应用做拓展。
Listener.cpp

#include <ros/ros.h>
#include <tf/transform_listener.h>

int main(int argc, char **argv){
	//初始化ROS节点
	ros::init(argc, argv, "node_name");
	//创建节点句柄
	ros::NodeHandle n;
	//获取frame1和frame2之间的坐标变换关系
	tf::TransformListener listener;
	while(ros.ok()){
		tf::StampedTransform transform;
		try{
			listener.waitForTransform("/frame1", "/frame2", ros::Time(0), ros::Duration(3.0));	//
			listener.lookupTransform("/frame1", "/frame2", ros::Time(0),transform);
		}catch{
			ROS_ERROR("%s",ex.what());
			ros::Duration(1.0).sleep();
			continue;
		}
		//使用transform中的数据进行运算
		float angular = atan2(transform.getOrigin().x(),transform.getOrigin().y());
		float linear = sqrt(pow(transform.getOrigin().x(),2))+
											pow(transform.getOrigin().y(),2));
		ROS_INFO(angular, linear);
	}

	return 0;
}

CMakelist.txt

add_executable(Listener src/Listener.cpp)
target_link_libraries(Listener ${catkin_LIBRARIES})

启动文件(.launch)的使用

启动文件可以同时启动多个节点并自动启动ROS Master节点管理器,并且可以实现每个节点的配置,为多节点的操作提供很大的便利性。在此写一个示例,简单的列举下基本元素如node、launch、param、arg等。每个元素内部的属性作用需要参考古月老师的《ROS机器人开发实践》或是参考launch文件解析
Test.launch

<launch>
	
	<---param为全局字典中的参数,arg为launch文件下使用的参数--->
	<param name="param_name"	value=""/>
	<arg name="argument_name" value="">
	
	<node pkg="package_name" type="node_file_name" name="node_name">
		<param name="argTest" value="&(arg argument_name)"/>
		<rosparam file="FILE_PATH/test.yaml" command=load/>
	</node>
	
	<node pkg="package_name" type="node_file_name" name="node_name" args="argument" output="screen"/>
	
</launch>

结语

至此算是了解了ROS编程的一些皮毛,仍然需要进一步在应用中实践。上述并非21讲全部内容,Ubuntu和ROS的安装在古月老师的21讲中也有详细介绍,此外还有Qt、Rivz、Gezabo等可视化工具的应用。十分感谢古月老师的课程和书籍,让ROS的学习之路变得相对平坦。
怕什么真理无穷,进一步有进一步的欢喜。

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帕里亚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值