1.服务端Server的编程实现

2.服务数据的定义与使用

3.参数的使用与编程方法


1.服务端Server的编程实现

①python实现

如何实现一个服务器

• 初始化ROS节点;

• 创建Server实例;

• 循环等待服务请求,进入回调函数;

• 在回调函数中完成服务功能的处理,并反馈应答数据。

 

/home/yys/catkin_ws/src/learning_service/python/turtle_command_server.py

 

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger

import rospy
import _thread
import time
from geometry_msgs.msg import Twist
from std_srvs.srv import Trigger, TriggerResponse

pubCommand = False
turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)


def command_thread():
    while True:
        if pubCommand:
            vel_msg = Twist()
            vel_msg.linear.x = 0.5
            vel_msg.angular.z = 0.2
            turtle_vel_pub.publish(vel_msg)

        time.sleep(0.1)


def commandCallback(req):
    global pubCommand
    pubCommand = bool(1 - pubCommand)

    # 显示请求数据
    rospy.loginfo("Publish turtle velocity command![%d]", pubCommand)

    # 反馈数据
    return TriggerResponse(1, "Change turtle command state!")


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

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

    # 循环等待回调函数
    print("Ready to receive turtle command.")

    _thread.start_new_thread(command_thread, ())
    rospy.spin()


if __name__ == "__main__":
    turtle_command_server()

 

cd ~/catkin_ws
catkin_make
roscore
rosrun turtlesim turtlesim_node
rosrun learning_service turtle_command_server.py 

 

②c++实现

/**
 * 该例程将执行/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.txt中的编译规则

• 设置需要编译的代码和生成的可执行文件;

• 设置链接库

add_executable(turtle_command_server src/turtle_command_server.cpp)
target_link_libraries(turtle_command_server ${catkin_LIBRARIES})
cd ~/catkin_ws
catkin_make
roscore
rosrun turtlesim turtlesim_node
rosrun learning_service turtle_command_server
rosservice call /turtle_command "{}"

2.服务数据的定义与使用

如何自定义服务数据

/home/yys/catkin_ws/src/learning_service/srv/Person.srv(在learning_service下先建立一个srv文件夹)

string name
uint8 age
uint8 sex
uint8 unknown = 
0
uint8 male = 1
uint8 female = 2
---
string result

定义srv文件

➢ 在package.xml中添加功能包依赖

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

文件夹里面位置

<exec_depend>rospy</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  <exec_depend>turtlesim</exec_depend>
    
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

➢ 在CMakeLists.txt添加编译选项

• find_package( …… message_generation)

message_generation

文件夹里面位置

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)

add_service_files(FILES Person.srv) 
generate_messages(DEPENDENCIES std_msgs)

文件夹里面位置

add_service_files(FILES Person.srv)
generate_messages(DEPENDENCIES std_msgs)

###################################
## catkin specific configuration ##
###################################

• catkin_package(…… message_runtime)

message_runtime

文件夹里面位置

catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES learning_service
CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime
#  DEPENDS system_lib
)

###########
## Build ##
###########

➢ 编译生成语言相关文件(python运行也要编译这一步)

yys@yys:~/catkin_ws$ catkin_make 

devel下会生成两个文件

①python实现

/home/yys/catkin_ws/src/learning_service/python/person_server.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将执行/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()

/home/yys/catkin_ws/src/learning_service/python/person_client.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程将请求/show_person服务,服务数据类型learning_service::Person

import sys

import rospy
from learning_service.srv import Person, PersonRequest


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

    # 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
    rospy.wait_for_service('/show_person')
    try:
        person_client = rospy.ServiceProxy('/show_person', Person)

        # 请求服务调用,输入请求数据
        response = person_client("Tom", 20, PersonRequest.male)
        return response.result
    except rospy.ServiceException as e:
        print ("Service call failed: %s" % e)


if __name__ == "__main__":
    # 服务调用并显示调用结果
    print ("Show person result : %s" % (person_client()))
cd ~/catkin_ws
roscore
rosrun learning_service person_server.py
rosrun learning_service person_client.py

可以看出右边每次运行一次person_client左边就输出一句话 

yys@yys:~$ rosrun learning_service person_server.py 
Ready to show person informtion.
[INFO] [1599276420.198249]: Person: name:Tom  age:20  sex:1
[INFO] [1599276422.967892]: Person: name:Tom  age:20  sex:1
[INFO] [1599276428.320552]: Person: name:Tom  age:20  sex:1

yys@yys:~$ rosrun learning_service person_
person_client.py  person_server.py  
yys@yys:~$ rosrun learning_service person_client.py 
Show person result : OK
yys@yys:~$ rosrun learning_service person_client.py 
Show person result : OK
yys@yys:~$ rosrun learning_service person_client.py 
Show person result : OK
 

②c++实现

如何实现一个服务器

• 初始化ROS节点;

• 创建Server实例;

• 循环等待服务请求,进入回调函数;

• 在回调函数中完成服务功能的处理,

并反馈应答数据。

/home/yys/catkin_ws/src/learning_service/src/person_server.cpp

/**
 * 该例程将执行/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;
}


/home/yys/catkin_ws/src/learning_service/src/person_client.cpp

/**
 * 该例程将请求/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;
};


如何配置CMakeLists.txt中的编译规则

• 设置需要编译的代码和生成的可执行文件;

• 设置链接库;

• 添加依赖项。

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)

在终端编译执行

cd ~/catkin_ws
catkin_make
roscore
rosrun learning_service person_server
rosrun learning_service person_client

 cd ~/catkin_ws/src
catkin_create_pkg learning_parameter roscpp rospy std_srvs

参数命令行使用

运行乌龟才能使用命令

roscore

 

rosrun turtlesim turtlesim_node

 (1)列出当前多有参数

rosparam list

(2)显示某个参数值 

 rosparam get param_key

如获取颜色r

rosparam get /turtlesim/background_r

(3)设置某个参数值

 rosparam set param_key param_value

例如设置b颜色为100

 

rosparam set /turtlesim/background_b 100

(4)保存参数到文件(file_name自定义)

rosparam dump file_name

 (5)从文件读取参数(file_name读取自己建立的名字)

rosparam load file_name

(6)删除参数

rosparam delete param_key

例如删除颜色r

rosparam delete /turtlesim/background_r

 

①python代码实现
/home/yys/catkin_ws/src/learning_parameter/script/parameter_config.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 该例程设置/读取海龟例程中的参数

import sys
import random
import rospy
from std_srvs.srv import Empty


def parameter_config():
    # ROS节点初始化
    rospy.init_node('parameter_config', anonymous=True)

    # 读取背景颜色参数
    red = rospy.get_param('/turtlesim/background_r')
    green = rospy.get_param('/turtlesim/background_g')
    blue = rospy.get_param('/turtlesim/background_b')

    rospy.loginfo("Get Backgroud Color[%d, %d, %d]", red, green, blue)

    # 设置背景颜色参数
    red = random.randint(0, 255)
    green = random.randint(0, 255)
    blue = random.randint(0, 255)
    rospy.set_param("/turtlesim/background_r", red)
    rospy.set_param("/turtlesim/background_g", green)
    rospy.set_param("/turtlesim/background_b", blue)
    rospy.loginfo("Set Backgroud Color[%d, %d, %d]", red, green, blue)

    # 读取背景颜色参数
    red = rospy.get_param('/turtlesim/background_r')
    green = rospy.get_param('/turtlesim/background_g')
    blue = rospy.get_param('/turtlesim/background_b')

    rospy.loginfo("Get Backgroud Color[%d, %d, %d]", red, green, blue)

    # 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
    rospy.wait_for_service('/clear')
    try:
        clear_background = rospy.ServiceProxy('/clear', Empty)

        # 请求服务调用,输入请求数据
        response = clear_background()
        return response
    except rospy.ServiceException as e:
        print("Service call failed: %s" % e)


if __name__ == "__main__":
    parameter_config()
roscore
rosrun turtlesim turtlesim_node
rosrun learning_parameter parameter_config.py

每运行一次颜色随机改变

yys@yys:~$ rosrun learning_parameter parameter_config.py 
[INFO] [1599362032.866086]: Get Backgroud Color[232, 128, 212]
[INFO] [1599362032.871158]: Set Backgroud Color[31, 32, 5]
[INFO] [1599362032.875218]: Get Backgroud Color[31, 32, 5]
 

②c++实现

如何获取/设置参数
初始化ROS节点;
get函数获取参数;
set函数设置参数;
/home/yys/catkin_ws/src/learning_parameter/src/parameter_config.cpp
/**
 * 该例程设置/读取海龟例程中的参数
 */
#include <string>
#include <ros/ros.h>
#include <std_srvs/Empty.h>

int main(int argc, char **argv)
{
	int red, green, blue;

    // ROS节点初始化
    ros::init(argc, argv, "parameter_config");

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

    // 读取背景颜色参数
	ros::param::get("/turtlesim/background_r", red);
	ros::param::get("/turtlesim/background_g", green);
	ros::param::get("/turtlesim/background_b", blue);

	ROS_INFO("Get Backgroud Color[%d, %d, %d]", red, green, blue);

	// 设置背景颜色参数
	ros::param::set("/turtlesim/background_r", 255);
	ros::param::set("/turtlesim/background_g", 255);
	ros::param::set("/turtlesim/background_b", 255);

	ROS_INFO("Set Backgroud Color[255, 255, 255]");

    // 读取背景颜色参数
	ros::param::get("/turtlesim/background_r", red);
	ros::param::get("/turtlesim/background_g", green);
	ros::param::get("/turtlesim/background_b", blue);

	ROS_INFO("Re-get Backgroud Color[%d, %d, %d]", red, green, blue);

	// 调用服务,刷新背景颜色
	ros::service::waitForService("/clear");
	ros::ServiceClient clear_background = node.serviceClient<std_srvs::Empty>("/clear");
	std_srvs::Empty srv;
	clear_background.call(srv);
	
	sleep(1);

    return 0;
}
如何配置CMakeLists.txt中的编译规则
设置需要编译的代码和生成的可执行文件;
设置链接库;
add_executable(parameter_config src/parameter_config.cpp)
target_link_libraries(parameter_config ${catkin_LIBRARIES})
cd ~/catkin_ws
catkin_make
roscore
rosrun turtlesim turtlesim_node
rosrun learning_parameter parameter_config