ROS基础

ROS

ROS架构的三个层次:

  1. 基于Linux的OS层;
  2. 实现ROS核心通信机制以及众多机器人开发库的中间层;
  3. 在ROS Master的管理下保证功能节点的正常运行的应用层。

ROS从系统实现角度划分成的三个层次:文件系统、计算图和开源社区
ROS的三种通信机制:基于Publish(发布)/Subscribe(订阅)的Topic(话题)通信

1、ROS客户端库

roscpp:ROS客户端库之一,实现大部分ROS概念,可用于高性能应用程序
rospy:ROS客户端库的Python纯实现。这个库的优点是易于原型设计,开发时间短,
roslisp:LISP的客户端库,通常用于构建机器人规划库。

2、ROS中的文件系统结构

在这里插入图片描述

2.1 工作空间与功能包

在这里插入图片描述

2.1.1 功能包(package)结构说明:

package常见的文件有:
├── CMakeLists.txt #package的编译规则(必须);定义package的包名、依赖、源文件等
├── package.xml #package的描述信息(必须);描述package的包名、版本号、作者、依赖等
├── src/ #存放ROS源代码文件;包括C++的源码和(.cpp)以及Python的module(.py)
├── include/ #存放C++源码对应头文件
├── scripts/ #可执行脚本;例如shell脚本(.sh)、Python脚本(.py)
├── msg/ #自定义消息;存放自定义格式的消息(.msg)
├── srv/ #自定义服务;存放自定义格式的服务(.srv)
├── models/ #3D模型文件;存放机器人或仿真场景的3D模型(.sda, .stl, .dae等)
├── urdf/ #urdf文件;存放机器人的模型描述(.urdf或.xacro)
├── launch/ #launch文件;存放launch文件(.launch或.xml)
定义package的是CMakeLists.txtpackage.xml,这两个文件是package中必不可少的。catkin编译系统在编译前,首先就要解析这两个文件。这两个文件就定义了一个package。
通常ROS文件组织都是按照以上的形式,这是约定俗成的命名习惯,建议遵守。以上路径中,只有CMakeLists.txtpackage.xml是必须的,其余路径根据软件包是否需要来决定

2.1.2 ROS相关的命令

3、ROS命令总结

命令功能
roscore启动ROS主节点Master
rosrun运行一个可执行程序并生成节点
rosnode显示ROS节点信息,(可查看其他子命令)
rostopic显示ROS主题信息
rosmsg显示ROS消息类型信息
rosservice显示各种服务的运行时信息,并运行显示发送该主题的消息
rosparam用于获取和设置节点使用的参数

参考文档:ROS命令

2.1.3 创建catkin工作空间

命令:

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_create_pkg my_pkg_1 std_msgs rospy roscpp
// catkin_create_pkg <package_name> [depend1] [depend2] [depend3]
cd ..
catkin_make
source ~/catkin_ws/devel/setup.bash

3 自定义功能包清单package.xml

<!-- 注释 -->
<!-- 定义文档的语法 ,内容遵循xml的1.0版本 -->
<?xml version="1.0"?>
<!-- 标签package 起始 ROS功能包的配置部分-->
<package format="2">

   //  包的名字(可随时更改),包的版本
  <name>my_pkg1</name>
  <version>0.0.0</version>
  
   // 描述标签<description>,将描述信息修改为任何你喜欢的内容,(应该为包的功能)  
  <description>The my_pkg1 package</description>

  // 需要一个维护人员标签,允许多个,每个标签一个人(维护者的邮箱,名字)
  <!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
  <maintainer email="peen@todo.todo">peen</maintainer>

  // 需要一个许可证标签,允许多个,每个标签一个许可证
  // 常用的许可证:BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 
  <license>TODO</license>


  // Url标记是可选的,但允许多个,每个标记一个(记录描述功能包的说明)
  // 可选属性类型可以是:website, bugtracker, or repository
  // 例如:
  <!-- <url type="website">http://wiki.ros.org/my_pkg1</url> -->

  // 作者标签使可选的,允许多个,每个标记一个(邮箱,名字)
  // 作者可以不是维护者,但可以是
  // 例如:
  <!-- <author email="jane.doe@example.com">Jane Doe</author> -->


  // *depend 标记用于指定依赖项
  // 依赖项可以是catkin包或系统依赖项
 
  // 例如:使用depend作为同时是build和exec依赖项的包的快捷方式
  // 即,代替 build_depend,exec_depend。
  <!--   <depend>roscpp</depend> -->
  // 上述 depend 标签依赖,相当于一下两句内容
  <!--   <build_depend>roscpp</build_depend> -->
  <!--   <exec_depend>roscpp</exec_depend> -->

  // 编译时需要的包,使用build_depend标签
  <!--   <build_depend>message_generation</build_depend> -->
  
  // 为了根据这个包进行构建,使用 build_export_depend标签
  // 构建导出依赖关系,如果功能包导出头文件,则其他包可能需要这个包,就应该使用这个标签,用来传递声明
  <!-- Use build_export_depend for packages you need in order to build against this package: -->
  <!--   <build_export_depend>message_generation</build_export_depend> -->
  
  // 对构建工具的包,使用 buildtool_depend标签
  // 构建工具依赖关系指定此软件包需要构建自身的构建系统工具。通常唯一的构建工具是 catkin
  <!--   <buildtool_depend>catkin</buildtool_depend> -->
  
  // 对运行时需要的包,使用 exec_depend标签
  // 执行依赖关系,指定此程序包中运行代码所需的软件包,如动态链接库,可执行文件,Python模块,脚本文件
  <!--   <exec_depend>message_runtime</exec_depend> -->
  
  // 对于仅用于测试的包,使用test_depend标签
  <!--   <test_depend>gtest</test_depend> -->
  
  // 对于仅用于生成文档的包,使用doc_depend标签
  <!--   <doc_depend>doxygen</doc_depend> -->

  // 例如:
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_export_depend>msgs</build_export_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>rospy</build_export_depend>
  <exec_depend>std_msgs</exec_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>rospy</exec_depend>

  // 导出标记 包含其他未指定的标记
  <!-- The export tag contains other, unspecified, tags -->  
  <export>
   // 其他工具可以请求在此处放置其他信息
    <!-- Other tools can request additional information be placed here -->
  </export>
  
<!-- 标签package 结束 -->  
</package>

4 ROS 计算图(ROS Computation Graph)

计算图(Computation Graph)是一个由ROS进程组成的点对点网络,它们能够共同处理数据。ROS的基本计算图概念有:节点(Nodes)、主节点(Master)、参数服务器(Parameter Server)、消息(Messages)、服务(Services)、话题(Topics)和包(Bags)

参考文档:ROS Concepts

roscpp:ROS客户端库之一,实现大部分ROS概念,可用于高性能应用程序
rospy:ROS客户端库的Python纯实现。这个库的优点是易于原型设计,开发时间短,
roslisp:LISP的客户端库,通常用于构建机器人规划库。

参考文档:ROS Client Libraries

5 使用rqt_console和rqt_logger_level

rqt_console连接到了ROS的日志框架,以显示节点的输出信息。rqt_logger_level允许我们在节点运行时改变输出信息的详细级别,包括Debug、Info、Warn和Error`。

日志记录器级别
Fatal (致命)
Error (错误)
Warn (警告)
Info (信息)
Debug (调试)

Fatal是最高优先级,Debug是最低优先级。通过设置日志级别,你可以获得所有优先级级别,或只是更高级别的消息。比如,将日志级别设为Warn时,你会得到Warn、Error和Fatal这三个等级的日志消息。

6 roslaunch

使用roslaunch来启动多个节点和一个模仿者节点。roslaunch可以用来启动定义在launch(启动)文件中的节点。
命令:roslaunch [package] [filename.launch]

使用roslaunch时,是不用启动roscore,roscore自动启动

<!-- 定义文档的语法 ,内容遵循xml的1.0版本 -->
<?xml version="1.0"?>

<!-- launch标签开头 -->
<launch>

<!-- 共享一个名称空间或重新映射组封闭元素 -->
<!-- 创建了分组,并以命名空间(namespace)标签来区分 -->
<!-- 启动节点多了可以分分组,不同组可以使用不同的命名空间,使得节点参数不冲突。该标签套在<node>标签外 -->
<group ns="turtlesim1">
       
<!-- 启动节点 -->
<!-- pkg是工作空间中节点包的名称,name是给这个节点起的名字, type是包中需要运行的具体节点,其指向的文件必须有对应的可执行文件。-->
node属性:pkg 节点包,type节点类型,必须要有对应的可执行文件,name节点名称,args 传递到节点的参数,respawn 自动重启
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>



<!-- 包含其它launch文件,被包含的launch文件将会被一同启动 -->
<include file="$(find ur5_e_moveit_config)/launch/move_group.launch">

<!-- 这个标签与上述<param>标签翻译完全一致,都是“参数”, 但<param>所设置的参数是可以在ros参数服务器中查看并设置的,而<arg>只是当前launch文件所使用的内部参数,外部不可见。-->
<arg name="load_robot_description" value="true"/>

<!-- 对参数服务器进行参数设置 -->
<!-- name是参数在程序内部的名称,value是给对应参数赋值。 -->
<param name="/use_gui" value="false"/> 
<param name="scale_linear" value="0.1" type="double"/>

<!-- 声明映射名 -->
<remap from="input" to="turtlesim1/turtle1"/>
 <remap from="output" to="turtlesim2/turtle1"/>

<!-- 使用rosparam文件启动设置ROS参数 -->
<!-- 当参数特别多时,全部写在launch文件里就不方便了,为此,可以通过加载文件来传递参数,上述代码从4个文件加载了参数,其中costmap_common_params.yaml文件中的参数值被传递到两个不同的命名空间中 -->
<rosparam file="$(find rbx1_nav)/config/fake/costmap_common_params.yaml" command="load" ns="global_costmap" />
<rosparam file="$(find rbx1_nav)/config/fake/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find rbx1_nav)/config/fake/local_costmap_params.yaml" command="load" />
<rosparam file="$(find rbx1_nav)/config/fake/global_costmap_params.yaml" command="load" />
<rosparam file="$(find rbx1_nav)/config/fake/base_local_planner_params.yaml" command="load" />

<!-- 结尾,launch文件的XML标签闭合 -->   
</launch>

7 创建ROS消息和服务

msg(消息):msg文件就是文本文件,用于描述ROS消息的字段。它们用于为不同编程语言编写的消息生成源代码。

srv(服务):一个srv文件描述一个服务。它由两部分组成:请求(request)和响应(response)。

msg文件存放在软件包的msg目录下,srv文件则存放在srv目录下。

msg文件就是简单的文本文件,每行都有一个字段类型和字段名称。可以使用的类型为:
int8, int16, int32, int64 (以及 uint*)
float32, float64
string
time, duration
其他msg文件
variable-length array[] 和 fixed-length array[C]

ROS中还有一个特殊的数据类型:Header,它含有时间戳和ROS中广泛使用的坐标帧信息。在msg文件的第一行经常可以看到Header header。

srv文件和msg文件一样,只是它们包含两个部分:请求和响应。这两部分用一条—线隔开。下面是一个srv文件的示例:

int64 A
int64 B
---
int64 Sum
//在上面的例子中,A和B是请求, Sum是响应。

7.1 创建msg

roscd package1
mkdir msg
echo “int64 num” > msg/Num.msg

不过还有关键的一步:我们要确保msg文件能被转换为C++、Python和其他语言的源代码。
打开package.xml, 确保它包含以下两行且没有被注释。如果没有,添加进去:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
注意,在编译时,其实只需要message_generation,而在运行时,我们只需要message_runtime。

在CMakeLists.txt文件中,为已经存在里面的find_package调用添加message_generation依赖项,这样就能生成消息了。直接将message_generation添加到COMPONENTS列表中即可,如下所示:

find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)

在这里插入图片描述

7.2 创建srv

roscd package1
mkdir srv
从另一个包复制现有的srv定义,而不是手动创建新的srv
roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv

还有关键的一步:我们要确保msg文件能被转换为C++、Python和其他语言的源代码。

如果没做过上面的教程,请先打开package.xml,确保它包含以下两行且没有被注释。如果没有,添加进去:

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
如前所述,在构建时,其实只需要message_generation,而在运行时,我们只需要message_runtime。

在这里插入图片描述

7.3 一般步骤

在这里插入图片描述

8 编写简单的发布者(C++)

<< 注释---头文件
<< ros/ros.h包括了使用ROS系统中最常见的公共部分所需的全部头文件
#include <ros/ros.h>

<< 节点发布String类型的消息,需要包含该消息类型的头文件String.h。
#include <std_msgs/String.h>
#include <sstream>


int main(int argc, char **argv)
{

  <<  初始化ROS节点,必须在起始位置
  <<  前两个参数是命令行或launch文件输入的参数,可以用来完成命名重映射等功能。
  <<3个参数定义了发布者(Publisher)节点的名称,而且该名称在运行ROS中必须是唯一的,不允许存在相同名称的两个节点。
  ros::init(argc, argv, "talker");


  << NodeHandle是与ROS系统通信的主要接入点。
  <<  创建节点句柄,方便对节点资源的使用和管理
  << 创建的第一个NodeHandle实际上将执行节点的初始化,而最后一个被销毁的NodeHandle将清除节点所使用的任何资源。
  ros::NodeHandle n;


  <<  advertise()函数的作用是告诉ROS发布者的主题名
  <<  advertize()返回一个Publisher对象,该对象允许您通过调用publish()来发布有关该主题的消息。
  <<  一旦销毁了返回的Publisher对象的所有副本,该主题将自动取消播发。advertise()的第二个参数是用于
  <<  发布消息的队列大小(最多缓存的消息数),多于该大小的缓存数就丢弃旧消息()
  <<  NodeHandle::advertise()返回一个ros::Publisher对象,
  << 它有2个目的:其一,它包含一个publish()方法,可以将消息发布到创建它的话题上;
  << 其二,当超出范围时,它将自动取消这一宣告操作。
  << 在ROS Master端创建一个发布者(Publisher),发布名为chatter的topic,消息类型为std_msgs::String
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);


  <<  设置循环的频率,单位Hz,此处设置10Hz,它会记录从上次调用Rate::sleep()到现在已经有多长时间,并休眠正确的时间
  <<  当调用Rate::sleep()时,ROS节点会根据此处频率休眠相应的时间,以保证循环维持一致的时间周期
  ros::Rate loop_rate(10);


  <<  记录发送的消息数,用于为每条消息创建唯一的字符串
  int count = 0;
  
  
  <<  进入节点的主循环,在节点未发生异常的情况下将一直运行,一旦发生异常,ros::ok()返回false,跳出循环。
  <<  默认情况,roscpp将安装一个SIGINT处理程序,它能够处理Ctrl+C操作,让ros::ok()返回false
  <<  ros::ok()在以下情况会返回false<<      1.收到SIGINT信号(Ctrl+C)
  <<      2.被另一个同名的节点踢出了网络
  <<      3.ros::shutdown()被程序的另一部分调用
  <<      4.所有的ros::NodeHandles都已被销毁
  while (ros::ok())
  {


    <<  ROS中定义了很多通用的消息类型,这里使用标准的String消息类型,其只有一个成员 data
    <<  初始化std_msgs::String类型的消息
    std_msgs::String msg;
    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();


    <<  ROS_INFO和它的朋友们可用来取代printf/cout。
    <<  将发布的消息进行打印,确保发出的数据符合要求
    ROS_INFO("%s", msg.data.c_str());

   
    <<  publish()函数的功能是发送消息,参数是消息(message)对象;该对象的类型必须与advertise<>()调用的模板参数给定的类型一致
    <<  发布消息,消息发布后,Master会查找订阅该话题的节点,并且帮助两个节点建立连接,完成消息的传输
    chatter_pub.publish(msg);


    <<  ros::spinOnce()用来处理节点订阅话题的所有回调函数
    <<  循环等待回调函数(建议默认加上该函数)
    ros::spinOnce();


    <<  现在发布者(Publisher)一个周期的工作完成,可让节点休息,调用休眠函数loop_rate.sleep(),进入休眠。
    <<  使用ros::Rate在剩下的时间内睡眠,以让我们达到10Hz的发布速率
    <<  按照循环频率延时
    loop_rate.sleep();
    
    
    ++count;
  }


<<  以上详细说明了发布者Publisher节点的实现过程,该结点较为简单,但麻雀虽小,五脏俱全,其包含了实现一个Publisher的所有流程,总结如下:
<<  1、初始化ROS节点
<<  2、向ROS Master注册节点消息,包括发布的话题名和话题中的消息类型
<<  3、以ros::Rate loop_rate()中设定的速率循环发布消息

  return 0;
}

9 编写订阅者节点

<<  头文件
#include "ros/ros.h"
#include "std_msgs/String.h"


<<  回调函数是订阅者(Subscriber) 节点接收消息的基础机制,当有消息到达 chatter(发布者话题名) 话题时,会自动以消息指针
<<  作为参数,再调用回调函数,完成对消息内容的处理
<<  接收到订阅的消息后,会进入到消息回调函数
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)节点声明自己订阅的消息话题,该消息会在ROS Master中注册。
  <<  Master会关注系统中是否有发布该话题的节点,如果有就帮助建立两个节点的连接,完成数据传输。
  <<  NodeHandle::subscribe()用来创建一个Subscriber。第一个参数为消息话题,第二个参数为接受消息队列的大小。
  <<  当消息入队数量超过设置的队列大小时,会自动舍弃最早的消息。
  <<  第三个参数是接受到话题消息后的回调函数。
  <<  创建一个Subscriber,订阅者名为chatter的话题,注册回调函数chatterCallback
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);


  <<  ros::spin()启动了一个自循环,它会尽可能快地调用消息回调函数。
  <<  一旦ros::ok()返回false,ros::spin()就会退出,
  <<  这意味着ros::shutdown()被调用了,主节点让我们关闭(或是因为按下Ctrl+C,它被手动调用)
  <<  循环等待回调函数
  ros::spin();

  return 0;
}

<<  实现Subscriber的简单流程,总结如下:
<<  1、初始化ROS节点。
<<  2、订阅需要的话题。
<<  3spin()循环等待话题消息,接受到消息后进入回调函数chatterCallback()<<  4、在回调函数中完成消息处理。

10 CMakeList.txt文件介绍

10.1 总体结构和顺序

CMakeList.txt文件必须遵循如下的格式,不然就无法正确地编译(编译ros软件包时提示“ros未定义的引用”的错误,原因就是CMakeList.txt文件中命令顺序不正确)。

必需的CMake版本:cmake_minimum_required()
软件包名:project()
查找编译依赖的其他CMake/Catkin包(声明依赖库):find_package()
启动Python模块支持:catkin_python_package()
消息/服务/操作(Message/Service/Action)生成器:add_message_files(),add_service_files(),add_action_files()
调用消息/服务/操作生成:generate_messages()
指定包编译信息导出:catkin_package()
添加要编译的库和可执行文件:add_library()/add_executable()/target_link_libraries()
测试编译:catkin_add_gtest()
安装规则:install()

常用变量:
1、预定义变量
PROJECT_SOURCE_DIR:工程的根目录
PROJECT_BINARY_DIR:运行 cmake 命令的目录,通常是 ${PROJECT_SOURCE_DIR}/build
PROJECT_NAME:返回通过 project 命令定义的项目名称
CMAKE_CURRENT_SOURCE_DIR:当前处理的 CMakeLists.txt 所在的路径
CMAKE_CURRENT_BINARY_DIR:target 编译目录
CMAKE_CURRENT_LIST_DIR:CMakeLists.txt 的完整路径
CMAKE_CURRENT_LIST_LINE:当前所在的行
CMAKE_MODULE_PATH:定义自己的 cmake 模块所在的路径,SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块
EXECUTABLE_OUTPUT_PATH:重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH:重新定义目标链接库文件的存放位置


2. 环境变量
使用环境变量
 $ENV{Name}
写入环境变量
 set(ENV{Name} value) # 这里没有“$”符号
3. 系统信息
­CMAKE_MAJOR_VERSION:cmake 主版本号,比如 3.4.1 中的 3
­CMAKE_MINOR_VERSION:cmake 次版本号,比如 3.4.1 中的 4
­CMAKE_PATCH_VERSION:cmake 补丁等级,比如 3.4.1 中的 1
­CMAKE_SYSTEM:系统名称,比如 Linux-­2.6.22
­CMAKE_SYSTEM_NAME:不包含版本的系统名,比如 Linux
­CMAKE_SYSTEM_VERSION:系统版本,比如 2.6.22
­CMAKE_SYSTEM_PROCESSOR:处理器名称,比如 i686
­UNIX:在所有的类 UNIX 平台下该值为 TRUE,包括 OS X 和 cygwin
­WIN32:在所有的 win32 平台下该值为 TRUE,包括 cygwin

4. 主要开关选项
BUILD_SHARED_LIBS:这个开关用来控制默认的库编译方式,如果不进行设置,使用 add_library 又没有指定库类型的情况下,默认编译生成的库都是静态库。如果 set(BUILD_SHARED_LIBS ON) 后,默认生成的为动态库
CMAKE_C_FLAGS:设置 C 编译选项,也可以通过指令 add_definitions() 添加
CMAKE_CXX_FLAGS:设置 C++ 编译选项,也可以通过指令 add_definitions() 添加
add_definitions(-DENABLE_DEBUG -DABC) # 参数之间用空格分隔

10.2 模板及说明

使用CMake编译程序时,cmake指令依据CMakeLists.txt 文件生成makefiles文件,make命令再依据makefiles文件编译链接生成可执行文件。

<< 注释
<< CMakeLists.txt都要以此开始,指定cmake的最小版本,
<< 如下:指定catkin的编译需要 3.0.2 以上版本的cmake
cmake_minimum_required(VERSION 3.0.2)

<< 通过project()函数指定包的名字,在CMake中指定后,可在其他地方使用变量${PROJECT_NAME}来引用包的名字
<<  例如:包的名字为my_pkg,则${PROJECT_NAME} == my_pkg
<<  my_pkg 应于package.xml 中的 <package name 中一致,
<<  也可以修改为与功能包文件夹的名字不一致,但是 rosrun package_name package_node 中的
<<  package_name 应该应用project(package_name)中的名字
<< 设置功能包名称 my_pkg
project(my_pkg)

<< 设置编译选项,此函数添加的编译选项是针对所有编译器的(包括c和c++编译器)
## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

<< 指明构建 功能包(my_pkg1) 所需要的依赖的包(package),由于使用catkin_make的编译方式,catkin包必须
<< find_package()语法:一个包被find_package,就会有一些CMake变量产生,这些变量在CMake的脚本中用到,
<< 这些变量描述了所依赖的包的输出头文件、源文件、库文件在哪里。
<< 变量的名字依照的惯例是:<PACKAGENAME>_<PROPERTY>,比如:
<< <NAME>_FOUND:这个变量说明这个库是否被找到,如果找到就被设置为true,否则设为false<< <NAME>_INCLUDE_DIRS or<NAME>_INCLUDES:这个包输出的头文件目录;
<< <NAME>_LIBRARIES or <NAME>_LIBS:这个包输出的库文件。
<< 所需要的包都可通过这种方式包含进来,如还需要roscpp、rospy、std_msgs;则可写成:
<< find_package(roscpp REQUIRED)
<< find_package(rospy REQUIRED)
<< find_package(std_msgs REQUIRED)
<< 如上述写,则每个依赖的package都会产生几个变量,很不方便,可将上述形式合并如下写:
<< find_package(catkin REQUIRED COMPONENTS
<<               roscpp
<<               rospy
<<               std_msgs
<<               message_generation
<<    ) 
<< 
<< 这样,它会把所有pacakge里面的头文件和库文件等等目录加到一组变量上,比如:
<< catkin_INCLUDE_DIRS,这样,我们就可以用这个变量查找需要的文件了。最终就只产生一组变量了。
find_package(catkin REQUIRED COMPONENTS
  msgs
  roscpp
  rospy
)


<< 如果使用C++和Boost库,需要在Boost上调用 find_package(),并指定Boost中将要作为组件的那部分。
<< 例如,如果想要使用Boost的线程,可以用:find_package(Boost REQUIRED COMPONENTS thread)
# find_package(Boost REQUIRED COMPONENTS system)

<< 如果ROS软件包提供了一些Python模块,就要创建一个setup.py文件并调用:catkin_python_setup()
<< 该调用要在generate_message()catkin_package()的调用之前。
<< 如果包(package)有 setup.py,取消注释。
<< 此宏确保安装其中声明的模块和全局脚本
<< 参考文档: http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()


################################################
## Declare ROS messages, services and actions ##
################################################

<< 声明和编译包的 messages,services 或者 actions,如下步骤:

## * Let MSG_DEP_SET be the set of packages whose message types you use in
##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
##   * add a build_depend tag for "message_generation"
##   * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in
##     but can be declared for certainty nonetheless:
##     * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
##   * add "message_generation" and every package in MSG_DEP_SET to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * add "message_runtime" and every package in MSG_DEP_SET to
##     catkin_package(CATKIN_DEPENDS ...)
##   * uncomment the add_*_files sections below as needed
##     and list every .msg/.srv/.action file to be processed
##   * uncomment the generate_messages entry below
##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

<< 在被ROS软件包编译和使用之前,ROS中的消息(.msg)、服务(.srv)和操作(.action)文件需要特殊的预处理器编译步骤。
<< 这些宏的要点是生成编程语言特定的文件,以便可以在编程语言中使用messages、services和actions。
<< 编译系统将使用所有可用的生成器(例如gencpp、genpy、genlisp)生成绑定。
<< 提供了三个宏来分别处理messages、services和actions:
<< add_message_files
<< add_service_files
<< add_action_files
<< 这些宏后面必须调用一个调用生成的宏:generate_messages()

<< 在msg文件夹产生 messages 
# add_message_files(
#   FILES
#   Message1.msg
#   Message2.msg
# )

<< 在srv文件夹产生 services
# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )

<< 在action文件夹产生 actions
# add_action_files(
#   FILES
#   Action1.action
#   Action2.action
# )

<< 使用此处列出的任何依赖项生成添加的消息和服务
# generate_messages(
#   DEPENDENCIES
#   std_msgs  # Or other packages containing msgs
# )


################################################
## Declare ROS dynamic reconfigure parameters ##
################################################

## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
##   * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
##   * add "dynamic_reconfigure" to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * uncomment the "generate_dynamic_reconfigure_options" section below
##     and list every .cfg file to be processed

<< 在cfg文件夹产生 dynamic reconfigure parameters
# generate_dynamic_reconfigure_options(
#   cfg/DynReconf1.cfg
#   cfg/DynReconf2.cfg
# )

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package



<< catkin_package()是一个由catkin提供的CMake宏。需要指定特定的catkin信息到编译系统,而这些信息又会被用于生成pkg-config和CMake文件(指出cmake生成pkg-config和CMake文件所必须的信息)<< 该函数必须在使用 add_library()add_executable()声明任何targets之前调用。其5个可选参数:
<< INCLUDE_DIRS:软件包导出的头文件路径(例如cflags),如果包 包含头文件,去掉注释
<< LIBRARIES:项目导出的库(在此项目中创建的依赖项目也需要的库)
<< CATKIN_DEPENDS:当前项目依赖的其他catkin包
<< DEPENDS:当前项目依赖的非catkin CMake包
<< CFG_EXTRAS:其他的配置选项
<< 这里表明软件包文件夹中的include文件夹是导出头文件的位置,
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES my_pkg1
#  CATKIN_DEPENDS msgs roscpp rospy
#  DEPENDS system_lib
)

<< 例如:
<< catkin_package( INCLUDE_DIRS include  
<<                     LIBRARIES ${PROJECT_NAME}   
<<                     CATKIN_DEPENDS roscpp nodelet   
<<                     DEPENDS eigen opencv
<< )
<< 这里表明软件包文件夹中的include文件夹是导出头文件的位置,
<< CMake环境变量 ${PROJECT_NAME}将会鉴定之前传递给project()函数的所有内容,
<< 在这种情况下它作为“robot_brain”。“roscpp”+“nodelet”是编译/运行此程序包需要存在的软件包,
<< “eigen”+“opencv”是编译/运行此程序包时需要存在的系统依赖项(ROS packages有时会需要操作系统提<< 供一些外部函数库,这些函数库就是所谓的“系统依赖项”)。

catkin_package()宏必须包含一个在message_runtime上的CATKIN_DEPENDS依赖。

###########
## Build ##
###########

## Specify additional locations of header files指定头文件的其他位置
## Your package locations should be listed before other locations
您的包的位置应列在其他位置之前

头文件和库路径,
在指定目标之前,需要指定可以为所述目标找到资源的位置,特别是头文件和库:
头文件目录:将要编译的代码(C/C++)所需的头文件路径

<< include_directories()的参数应该是由调用find_package生成的* _INCLUDE_DIRS变量以及
<< 需要包含的任何其他目录。如果使用catkin和Boost,include_directories()的调用为:
<< include_directories(include {Boost_INCLUDE_DIRS} {catkin_INCLUDE_DIRS})
<< 第一个参数“include”表示包中的include/目录也是路径的一部分
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)


<< 声明C++<< CMake函数 add_library()指定用于编译的库文件,默认情况下,catkin编译共享库。
# add_library(${PROJECT_NAME}
#   src/${PROJECT_NAME}/my_pkg1.cpp
# )



## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})


<<  声明C++可执行文件
<<  采用catkin_make在CMake中编译所有的包
<<  前缀可以确保包之间的目标名称不冲突
<<  设置需要编译的代码和生成的可执行文件,
<<1个参数:期望生成的可执行文件的名称
<<2个参数:参与编译的源码文件名称(.cpp)
<<  如果有多个代码文件,则可在后面依次列出
# add_executable(${PROJECT_NAME}_node src/my_pkg1_node.cpp)


<<  重命名C++可执行文件使其没有前缀(去掉前缀)
<<  上面推荐的前缀使得目标的名字较长,为方便用户使用将目标名字重命名为短名字。
<<  例如:用 rosrun someones_pkg node 代替 rosrun someones_pkg someones_pkg_node
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
<<  用于设置依赖
<<  在很多应用中,需要定义与语言无关的消息类型,消息类型会在编译过程中产生相应的语言代码,如果编译文件
<<  依赖这些动态生成的代码,则需要使用add_dependencies()添加
<<  ${PROJECT_NAME}_generate_messages_cpp配置,即该功能包动态产生的消息代码。
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})


<< 指定要链接库或可执行目标的库
<< 使用 target_link_libraries()函数指定可执行目标所要链接的库,即告诉CMake当链接此可执行文件时
<< 需要链接哪些库(这些库在上面的find_package中定义),通常在调用完add_executable()后被调用。
<< 如果出现ros未定义的引用错误,则添加${catkin_LIBRARIES}<< 语法:target_link_libraries(<executableTargetName>, <lib1>, <lib2>, ... <libN>)
<<  用于设置链接库,很多功能需要使用系统或者第三方库函数,通过该选项配置库文件
<<1个参数:可执行文件的名称--add_executable()的第1个参数相同
<<2个及后续:如果没有链接库,去掉注释,采用默认即可
# target_link_libraries(${PROJECT_NAME}_node
#   ${catkin_LIBRARIES}
# )



#############
## Install ##
#############
<< 明确安装目标
<< 所有安装目标都应该使用catkin目标变量
<< 参考文档 http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html



<< Python代码的安装规则有些不同,它不需要使用 add_library()add_executable()函数来告知CMake
<< 哪个文件是目标文件、目标文件是什么类型的。而是使用如下的catkin_install_python()<< 如果只是安装了Python的脚本,不提供任何模块的话,就不用创建上文提到的 setup.py文件,也不用调用
<< catkin_python_setup()
<< 标记可执行脚本(Python等)以便安装
<< 与 setup.py 对比,你可选择目标(destination)
# catkin_install_python(PROGRAMS
#   scripts/my_python_script
#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

<< 标记要安装的可执行文件
<< 参考文档:
<< http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
#   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

<< 编译完成后,目标被放入catkin工作空间下的devel目录。
<< 一般希望将目标安装到系统上,以使其他用户使用,或者安装到本地目录来测试系统级别的安装。
<< 也就是说,如果希望能够对代码进行make install,就需要明确目标结束的位置。
<< 上述过程可以使用CMake的 install()函数实现,该函数的参数有:
<< TARGETS:要安装的目标
<< ARCHIVE DESTINATION:静态库和动态链接库DLL(Windows).lib存根
<< LIBRARY DESTINATION:非DLL共享库和模块
<< RUNTIME DESTINATION:可执行目标和DLL(Windows)模式共享库
<< 标记要安装的库
<< 参考文档:http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
#   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )

<< 头文件必须安装到include目录下,这通常通过安装整个文件夹的文件来完成(可以根据文件名模式进行过滤,
<< 并排除SVN子文件夹)。可以通过一下安装规则实现:
<< 标记要安装的 cpp 头文件
# install(DIRECTORY include/${PROJECT_NAME}/
#   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
#   FILES_MATCHING PATTERN "*.h"
#   PATTERN ".svn" EXCLUDE
# )

<< 标记要安装的其他文件(如:launch 、包文件等)
<< 安装roslaunch文件或其他源
# install(FILES
#   # myfile1
#   # myfile2
#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )



#############
## Testing ##
#############


<< 特定的catkin宏 catkin_add_gtest()用于处理基于gtest的单元测试:
# catkin_add_gtest(${PROJECT_NAME}-test test/test_my_pkg1.cpp)
# if(TARGET ${PROJECT_NAME}-test)
#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()

<< 添加要由python测试运行的文件夹
# catkin_add_nosetests(test)

11 服务节点

<<  头文件
#include "ros/ros.h"
<<  该头文件是根据之前创建的服务数据类型的描述文件AddTwoInts.srv自动生成
<<  beginner_tutorials/AddTwoInts.h是从我们之前创建的srv文件中生成的头文件
#include "beginner_tutorials/AddTwoInts.h"


<<  service回调函数,输入参数req,输出参数res
<<  这个函数提供了AddTwoInts服务,它接受srv文件中定义的请求(request)和响应(response)类型,并返回一个布尔值。
bool add(beginner_tutorials::AddTwoInts::Request  &req,
         beginner_tutorials::AddTwoInts::Response &res)
{
  <<  将输入参数中的请求数据相加,结果放到应答变量中。
  <<  两个整数被相加,和已经存储在了响应中。然后记录一些有关请求和响应的信息到日志中。完成后,服务返回true。
  res.sum = req.a + req.b;
  ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
  ROS_INFO("sending back response: [%ld]", (long int)res.sum);
  return true;
}




<<  主函数
<<  先初始化节点,创建节点句柄,重点是要创建一个服务的Server,指定服务名称及接受到服务数据后的回调函数。
<<  然后开始循环等待服务请求;一旦有服务请求,Server就跳入回调函数进行处理
int main(int argc, char **argv)
{
  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;
}

<<  服务中的Server类似于话题中的Subscriber,实现流程总结如下:
<<  1、初始化ROS节点
<<  2、创建Server实例
<<  3、循环等待服务请求,进入回调函数
<<  4、在回调函数中完成服务功能的处理并反馈应答数据

12 客户端节点

#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
#include <cstdlib>

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;

  <<  这将为add_two_ints服务创建一个客户端。ros::ServiceClient对象的作用是在稍后调用服务。
  <<  创建一个add_two_ints的Client实例
  ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");

  <<  这里我们实例化一个自动生成的服务类,并为它的request成员赋值。一个服务类包括2个成员变量:request和response,以及2个类定义:Request和Response
  <<  实例化一个服务数据类型的变量,该变量包含两个成员:request和response
  beginner_tutorials::AddTwoInts srv;
  srv.request.a = atoll(argv[1]);
  srv.request.b = atoll(argv[2]);
  
  
  <<  此处实际上调用了服务。由于服务调用被阻塞,它将在调用完成后返回。如果服务调用成功,call()将返回true,并且srv.response中的值将是有效的。如果调用不成功,则call()将返回false且srv.response的值将不可用。
  if (client.call(srv))
  {
    ROS_INFO("Sum: %ld", (long int)srv.response.sum);
  }
  else
  {
    ROS_ERROR("Failed to call service add_two_ints");
    return 1;
  }

  return 0;
}

<<  服务中的Client类似于话题中的Publisher,实现流程总结如下:
<<  初始化ROS节点
<<  创建一个Client实例
<<  发布服务请求数据
<<  等待Server处理之后的应答结果

二、使用步骤

1.引入库

代码如下(示例):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

2.读入数据

代码如下(示例):

data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())

该处使用的url网络请求的数据。


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值