ROS学习第六天

今天学习客户端与服务端的分别编程实现以及他们之间的通信,前面几天学习的是ROS里面的话题通信,今天学习是ROS的另外一种服务通信,

1:客服端的编程实现

在这里插入图片描述
首先看上面框图,之前用命令行的形式实现请求服务生成一只新的小海龟,现在通过编程的实现运行程序代替生成一直小海龟。在你的工作空间下创建一个新的功能包:
输入指令:1:cd ~/catkin_ws/src
接下来输入第二条指令:

catkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim

创建完成后,在你文件夹下建立相应的cpp文件,然后新建的文件名字为:turtle_spawn.cpp。
在这里插入图片描述
下面是对应的c++的程序:首先查看下请求服务的数据类型: rosservice info /spawn
在这里插入图片描述

具体思路就是:

  1. 初始化ROS节点;
  2. 创建一个Client实例
  3. 发布服务请求数据;
  4. 等待Server处理后的应答结果。
#include <ros/ros.h>
#include <turtlesim/Spawn.h>

int main(int argc,char** argv)
{
    //初始化ROS节点
    ros::init(argc,argv,"turtle_spawn");
    //创建节点句柄
    ros::NodeHandle node;
    //发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
    ros::service::waitForService("/spawn");
    ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
    //初始化turtlesim::spawn的请求数据
    turtlesim::Spawn srv;
    srv.request.x = 2.0;
    srv.request.y = 2.0;
    srv.request.name = "turtle2";
    //请求服务调用
    ROS_INFO(("Call service to spawn turtle[x:0.6%f,y:%0.6f,name:%s]"),srv.request.x,srv.request.y,srv.request.name.c_str());
    add_turtle.call(srv);
    //显示服务调用结果
    ROS_INFO("Spawn turtle successfully [name:%s]",srv.response.name.c_str());
    return 0;
};

接下来接着是配置c++代码的编译规则:
仍旧是在Cmakelist里面添加编译规则,如下:

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

回到工作空间的根目录进行编译:
编译完成后正常开启海龟节点运行,如下图:
在这里插入图片描述
Python的程序为:

/***********************************************************************

Copyright 2020 GuYueHome (www.guyuehome.com).

***********************************************************************/



/**

 * 该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger

 */

 

#include <ros/ros.h>

#include <geometry_msgs/Twist.h>

#include <std_srvs/Trigger.h>



ros::Publisher turtle_vel_pub;

bool pubCommand = false;



// service回调函数,输入参数req,输出参数res

bool commandCallback(std_srvs::Trigger::Request  &req,

         			std_srvs::Trigger::Response &res)

{

	pubCommand = !pubCommand;



    // 显示请求数据

    ROS_INFO("Publish turtle velocity command [%s]", pubCommand==true?"Yes":"No");



	// 设置反馈数据

	res.success = true;

	res.message = "Change turtle command state!"



    return true;

}



int main(int argc, char **argv)

{

    // ROS节点初始化

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



    // 创建节点句柄

    ros::NodeHandle n;



    // 创建一个名为/turtle_command的server,注册回调函数commandCallback

    ros::ServiceServer command_service = n.advertiseService("/turtle_command", commandCallback);



	// 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10

	turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);



    // 循环等待回调函数

    ROS_INFO("Ready to receive turtle command.");



	// 设置循环的频率

	ros::Rate loop_rate(10);



	while(ros::ok())

	{

		// 查看一次回调函数队列

    	ros::spinOnce();

		

		// 如果标志为true,则发布速度指令

		if(pubCommand)

		{

			geometry_msgs::Twist vel_msg;

			vel_msg.linear.x = 0.5;

			vel_msg.angular.z = 0.2;

			turtle_vel_pub.publish(vel_msg);

		}



		//按照循环频率延时

	    loop_rate.sleep();

	}



    return 0;

}

2:服务端的编程实现

在这里插入图片描述
实现步骤:
1:初始化ROS节点,
2:创建Server实例,
3:循环等待服务请求,进入回调函数;
4:在回调函数完成服务功能的处理,并反馈应答数据。
具体c++程序是:

/***********************************************************************

Copyright 2020 GuYueHome (www.guyuehome.com).

***********************************************************************/



/**

 * 该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger

 */

 

#include <ros/ros.h>

#include <geometry_msgs/Twist.h>

#include <std_srvs/Trigger.h>



ros::Publisher turtle_vel_pub;

bool pubCommand = false;



// service回调函数,输入参数req,输出参数res

bool commandCallback(std_srvs::Trigger::Request  &req,

         			std_srvs::Trigger::Response &res)

{

	pubCommand = !pubCommand;



    // 显示请求数据

    ROS_INFO("Publish turtle velocity command [%s]", pubCommand==true?"Yes":"No");



	// 设置反馈数据

	res.success = true;

	res.message = "Change turtle command state!"



    return true;

}



int main(int argc, char **argv)

{

    // ROS节点初始化

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



    // 创建节点句柄

    ros::NodeHandle n;



    // 创建一个名为/turtle_command的server,注册回调函数commandCallback

    ros::ServiceServer command_service = n.advertiseService("/turtle_command", commandCallback);



	// 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10

	turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);



    // 循环等待回调函数

    ROS_INFO("Ready to receive turtle command.");



	// 设置循环的频率

	ros::Rate loop_rate(10);



	while(ros::ok())

	{

		// 查看一次回调函数队列

    	ros::spinOnce();

		

		// 如果标志为true,则发布速度指令

		if(pubCommand)

		{

			geometry_msgs::Twist vel_msg;

			vel_msg.linear.x = 0.5;

			vel_msg.angular.z = 0.2;

			turtle_vel_pub.publish(vel_msg);

		}



		//按照循环频率延时

	    loop_rate.sleep();

	}



    return 0;

}

仍旧是在cmakelists添加以下俩句指令,

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

编译,运行程序。如下图所示:
在这里插入图片描述

3:服务数据的定义和使用

在这里插入图片描述
首先要根据前俩个去定一个服务端和客户端的程序,首先新建一个Person.srv文件。
在这里插入图片描述
然后在srv补充以下:

string name

uint8  age

uint8  sex



uint8 unknown = 0

uint8 male    = 1

uint8 female  = 2



---

string result

接下来修改cmakelists.txt文件的内容,
首先在package.xml中添加功能包依赖。

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

在cmakelists.txt添加编译选项。

find_package(catkin REQUIRED COMPONENTS
  geometry_msgs
  roscpp
  rospy
  std_msgs
  turtlesim
  message_generation
)

如下图所示:
在这里插入图片描述
再添加:

add_service_files(
  FILES
  Person.srv
)

generate_messages(
  DEPENDENCIES
  std_msgs
)

接下来添加以下指令:message_runtime
在这里插入图片描述

4:创建服务器代码并运行代码

代码如下:
实现步骤:
1:初始化ROS节点,
2:创建Server实例,
3:循环等待服务请求,进入回调函数;
4:在回调函数完成服务功能的处理,并反馈应答数据
具体代码如下:

/***********************************************************************

Copyright 2020 GuYueHome (www.guyuehome.com).

***********************************************************************/



/**

 * 该例程将执行/show_person服务,服务数据类型learning_service::Person

 */

 

#include <ros/ros.h>

#include "learning_service/Person.h"



// service回调函数,输入参数req,输出参数res

bool personCallback(learning_service::Person::Request  &req,

         			learning_service::Person::Response &res)

{

    // 显示请求数据

    ROS_INFO("Person: name:%s  age:%d  sex:%d", req.name.c_str(), req.age, req.sex);



	// 设置反馈数据

	res.result = "OK";



    return true;

}



int main(int argc, char **argv)

{

    // ROS节点初始化

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



    // 创建节点句柄

    ros::NodeHandle n;



    // 创建一个名为/show_person的server,注册回调函数personCallback

    ros::ServiceServer person_service = n.advertiseService("/show_person", personCallback);



    // 循环等待回调函数

    ROS_INFO("Ready to show person informtion.");

    ros::spin();



    return 0;

}

python代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

########################################################################
####          Copyright 2020 GuYueHome (www.guyuehome.com).          ###
########################################################################

# 该例程将执行/show_person服务,服务数据类型learning_service::Person

import rospy
from learning_service.srv import Person, PersonResponse

def personCallback(req):
	# 显示请求数据
    rospy.loginfo("Person: name:%s  age:%d  sex:%d", req.name, req.age, req.sex)

	# 反馈数据
    return PersonResponse("OK")

def person_server():
	# ROS节点初始化
    rospy.init_node('person_server')

	# 创建一个名为/show_person的server,注册回调函数personCallback
    s = rospy.Service('/show_person', Person, personCallback)

	# 循环等待回调函数
    print "Ready to show person informtion."
    rospy.spin()

if __name__ == "__main__":
    person_server()

创建客户端代码:
如下:

/***********************************************************************
Copyright 2020 GuYueHome (www.guyuehome.com).
***********************************************************************/

/**
 * 该例程将请求/show_person服务,服务数据类型learning_service::Person
 */

#include <ros/ros.h>
#include "learning_service/Person.h"

int main(int argc, char** argv)
{
    // 初始化ROS节点
	ros::init(argc, argv, "person_client");

    // 创建节点句柄
	ros::NodeHandle node;

    // 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
	ros::service::waitForService("/show_person");
	ros::ServiceClient person_client = node.serviceClient<learning_service::Person>("/show_person");

    // 初始化learning_service::Person的请求数据
	learning_service::Person srv;
	srv.request.name = "Tom";
	srv.request.age  = 20;
	srv.request.sex  = learning_service::Person::Request::male;

    // 请求服务调用
	ROS_INFO("Call service to show person[name:%s, age:%d, sex:%d]", 
			 srv.request.name.c_str(), srv.request.age, srv.request.sex);

	person_client.call(srv);

	// 显示服务调用结果
	ROS_INFO("Show person result : %s", srv.response.result.c_str());

	return 0;
};

接下来添加编译规则:
在这里插入图片描述
添加的具体内容如下:

add_executable(person_server src/person_server.cpp)
target_link_libraries(person_server ${catkin_LIBRARIES})
add_dependencies(person_server ${PROJECT_NAME}_gencpp)

add_executable(person_client src/person_client.cpp)
target_link_libraries(person_client ${catkin_LIBRARIES})
add_dependencies(person_client ${PROJECT_NAME}_gencpp)

然后切换工作空间的主目录下:
输入catkin_make进行编译。由于添加环境变量已经生效,所以接下来直接启动roscore即可。运行以下俩个指令执行c++程序。执行出的效果如下图所示:

在这里插入图片描述

posted @ 2022-02-27 12:43  Bathwind_W  阅读(8)  评论(0编辑  收藏  举报  来源