【ROS实践入门(六)消息msg和服务srv文件创建与使用】

【ROS实践学习入门系列(六)消息msg和服务srv文件创建与使用

本内容经官网实例以及一些参考书总结而成,欢迎留言评论交流~

联系方式:ziyuanw#foxmail.com(#换成@)

一、创建msg和srv文件

节点中创建msg和srv文件的常用功能为:用于说明传输数据的类型和数据值的文件,ROS会根据这些文件内容自动创建所需的代码(一般是在源程序中以*.h的头文件格式添加进去),以便于msg和srv文件能够被使用。

以一个求三个整数的加法的服务使用为例子:

1.创建一个空的msg文件夹。

①在上个博客中创建完成一个test功能包的两个节点基础上:在test功能包文件下创建msg目录,并在其中添加三行文本:

int32 A
int32 B
int32 C

 echo输出多行消息到自定义命名为test_msg1.msg的文件。(或者文本方式打开)效果如下:

  • 默认情况下msg和srv相关功能是被注释掉的,因此为了编译新的msg和srv文件,必须取消CMakeLists.txt和package.xml行中包含消息和服务的配置信息内容,并且告知ROS这些文件在哪,如何编译以及编译什么。

②修改test功能包下的CMakeLists.txt文件中msg部分:

  • 添加message_generation:
find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  message_generation
)
  • 找到generate_messages()取消“#”注释部分。
## Generate added messages and services with any dependencies listed here
 generate_messages(
   DEPENDENCIES
   std_msgs
 )
  • 找到:add_message_files()部分,取消注释并修改Message1.msg为test_msg1.msg(因为就一个msg文件,所以删除Message2.msg行即可。修改前后如下图:

③编辑test功能包下的package.xml文件。

取消注释:

<build_depend>message_generation</build_depend>

行和:

<exec_depend>message_runtime</exec_depend> -->

  • 注:在构建功能消息服务时需要"message_generation"即构建环境,在构建完执行时需要"message_runtime"即运行环境。

④回到~/catkin_ws编译检查msg文件是否正常输出:

cd ~/catkin_ws
catkin_make
  • (不要忘了更新一下配置环境)正常完成后输出如下和上面第一步添加的内容完全一致:
rosmsg show test/test_msg1

2.创建一个空的srv文件夹:

①在test功能包下创建一个名为srv的空文件夹,并准备往里面添加一个名为test_srv1.srv的文件:

②在修改完上面msg基础上同样修改CMakeLists.txt文件:

## Generate services in the 'srv' folder
add_service_files(
  FILES
  test_srv1.srv
 )



## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES test
 CATKIN_DEPENDS roscpp std_msgs message_runtime
#  DEPENDS system_lib
)



## Generate added messages and services with any dependencies listed here
 generate_messages(
   DEPENDENCIES
   std_msgs
 )


##Declare a catkin package
catkin_package(CATKIN_DEPENDS message_runtime)
  • 注意:下面两个的顺序,不然会报错。

CMake Error at /opt/ros/melodic/share/genmsg/cmake/genmsg-extras.cmake:197 (message):

generate_messages() must be called before catkin_package() in project

##Generate added messages and services

generate_messages(DEPENDENCIES std_msgs)

##Declare a catkin package

catkin_package(CATKIN_DEPENDS message_runtime)

  • 当我们需要使用.msg .srv .action形式的文件时,我们需要特殊的预处理器把他们转化为系统可以识别特定编程语言(.h   .cpp)。系统会用里面所有的(一些编程语言)生成器(比如 gencpp, genpy, genlisp, etc)生成相应的.cpp .py文件。这就需要三个宏:add_message_files, add_service_files,add_action_files来相应的控制.msg .srv .action。这些宏后面必须跟着一个调用generate_messages()

③同上package.xml取消注释:

<build_depend>message_generation</build_depend>

行和:

<exec_depend>message_runtime</exec_depend> -->

  • (PS:package .xml:还可以自定义个性化程序包,添加个人邮件信息标签,许可方案等可选。)

④在~/catkin_ws进行编译:

cd ~/catkin_ws
catkin_make

(更新一下环境)验证是否成功:

rossrv show test/test_srv1.srv

3.使用msg和srv文件

为创建一个服务并在ROS中使用,本服务对刚刚创建的三个int32型整数求和。需要两个节点,一个服务器和一个客户端。

①在test的src文件下创建两个新的文件,example2_a.cpp(服务端)和example2_b.cpp(客户端)

 

example2_a.cpp源文件:

#include<ros/ros.h>			//包含ROS节点所有必要的文件
#include "test/test_srv1.h" 		//添加新建的服务test_srv1


//定义一个返回值为bool类型的加法函数
bool add(test::test_srv1::Request &req,
		test::test_srv1::Response &res)
{	//函数对三个变量ABC求和并将计算结果发送给其他节点
	res.sum=req.A+req.B+req.C;
	ROS_INFO("Request: A=%1d,B=%1d,C=%1d",(int)req.A,(int)req.B,(int)req.C);
	//输出求和结果信息sum
	ROS_INFO("Sending back response: [%1d]",(int)res.sum);
	return true;
}

int main(int argc,char **argv)
{	//初始化节点并设置名称(该名称唯一)
	ros::init(argc,argv,"add_server");
	//进程的处理程序,允许我们与环境交互
	ros::NodeHandle n;
	//将节点实例化为服务发布者,将发布主题和名称告知节点管理器,名为add_service,并在ROS中发布广播。
	ros::ServiceServer service=n.advertiseService("add_ints",add);
	ROS_INFO("Ready to add 3 ints.");
	ros::spin();

	return 0;
}

example2_b.cpp文件:

#include <ros/ros.h>		//包含ros程序的基本核心头文件
#include<test/test_srv1.h>	//包含创建的test_srv1服务文件
#include<cstdlib>			//包含C的标准库要用到函数atoll

int main(int argc,char **argv)
{
	ros::init(argc,argv,"add_ints_client");
	//键盘输入三个数字
	if(argc!=4)
	{
		ROS_INFO("Useage: add_ints_client A B C");
		return 1;
	}
	//实例化节点以交互
	ros::NodeHandle n;
	ros::ServiceClient client=n.serviceClient<test::test_srv1>("add_ints");
	test::test_srv1 srv;
	//讲键盘输入三个数据值转换为长长字符串类型并传入给服务
	srv.request.A=atoll(argv[1]);
	srv.request.B=atoll(argv[2]);
	srv.request.C=atoll(argv[3]);
	//计算三个数据相加结果sum并转换为long int类型。
	if(client.call(srv))
		ROS_INFO("Sum: %1d",(long int)srv.response.sum);
	else
		{
			ROS_ERROR("Failed to call service add_ints...");
			return 1;
		}
	return 0;
}

②编译:

为了编译新的节点,同样需要修改CMakeLists.txt和package.xml(上面已经修改过了)文件:


##Build the example2_a and example2_b
add_executable(example2_a src/example2_a.cpp)
add_executable(example2_b src/example2_b.cpp)

add_dependencies(example2_a test_generate_message_cpp)
add_dependencies(example2_b test_generate_message_cpp)

target_link_libraries(example2_a ${catkin_LIBRARIES})
target_link_libraries(example2_b ${catkin_LIBRARIES})

③rosrun执行节点(只要涉及rosrun的命令。此时注意一定要打开新终端执行roscore)

第一个终端:
 

roscore

 第二个终端:

rosrun test example2_a

第三个终端:

rosrun test example2_b 1 2 3

效果如下:

在第三个终端注意要输入要参与计算的三个整数:

输入完之后同时可在第二个窗口看到输入和输出消息:

二、使用自定义msg文件创建节点

在example2_a.cpp的头文件以及上图的终端输出消息可以看出,上面我使用的msg来自test_srv1,那么如果想要使用msg文件,则应该添加test_msg1.msg。

1.在test的src文件下创建example3_a.cpp和example3_b.cpp源文件:

example3_a.cpp:

#include<ros/ros.h>			//添加ros核心头文件
#include<test/test_msg1.h>	//添加自建服务消息头文件
#include<sstream>			//添加字符串流

int main(int argc , char **argv)
{
	//初始化ROS节点并命名,名称唯一
	ros::init(argc,argv,"example3_a");
	//进程处理程序,允许我们与环境进行交互
	ros::NodeHandle n;
	//讲节点实例化为发布者,并将所发布主题类型的名称告知节点管理器。第二个参数是缓冲区大小,如果发布数据速度较快,将缓冲区至少设置1000条消息。
	ros::Publisher pub=n.advertise<test::test_msg1>("message",1000);
	//设置消息频率10HZ
	ros::Rate loop_rate(10);
	//CTRL+C组组合键或ROS停止所有节点时,ros::ok()停止该节点
	while(ros::ok())
	{	//定义msg内A B C 三个变量的数据。
		test::test_msg1 msg;
		msg.A=1;
		msg.B=2;
		msg.C=3;
		//继续发布消息,使用上面定义的发布器定义发布消息
		pub.publish(msg);
		//spinOnce()函数负责处理ROS内部事件和操作,例如读取订阅的消息,在主循环中执行一次迭代允许用户执行操作,与spin函数不同,spinOnce在不中断的情况下允许主循环。
		ros::spinOnce();
		//最后按照10HZ的频率讲程序挂起
		loop_rate.sleep();
	}
	return 0;
	
}

example3_b.cpp

#include<ros/ros.h>			//包含ROS核心程序头文件
#include<test/test_msg1.h>	//包含自建服务消息的头文件

//定义一个消息回调函数,输入参数为消息结构体指针类型msg
void messageCallback(const test::test_msg1::ConstPtr& msg)
{	
	ROS_INFO("I heard: [%d] [%d] [%d]",msg->A,msg->B,msg->C);
}
int main(int argc,char **argv)
{
	//初始化ROS节点,命名为example3_b,名称唯一
	ros::init(argc,argv,"example3_b");
	//进程处理程序,允许我们与环境交互
	ros::NodeHandle n;
	//实例化节点成订阅者sub,订阅内容为message;第二个参数为缓冲区大小1000,如果message主题发布消息速度过快,则将缓冲区至少存放1000条消息;第三个参数为回调函数messageCallback。
	ros::Subscriber sub=n.subscribe("message",1000,messageCallback);
	//节点开始读取主题和消息到达时,灰调函数messageCallback被调用的主循环,当用户按CTRL+C,节点退出消息循环,循环结束。
	ros::spin();
	return 0;
}

2.编译:

同样修改test下的CMakeLists.txt文件添加:

##Build the example3_a and example3_b
add_executable(example3_a src/example3_a.cpp)
add_executable(example3_b src/example3_b.cpp)

add_dependencies(example3_a test_generate_message_cpp)
add_dependencies(example3_b test_generate_message_cpp)

target_link_libraries(example3_a ${catkin_LIBRARIES})
target_link_libraries(example3_b ${catkin_LIBRARIES})

重复上面的同样过程编译:

最后效果如下:

roscore
rosrun test example3_a
rosrun test example3_b

 

至此,自建简单的服务器和客户端参数服务已经完成。下篇将会学习roslaunch的用法和动态参数的配置。

 

 

上一篇:【ROS实践入门(五)自定义节点功能包使用与消息传递】

下一篇:【ROS实践入门(七)launch文件和动态参数使用】

 

参考资料:

【1】ROS官网:http://wiki.ros.org/cn

【2】ROS机器人高效编程

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROS中,使用.msg文件来定义消息类型。具体步骤如下: 1. 在ROS包中创建一个名为msg的文件夹。 2. 在msg文件夹中创建一个名为*.msg的文件,其中*为你所定义的消息类型名称。 3. 在*.msg文件中定义消息类型的字段和类型。例如,定义一个包含字符串和整数字段的消息类型可以写作: ``` string name int32 age ``` 4. 在CMakeLists.txt文件中添加以下代码: ``` add_message_files( FILES *.msg ) generate_messages( DEPENDENCIES std_msgs ) ``` 其中,add_message_files()函数将*.msg文件添加到ROS包中,generate_messages()函数根据*.msg文件生成对应的消息类型代码。 5. 在package.xml文件中添加以下代码: ``` <build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend> ``` 这些代码声明了消息生成器(message_generation)和消息运行时(message_runtime)的依赖项。 6. 编译ROS包。在ROS包的根目录下运行以下命令: ``` catkin_make ``` 编译成功后,将生成一个msg_gen_cpp文件夹,其中包含自动生成消息类型代码。 7. 在ROS节点中使用自定义的消息类型。 在ROS节点中使用自定义的消息类型时,需要包含消息类型的头文件,并使用消息类型的变量进行消息传递。例如,使用上面定义的消息类型可以写作: ``` #include <ros/ros.h> #include <your_package_name/your_message_type_name.h> int main(int argc, char **argv) { ros::init(argc, argv, "your_node_name"); ros::NodeHandle nh; your_package_name::your_message_type_name msg; msg.name = "John"; msg.age = 30; ros::Publisher pub = nh.advertise<your_package_name::your_message_type_name>("your_topic_name", 10); pub.publish(msg); ros::spin(); return 0; } ``` 这个例子中,我们使用了自定义的消息类型your_message_type_name,将消息发布到your_topic_name话题上。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值