学习ROS基础的电子笔记,摘录自各类ROS基础课程,文末会附上本次内容参考的相关课程视频链接。
这次课程主要实现通过/spawn话题实现服务端与客户端的通讯,其中客户端Client发送request请求给服务端,其消息格式为turtlesim::Span,即turtlesim包中定义的Span数据结构,并且接收Server端回复的Request。
cd ~/catkin_ws/src
catkin_create_pkg learning_service roscpp rospy geometry_msgs turtlesim
vim ~/catkin_ws/src/learning_service/src/turtle_spawn.cpp
/**
*该例程将请求/spawn服务,服务数据类型turtlesim::Spawn
*/
#include<ros/ros.h>
#include<turtlesim/Spawn.h>
int main(int argc, char** argv){
// 初始化ROS节点
ros :: init(argc, argv, "turtle_spawn") ;
// 创建节点句柄
ros :: NodeHandle n ;
// 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
ros :: service :: waitForService("/spawn") ;
ros :: ServiceClient add_turtle = n.serviceClient<turtlesim :: Spawn>("/spawn") ;
// 初始化turtle::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.6f, 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 ;
}
call()是一个阻塞型函数,直到得到反馈,才会有下一步反应。
vim ~/catkin_ws/src/learning_service/CMakeLists.txt
add_executable(turtle_spawn src/turtle_spawn.cpp)
target_link_libraries(turtle_spawn
${catkin_LIBRARIES}
)
cd ~/catkin_ws
catkin_make
// terminal1
roscore
//terminal2
rosrun turtlesim turtlesim_node
// terminal3
cd ~/catkin_ws
source devel/setup.bash
rosrun learning_topic turtle_spawn
client主要负责请求海龟是否运动,Server主要接收海龟是否运动的请求,并通过topic发送速度的指令来响应client的运动请求。所以这一节除了完成server端的代码,还需要实现一个新的话题.turtle_command。关于client的request,通过数据结构为std_srvs::Trigger实现。关于service的运动速度控制响应,通过发布新话题/turtle1/cmd_vel实现,其信息类型为geometry_msgs::Twist。
vim ~/catkin_ws/src/learning_service/src/turtle_command_server.cpp
/**
*该例程将执行/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 ;
// 显示请求数据, 当有请求唤醒commandCallback函数时,会改变现在pubcomman>的状态,即乌龟会改变现在的运动状态(运动/静止);
ROS_INFO("Publish turtle velocity command [%s]", pubCommand == true ? "yes" : "No") ;
// 设置反馈数据
res.success = true ;
res.message = "Change turtle command status!" ;
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_server = 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() ;
// 如果标志pubCommand为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 ;
}
我们可以通过
rossrv show std_srvs/Trigger
命令来查看Trigger的数据格式
bool success
string message
vim ~/catkin_ws/src/learning_service/CMakeLists.txt
add_executable(turtle_command_server src/turtle_command_server.cpp)
target_link_libraries(turtle_command_server
${catkin_LIBRARIES}
)
// ESC+:wq保存退出后完成编译
cd ~/catkin_ws
catkin_make
// terminal1
roscore
// terminal2
rosrun turtlesim turtlesim_node
// terminal3
cd ~/catkin_ws
source devel/setup.bash
rosrun learning_service turtle_command_server
//terminal4,模拟客户端请求的情况
rosservice call /turtle_command "{}"
"{}的理解:Trigger数据格式的请求的message是空的,所以在{}中没有内容,这里可以用tap快捷键补全,实现效果如下。
当再次从client端发送request时,海龟停止运动,如图:
参考课程: ROS入门21讲(古月居-第13讲&第14讲)