ROS 入门实践


ROS的官方教程极其详细,不用辅导班:http://wiki.ros.org/melodic/Installation

概念

机器人需要许多进程管理各个模块,ROS中每个节点都是一个进程,由ROS Master管理

workspace:类似于IDE创建的工程,存放工程开发相关文件,所有的源码、编译的结果都在这里
    • src:代码空间(放置功能包:为源码的最小单元,所有源码都必须放到功能包里)
    • build:编译空间(编译过程中产生的中间文件)
    • devel:开发空间(开发过程中编译生成的可执行文件、库、脚本)
    • install:安装空间(开放结束后生成的可执行文件、库、脚本,用于后期发布给客户)
    • scripts:如果编写的是py文件,则可以右键创建这个文件夹放py文件,以区别cpp所在文件夹(注意设置py文件的文件属性:运行列为可执行文件)

Topic:异步,所以Publish端会一直执行,而Subscribe端接收评率比Publish低,导致Publish浪费很多计算资源
Service实现如下(参考pdf):Client端发布request并阻塞,Server端收到request并向Client发送reply反馈,Client继续执行;
Action(升级版的Service: 有些 server 执行时间很长,client 不知道是否死机了):server 执行过程中,给 client 提供实时的执行状态的反馈;service 执行太久了,需要 client 取消动作指令;通常用在长时间、可抢占(类似中断)的任务中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2KKGtbTk-1606603957880)(C:\Users\香妃\AppData\Roaming\Typora\typora-user-images\image-20201128150617152.png)]

命令行操作

命令都以ros开头

#运行小海龟:
roscore #启动:ROS Master(建立talker与listener的连接后,此终端就可退出了)、rosout(日志输出)、parameter server(参数服务器)
rosrun turtlesim turtlesim_node #rosrun <功能包名> <节点名> -运行某个功能包里的某个节点:启动仿真器节点,出现海龟
rosrun turtlesim turtle_teleop_key #键盘控制海龟节点

rosrun rqt_graph tqt_graph #显示系统中所有节点和话题关系图
rqt_:表示基于QT的可视化工具
rqt_graph #导出系统计算图(有助于了解系统结构)

rosnode +回车 //获取rosnode的帮助信息
rosnode list //把现存所有节点都列出来;其中,rosout 是系统默认本来就有的,不用关心
rosnode info [节点名]  //获取节点的具体信息;这个节点此时正在发布、订阅(如键盘发送给海龟)的话题、一些服务、主机号、pid号、底层通信机制
rosnode kill [节点名] //结束某个node
		 
rostopic +回车 //获取有关话题的帮助信息
rostopic list //列出当前系统话题列表
rostopic pub [发布的话题名称] [发布的消息] [参数] //发布话题消息,eg.如下
rostopic pub  /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'  //可用tab键补全;'[]' '[]':直线行走距离、旋转弧度;ros默认单位:米和弧度 
rostopic info /topic name //显示某个topic的属性信息
rostopic echo /topic name //显示某个topic的内容
rostopic pub -r 10 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]' //-r 10(rate 10):以10次/s的频率发布当前指令
rostopic pub topic_name ...	向某个topic发布内容
rostopic bw topic_name	查看某个topic的带宽
rostopic hz topic_name	查看某个topic的频率
rostopic find topic_type	查找某个类型的topic
rostopic type topic_name	查看某个topic的类型(msg)
    
rosmsg list //列出系统中所有 msg
rosmsg show [消息名] //查看某个 msg 内容

rosservice list //输出服务列表
rosservice info service name //显示某个service的属性信息
rosservice call [服务名] //调用某个服务,使用所提供的args调用服务
rosservice call /spawn tab键 //call:调用服务(发布请求);/spawn:产生新的海龟;x、y:海龟原始坐标;//生成一个新的turtle
rosservice type	打印服务类型
rosservice uri	打印服务ROSRPC uri
rosservice find	按服务类型查找服务
rosservice args	打印服务参数

rossrv md5	显示服务md5sum
rossrv package	列出包中的服务
rossrv packages	列出包含服务的包    
rossrv list //列出系统上所有srv
rossrv show srv name //显示某个srv内容

roswtf //运行时检查(在有ROS节点运行时);对一些系统中看起来异常但可能是正常的运行情况发出警告。也会对确实有问题的情况报告错误。

rosmsg list
rosmsg show /msg_name //显示某个 msg 内容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dEfZxpeG-1606603957882)(file:///C:/Users/香妃/OneDrive/文档/My Knowledge/temp/43fcaf36-c52d-4cec-86a6-6479463c69a5/128/index_files/65f78561-e0b0-4ee4-92c5-f6aade33b3ee.jpg)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UTkfoJ6m-1606603957883)(file:///C:/Users/香妃/OneDrive/文档/My Knowledge/temp/43fcaf36-c52d-4cec-86a6-6479463c69a5/128/index_files/63266802-f383-4ead-8f56-b9fc13d83f62.jpg)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k6Mk3ALy-1606603957884)(file:///C:/Users/香妃/OneDrive/文档/My%20Knowledge/temp/43fcaf36-c52d-4cec-86a6-6479463c69a5/128/index_files/13104390.jpg)]远程过程调用(Remote Procedure Call,RPC),可以理解为在一个进程里调用另一个进程的函数

建立工作空间

#创建工作空间
    mkdir -p ~/catkin_ws/src #src名不可变;-p:递归创建所有层级目录
    cd ~/catkin_ws/src
    catkin_init_workspace #把当前文件夹src初始化成ros工作空间(属性变化)

#编译工作空间
    cd ~/catkin_ws/
    catkin_make #编译src下所有功能包的源码;生成build、devel
    catkin_make install #生成install,非必须的

#创建功能包;同一工作空间下,功能包不可同名;工作空间中的功能包文件夹可直接delete删除,并创建其他功能包
	cd ~/catkin_ws/src #功能包要放在src中

#配置功能包程序依赖:catkin_create_pkg <功能包名字> [依赖1] [依赖2];一个功能包要依赖其他功能包工作;rospy:要编译python;roscpp:编译c++
	catkin_create_pkg <功能包名> roscpp rospy std_msgs geometry_msgs turtlesim 	#src中生成功能包learning_topic(文件夹), 其中包含package.xml、CMakeLists.txt以来

#设置环境变量:告知系统上编译的可执行文件位置,因为可执行文件都放在工作空间里;修改完环境变量之后重启终端窗口才可以生效
    source ~/catkin_ws/devel/setup.bash #要运行功能包中的程序,需要设置工作空间的环境变量,才能让系统找到工作空间和其中的功能包
    #在 ~/.bashrc文件最后一行写入:source /home/fei/catkin_ws/devel/setup.bash;终端中输入:echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc  接着,使修改生效:source ~/.bashrc

#检查是否成功设置环境变量
    echo $ROS_PACKAGE_PATH #ROS_PACKAGE_PATH:代表ros中所有功能包的路径(包括上面自己写的功能包)

四种通信方式

话题与订阅

话题 Topic

/* 在文件夹 “learning_topic/src” 中创建发布者代码
    代码实现发布者的步骤:
    1. 初始化ROS节点
    2. 向ROS Master注册节点信息,包括发布的话题名和话题的消息类型
    3. 创建消息数据
    4. 循环发布消息

创建发布者,发布话题:turtle1/cmd_vel,消息类型:geometry_msgs::Twist
*/ 
#include <ros/ros.h> //包含了大部分ROS中常用的头文件
#include <geometry_msgs/Twist.h>

int main(int argc, char **argv)
{
    // 1. ROS节点初始化
    ros::init(argc, argv, "velocity_publisher"); //定义节点的名字(不能重名):velocity_publisher

    	// 创建节点句柄:管理节点相关的API资源
    ros::NodeHandle n;

    // 2. 创建一个Publisher,系统Publisher节点发布类型为geometry_msgs::Twist、名为/turtle1/cmd_vel(订阅者就是通过与这个同名调用这里发布的消息)的topic,队列长度10
    ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10); //小海龟订阅的就是 /turtle1/cmd_vel,所以当小海龟启动后和这个节点都启动后,海龟就会照着运动
        /*向话题turtle1/cmd_vel中发布数据,10:队列长度,当底层(包括以太网等)来不及响应发布的命令,而此时100ms已到,要再次发布数据了,没响应的就先存放在这个队列中,当还不够,则队列中只存放最新的10个数据)*/

    	// 设置循环的频率,10Hz
    ros::Rate loop_rate(10); //发送的频率

    int count = 0;
    while (ros::ok()) //当节点发生异常,ros::ok() 返回 false
    {
    // 3. 初始化geometry_msgs::Twist类型消息的内容
        geometry_msgs::Twist vel_msg;
        vel_msg.linear.x = 0.5; //m/s
        vel_msg.angular.z = 0.2; //绕z轴旋转0.2弧度/s
        //其他未赋值的参数默认为0

    // 4. 发布消息 .publish()
        turtle_vel_pub.publish(vel_msg); //ros底层通过一定的通信机制将数据压入队列,再发送
        
        // 打印信息,类似printf()
        ROS_INFO("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]", vel_msg.linear.x, vel_msg.angular.z);
		
        //处理节点订阅话题的所有回调函数
		ros::spinOnce(); //虽然此段代码没有订阅话题,但是为了保证功能的无误,建议所有节点都默认写上
 
            
        // 按照ros::Rate 的循环频率延时100ms,之后再次进入节点信息发布工作
        loop_rate.sleep();
    }

    return 0;
}  
#修改leaning_topic中的CMakeLists.txt文件:(添加在 ## Install ## 上面)
    add_executable(velocity_publisher src/velocity_publisher.cpp)
    target_link_libraries(velocity_publisher ${catkin_LIBRARIES})

#编译功能包
    cd ~/catkin_ws
    catkin_make

#运行C++可执行文件:
    roscore、rosrun turtlesim turtlesim_node、rosrun learning_topic velocity_publisher(这是 cpp 文件名字,而不是节点名)
    	#learning_topic:catkin_create_pkg定义的功能包;velocity_publisher:在ros::init()中定义的节点

#运行python文件:(打开 velocity_publisher.py 的可执行权限,且无需catkin_make编译)
    1、将文件设置为可执行文件
    2、roscore、rosrun turtlesim turtlesim_node、rosrun learning_topic velocity_publisher.py 

订阅 Subscriber

/** 创建Subscriber,该例程将订阅/turtle1/pose话题,消息类型turtlesim::Pose
    代码实现订阅者的步骤:
    1、初始化ROS节点
    2、订阅需要的话题
    3、循环等待话题消息,接收到消息后进入回调函数
    4、在回调函数中完成消息处理
*/

#include <ros/ros.h>
#include "turtlesim/Pose.h"

//回调函数
void poseCallback(const turtlesim::Pose::ConstPtr& msg)
{
   ROS_INFO("Turtle pose: x:%0.6f, y:%0.6f", msg->x, msg->y); // 将接收到的消息打印出来
}

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

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

   // 创建一个Subscriber;订阅名为/turtle1/pose的topic(由Publisher定义);10:订阅的话题容许的最大队列长度,超过10则舍弃时间最早的话题数据;一旦订阅到数据就进入回调函数poseCallback;订阅段不用定义消息类型,程序之查找系统中是否有同名的消息名
   ros::Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, poseCallback); // 接收到Publisher定义的话题 /turtle1/pose 后(这里订阅的是小海龟的位姿,小海龟启动后就开始发布 /turtle1/pose ),会进入回调函数poseCallback()

   ros::spin(); // 这里内部永远循环,不断查看队列中是否有上面寻求的消息"/turtle1/pose"进来,有的话就调用上面回调函数;没有的话就一直死循环等待;直到ros::ok()返回false时退出

   return 0; //一般永远不会运行到这里
}
add_executable(pose_subscriber src/pose_subscriber.cpp)
target_link_libraries(pose_subscriber ${catkin_LIBRARIES})
#编译、设置环境变量
roscore、rosrun turtlesim turtlesim_node、rosrun learning_topic pose_subscriber
#移动海龟可发现输出海龟位置数据开始变化

msg 文件

msg类型:bool、int8、int16、int32、int64(以及uint)、float、float64、string、time、duration、header、可变长数组array[]、固定长度数组array[C]
每个 message 就像一个类,包含不同类型的参数,每次发布的内容为对象

自定义话题消息:(如果想要发布、订阅非系统自带的消息类型,就要自定义消息类型)
1、//在功能包learning_topic中创建msg/Person.msg文件,内容如下:
    string name
    uint8 sex
    uint8 age

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

2、修改package.xml、CMakeLists.txt,添加动态生成程序的功能包依赖:
    -- package.xml
    在 <!-- The export tag contains other ...> 上面:添加一个动态生成程序的功能包的依赖
    <build_depend>message_generation</build_depend> #编译依赖;依赖于会动态产生message的依赖的功能包
    <exec_depend>message_runtime</exec_depend> #执行依赖;依赖于动态message的runtime的运行时依赖

    -- CmakeLists.txt
    find_package(... roscpp rospy std_msgs message_generation ...)
    catkin_package(... CATKIN_DEPENDS message_runtime ...) //创建message运行的依赖,对应<exec_depend>message_runtime</exec_depend>

    //把 msg、srv 编译成对应的不同的程序文件的配置项
    add_message_files(FILES Person.msg Person.srv) #catkin命令;把Person.msg作为我的交际接口,在编译时就会发现这个接口并针对Person.msg做编译
    generate_messages(DEPENDENCIES std_msgs) #catkin命令;编译Person.msg时需要依赖哪些已有的库、包   ;DEPENDENCIES后面指定生成msg需要依赖其他什么消息,由于gps.msg用到了flaot32这种ROS标准消息,因此需要再把std_msgs作为依赖
    
3、//编译工作空间(可在devel/include中找到对 .msg编译出的 .h文件)
	catkin_make
	#在devel路径下生成 .msg 对应的头文件,头文件按照C++的语法规则定义了topic_demo::gps类型的数据
	
4、调用方法
	1)#include "learning_topic/Person.h"
	
	2)按照对结构体操作的方式定义、赋值内容
/** 
    创建发布者代码:
    步骤同之前方法,只不过创建消息数据是Person
    将 person_publisher.cpp、person_subscriber.cpp放在功能包的src下,
     * 该例程将发布/person_info话题,自定义消息类型learning_topic::Person
 */
#include <ros/ros.h>
#include "learning_topic/Person.h" //调用 .msg生成的 .h文件 (/devel/include/learning_topic)

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

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

    // 创建一个Publisher,发布名为 “/person_info” 的topic(自己创建的消息);消息类型为learning_topic::Person,而消息名可以任意起;队列长度10
    ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);

    // 设置循环的频率
    ros::Rate loop_rate(1);

    int count = 0;
    while (ros::ok())
    {
        // 初始化learning_topic::Person类型的消息
        learning_topic::Person person_msg;
        person_msg.name = "Tom";
        person_msg.age  = 18;
        person_msg.sex  = learning_topic::Person::male;

        // 发布消息
        person_info_pub.publish(person_msg);

        ROS_INFO("Publish Person Info: name:%s  age:%d  sex:%d", person_msg.name.c_str(), person_msg.age, person_msg.sex);

        // 按照循环频率延时
        loop_rate.sleep();
    }

    return 0;
}
/**创建订阅者代码:
 * 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
 */
#include <ros/ros.h>
#include "learning_topic/Person.h"

// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
    // 将接收到的消息打印出来
    ROS_INFO("Subcribe Person Info: name:%s  age:%d  sex:%d", msg->name.c_str(), msg->age, msg->sex);
}

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

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

    // 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
    ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);

    // 循环等待回调函数
    ros::spin();

    return 0;
}
#配置代码编译规则(在CMakeLists.txt中的 ## Install ## 上面):
add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp) #因为有些代码是动态生成的,所以需要可执行文件和动态生成的程序也要产生依赖的关系

add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp) 
#上面的两个add_dependencies():动态的和 .msg生成的 .h文件做连接    
         
#编译、加入环境变量
roscore、rosrun learning_topic person_subscriber、rosrun learning_topic person_publisher
#运行后关闭roscore发现并不影响发布者和订阅者传输消息,这是因为roscore只负责建立联系,而和传输消息无关

客户端 Client

/**
客户端Client请求服务的编程实现:
创建功能包:catkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim
初始化ROS节点
创建一个Client实例
发布服务请求
等待Server处理之后的应答结果
 * 该例程将请求/spawn服务,服务数据类型turtlesim::Spawn;实现请求创建海龟的服务
 */
#include <ros/ros.h>
#include <turtlesim/Spawn.h>

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

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

    // 查看系统中是否有/spawn服务(就像想使用服务器,服务器要开机),没有就阻塞等待
    ros::service::waitForService("/spawn");
    // 创建客户端Client,给spawn服务发送请求,请求的数据类型为turtlesim::Spawn;"/spawn":服务的名字
    ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");

    // 初始化turtlesim::Spawn的请求数据;没添加角度则=0
    turtlesim::Spawn srv;
    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());

    // 将request发送出去,并一直等待服务器给反馈,没有反馈则阻塞
    add_turtle.call(srv);

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

    return 0;
};
#修改CMakeLists.txt的 ## Install ## 上面:
add_executable(turtle_spawn src/turtle_spawn.cpp)
target_link_libraries(turtle_spawn ${catkin_LIBRARIES})

#编译、设置环境变量
roscore、rosrun turtlesim turtlesim_node、rosrun learning_service turtle_spawn

服务端 Server

/**
服务端Server接收与回馈的编程实现:
初始化ROS节点
创建Server实例
循环等待服务请求,进入回调函数
在回调函数中完成服务功能的处理,并反馈给应答数据
 * 该例程执行数据类型为std_srvs/Trigger的/turtle_command服务;服务器在接受到请求时之后发出回应,然后发布者发布消息令海龟动起来
 */
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <std_srvs/Trigger.h>

ros::Publisher turtle_vel_pub;
bool pubCommand = false;

// service回调函数,是真正实现服务功能的部分;参数是服务数据类型描述文件中声明的请求与应答数据结构:输入参数req,输出参数res;结束后,结果会放到应答数据中,反馈给Client
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;因为不知道request什么时候进来,通过回调函数机制处理请求;收到服务request后,跳入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;
}
#查看Trigger:rossrv show std_srvs/Trigger;  ---上方为request、下方为response   
add_executable(turtle_command_server src/turtle_command_server.cpp)
target_link_libraries(turtle_command_server ${catkin_LIBRARIES})

#编译、设置环境变量
roscore、rosrun turtlesim turtlesim_node、rosrun learning_service turtle_command_server、rosservice call /turtle_command tab键 #发送请求

srv 文件

自定义服务数据:在工作空间learning_service中创建srv/Person.srv文件
srv可以嵌套msg,但不能嵌套srv
---------------------------------------
    #Client发送给Server的内容
    uint8 age
    uint8 sex
    uint8 male=1
    uint8 female=2
    ---
    #Server反馈给Client的内容
    string result
---------------------------------------

#修改package.xml、CMakeList.txt,添加动态生成程序的功能包依赖:
    -- package.xml ---
    在 <!-- The export tag contains other ...> 上面:添加一个动态生成程序的功能包的依赖
    <build_depend> message_generation </build_depend> #编译依赖;依赖于会动态产生message的依赖的功能包
    <exec_depend> message_runtime </exec_depend> #执行依赖;依赖于动态message的runtime的运行时依赖

    -- CmakeLists.txt ---
    find_package(... roscpp rospy message_generation)
    catkin_package(... CATKIN_DEPENDS message_runtime ...) //创建message运行的依赖,对应<exec_depend>message_runtime</exec_depend>
    //把 msg、srv 编译成对应的不同的程序文件的配置项
    add_message_files(FILES Person.msg Person.srv) #把Person.msg作为我的交际接口,在编译时就会发现这个接口并针对Person.msg做编译
    generate_messages(DEPENDENCIES std_msgs) #编译Person.msg时需要依赖哪些已有的库、包    

#编译:devel/include/learning_topic 中生成三个 .h 文件
	Person.srv中的 --- 上面部分放在devel/include/learning_service/PersonRequest.h中、下面部分放在PersonResponse.h中、包含上两个头文件的一个总头文件Person.h(使用时只包含这一个就可以)

服务端 Server

/**
客户端和服务端处理自定义服务数据:

服务端
 * 该例程将执行/show_person服务,服务数据类型learning_service::Person
 */
#include <ros/ros.h>
#include "learning_service/Person.h" 

// service回调函数,输入参数req,输出参数res
bool personCallback(learning_service::Person::Request  &req, //Request:这是ROS强制定义的,必须要加上
                    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 informtion.");
    ros::spin();

    return 0;
}

客户端 Client

/** 客户端
 * 该例程请求/show_person服务,服务数据类型为learning_service::Person
 */
#include <ros/ros.h>
#include "learning_service/Person.h" //Person.h中含盖了 PersonRequest.h、PersonResponse.h,所以包含 Person.h就可以了

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

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

    // 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
    ros::service::waitForService("/show_person"); // 查看系统中是否有/show_person(就像想使用服务器,服务器要开机),没有就阻塞直到 show_person 服务的提供者运行起来
        
    // 创建 /show_person 的Client实例,指定服务类型为learning_service::Person;client会发布Person类型请求数据,请求的对象是"/show_person"
    ros::ServiceClient person_client = node.serviceClient<learning_service::Person>("/show_person");

    // 初始化learning_service::Person的请求数据
    learning_service::Person srv;
    srv.request.name = "Tom";
    srv.request.age  = 20;
    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); //cal:将请求数据发送出去;阻塞等待server的反馈结果

    // 显示服务调用结果
    ROS_INFO("Show person result : %s", srv.response.result.c_str());

    return 0;
};
add_executable(person_server src/person_server.cpp)
target_link_libraries(person_server ${catkin_LIBRARIES})
add_dependencies(person_server ${PROJECT_NAME}_gencpp)

add_executable(person_client src/person_client.cpp)
target_link_libraries(person_client ${catkin_LIBRARIES})
add_dependencies(person_client ${PROJECT_NAME}_gencpp)
    
#编译:
catkin_make #生成devel/person_client_ 、person_server可执行文件
#添加环境变量?
roscore、rosrun learning_service person_server、rosrun learning_service person_client

action 文件

利用动作库进行请求响应,动作的内容格式应包含三个部分:
	目标:
		机器人执行一个动作,应该有明确的移动目标信息
	反馈:
		动作进行的过程中,应该有实时的状态信息反馈给服务器的实施者,告诉实施者动作完成的状态,可以使实施者作出准确的判断去修正命令
	结果:
	运动完成时,动作服务器把本次运动的结果数据发送给客户端
	
--------------------------------------------------------------	
# Define the goal,client发布的目标
uint32 dishwasher_id  # Specify which dishwasher we want to use
---
# Define the result;回传 server 执行结果(传一次)
uint32 total_dishes_cleaned
---
# Define a feedback message;实时反馈(传多次)
float32 percent_complete	
--------------------------------------------------------------	

---- CmakeLists.txt ----
find_package(catkin REQUIRED genmsg actionlib_msgs actionlib)
add_action_files(DIRECTORY action FILES DoDishes.action) generate_messages(DEPENDENCIES actionlib_msgs)
add_action_files(DIRECTORY action FILES Handling.action)
generate_messages( DEPENDENCIES actionlib_msgs)

---- package.xml: 添加所需要的依赖 ----
<build_depend>actionlib </build_depend>
<build_depend>actionlib_msgs</build_depend>
<run_depend>actionlib</run_depend>
<run_depend>actionlib_msgs</run_depend>

客户端 Client

/**实现功能发送action请求

*/

服务器 Server

/**返回客户端活动当前状态信息,结果信息,和反馈信息

*/

Parameter Server

ROS Master中的参数服务器Parameter Server(相当于全局字典)用来保存各个节点间的配置参数,被所有节点共享
参数服务器维护着一个数据字典,字典里存储着各种参数和配置(每一个key不重复,且每一个key对应着一个value);使用互联网传输,在节点管理器中运行;
因为字典的这种静态的映射特点,项目中往往将一些不常用到的参数和配置放入参数服务器的字典里,这样对这些数据进行读写都将方便高效

参数服务器三种护方式:
	命令行维护
        rosparam set param_key param_value	设置参数
        rosparam get param_key	显示参数
        rosparam load file_name.yaml	从文件加载参数
        rosparam dump file_name.yaml	保存参数到文件
        #load、dump文件需要遵守YAML格式,可以把YAML文件的内容理解为字典,因为它也是键值对的形式(Key: Value)
        rosparam delete	删除参数
        rosparam list	列出参数名称
        
    launch文件内读写:标签 <param>、<rosparam>
    	# param只给出了key,没有直接给出value,这里的value是由后没的脚本运行结果作为value进行定义的
        <param name= "robot description" command= "$(find xacro)/xacro.py $(find robot _sim_ demo)/urdf/robot.xacro" />
     	# <param>一般只设置一个参数
   	 	<param name= "publish frequency" value="100.0"/> 
        # rosparam的典型用法,先指定一个YAML文件,然后施加command,其效果等于rosparam load file_name
        <rosparam file="$(find robot sim_ _demo)/config/xbot2_ control,yaml" command= "load"/>

    node源码:利用API修改ROS的源码,也就是对参数服务器操作
/**
创建功能包:catkin_create_pkg learning_parameter roscpp rospy std_srvs

首先 roscore、rosrun turtlesim turtlesim_node 后rosparam才能起作用:
    列出所有的参数:rosparam list
    获取rosparam list出的某个参数的值:rosparam get [param_key]
    设置参数值 param_key = param_value:rosparam set [param_key] [param_value]
    上面修改值后虽然值已经变了,但是需要发送请求后才能起作用:rosservice call clear tab键
    保存ROS系统中的参数到文件中:rosparam dump [file_name.yaml] //保存在终端当前路径下
    加载文件中的参数到系统中:rosparam load [路径/file_name] (刷新后才能起作用)
    删除系统中的参数:rosparam delete [param_key]
    常用YAML存储参数,并加载

实现上面命令行的操作:
    1、初始化ROS节点
    2、get函数获取参数
    3、set函数设置参数
 */
 
#include <string>
#include <ros/ros.h>
#include <std_srvs/Empty.h>

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", 0);
    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;
}
add_executable(parameter_config src/parameter_config.cpp)
target_link_libraries(parameter_config ${catkin_LIBRARIES})

#编译、加入环境变量    
roscore、rosrun turtlesim turtlesim_node、rosrun learning_parameter parameter_config    

tf 坐标系统

ROS中的坐标系管理系统:(坐标变换:旋转、平移)

TF坐标变换功能包(sudo apt-get install -y ros-melodic-turtle-tf):记录着10s内机器各个部位间的坐标关系;坐标变换通过广播TF变换和监听TF变换实现

roslaunch:launch 脚本文件,可以将很多节点的启动都写进去,一次性启动这些节点
roslaunch turtle_tf turtle_tf_demo.launch //启动实例:两个海龟自动跟踪
rosrun tf view_frames //查看并保存当前ROS系统中所有tf之间的关系,保存为 ~/frames.pdf(有world(全局坐标系)+turtle1+turtle2三个坐标系)
rosrun tf tf_echo turtle1 turtle2 //tf_echo工具:实时查询tf树中任意两个坐标系间的关系
rosrun rviz rviz -d `rospack find turtle_tf`/rviz/turtle_rviz.rviz //启动可视化工具rviz
/**
创建tf广播器与监听器:将上一节中一个海龟相对于world系的pose广播出来,监听器获取turtle1和turtle2的坐标关系,使turtle2跟随着turtle1运动

catkin_create_pkg learning_tf roscpp rospy tf turtlesim

实现一个tf广播器步骤:
    定义TF广播器
    创建坐标变换值
    发布坐标变换
 * 该例程产生tf数据,并计算、发布turtle2的速度指令
 */
 
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>

std::string turtle_name;

void poseCallback(const turtlesim::PoseConstPtr& msg)
{
    // 创建tf的广播器实例
    static tf::TransformBroadcaster br;

    // 初始化tf数据
    tf::Transform transform; //坐标变换矩阵
      /*平移变换*/
    transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) ); //setOrigin:设置在(x, y, z)上的平移值
      /*旋转变换*/
    tf::Quaternion q; //四元素描述旋转
    q.setRPY(0, 0, msg->theta); //旋转
    transform.setRotation(q); //加载平移、旋转数据

    // sendTransform():广播器将坐标变换关系插入TF树并发布
        //发布的TF消息类型为StampedTransform:此处包含 坐标变换关系、时间戳、要变换的源坐标系、目标坐标系
    br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name)); //默认保存10s内的数据
}

int main(int argc, char** argv)
{
    // 初始化ROS节点
    ros::init(argc, argv, "my_tf_broadcaster"); //本程序需要运行两次分别计算turtle1、turtle2与world的位置关系,但会导致节点重名;对节点名重映射: __name:=

    // 输入参数作为海龟的名字
    if (argc != 2)
    {
        ROS_ERROR("need turtle name as argument"); 
        return -1;
    }

    turtle_name = argv[1]; //argv[]:输入的海龟名(turtle1, turtle2)

    ros::NodeHandle node;
    
    // 订阅海龟仿真器中不断发布的海龟位姿消息
    ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);

    // 循环等待回调函数
    ros::spin();

    return 0;
};
/**
创建tf监听器步骤:
    定义tf监听器
    查找坐标变换
 * 该例程监听tf数据,并计算、发布turtle2的速度指令
 */

#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>

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

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

    // 服务请求:产生turtle2
    ros::service::waitForService("/spawn");
    ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
    turtlesim::Spawn srv;
    add_turtle.call(srv);

    // 创建发布turtle2速度控制指令的发布者,发布消息类型为Twist的话题 "/turtle2/cmd_vel" 
    ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 10);

    // 创建tf监听器(监听tf中任意两个坐标系之间的关系),创建成功后监听器会自动接收TF树的消息,并且缓存10秒
    tf::TransformListener listener;

    ros::Rate rate(10.0);
    while (node.ok())
    {
        // 获取turtle1与turtle2坐标系之间的tf数据
        tf::StampedTransform transform; //transform:用来保存tf关系
        
        try
        {
            //阻塞程序,直到系统中存在 源坐标系"/turtle2" 和目标坐标系"/turtle1" 之间在指定时间的变换关系, 所以要设置超时时间:等待超过3s则提示错误
            listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0)); //Time(0):在保存的10s数据中查询当前最新的数据
            
            listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform); //查询两坐标在指定时间的坐标变换关系,并保存在transform中
        }
        catch (tf::TransformException &ex) 
        {
            ROS_ERROR("%s",ex.what());
            ros::Duration(1.0).sleep();
            continue;
        }

        // 根据turtle1与turtle2坐标系之间的位置关系,发布turtle2的速度控制指令(turtle2跟随turtle1)
        geometry_msgs::Twist vel_msg;
        vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),
                                        transform.getOrigin().x());
        vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +
                                      pow(transform.getOrigin().y(), 2)); //2s完成跟随
        turtle_vel.publish(vel_msg);

        rate.sleep();
    }
    return 0;
};
#CMakeLists.txt:
add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})

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

#编译、加入环境变量
roscore、rosrun turtlesim turtlesim_node
rosrun learning_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1 //节点名重映射为:turtle1_tf_broadcaster;argv[]=turtle1
rosrun learning_tf turtle_tf_broadcaster __name:=turtle2_tf_broadcaster /turtle2
rosrun learning_tf turtle_tf_listener //查询两个海龟间的速度关系,并给turtle2发布速度指令
rosrun turtlesim turtle_teleop_key

launch 文件

快速实现多个节点的配置、启动;实现后搭建相关系统多是搭建积木式的调用launch,很少修改原程序;教程:http://wiki.ros.org/roslaunch/XML

Launch文件启动时,如果ROS Master没有启动,则自动启动 ROS Master
XML文件必须要包含-一个根元素,launch 文件中的根元素采用<launch>标签定义,文件中的其他内容都必须包含在这个标签之中

Launch文件语法:
    运行Launch文件:roslaunch+功能包名+launch文件名
    
    启动:			<launch>
    节点配置与启动: <node pkg=[“节点所在功能包”] name=["启动的节点名,覆盖ros::init()中赋予的节点名"] type=["节点的可执行文件名"]/> #节点至少需要三个属性:name、pkg、type
    参数设置:	    <param name="参数名" value="参数值" output="screen"/> #对参数赋值value,保存在参数服务器中(ROS Master);output=“screen”则会将日志信息打印到当前终端
    	当参数很多,<param>会非常麻烦: <rosparam file=".../params.yaml" command="load" ns="params"/> #将YAML中参数加载到ROS参数服务器中,command属性为“load”,命名空间为“ns”
    结束:			</launch>
    
    pkg、type:相当于使用rosrun命令后跟着的两个参数;还有其他参数属性见pdf
eg:
    <launch>
        <node pkg="turtlesim" name="sim1" type="turtlesim_node"/>  //<node pkg="package-name" type="executable-name" name="node-name"/>
        <node pkg="turtlesim" name="sim2" type="turtlesim_node"/>
    </launch>
    
其他可选属性:
    output:将某个节点日志信息打印到终端中
    respawn:重启
    required:要求节点是否必须要启动
    args:类似 rosrun 后卖弄跟随的指令,给节点输入参数供使用

arg 标签用来在launch文件中定义参数,arg和param在ROS里有根本性的区别,就像局部变量和全局变量的区别-一样。arg 不储存在参数服务器中,不能提供给节点使用,只能在launch文件中使用。param则是储存在参数服务器中,可以被节点使用。
<arg ...> :存在launch文件内部使用局部参数;<arg name="参数名" default="默认参数名"/>
	调用本参数的方式:<param name="foo" value="$(arg 参数名)"/> ; <node name="node" pkg="package" type="type" args="$arg arg-name)"/>

<remap ...> :重映射,对ROS计算图中的资源重新命名(原名称销毁);<remap from="/turtlebot/cmd_vel" to="/cmd_vel"/>

<include ...> :类似 .h文件,launch文件内容较多时,将其拆分模块化,但是各个模块之间互相包含,则用 <include >调用其他模块;<include file="$(dirname)/other.launch"/>

实例:
catkin_create_pkg learning_launch (launch是一个启动文件,基本都是链接,不需要任何的依赖)
在文件夹learning_launch中创建放置 .launch 文件的文件夹名launch,便于管理功能包中各种资源

<launch>
    <node pkg="learning_topic" type="person_subscriber" name="talker" output="screen" />
    <node pkg="learning_topic" type="person_publisher" name="listener" output="screen" /> 
</launch>
编译、加入环境变量
roslaunch learning_launch simple.launch (此时可发现,两个节点并行运行)

<launch>
	<param name="/turtle_number"   value="2"/> // 参数为 /turtle_number
    <node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node"> //“turtlesim_node”:下面节点名字的命名空间,解决同名冲突
		<param name="turtle_name1" value="Tom"/> // 参数为 /turtlesim_node/turtle_name1
		<param name="turtle_name2" value="Jerry"/>
		<rosparam file="$(find learning_launch)/config/param.yaml" command="load"/> //(find learning_launch):搜索功能包并输出路径;要在learning_launch下创建 config/param.yaml
	</node>
    <node pkg="turtlesim" type="turtle_teleop_key" name="turtle_teleop_key" output="screen" /> // output="screen":日志输出
</launch>
编译、 roslaunch learning_launch turtlesim_parameter_config.launch
新终端下:rosparam list,可发现参数都被加载进去了

海龟跟随:
 <launch>
    <!-- Turtlesim Node-->
    <node pkg="turtlesim" type="turtlesim_node" name="sim"/>
    <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
    <node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle1" name="turtle1_tf_broadcaster" /> // rosrun learning_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1
    <node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle2" name="turtle2_tf_broadcaster" />
    <node pkg="learning_tf" type="turtle_tf_listener" name="listener" />
  </launch>
编译、roslaunch learning_launch start_tf_demo_c++.launch 

<launch>
	<include file="$(find learning_launch)/launch/simple.launch" /> //载入simple.launch文件,并启动simple.launch中所有内容
    <node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node"> //启动一个仿真器节点
		<remap from="/turtle1/cmd_vel" to="/cmd_vel"/> //重映射
	</node>
</launch>
编译、 roslaunch learning_launch turtlesim_remap.launch
打开新终端:rostopic list (可发现 "/turtle1/cmd_vel" 消失了);rostopic pub /cmd_vel tab键 tab键 (可发现,海龟动了,说明重命名其作用了)

<launch>    <!--根标签-->
<node>    <!--需要启动的node及其参数-->
<include>    <!--包含其他launch-->
<machine>    <!--指定运行的机器-->
<env-loader>    <!--设置环境变量-->
<param>    <!--定义参数到参数服务器-->
<rosparam>    <!--启动yaml文件参数到参数服务器-->
<arg>    <!--定义变量-->
<remap>    <!--设定参数映射-->
<group>    <!--设定命名空间-->
</launch>    <!--根标签-->


工具

rqt:基于Qt开发,所以扩展性好(加入插件),易于跨平台开发	
    rqt_ tab健:列出ROS提供的Qt工具箱里的工具
    rqt_console:日志输出工具:输出错误,警告等,查看日志
    rqt_graph:计算图可视化工具,显示通信架构;常用此分析别人的系统
    rqt_plot:数据绘图工具,绘制曲线
    rqt_image_view:图像渲染工具
    rqt 整合了上面的系学习工具,可以自定义整合多种工具
	
	rosrun rqt_graph rqt_graph :查看当前运行的话题,以及显示该话题的发布者和订阅者

Rviz:
    机器人开发过程中的数据可视化界面,是一款三维可视化工具
    可以使用可扩展标记语言XML对机器人、周围物体等任何实物进行尺寸、质量、位置、材质、关节等属性的描述,并在界面中呈现出来
    同时,rviz还可以通过图形化的方式,实时显示机器人传感器的信息、机器人的运动状态、周围环境的变化等信息
	呈现接收到的信息;左侧的插件相当于是一个个的subscriber,RViz接收信息,并且显示
	
Gazebo:(服务器在国外,所以可能加载不出来)(如下报错不用管:[Err] [REST.cc:205] Error in REST request)
    一款功能强大的三维物理仿真平台
    有强大的物理引擎,高质量的图形渲染,方便的编程与图形接口,开源免费
    应用场景:测试机器人算法,机器人设计,现实情况下的回溯测试
    
record:系统运行时,记录topic,供后期回放旨在提高性能,并避免消息的反序列化和重新排序
    rosbag record -a -O cmd_record //-a:记录所有topic;-o:保存成压缩包;cmd_record:压缩包文件名;之后所有动作都会被记录,直到按键按下ctrl+c(保存压缩包在home下)
    rosbag record <topic-names> //记录某个话题信息
    rosbag play <bag-files-name>.bag //回放话题;要重新启动roscore、rosrun turtlesim turtlesim_node
    rosbag info <bag-files-name>.bag //查看bag文件记录了多少消息,记录的是哪个topic的消息,消息的类型是什么等 
    --------------------------------------------
        cheak	确定一个包是否可以在当前系统中进行,或者是否可以迁移。
        decompress	压缩一个或多个包文件。
        filter	解压一个或多个包文件。
        fix	在包文件中修复消息,以便在当前系统中播放。
        help	获取相关命令指示帮助信息
        info	总结一个或多个包文件的内容。
        play	以一种时间同步的方式回放一个或多个包文件的内容。
        record	用指定主题的内容记录一个包文件。
        reindex	重新索引一个或多个包文件。
    -------------------------------------------  

Client Library

Client Library 对 API 做了进一步封装,对开发者更友好

C++接口叫做roscpp,Python接口叫做rospy,Java接口叫做rosjava。尽管语言不通,但这些接口都可以用来创建topic、service、param

roscpp	ROS的C++库,是目前最广泛应用的ROS客户端库,执行效率高
rospy	ROS的Python库,开发效率高,通常用在对运行时间没有太大要求的场合,例如配置、初始化等操作
roslisp	ROS的LISP库
roscs	Mono/.NET.库,可用任何Mono/.NET语言,包括C#,Iron Python, Iron Ruby等
rosgo	ROS Go语言库
rosjava	ROS Java语言库
rosnodejs	Javascript客户端库

img

roscpp

http://docs.ros.org/en/api/roscpp/html/index.html

roscpp的主要部分包括:
    ros::init() : 解析传入的ROS参数,创建node第一步需要用到的函数
    ros::NodeHandle : 和topic、service、param等交互的公共接口
    ros::master : 包含从master查询信息的函数
    ros::this_node:包含查询这个进程(node)的函数
    ros::service:包含查询服务的函数
    ros::param:包含查询参数服务器的函数,而不需要用到NodeHandle
    ros::names:包含处理ROS图资源名称的函数

以上功能可以分为以下几类:
    Initialization and Shutdown 初始与关闭
    Topics 话题
    Services 服务
    Parameter Server 参数服务器
    Timers 定时器
    NodeHandles 节点句柄
    Callbacks and Spinning 回调和自旋(或者翻译叫轮询?)
    Logging 日志
    Names and Node Information 名称管理
    Time 时钟
    Exception 异常
    
ros::NodeHandle Class:就是对节点资源的描述,有了它你就可以操作这个节点了,比如为程序提供服务、监听某个topic上的消息、访问和修改param
    常用成员函数
    //创建话题的publisher
    rosPublisher advertise(const string &topic, uint32_ _t queue_ size);
    //创建话题的subscriber
    ros.:Subscriber subscribe(const string &topic, uint32_ _t queue_ size, void(*)(M));
    //创建服务的server
    ros::ServiceServer advertiseService(const string &service, bool(*srv_ _func)(Mreq &, Mres &));
    //创建服务的client
    ros::ServiceClient serviceClient(const string &service_ name, bool persistent= false);
    //查询某个参数的值
    bool getParam(const string &key, void &val);
    //给参数赋值
    bool setparam(const string &key, void val);  
    
    //创建话题的publisher 
    ros::Publisher advertise(const string &topic, uint32_t queue_size, bool latch=false); 
    //第一个参数为发布话题的名称
    //第二个是消息队列的最大长度,如果发布的消息超过这个长度而没有被接收,那么就的消息就会出队。通常设为一个较小的数即可。
    //第三个参数是是否锁存。某些话题并不是会以某个频率发布,比如/map这个topic,只有在初次订阅或者地图更新这两种情况下,/map才会发布消息。这里就用到了锁存。

    //创建话题的subscriber
    ros::Subscriber subscribe(const string &topic, uint32_t queue_size, void(*)(M));
    //第一个参数是订阅话题的名称
    //第二个参数是订阅队列的长度,如果受到的消息都没来得及处理,那么新消息入队,就消息就会出队
    //第三个参数是回调函数指针,指向回调函数来处理接收到的消息

    //创建服务的server,提供服务
    ros::ServiceServer advertiseService(const string &service, bool(*srv_func)(Mreq &, Mres &)); 
    //第一个参数是service名称
    //第二个参数是服务函数的指针,指向服务函数。指向的函数应该有两个参数,分别接受请求和响应。

    //创建服务的client
    ros::ServiceClient serviceClient(const string &service_name, bool persistent=false); 
    //第一个函数式service名称
    //第二个参数用于设置服务的连接是否持续,如果为true,client将会保持与远程主机的连接,这样后续的请求会快一些。通常我们设为flase

    //查询某个参数的值
    bool getParam(const string &key, std::string &s); 
    bool getParam (const std::string &key, double &d) const;
    bool getParam (const std::string &key, int &i) const;
    //从参数服务器上获取key对应的值,已重载了多个类型

    //给参数赋值
    void setParam (const std::string &key, const std::string &s) const;
    void setParam (const std::string &key, const char *s) const;
    void setParam (const std::string &key, int i) const;
    //给key对应的val赋值,重载了多个类型的val
    
ros::master Namespace
    常用函数
    bool check(); //检查master是否启动
    const string& getHost (); //返回master所处的hostname
    bool getNodes(V_ string &nodes); //从master返回已知的node名称列表
    bool getTopics(V_ TopicInfo &topics); //返回所有正在被发布的topic列表
    bool getURI); //返回到master的URI地址,如http://host:port/
    uint32_ t getPort(); //返回master运行在的端口
    
ros::this node Namespace
    常用函数
    void getAdvertisedTopics(V_ string &topics); //返回本node发布的topic
    const string &getName ); //返回当前node的名称
    const string &getNamespace(); //返回当前node的命名空间
    void getSubscribedTopics (V_ string &topics); //返回当前node订阅的topic
    
ros:service Namespace
    常用函数
    //调用一个RPC服务
    bool call(const string &service, Service &service);
    //创建一个服务的client
    ServiceClient createClient(const string& service_ name, bool persistent=false,
    const M_ string &header. _values=M_ _string();
    //确认服务可调用
    bool exists(const string &service_ name, bool print_ failure_ _reason);
    //等待一个服务,直到它可调用
    bool waitForService(const string &service_ name, int32_ _t timeout);
    
ros::names N amespace
    常用函数
    string append(const std:string &left, const stl::string &right); //追加名称
    string clean (const string &name); //清除图资源名称:删去双斜线、结尾斜线
    const M string &getRemappings (); //返回重映射remapping
    string remap (const string &name); //对名称重映射
    string resolve(const string &name, bool remap= true); //解析出名称的全名
    bool validate(const string &name, string &error); //验证名称    

关闭一个节点可以直接在终端上按Ctrl+C,系统会自动触发SIGINT句柄来关闭这个进程。 你也可以通过调用ros::shutdown()来手动关闭节点,但通常我们很少这样做

最后一节值得再看

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页