ros的话题、服务和动作的基础编程

一、工作空间

  1. 简介
    存放相关文件的文件夹,其中主要包括srcbuilddevelinstall文件夹。
  • src:代码空间(Source Space
  • build:编译空间(Build Space
  • devel:开发空间(Development Space
  • install:安装空间(Install Space
  1. 创建
mkdir -p ~/catkin_ws/src  #创建工作空间
cd ~/catkin_ws/src        #进入目录
catkin_init_workspace     #初始化让其成为ROS的工作空间

在这里插入图片描述

  1. 编译工作空间
cd ..
catkin_make

在这里插入图片描述
在这里插入图片描述

  1. 设置环境变量
source /home/yunkaichu/catkin_ws/devel/setup.bash   #该环境变量设置只对当前终端有效,yunkaichu是用户名
sudo nano ~/.bashrc#将上面命令放置到~/.bashrc文件中,让其对所有终端都有效
  1. 检查环境变量
echo $ROS_PACKAGE_PATH

在这里插入图片描述

二、功能包

  1. 创建功能包
cd ~/catkin_ws/src
catkin_create_pkg learning_communication std_msgs rospy roscpp
  • catkin_create_pkg :功能包名字 依赖
  • std_msgs:定义的标准的数据结构
  • rospy:提供python编程接口
  • roscpp:提供c++编程接口

在这里插入图片描述

  1. 编译功能包
cd ~/catkin_ws
catkin_make

在这里插入图片描述

在同一个工作空间下,不允许存在同名的功能包
不同工作空间,可以存在同名的功能包

三、ROS通信编程

  1. 话题编程
    1)话题编辑流程
  • 创建发布者
    • 初始化ROS节点
    • ROS Master注册节点信息,包括发布的话题名和话题中的消息类型
    • 按照一定频率循环发布消息
  • 创建订阅者
    • 初始化ROS节点
    • 订阅需要的话题
    • 循环等待话题消息,接受到消息后进行回调函数
    • 回调函数中完成消息处理
  • 添加编译选项
    • 设置需要编译的代码和生成的可执行文件
    • 设置链接库
    • 设置依赖
  • 运行可执行程序

2)在catkin_ws/src/learning_communication/src文件中编写代码:

  • talker.cpp
#include<sstream>
#include"ros/ros.h"
#include"std_msgs/String.h"
int main(int argc,char **argv)
{
	//ROS节点初始化
	ros::init(argc,argv,"talker");
	//创建节点句柄
	ros::NodeHandle n;
	//创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String
	ros::Publisher chatter_pub=n.advertise<std_msgs::String>("chatter",1000);
	//设置循环的频率
	ros::Rate loop_rate(10);
	int count=0;
	while(ros::ok())
	{
		//初始化std_msgs::String类型的消息
		std_msgs::String msg;
		std::stringstream ss;
		ss<<"hello world"<<count;
		msg.data=ss.str();
		//发布消息
		ROS_INFO("%s",msg.data.c_str());
		chatter_pub.publish(msg);
		//循环等待回调函数
		ros::spinOnce();
		//接受循环频率延时
		loop_rate.sleep();
		++count;
	}
	return 0;
}
  • listener.cpp
#include"ros/ros.h"
#include"std_msgs/String.h"
//接收到订阅的消息,会进入消息的回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
	//将接收到的消息打印处理
	ROS_INFO("I heard:{%s}",msg->data.c_str());
}
int main(int argc,char **argv)
{
	//初始化ROS节点
	ros::init(argc,argv,"listener");
	//创建节点句柄
	ros::NodeHandle n;
	//创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback
	ros::Subscriber sub=n.subscribe("chatter",1000,chatterCallback);
	//循环等待回调函数
	ros::spin();
	return 0;
}

3)设置CMakeLists.txt文件:

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

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

在这里插入图片描述
4)编译:
在这里插入图片描述
在这里插入图片描述

5)分别用3个命令窗口执行以下命令,运行可执行文件:

roscore
rosrun learning_communication talker
rosrun learning_communication listener

在这里插入图片描述

  1. 自定义话题消息
    1)定义msg文件:
mkdir ~/catkin_ws/src/learning_communication/msg
sudo nano Person.msg
  • Person.msg文件代码:
string name
uint8 sex
uint8 age

uint8 unknown=0
uint8 male=1
uint8 female=2
  • package.xml中添加功能包依赖:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
  • 在CMakeLists.txt添加编译选项
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

2)编译成功
在这里插入图片描述
3)查看自定义消息

rosmsg show Person

在这里插入图片描述

  1. 服务编程
    1)服务编程流程
  • 创建服务器
    • 初始化ROS节点
    • 创建Serve实例
    • 循环等待服务请求,进入回调函数
    • 在回调函数中完成服务功能的处理,并反馈应答数据
  • 创建客户端
    • 初始化ROS节点
    • 创建一个Client实例
    • 发布服务请求数据
    • 等待Serve处理之后的应答结果
  • 添加编辑选项
    • 设置需要编译的代码和生成的可执行文件
    • 设置链接库
    • 设置依赖
  • 运行可执行文件

2)定义服务请求与应答的方式

定义srv文件

mkdir ~/catkin_ws/src/learning_communication/srv
sudo nano AddTwoInts.srv
  • AddTwoInts.srv文件中代码:
int64 a
int64 b
---
int64 sum
  • package.xml中添加功能包依赖:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

注:部分ROS版本中exec_depend需要修改为run_depend

  • CMakeLists.txt添加编译选项

前部分与自定义话题消息一致,后需要添加设置CMakeLists.txt文件,内容如下:

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

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

3)在catkin_ws/src/learning_communication/src文件中编写代码:

  • server.cpp
#include<ros/ros.h>
#include"learning_communication/AddTwoInts.h"
//service回调函数,输入参数req,输出参数res
bool add(learning_communication::AddTwoInts::Request &req,learning_communication::AddTwoInts::Response &res)
{
	//将输入的参数中的请求数据相加,结果放到应答变量中
	res.sum=req.a+req.b;
	ROS_INFO("request: x=%1d,y=%1d",(long int)req.a,(long int)req.b);
	ROS_INFO("sending back response:[%1d]",(long int)res.sum);
	return true;
}
int main(int argc,char **argv)
{
	//ROS节点初始化
	ros::init(argc,argv,"add_two_ints_server");
	//创建节点句柄
	ros::NodeHandle n;
	//创建一个名为add_two_ints的server,注册回调函数add()
	ros::ServiceServer service=n.advertiseService("add_two_ints",add);
	//循环等待回调函数
	ROS_INFO("Ready to add two ints.");
	ros::spin();
	return 0;
}
  • client.cpp
#include<cstdlib>
#include<ros/ros.h>
#include"learning_communication/AddTwoInts.h"
int main(int argc,char **argv)
{
	//ROS节点初始化
	ros::init(argc,argv,"add_two_ints_client");
	//从终端命令行获取两个加数
	if(argc!=3)
	{
		ROS_INFO("usage:add_two_ints_client X Y");
		return 1;
	}
	//创建节点句柄
	ros::NodeHandle n;
	//创建一个client,请求add_two_ints_service
	//service消息类型是learning_communication::AddTwoInts
	ros::ServiceClient client=n.serviceClient<learning_communication::AddTwoInts>("add_two_ints");
	//创建learning_communication::AddTwoInts类型的service消息
	learning_communication::AddTwoInts srv;
	srv.request.a=atoll(argv[1]);
	srv.request.b=atoll(argv[2]);
	//发布service请求,等待加法运算的应答请求
	if(client.call(srv))
	{
		ROS_INFO("sum: %1d",(long int)srv.response.sum);
	}
	else
	{
		ROS_INFO("Failed to call service add_two_ints");
		return 1;
	}
	return 0;
}

4)分别用3个命令窗口执行以下命令,运行可执行文件:

roscore
rosrun learning_communication server
rosrun learning_communication client 整数1 整数2

在这里插入图片描述

  1. 动作编程
    1)什么是动作
  • 一种问答通信系统
  • 带有连续反馈
  • 可以在任务过程终止运行
  • 基于ROS的消息机制实现

2)接口

  • goal:发布任务目标
  • cancel:请求取消任务
  • status:通知客户端当前的状态
  • feedback:周期反馈任务运行的监控数据
  • result:向客户端发送任务的执行结果,只发布一次

3)动作编程流程

  • 创建动作服务器
    • 初始化ROS节点
    • 创建动作服务器实例
    • 启动服务器,等待动作请求
    • 在回调函数中完成动作服务功能的处理,并反馈进度信息
    • 动作完成,发送结束信息
  • 创建动作客户端
    • 初始化ROS节点
    • 创建动作客户端实例
    • 连接动作服务器
    • 发送动作目标
    • 根据不同类型的服务器端反馈处理回调函数
  • 添加编译选项
    • 设置需要编译的代码和生成的可执行文件
    • 设置链接库
    • 设置依赖
  • 运行可执行程序

4)自定义动作消息
定义action文件

mkdir ~/catkin_ws/src/learning_communication/action
sudo nano DoDishes.action
  • DoDishes.action文件代码:
#定义目标信息
uint32 dishwasher_id
---
#定义结果信息
uint32 total_dishes_cleaned
---
#定义周期反馈的消息
float32 percent_complete
  • package.xml中添加功能包依赖:
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
  • CMakeLists.txt添加编译选项:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
  actionlib_msgs 
  actionlib
)

add_message_files(FILES Person.msg)
add_service_files(FILES AddTwoInts.srv)
add_action_files(DIRECTORY action FILES DoDishes.action)
generate_messages(DEPENDENCIES std_msgs actionlib_msgs)

CATKIN_DEPENDS roscpp rospy std_msgs message_runtime

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

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

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

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

add_executable(DoDishes_client src/DoDishes_client.cpp)
target_link_libraries(DoDishes_client ${catkin_LIBRARIES})
add_dependencies(DoDishes_client ${${PROJECT_NAME}_EXPORTED_TARGETS})

add_executable(DoDishes_server src/DoDishes_server.cpp)
target_link_libraries(DoDishes_server ${catkin_LIBRARIES})
add_dependencies(DoDishes_server ${${PROJECT_NAME}_EXPORTED_TARGETS})

5)在catkin_ws/src/learning_communication/src文件中编写代码:

  • DoDishes_server.cpp
#include "ros/ros.h"
#include "actionlib/server/simple_action_server.h"
#include "learning_communication/DoDishesAction.h"
typedef actionlib::SimpleActionServer<learning_communication::DoDishesAction> Server;
// 收到action的goal后调用该回调函数
void execute(const learning_communication::DoDishesGoalConstPtr &goal, Server *as)
{
	ros::Rate r(1);
	learning_communication::DoDishesFeedback feedback;
	ROS_INFO("Dishwasher %d is working.", goal->dishwasher_id);
	// 假设洗盘子的进度,并且按照1Hz的频率发布进度feedback 
	for(int i = 1; i <= 10; i++)
	{
		feedback.percent_complete = i * 10;
		as->publishFeedback(feedback);
		r.sleep();
	}	
	// 当action完成后,向客户端返回结果
	ROS_INFO("Dishwasher %d finish working.", goal->dishwasher_id);
	as->setSucceeded();
}
int main(int argc, char **argv)
{
	ros::init(argc, argv, "do_dishes_server");
	ros::NodeHandle hNode;
	// 定义一个服务器
	Server server(hNode, "do_dishes", boost::bind(&execute, _1, &server), false);
	// 服务器开始运行
	server.start();
	ros::spin();
	return 0;
}
  • DoDishes_client.cpp
#include "ros/ros.h"
#include "actionlib/client/simple_action_client.h"
#include "learning_communication/DoDishesAction.h"
typedef actionlib::SimpleActionClient<learning_communication::DoDishesAction> Client;
// 当action完成后会调用该回调函数一次
void doneCallback(const actionlib::SimpleClientGoalState &state
	, const learning_communication::DoDishesResultConstPtr &result)
{
	ROS_INFO("Yay! The dishes are now clean");
	ros::shutdown();
}
// 当action激活后会调用该回调函数一次
void activeCallback()
{
	ROS_INFO("Goal just went active");
}
// 收到feedback后调用该回调函数
void feedbackCallback(const learning_communication::DoDishesFeedbackConstPtr &feedback)
{
	ROS_INFO("percent_complete : %f", feedback->percent_complete);
}
int main(int argc, char **argv)
{
	ros::init(argc, argv, "do_dishes_client");
	// 定义一个客户端
	Client client("do_dishes", true);
	// 等待服务器端
	ROS_INFO("Waiting for action server to start.");
	client.waitForServer();
	ROS_INFO("Action server started, sending goal.");
	// 创建一个 action 的 goal
	learning_communication::DoDishesGoal goal;
	goal.dishwasher_id = 1;
	// 发送action的goal给服务端,并且设置回调函数
	client.sendGoal(goal, &doneCallback, &activeCallback, &feedbackCallback);
	ros::spin();
	return 0;
}

6)分别用3个命令窗口执行以下命令,运行可执行文件:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值