ROS学习笔记——roscpp

PS:采用的视频教程是大学慕课的《机器人操作系统入门》 https://www.icourse163.org/learn/ISCAS-1002580008#/learn/announce, 由于使用的是Ubuntu 18.04,使用的ROS版本为 Melodic 和视频所用的ROS kinetic版本有所出入,产生以下问题:1.使用模块不在,解决方法 用 git clone 克隆;2.加载robot_sim_demo只有地板,没有模型和小车,解决办法 由于gazebo9版本不兼容这个demo的模型,所以要么换低版本gazebo,要么换模型。

ROS中相关函数的使用方法可以见网站:https://docs.ros.org/api/roscpp/html/
以下记录下roscpp中相关的程序,以便自查自用。所有代码均可以从github找到。
https://github.com/DroidAITech/ROS-Academy-for-Beginners

  • topic_demo
    功能描述:两个node,一个发布模拟的GPS消息(格式为自定义,包括坐标和工作状态),另一个接收并处理该信息(处理方式为计算到原点距离)。
    个人笔记:这个topic可以用来处理传感器传入的数据
    步骤:
  1. package
  2. msg
  3. talker.cpp
  4. listener.cpp
  5. CMakeList.txt & package.xml

package

cd ~/catkin_ws/src
catkin_crate_pkg topic_demo roscpp rospy std_msgs

个人笔记:其中catkin_ws是工作空间;第二行指令是创建一个包,后面是依赖包

msg

cd topic_demo
mkdir msg
cd msg
vim gps.msg

gps.msg的内容如下:

float32 x
float32 y
string state

个人笔记:msg文件不能直接使用,要经过catkin_make编译之后,会在 ~/catkin_ws/devel/include/topic_demo 中生成一个 gps.h文件,然后在c++中include这个头文件就可以使用了,使用方法为:topic_demo::gps msg;

talker.cpp

//ROS头文件
#include 
//自定义msg产生的头文件
#include 

int main(int argc, char **argv)
{
  //用于解析ROS参数,第三个参数为本节点名
  ros::init(argc, argv, "talker");

  //实例化句柄,初始化node
  ros::NodeHandle nh;
  
  //创建publisher 
  ros::Publisher pub = nh.advertise("gps_info", 1);

  //自定义gps msg
  topic_demo::gps msg;
  msg.x = 1.0;
  msg.y = 1.0;
  msg.state = "working";

  //定义发布的频率 
  ros::Rate loop_rate(1.0);
  //循环发布msg
  while (ros::ok())
  {
    //以指数增长,每隔1秒更新一次
    msg.x = 1.03 * msg.x ;
    msg.y = 1.01 * msg.y;
    ROS_INFO("Talker: GPS: x = %f, y = %f ",  msg.x ,msg.y);
    //以1Hz的频率发布msg
    pub.publish(msg);
    //根据前面定义的频率, sleep 1s
    loop_rate.sleep();//根据前面的定义的loop_rate,设置1s的暂停
  }

  return 0;
} 

个人笔记:1.ros::Publisher pub = nh.advertise("gps_info", 1);
中第一个参数为发布topic的名称 第二个参数为储存数据的队列长度,队列长度越短,处理的速度越快:2.由于消息是不停发送的,因而需要加一个while循环

listener.cpp

//ROS头文件
#include 
//包含自定义msg产生的头文件
#include 
//ROS标准msg头文件
#include 

void gpsCallback(const topic_demo::gps::ConstPtr &msg)
{  
    //计算离原点(0,0)的距离
    std_msgs::Float32 distance;
    distance.data = sqrt(pow(msg->x,2)+pow(msg->y,2));
    //float distance = sqrt(pow(msg->x,2)+pow(msg->y,2));
    ROS_INFO("Listener: Distance to origin = %f, state: %s",distance.data,msg->state.c_str());
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "listener");
  ros::NodeHandle n;
  ros::Subscriber sub = n.subscribe("gps_info", 1, gpsCallback);
  //ros::spin()用于调用所有可触发的回调函数。将进入循环,不会返回,类似于在循环里反复调用ros::spinOnce()。
  ros::spin(); 
  return 0;
}

个人笔记: 1.ros::Subscriber sub = n.subscribe("gps_info", 1, gpsCallback);中第一个参数是监听的哪个topic,第二个参数是subscriber的队列,第三个参数是回调函数,回调函数是一个指针,用来处理信息的函数。2.void gpsCallback(const topic_demo::gps::ConstPtr &msg)中的ConstPtr是一个长指针,定义在msg文件生成的头文件中。

CMakeList.txt

需要增加修改的地方如下,主要是增加生成自定义massage文件以及生成头文件的宏:

find_package(catkin REQUIRED COMPONENTS
  message_generation
  roscpp
  rospy
  std_msgs
)#生成自定义massage文件
 add_message_files(
   FILES
   gps.msg
)#添加上自定义的msg
 generate_messages(
  DEPENDENCIES
  std_msgs
)#用于生成msg对应的头文件
catkin_package(
  CATKIN_DEPENDS  roscpp rospy std_msgs message_runtime
)
include_directories(
  include
  ${catkin_INCLUDE_DIRS}
)
add_executable(talker src/talker.cpp )#必须添加add_dependencies,否则找不到自定义的msg产生的头文件
add_dependencies(talker topic_demo_generate_messages_cpp)#添加依赖,必须有此句以生成msg
target_link_libraries(talker ${catkin_LIBRARIES})#链接

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

个人笔记:需要将cpp文件放在src文件中,如果不在src中,要修改 src/listener.cpp

package.xml

增加

message_generation
 message_runtime

个人笔记:在Ubuntu18.04中使用的时候,package.xml中有细微区别

  • service_demo
    功能描述:两个node,一个发布请求(格式自定义),另一个接收处理该信息,并返回信息。
    步骤:
  1. package
  2. srv
  3. server.cpp
  4. client.cpp
  5. CMakeList.txt & package.xml

package

同topic_demo的创建方式

srv

同topic_demo

vi Greeting.srv

srv中的内容如下:

string name
int32 age
---
string feedback

个人笔记:生成的头文件有:/Greeting.h /GreetingRequest.h /GreetingResponse.h

server.cpp

// This is the C++ version server file of the service demo
//

// 加载必要文件,注意Service_demo的加载方式
# include "ros/ros.h"
# include "service_demo/Greeting.h"
# include "string"

// 定义请求处理函数
bool handle_function(service_demo::Greeting::Request &req,
					service_demo::Greeting::Response &res)
{
	// 此处我们对请求直接输出
	ROS_INFO("Request from %s with age %d ", req.name.c_str(), req.age);
	
	// 返回一个反馈,将response设置为"..."
	res.feedback = "Hi " + req.name + ". I'm server!";
	return true;
}

int main(int argc, char **argv)
{
	// 初始化节点,命名为"greetings_server"
	ros::init(argc, argv, "greetings_server");
	
	// 定义service的server端,service名称为“greetings”,收到request请求之后传递给handle_function进行处理
	ros::NodeHandle nh;
	ros::ServiceServer service = nh.advertiseService("greetings", handle_function);
	
	// 调用可
	ros::spin();

	return 0;
}

client.cpp

// This is client of the service demo
// 包含必要文件,注意Service文件的包含方式,我们定义的srv文件为Greeting.srv,在包含时需要写成Greeting.h
# include "ros/ros.h"
# include "service_demo/Greeting.h"

int main(int argc, char **argv)
{
	// 初始化,节点命名为"greetings_client"
	ros::init(argc, argv, "greetings_client");
	
	// 定义service客户端,service名字为“greetings”,service类型为Service_demo
	ros::NodeHandle nh;
	ros::ServiceClient client = nh.serviceClient("greetings");
	
	// 实例化srv,设置其request消息的内容,这里request包含两个变量,name和age,见Greeting.srv
	service_demo::Greeting srv;
	srv.request.name = "HAN";
	srv.request.age = 20;

	if (client.call(srv))
	{
		// 注意我们的response部分中的内容只包含一个变量response,另,注意将其转变成字符串
		ROS_INFO("Response from server: %s", srv.response.feedback.c_str());
	}
	else
	{
		ROS_ERROR("Failed to call service Service_demo");
		return 1;
	}
	return 0;
}

CMakeList.txt & package.xml

同topic_demo的修改方式

  • param_demo
    有两种API ros::paramros::NodeHandle

param_demo.cpp

#include

int main(int argc, char **argv){
	ros::init(argc, argv, "param_demo");
	ros::NodeHandle nh;
	int parameter1, parameter2, parameter3, parameter4, parameter5;
	
	//Get Param的三种方法
	//① ros::param::get()获取参数“param1”的value,写入到parameter1上
	bool ifget1 = ros::param::get("param1", parameter1);
	
	//② ros::NodeHandle::getParam()获取参数,与①作用相同
	bool ifget2 = nh.getParam("param2",parameter2);
	
	//③ ros::NodeHandle::param()类似于①和②
	//但如果get不到指定的param,它可以给param指定一个默认值(如33333)
        nh.param("param3", parameter3, 33333);
	
	if(ifget1)
		ROS_INFO("Get param1 = %d", parameter1);
	else
		ROS_WARN("Didn't retrieve param1");
	if(ifget2)
		ROS_INFO("Get param2 = %d", parameter2);
	else
		ROS_WARN("Didn't retrieve param2");
	if(nh.hasParam("param3"))
		ROS_INFO("Get param3 = %d", parameter3);
	else
		ROS_WARN("Didn't retrieve param3");


    //Set Param的两种方法
	//① ros::param::set()设置参数
	parameter4 = 4;
	ros::param::set("param4", parameter4);

	//② ros::NodeHandle::setParam()设置参数
	parameter5 = 5;
	nh.setParam("param5",parameter5);
	
	ROS_INFO("Param4 is set to be %d", parameter4);
	ROS_INFO("Param5 is set to be %d", parameter5);


	//Check Param的两种方法
	//① ros::NodeHandle::hasParam()
	bool ifparam5 = nh.hasParam("param5");

	//② ros::param::has()
	bool ifparam6 = ros::param::has("param6");

	if(ifparam5) 
		ROS_INFO("Param5 exists");
	else
		ROS_INFO("Param5 doesn't exist");
	if(ifparam6) 
		ROS_INFO("Param6 exists");
	else
		ROS_INFO("Param6 doesn't exist");


	//Delete Param的两种方法
	//① ros::NodeHandle::deleteParam()
	bool ifdeleted5 = nh.deleteParam("param5");

	//② ros::param::del()
	bool ifdeleted6 = ros::param::del("param6");
	

	if(ifdeleted5)
		ROS_INFO("Param5 deleted");
	else
		ROS_INFO("Param5 not deleted");
	if(ifdeleted6)
		ROS_INFO("Param6 deleted");
	else
		ROS_INFO("Param6 not deleted");


	ros::Rate rate(0.3);
	while(ros::ok()){
		int parameter = 0;
		
		ROS_INFO("=============Loop==============");
		//roscpp中尚未有ros::param::getallparams()之类的方法
		if(ros::param::get("param1", parameter))
			ROS_INFO("parameter param1 = %d", parameter);
		if(ros::param::get("param2", parameter))
			ROS_INFO("parameter param2 = %d", parameter);
		if(ros::param::get("param3", parameter))
			ROS_INFO("parameter param3 = %d", parameter);
		if(ros::param::get("param4", parameter))
			ROS_INFO("parameter param4 = %d", parameter);
		if(ros::param::get("param5", parameter))
			ROS_INFO("parameter param5 = %d", parameter);
		if(ros::param::get("param6", parameter))
			ROS_INFO("parameter param6 = %d", parameter);
		rate.sleep();
	}
}

你可能感兴趣的:(ROS学习)