ROS基础学习(一)

参考古月居胡春旭ROS入门21讲视频等内容,跟着练习整理

ROS基本概念

ROS(Robot Operating System)是一个适用于机器人的开源的元操作系统。是一种新的标准化机器人软件框架,它提供了操作系统应有的服务,为机器人开发提供包括硬件抽象,底层驱动,消息传递,程序管理,应用原型等功能机制,并整合了许多第三方工具和库文件。ROS被设计为一种分布式结构,框架中每个功能模块都可以被单独设计、编译,并且运行时以松散耦合方式结合在一起,它也提供用于获取、编译、编写、和跨计算机运行代码所需的工具和库函数。

工作空间(workspace);是ROS存放工程开发相关文件的文件夹,工作空间中一般包括四个目录空间:src(代码空间):最常用的文件夹,用来储存所有ROS功能包的源码文件;build(编译空间):储存编译过程中的缓存信息和中间文件;devel(开发空间):放置编译生成的可执行文件;install(安装空间):编译成功后使用make install执行安装文件到该空间,运行该空间中的环境变量脚本,在终端运行这些可执行文件。

功能包(package):是ROS应用程序代码的组织单元,每个软件包都可以包含程序库、节点、可执行文件、脚本或者其它手动创建的东西。 创建了相应的软件包才能实现相应的功能。功能包中通常可包含如下文件夹:include(放置头文件)scripts(放置可直接运行的python文件)src(放置需要编译的C++文件)launch(放置启动文件)msg(放置自定义的消息类型)srv(放置自定义的服务类型)action(放置自定义的动作指令)CMakeLists.txt(编译器编译功能包的规则)package.xml(功能包清单:包括功能包名称,版本号,信息描述,作者信息,许可信息等,以及功能包中代码编译所依赖的其他功能包,运行功能包中可执行程序所依赖的其他功能包)

节点(Node):是 ROS 功能包中的一个可执行文件,一个系统通常由多个节点构成,节点之间可以通过 ROS 客户端库(如roscpp 、 rospy )相互通信。一个机器人控制系统由许多节点组成,这些节点各司其职,如,一个节点控制激光距离传感器,一个节点控制轮子的马达,一个节点执行定位,一个节点执行路径规划,一个节点提供系统的整个视图等。也就是能够执行特定工作任务的工作单元。

节点管理器(Master):顾名思义,使所有节点有条不紊执行的模块,节点通过与节点管理器通信来报告他们的注册信息,帮助节点之间相互查找,建立连接,同时还为系统提供参数服务器,管理全局参数,ROS master作为管理者,必不可少。

消息(Message):是一种ROS数据类型。节点之间做重要的通讯机制就是基于发布/订阅模型的消息(Message)通讯,一个节点向特定主题发布消息,从而将数据发送到另一个节点。消息具有一定的类型和数据结构,包括ROS提供的标准类型和用户自定义类型。消息与话题(发布 / 订阅模式)是紧密联系在一起的,msg文件实质上就是一个从结构上描述ROS中所使用消息类型的简单文本,描述的是消息的数据类型。

话题( Topic ): 是节点间用来传输数据的总线,通过主题进行消息路由不需要节点之间直接连接。节点可以发布消息到话题,也可以订阅话题以接收消息。一个话题可能对应有许多节点作为话题发布者和话题订阅者。当然,一个节点可以发布和订阅许多话题。一个节点对某一类型的数据感兴趣,它只需订阅相关话题即可。一般来说。话题发布者和话题订阅者不知道对方的存在。发布者将信息发布在一个全局的工作区内,当订阅者发现该信息是它所订阅的,就可以接收到这个信息。

服务: 当需要直接与节点通信并获得应答时,将无法通过话题来实现,而需要服务(Service)。可以看出,发布 / 订阅模式是一种多对多的传输方式,显然这种传输方式不适用于请求 / 回复的交互方式。请求 / 回复交互方式经常被用于分布式系统中。请求服务通过 Service 来进行,Service 被定义为一对消息结构:一个用于请求,一个用于回复。一个客户通过发送请求信息并等待响应来使用服务( ROS中的请求 / 回复交互方式类似于一个远程函数调用,srv文件类似于函数参数和返回值的定义)。

参数管理器( Parameter Server ): 是节点管理器的一部分,是一个可通过网络访问的共享的多变量字典。节点使用此服务器来存储和检索运行时参数。

消息记录包(Bag): 是一种用于保存和回放 ROS 消息数据的格式。消息记录包是检索传感器数据的重要机制,这些数据虽然很难收集,但是对于发展和测试算法很有必要。

清单(Manifest): 是对于功能包相关信息的描述,用于定义软件包相关元信息之间的依赖关系,这些信息包括版本、维护者和许可协议等。 也就是 package.xml文件。

脚本文件: CMakeLists.txt是一个Cmake的脚本文件,Cmake是一个符合工业标准的跨平台编译系统。这个文件包含了一系列的编译指令,包括应该生成哪种可执行文件,需要哪些源文件,以及在哪里可以找到所需的头文件和链接库。
ROS中有四种通讯方式:Topic(话题)、Service(服务)、Parameter Serivce(参数服务器)、Actionlib(动作库)。

常用指令:

roscore 			//开启rosmaster
catkin_create_pkg 	//创建功能包
rospack  			//获取功能包信息
	rospack find [包名称]    				//找到一个软件包的目录
	rospack list                  //显示出当前的包信息
	rospack depends1 beginner_tutorials        //显示当前包的一级依赖
	rospack depends beginner_tutorials     //显示当前包的所有依赖
catkin_make  //编译工作空间中功能包
rosdep  //自动安装功能包依赖的其他包
roscd [ 本地包名称[/子目录] ] //允许你直接切换(cd)工作目录到某个软件包或者软件包集当中
roscp  //拷贝功能包中文件
rosed  //编辑功能包中文件
rosrun [package_name] [node_name]  //运行功能包中可执行文件
roslaunch  //运行启动文件
rosnode 显示当前运行的ROS节点信息
	rosnode list                  获得运行节点列表
	rosnode info node-name        获得特定节点的信息
	rosnode ping  node-name       测试节点是否连通
	rosnode kill node-name        终止节点
rostopic		//获取有关ROS话题的信息
rossrv       //针对静态服务文件
     rossrv show <pkg_name>/srv_name            显示服务类型的所有信息
     rossrv list                                显示所有服务类型信息
     rossrv md5  <pkg_name>/srv_name             显示服务类型的md5sum信息
     rossrv package <pkg_name>                         列出一个程序包中的所有服务类型
     rossrv packages <srv_name>                         列出包含服务的所有程序包
rossevice     
      rossevice args             //打印服务的参数
      rosservice call             //以命令行的形式调用服务,可以指定具体参数
      rosservice find             //根据服务类型寻找当前服务
      rosservice info             //打印服务信息
      rosservice list              //列出当前服务类型
      rosservice type            //打印服务类型    
      rosservice uri               //打印服务的ROSRPC uri
rosmsg					//查看消息的详细情况

ROS创建工作空间和包

工作空间:一个存放工程开发相关的文件夹
功能包package:是ROS软件的基本单元,包含ROS节点、库、配置文件等
Src:代码空间(source space)功能包代码,配置文件,launch文件
Build:编译空间(build space)
Devel:开发空间(development space)放置编译生成的可执行文件,一些库,脚本
Install:安装空间(install space)

创建工作空间

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

编译工作空间

cd ~/catkin_ws/
catkin_make
catkin _make install(创建安装空间)

创建功能包

catkin_create_pkg test_pkg roscpp rospy std_msgs
创建的功能包名为 test_pkg,依赖c++,python以及msgs等库,后期可添加,生成的test_pkg文件中包含src文件,用来存放代码,其中include用来存放头文件

设置环境变量

source devel/setup.bash

检查环境变量

echo $ROS_PACKAGE_PATH

通过运行小海龟了解ROS基本信息

roscore                               // 启动rosmaster  运行节点管理器
rosrun turtlesim turtlesim_node 	  //运行海龟仿真器节点指令
rosrun turtlesim turtle_teleop_key    //启动键盘操作节点
rqt_graph/rqt                         //是基于qt的可视化工具,rqt_graph为显示系统计算处理工具

Ros中默认单位为m和弧度
## 常用指令:

rosnode 显示系统中所有节点相关信息的指令;
rostopic 显示系统中所有话题相关的指令;
rosmsg 与消息相关的指令;
rosservice 显示与服务相关指令
rosbag
rosnode list 列出系统中所有节点;
rosnode info 节点名称 查看节点具体信息;
rostopic list 列出系统中所有话题;

发布话题:
rostopic pub /话题名/节点类型/数据结构“具体数据”可用tab键补齐
例子:rostopic pub –r 10 /turtle1/cmd_vel geometry_msgs/Twist "linear:
  x: 0.0
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 0.0"
其中-r 10 为频率,以10Hz每秒发送数据,具体数据包括:角速度和线速度。

查看发布话题的消息结构:rosmsg show geometry_msgs/Twist
eg:发布服务请求:rosservice call/spawn”x:5.0
Y:5.0
Theta:0.0
name:’turtle2’”

话题记录:rosbag record –a-O cmd_record/record记录,-a所有信息-o保存为压缩包+名字
话题复现:rosbag play 压缩包名字


# 话题通讯模型
## 创建功能包
cd~/catkin_ws/src
catkin_create_pkg learning_topic rospy std_msgs geometry_msgs turtlesim
## 发布者publisher编程实现
 
### 创建发布者代码
## 订阅者subscriber编程实现
初始化ROS节点
订阅需要的话题
循环等待话题消息,接收到消息后进入回调函数,在回调函数中完成消息处理
话题消息订阅使用
## 自定义话题消息
### 定义msg文件
创建msg文件夹,存放与消息有关的文件。使用touch person.msg命令创建文件,打开后编写msg文件
string name
uint8 sex
uint8 age
uint8 unknow = 0
uint8 male = 1
uint8 female = 2
### 在package.xml中添加功能包依赖
编译依赖message_generation动态产生msg的功能包
执行依赖message_runtime
### 在CMakeList.txt添加编译选项
1.添加功能包依赖find…package(…message_generation)
2.把msg文件编译成对应的程序文件add_message_files(FILES person.msg)
3.声明编译msg文件时需要依赖ros已有的库generate_messages(DEPENDENCIES std_msgs)
4创建msg运行依赖
catkin_package(
  INCLUDE_DIRS include
  LIBRARIES lenrnking_topic
  CATKIN_DEPENDS roscpp rospy std_msgs
  DEPENDS system_lib
  message_runtime
)
5编译catkin_make;根据msg文件生成include头文件,必须采用大写首字母命名
###	编译生成语言相关文件
创建subscriber和publisher文件

在CMakeList.txt中设置需要编译的代码和生成的可执行文件;
add_executable(person_publisher src/person_publisher.cpp)
add_executable(person_subscriber src/person_subscriber.cpp)
设置链接库;
target_link_libraries(person_publisher ${catkin_LIBRARIES}) 
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
添加依赖项
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)

运行roscore;
运行rosrun learning_topic person_subscriber;
运行rosrun learning_topic person_publisher;
可观察到发布者发布信息,订阅者显示打印信息

# 服务通信模型
服务(services)是节点之间通讯的另一种方式。服务允许节点发送请求(request) 并获得一个响应(response)
## 在工作空间src目录下创建learning_service功能包并添加依赖
catkin_create_pkg learning_service roscpp rospy std_msg geometry_msg turtlesim
## 客户端client编程实现
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200602173756793.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzM2NTc1MQ==,size_16,color_FFFFFF,t_70)
创建客户端turtle_spawn节点,发布request给server(海龟仿真器)
当server收到request后会回馈一个response给客户端,client端与server端通过service消息结构建立联系

在创建的learning_service功能包的src中使用命令行创建文件
touch turtle_spawe.cpp  
编写client端程序
```cpp
/**
请求/spawn服务,服务数据类型turtlesim::spawn

**/
#include 
#include 

int main(int argc, char** argv)
{
    //初始化节点 节点名为turtle_spawn
    ros::init(argc,argv,"turtle_spawn");

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

    //发现/spawn服务后,创建一个ServiceClient实例服务客户端,连接名为/spawn的service,请求的数据类型为turtlesim::Spawn
    ros::service::waitForService("/spawn");//查询当前系统中是否有/spawn服务,阻塞型api,循环等待
    ros::ServiceClient add_turtle = node.serviceClient("/spawn");

    //封装初始化turtlesim::Spawn的请求数据
    turtlesim::Spawn srv;//定义名为srv,类型为turtlesim::Spawn的请求数据
    srv.request.x=2.0;
    srv.request.y = 2.0;
    srv.request.name = "turtle2";

    //请求服务调用
    ROS_INFO("Call service to spwan turtle[x:%0.6f,y:%0.6f,name:%s]",
             srv.request.x,srv.request.y,srv.request.name.c_str());
    add_turtle.call(srv);//调用call方法,把封装好的srv发出去,等待服务器反馈。

    //显示服务调用结果
    ROS_INFO("Spwan turtle successfully [name:%s]",srv.response.name.c_str());

    return 0;
}

配置CMakeList.txt编译规则

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

打开learning_service功能包中的CMakeList.txt,在build中声明C++可执行文件并指定库链接另一个库或一个可执行目标:

add_executable(turtle_spawn  src/turtle_spawn.cpp)        //把spawn.cpp代码编译为turtle_spawn规则
 target_link_libraries(turtle_spawn ${catkin_LIBRARIES})     //把生成的turtle_spawn连接到ros中的库

返回到工作空间根目录进行编译
catkin_make
ROS基础学习(一)_第1张图片
编译结束中,可在工作空间目录的devel文件夹中的lib文件夹可找到生成的learning_service功能包,包含代码编译生成的可执行文件turtle_spawn

编译并运行客户端

cd ~/ros_novice
catkin_make
roscore
rosrun turtlesim tuetlesim_node
rosrun learning_service turtle_spawn

ROS基础学习(一)_第2张图片

Server服务器模型

ROS基础学习(一)_第3张图片
在功能包src文件夹下打开命令行 touch turtle_command_server.cpp 创建代码文件

编写服务器端turtle_command_server代码

/**
 * 该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger
 */
 
#include 
#include 
#include 

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

配置CMakeList.txt编译规则

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

打开learning_service功能包中的CMakeList.txt,在build中声明C++可执行文件并指定库链接另一个库或一个可执行目标:

add_executable(turtle_command_server src/turtle_command_server.cpp)        //把spawn.cpp代码编译为turtle_spawn规则
 target_link_libraries(turtle_command_server ${catkin_LIBRARIES})     //把生成的turtle_spawn连接到ros中的库

返回到工作空间根目录进行编译
catkin_make
ROS基础学习(一)_第4张图片

编译并运行客户端

cd ~/ros_novice
catkin_make
roscore
rosrun turtlesim tuetlesim_node
rosrun learning_service turtle_command_server
rosservice call/turtle_command

ROS基础学习(一)_第5张图片
通过反复call service可以实现对海龟的开关命令

服务数据的定义使用

ROS基础学习(一)_第6张图片
Client端request发布显示人信息的请求,把信息通过service端发布数据,server端收到service发布的数据

自定义show_person服务数据结构

1、在功能包下创建srv文件夹,touch show_person.srv
定义.src文件;//—以上为request数据,—以下为response数据
string name
uint8 age
uint8 sex

uint8 unknown = 0
uint8 male =1
uint8 female = 2


string result
2、在package.xml中添加动态生成msg依赖的功能包

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

3、在CMakeList.txt添加编译选项

################################################
## Declare ROS messages, services and actions ##
################################################
在此目录下添加如下命令
add_service_files(FILES Person.srv)
##自动搜索srv下的文件
generate_messages(DEPENDENCIES std_msgs )
##根据文件定义和std_msgs依赖产生对应头文件
添加依赖
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES learning_service
   CATKIN_DEPENDS geometry_msg roscpp rospy std_msg turtlesim message_runtime
#  DEPENDS system_lib
)

在工作目录中使用catkin_make编译生成头文件

创建服务器代码

/*
 * 该例程用于将请求/show_person服务,服务数据类型learning_service::Person
 *
*/
#include
#include


int main(int argc,char** argv)
{
    //初始化节点
    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_serviceL::Person请求数据
    learning_service::Person srv;
    srv.request.name = "Tom";
    srv.request.age = 27;
    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 ;
}

创建客户端代码

/*
 * 该例程将执行/show_person服务,服务数据类型learning_service::Person
 * */
#include
#include

//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 inforntion");

    ros::spin();
    return 0;
}

配置服务器/客户端代码编译规则

add_executable(person_client  src/person_client.cpp)        
##把person_client.cpp代码编译为person_client可执行文件
target_link_libraries(person_client ${catkin_LIBRARIES})     
##把生成的person_client连接到ros中的库
add_executable(person_server  src/person_server.cpp)        
##把person_server.cpp代码编译为person_server可执行文件
target_link_libraries(person_server ${catkin_LIBRARIES})     
##把生成的person_server连接到ros中的库

add_dependencies(person_client ${PROJECT_NAME}_gencpp)
## 和动态生成的头文件做依赖
add_dependencies(person_server ${PROJECT_NAME}_gencpp)
## 和动态生成的头文件做依赖

在工作空间根目录使用catkin_make编译

运行查看

roscore
rosrun learning_service person_server
rosrun learning_service person_client

可查看结果
在这里插入图片描述
运行server会发现等待show_person的数据
ROS基础学习(一)_第7张图片
运行client请求service端show_person,与此同时可发现server端打印显示client发送的信息。

参数模型

参数命令行使用

rosparam list		列出当前所有参数
rosparam get param_key 		显示某个参数值
rosparam set param_key param_value 		设置某个参数值
rosparam dump file_name 	保存参数到文件
rosparam load file_name		从文件读取参数
rosparam delete param_key		删除参数

首先在src中创建参数功能包

catkin_create_pkg learning_parameter roscpp rospy std_srvs

创建parameter.cpp文件

/**
 * 该例程设置/读取海龟例程中的参数
 */
#include 
#include 
#include 

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

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

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

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

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

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

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

    // 读取背景颜色参数
	ros::param::get("/background_r", red);
	ros::param::get("/background_g", green);
	ros::param::get("/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;
}

在CMakeList.txt中添加依赖

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

在工作空间下catkin_make编译
运行测试
roscore
rosrun toutlesim turtlesim_node
rosrun learning_perameter parameter_config
ROS基础学习(一)_第8张图片

你可能感兴趣的:(ROS)