第二周作业

本文详细介绍了ROS工作空间的管理、如何创建和编译功能包,以及ROS通信编程的三种方式:话题编程、服务编程和动作编程,包括C++实现的小海龟圆周运动示例。
摘要由CSDN通过智能技术生成

目录

工作空间

​编辑功能包

1.创建功能包

​2.编译功能包

ROS通信编程

1.话题编程

2.服务编程

​3.动作编程

C++编码实现小海龟圆周运动.

 参考资料


工作空间

存放工程开发相关文件的文件夹,其中主要包括src,build,devel,install文件夹。

文件夹名称用途
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的工作空间

2.编译工作空间

cd ..
catkin_make


3.设置环境变量

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


4.检查环境变量

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++编程接口

2.编译功能包

cd ~/catkin_ws
catkin_make

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

ROS通信编程

1.话题编程

步骤:

创建发布者

1.初始化ROS节点
2.向ROS Master注册节点信息,包括发布的话题名和话题中的消息类型
3.按照一定频率循环发布消息


创建订阅者
1.初始化ROS节点
2.订阅需要的话题
3.循环等待话题消息,接受到消息后进行回调函数
4.回调函数中完成消息处理


添加编译选项
1.设置需要编译的代码和生成的可执行文件
2.设置链接库
3.设置依赖


运行可执行程序
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;
}


设置CMakeLists.txt文件

编译


运行可执行文件

roscore
rosrun learning_communication talker
rosrun learning_communication listener

补充内容
自定义话题消息

定义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>


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

在CMakeLists.txt添加编译选项

编译成功


查看自定义消息

rosmsg show Person

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添加编译选项

步骤:

创建服务器

1.初始化ROS节点
2.创建Serve实例
3.循环等待服务请求,进入回调函数
4.在回调函数中完成服务功能的处理,并反馈应答数据
创建客户端

1.初始化ROS节点
2.创建一个Client实例
3.发布服务请求数据
4.等待Serve处理之后的应答结果
添加编译选项

1.设置需要编译的代码和生成的可执行文件
2.设置链接库
3.设置依赖
运行可执行程序
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;
}


设置CMakeLists.txt文件


运行可执行文件
先运行server,再运行client

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


3.动作编程


动作是一种基于ROS消息实现的问答通信机制,它包含连续反馈,可以在任务过程中止运行。
动作(Action)的接口

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

 


自定义动作消息

定义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添加编译选项


步骤:

创建动作服务器

1.初始化ROS节点
2.创建动作服务器实例
3.启动服务器,等待动作请求
4.在回调函数中完成动作服务功能的处理,并反馈进度信息
5.动作完成,发送结束信息
创建动作客户端

1.初始化ROS节点
2.创建动作客户端实例
3.连接动作服务器
4.发送动作目标
5.根据不同类型的服务器端反馈处理回调函数
添加编译选项

1.设置需要编译的代码和生成的可执行文件
2.设置链接库
3.设置依赖
运行可执行程序

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;
}


设置CMakeLists.txt文件


运行可执行文件

roscore
rosrun learning_communication DoDishes_client
rosrun learning_communication DoDishes_server

C++编码实现小海龟圆周运动.

1.创建ROS工作空间

①创建工作空间

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace

②编译工作空间

cd ~/catkin_ws
catkin_make


③编译工作空间

source devel/setup.bash


④检查环境变量

echo $ROS_PACKAGE_PATH

2.创建ROS功能包
①创建功能包

cd ~/catkin_ws/src
catkin_create_pkg learning_topic std_msgs rospy roscpp geometry_msgs


②编译功能包

cd ~/catkin_ws
catkin_make
source devel/setup.bash


3.C++代码
①.创建publisher.cpp文件
在位置~/catkin_ws/src/learning_topic/src/下创建publisher.cpp文件.

②.发布话题的逻辑:

1.初始化ROS节点;
2.向ROS Master注册节点信息,包括发布的话题名和话题中的消息类型;
3.创建消息数据;
4.按照一定频率循环发布消息。

③.C++代码:

#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "geometry_msgs/Twist.h"

int main(int argc, char **argv)
{
	//初始化节点
	ros::init(argc, argv, "publisher");
	//创建节点句柄
	ros::NodeHandle n;
	//创建publisher,发布话题[/turtle1/cmd_vel],消息类型[geometry_msgs::Twist]
	ros::Publisher chatter_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 1000);
	//设置循环频率
	ros::Rate loop_rate(100);
	while (ros::ok())
	{
		//定义发布的数据
		geometry_msgs::Twist msg;
		//定义linear数据
		geometry_msgs::Vector3 linear;
		linear.x=1;
		linear.y=0;
		linear.z=0;
		//定义angular数据
		geometry_msgs::Vector3 angular;
		angular.x=0;
		angular.y=0;
		angular.z=1;
		msg.linear=linear;
		msg.angular=angular;
		//发布msg数据
		chatter_pub.publish(msg);
		//循环等待回调函数
		ros::spinOnce();
		//按照循环频率延时
		loop_rate.sleep();
	}
	return 0;
}

4.添加编译选项

Python在ubuntu是可以直接执行的脚本语言,而C++是编译语言,在执行之前需要经过编译过程,因此在编译工作空间之前需要配置文件。

cd ~/catkin_ws/src/learning_topic
gedit CMakeLists.txt


在最后加上以下代码

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


保存退出,再进行编译命令。

cd ~/catkin_ws
catkin_make


5.启动小海龟并实现控制

roscore
#打开新终端
rosrun turtlesim turtlesim_node
#打开新终端
cd ~/catkin_ws
#下面这一步是为了保证rosrun命令能够找到相应的功能包,有可以省去这一步骤的方法,各位可以自行查找
source ~/catkin_ws/devel/setup.bash
rosrun learning_topic publisher

完成命令后可以看到小海龟以圆周运动.

 参考资料

ROS机器人开发入门

[ROS基础-6] 发布话题消息控制Turtlesim运动实验
ROS通信实操(小乌龟通信实现)(九)C++、Python

           

  • 26
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值