ROS系列:第三章(一)

系列文章目录

第一章 ROS概述与环境搭建
第二章 ROS通信机制
第三章 ROS通信机制进阶
第四章 ROS运行管理
第五章 ROS常用组件
第六章 机器人系统仿真
第七章 机器人系统仿真
第八章 机器人系统仿真
第九章 机器人系统仿真
第十章 机器人系统仿真



前言

  • 用来复习回顾使用
  • 本篇笔记视频讲解地址:Bilibili大学
  • 笔记配合视频效果更好
  • 如有错误和遗漏,可私信与评论进行指正,看到了会及时更改
  • 怕什么真理无穷,进一寸有一寸的欢喜
  • 与各位共勉

三、ROS通信机制进阶

API的英文即Application Programming Interface首字母的缩写。即程序之间的接口。我更倾向于把API理解为,程序之间的合约。

1.常用API

  • ROS节点的初始化相关API;

  • NodeHandle 的基本使用相关API;

  • 话题的发布方,订阅方对象相关API;

  • 服务的服务端,客户端对象相关API;

  • 时间相关API;

  • 日志输出相关API

准备工作:

  1. 在上一章节中的demo03_ws/src下创建/plumbing_apis功能包

实现流程:

  1. 右键src选择Create Catkin Package;

  2. 输入功能包名字/plumbing_apis,添加功能包依赖roscpp rospy std_msgs;

  3. 在/plumbing_apis/src下创建demo01_apis_pub.cpp;

  4. 代码如下:

    #include "ros/ros.h"
    #include "std_msgs/String.h"//普通文本类型信息
    #include <sstream>
    //字符拼接数字
    //要求以10hz频率发布数据,并且文本后添加发布编号
        // 1.包含头文件
    int main(int argc, char *argv[])
    {
        // 解决乱码问题
        setlocale(LC_ALL,"");
        //2.初始化ros节点
        ros::init(argc,argv,"talker");
        //3.创建节点句柄
        ros::NodeHandle nh;
        //4.创建发布者对象
        ros::Publisher pub;
        pub = nh.advertise<std_msgs::String>("informations", 10);
        //5.编写发布逻辑并发布数据
        std_msgs::String msg;
        //设置发布频率,专有函数调用就可以
        ros::Rate rate(0.5);
        //设置发布编号
        int count = 0;
        //发送数据之前添加一个休眠汗说,出现订阅丢失前几条数据,原因是注册第一条数据的时候,发布者还没有完成注册
        ros::Duration(3).sleep();
        //编写循环,循环发布数据
        while (ros::ok())
        {
            count++;
            //实现字符串拼接数字 两者需要拼接成一个字符串,需要包含新的头文件
            std::stringstream ss;
            ss <<"hello ---> " << count;
            // msg.data = "hello";
            msg.data = ss.str();
    
            pub.publish(msg);
            //添加日志   先把字符串类stringstream转化为string再转化为c语言的string
            ROS_INFO("发布的数据:%s",ss.str().c_str());
            //延时函数0.1s
            rate.sleep();
            
        }
        return 0;
    }
    
  5. 在/plumbing_apis/src下创建demo02_apis_sub.cpp;

  6. 代码如下:

    #include "ros/ros.h"
    #include "std_msgs/String.h"//普通文本类型信息
    /**
     * @brief 订阅方实现:包含消息头文件、初始化ROS节点、创建节点句柄、创建订阅者对象、处理订阅到的数据、spin函数
     * 
     * @param argc 
     * @param argv 
     * @return int 
     */
    
    void  doMsg(const std_msgs::String::ConstPtr &msg)
    {
        ROS_INFO("订阅的数据:%s",msg->data.c_str());
    }
    int main(int argc, char *argv[])
    {
        // 解决乱码问题
        setlocale(LC_ALL,"");
        //2.初始化ros节点  节点名称要唯一
        ros::init(argc,argv,"lisener");
        //3.创建节点句柄
        ros::NodeHandle nh;
        //4.创建发布者对象
        ros::Subscriber  sub;
        sub = nh.subscribe<std_msgs::String>("informations", 10, doMsg);
        // 5.处理订阅到的数据
        ros::spin(); // spin函数是回旋函数,用于处理回调函数
    
    
        return 0;
    
    }
    
  7. 在/plumbing_apis下新建目录srv并在目录下创建自定义文件/srv/AddInts.srv;

  8. 代码如下:

    # 客户端请求时发送的两个数字
    int32 num1
    int32 num2
    ---
    # 服务器响应发送的数据
    int32 sum
    
  9. 在/plumbing_apis/src下创建demo03_apis_server.cpp;

  10. 代码如下:

    #include "ros/ros.h"
    #include "plumbing_apis/AddInts.h"
    
    /**
     * @brief 服务端实现
     * 解析客户端提交的数据,并运算产生响应
     * 1、包含头文件
     * 2、初始化ros节点
     * 3、创建节点句柄
     * 4、创建服务对象
     * 5、处理请求并产生响应
     * 6、spin()一定要加,循环则是spinOnce
     * @param argc
     * @param argv
     * @return int
     */
    bool doNums(plumbing_apis::AddInts::Request &request,
                plumbing_apis::AddInts::Response &response)
    {
        // 1、处理请求
        int num1 = request.num1;
        int num2 = request.num2;
        ROS_INFO("收到的请求数据是:num1=%d,num2=%d", num1, num2);
        // 2、组织响应
        int sum = num1 + num2;
        response.sum = sum;
        ROS_INFO("求和结果sum=%d", sum);
    
        return true;
    }
    int main(int argc, char *argv[])
    {
        setlocale(LC_ALL, "");
        ROS_INFO("服务端已启动");
        //  * 2、初始化ros节点
        ros::init(argc, argv, "HeiShui"); //保证名称唯一
        //  * 3、创建节点句柄
        ros::NodeHandle nh;
    
        //  * 4、创建服务对象 服务起了个话题名称addInts
        ros::ServiceServer server = nh.advertiseService("addInts", doNums);
        //  * 5、处理请求并产生响应
        //  * 6、spin()一定要加,循环则是spinOnce
        ros::spin();
        return 0;
    }
    
  11. 在/plumbing_apis/src下创建demo04_apis_client.cpp;

  12. 代码如下:

    #include "ros/ros.h"
    #include "plumbing_apis/AddInts.h"
    
    /**
     * @brief
     * 1、包含头文件
     * 2、初始化ros节点
     * 3、创建节点句柄
     * 4、创建一个客户端对象
     * 5、提交申请,并进行处理响应
     *
     * 6、添加argc、argv使用
     * argc是判断有几个元素,写入argv
     * argv[]并不是指针,只是保存c风格的字符串数组,其中argv[0]保存的是客户端的地址,因此从argv[1]开始
     *
     * 问题u:
     *      如果先启动客户端,那么会请求异常
     * 需求:
     *      如果先启动客户端,不能直接出现异常,而是挂起,等待服务器启动之后,再正常请求
     * 解决:
     *      在ros中内置了相关函数,这些函数可以让客户端启动后挂起,等待服务器的启动
     *           client.waitForExistence();
     *           ros::service::waitForService("服务话题");
     */
    
    int main(int argc, char* argv[])
    {
        setlocale(LC_ALL, "");
        if(argc!=3){
            ROS_INFO("输入错误!请重新输入!");
            return 1;
        }
        ROS_INFO("客户端启动成功");
        //  * 2、初始化ros节点
        ros::init(argc, argv, "daBao");
        //  * 3、创建节点句柄
        ros::NodeHandle nh;
        //  * 4、创建一个客户端对象
        ros::ServiceClient client = nh.serviceClient<plumbing_apis::AddInts>("addInts");
        //  * 5、提交申请,并进行处理响应
        plumbing_apis::AddInts ai;
        // 5.1 发出请求
        ai.request.num1 = atoi(argv[1]);
        ai.request.num2 = atoi(argv[2]);
        // 5.2 响应处理
        // 调用判断服务器状态的函数
        // 函数1
        // client.waitForExistence();
        ros::service::waitForService("addInts");
    
        bool flag = client.call(ai);
        if (flag)
        {
            ROS_INFO("成功!");
            ROS_INFO("sum=%d", ai.response.sum);
        } else {
            ROS_INFO("失败。。。");
        }
        return 0;
    }
    
  13. 在/plumbing_apis/src下创建demo05_apis_time.cpp;

  14. 代码如下:

  15. #include <ros/ros.h>
    /**
     * @brief
     * 需求1:演示时间相关的操作(获取当前时刻+设置指定时刻)
     * 实现:
     *      1、准备(头文件、节点初始化、节点句柄创建)
     *      2、获取当前时刻
     *      3、设置指定时刻
     * 需求2:程序执行中停顿5秒
     * 实现:
     *      1、创建持续时间对象
     *      2、休眠
     * 需求3:已知程序开始运行的时刻 和程序运行的时间 ,求运行结束的时刻
     * 实现:
     *      1、获取开始执行的时刻
     *      2、模拟运行时间(N秒)
     *      3、计算结束时刻 = 开始 + 持续时间
     * 注意:
     *      1、时刻与持续时间可以执行加减;
     *      2、持续时间之间也可以执行加减;
     *      3、时刻之间可以相减,不可以相加。
     * 需求4:每隔1秒钟,在控制台输出一段文本
     * 实现:
     *      1、策略1: ros::Rate()
     *      2、策略2: 定时器
     * 注意:
     *      创建:nh.createTimer()
     *      參数1:时间间隔
     *      參数2:回调函数(时间事件 TimerEvent)
     *      參数3:是否只执行一次
     *      參数4:是香自动启动(当设置为 false, 需要手动调用 timer
     *
     *      定时器启动后:ros::spin()
     */
    //回调函数
    void cb(const ros::TimerEvent &event){
        ROS_INFO("------------");
        ROS_INFO("函数被调用的时刻:%.2f",event.current_real.toSec());
    }
    int main(int argc, char *argv[])
    {
        //  *1、准备(头文件、节点初始化、节点句柄创建)
        setlocale(LC_ALL,"");
        ros::init(argc, argv, "hello_time");
        ros::NodeHandle nh;//注意:必须创建句柄,否则会导致时间API调用失败(再NodeHandle会初始化时间操作)
        //  *2、获取当前时刻
        //now 函数会将当前时刻封装并返回
        //当前时刻:now 被调用执行的那一刻
        //参考系:1970年01月01日  00:00:00
        ros::Time right_now =ros::Time::now();
        ROS_INFO("当前时刻:%.2f", right_now.toSec());//将时间转化为秒
        ROS_INFO("当前时刻:%d", right_now.sec);       //返回整型
        //  *3、设置指定时刻
        ros::Time t1(20,312345678);
        ros::Time t2(100.35);
        ROS_INFO("t1=%.2f", t1.toSec());
        ROS_INFO("t2=%.2f", t2.toSec());
        //------------------------------------------------
        ROS_INFO("---------------持续时间---------------");
        ros::Time start=ros::Time::now();
    
        ROS_INFO("开始休眠:%.2f", start.toSec());
        ros::Duration du(4.5);
        du.sleep();
        ros::Time end = ros::Time::now();
        ROS_INFO("休眠结束:%.2f", end.toSec());
        //------------------------------------------------
        ROS_INFO("---------------时间运算---------------");
        //时刻与持续时间运算
        //*1、获取开始执行的时刻
        ros::Time begin = ros::Time::now();
        //*2、模拟运行时间(N秒)
        ros::Duration du1(5);
        //*3、计算结束时刻 = 开始 + 持续时间
        ros::Time stop = begin + du1;//也可以减
        ROS_INFO("开始时刻:%.2f", begin.toSec());
        ROS_INFO("结束时刻:%.2f", stop.toSec());
        //时刻与时刻运算
        // ros::Time sum = begin + stop; // error 不可以相加
        ros::Duration du2 = begin - stop;
        ROS_INFO("时刻相减:%.2f", du2.toSec());
        //持续时间与持续时间运算
        ros::Duration du3 = du1 + du2;//0
        ros::Duration du4 = du1 - du2;//10
        ROS_INFO("持续时间与持续时间相加:%.2f", du3.toSec());
        ROS_INFO("持续时间与持续时间相减:%.2f", du4.toSec());
        //------------------------------------------------
        ROS_INFO("---------------定时器---------------");
        /* ros::Timer createTimer(ros::Duration period, //时间间隔----1s
                const ros::TimerCallback &callback,     //回调函数----封装业务
                bool oneshot = false,                   //是否是一次性  true隔1s执行一次,但只运行一次回调函数;false则循环每秒一次
                bool autostart = true)                  //自动启动    
    
        */
        // ros::Timer timer = nh.createTimer(ros::Duration(1),cb);
        // ros::Timer timer = nh.createTimer(ros::Duration(1),cb,true);
        ros::Timer timer = nh.createTimer(ros::Duration(1), cb,false,false);//关闭自动启动
        timer.start();//手动启动
    
        ros::spin();//回调函数需要回旋函数
        return 0;
    }
    
  16. 在/plumbing_apis/src下创建demo06_apis_log.cpp;

  17. 代码如下:

  18. #include <ros/ros.h>
    /**
     * @brief
     * ROS 中日志:
     *      演示不同级别日志的基本使用
     */
    int main(int argc, char *argv[])
    {
        setlocale(LC_ALL, "") ;
        ros::init (argc,argv,"hello_log") ;
        ros ::NodeHandle nh;
        //日志榆出
        ROS_DEBUG("调试信息");//不会打印在控制台
        ROS_INFO("一般信息");
        ROS_WARN("警告信息");
        ROS_ERROR("错误信息");
        ROS_FATAL("严重错误");
        return 0;
    }
    
  19. 在/plumbing_apis/package.xml下添加依赖库相:

    <build_depend>message_generation</build_depend>
    <exec_depend>message_runtime</exec_depend>
    
  20. 在/plumbing_apis/CMakeLists.txt下配置相关文件:

    find_package(catkin REQUIRED COMPONENTS
      roscpp
      rospy
      std_msgs
      message_generation
    )
    
    add_service_files(
      FILES
      AddInts.srv
    )
    
    generate_messages(
      DEPENDENCIES
      std_msgs
    )
    
    catkin_package(
    #  INCLUDE_DIRS include
    #  LIBRARIES plumbing_apis
     CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
    #  DEPENDS system_lib
    )
    
    
    add_executable(demo01_apis_pub src/demo01_apis_pub.cpp)
    add_executable(demo02_apis_sub src/demo02_apis_sub.cpp)
    add_executable(demo03_apis_server src/demo03_apis_server.cpp)
    add_executable(demo04_apis_client src/demo04_apis_client.cpp)
    add_executable(demo05_apis_time src/demo05_apis_time.cpp)
    add_executable(demo06_apis_log src/demo06_apis_log.cpp)
    
    add_dependencies(demo01_apis_pub ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    add_dependencies(demo02_apis_sub ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    add_dependencies(demo03_apis_server ${PROJECT_NAME}_gencpp)
    add_dependencies(demo04_apis_client ${PROJECT_NAME}_gencpp)
    add_dependencies(demo05_apis_time ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    add_dependencies(demo06_apis_log ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    
    target_link_libraries(demo01_apis_pub
      ${catkin_LIBRARIES}
    )
    target_link_libraries(demo02_apis_sub
      ${catkin_LIBRARIES}
    )
    target_link_libraries(demo03_apis_server
      ${catkin_LIBRARIES}
    )
    target_link_libraries(demo04_apis_client
      ${catkin_LIBRARIES}
    )
    target_link_libraries(demo05_apis_time
      ${catkin_LIBRARIES}
    )
    target_link_libraries(demo06_apis_log
      ${catkin_LIBRARIES}
    )
    

1.1ROS节点的初始化相关API

C++

初始化API代码如下:

#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型信息
#include <sstream>
//字符拼接数字
//要求以10hz频率发布数据,并且文本后添加发布编号
// 1.包含头文件
int main(int argc, char *argv[])
{
    // 解决乱码问题
    setlocale(LC_ALL, "");
    /*
        作用:ROS初始化函数
        参数:
            1、argc     封装函数传入的实参个数(n+1)
            2、argv     封装参数的数组
            3、name     为节点命名   应该保证唯一性
            4、option   节点启动选择

            返回值为空
        使用:
            1、argc  argv 的使用
                如果按照ros中的特定格式传入实参,那么ros可以加以使用,比如用来设置全局参数、给节点重新命名。。。。
            2、options 的使用
                节点名称需要保证唯一,同一个节点不能重复启动
                同名节点再次启动,之前的的节点会被关闭
                需求:特定场景下,一个节点需要多次启动,需要保证两次启动都是正常运行的情况,怎么办?
                解决:设置启动项--ros::init_options::AnonymousName添加随机数
                创建ROS节点是,会在用户自定义的节点名称后缀随机数,从而避免重名问题
    */
    // 2.初始化ros节点
    ros::init(argc, argv, "talker",ros::init_options::AnonymousName);
     //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建发布者对象
    ros::Publisher pub;
    pub = nh.advertise<std_msgs::String>("informations", 10);
    //5.编写发布逻辑并发布数据
    std_msgs::String msg;
    //设置发布频率,专有函数调用就可以
    ros::Rate rate(0.5);
    //设置发布编号
    int count = 0;
    //发送数据之前添加一个休眠汗说,出现订阅丢失前几条数据,原因是注册第一条数据的时候,发布者还没有完成注册
    ros::Duration(3).sleep();
    //编写循环,循环发布数据
    while (ros::ok())
    {
        count++;
        //实现字符串拼接数字 两者需要拼接成一个字符串,需要包含新的头文件
        std::stringstream ss;
        ss <<"hello ---> " << count;
        // msg.data = "hello";
        msg.data = ss.str();

        pub.publish(msg);
        //添加日志   先把字符串类stringstream转化为string再转化为c语言的string
        ROS_INFO("发布的数据:%s",ss.str().c_str());
        //延时函数0.1s
        rate.sleep();
        
    }
    return 0;
}

注意-- 同名节点再次启动,上一个启动的节点会自动关闭,然后操作节点启动成功

2.NodeHandle 的基本使用相关API

在 roscpp 中,话题和服务的相关对象一般由 NodeHandle 创建 。

2.1消息发布对象

代码如下:

#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型信息
#include <sstream>
//字符拼接数字
//要求以10hz频率发布数据,并且文本后添加发布编号
// 1.包含头文件
int main(int argc, char *argv[])
{
    // 解决乱码问题
    setlocale(LC_ALL, "");
    /*
        作用:ROS初始化函数
        参数:
            1、argc     封装函数传入的实参个数(n+1)
            2、argv     封装参数的数组
            3、name     为节点命名   应该保证唯一性
            4、option   节点启动选择

            返回值为空
        使用:
            1、argc  argv 的使用
                如果按照ros中的特定格式传入实参,那么ros可以加以使用,比如用来设置全局参数、给节点重新命名。。。。
            2、options 的使用
                节点名称需要保证唯一,同一个节点不能重复启动
                同名节点再次启动,之前的的节点会被关闭
                需求:特定场景下,一个节点需要多次启动,需要保证两次启动都是正常运行的情况,怎么办?
                解决:设置启动项--ros::init_options::AnonymousName添加随机数
                创建ROS节点是,会在用户自定义的节点名称后缀随机数,从而避免重名问题
    */
    // 2.初始化ros节点
    ros::init(argc, argv, "talker",ros::init_options::AnonymousName);
    // 3.创建节点句柄
    ros::NodeHandle nh;
    // 4.创建发布者对象
    ros::Publisher pub;
    pub = nh.advertise<std_msgs::String>("informations", 10,true);
    /*
        作用:创建发布者对象
        模板:被发布的消息的类型
        参数:
            1、话题名称
            2、队列长度
            3、latch(可选)  如果设置为true ,会保存发布方的最后一条消息,并且新的订阅对象连接到发布方时,发布方会将这条消息发布给订阅者
        使用:
            latch 设置为true 的作用?
            导航之中 以静态地图发布为例,方案1:可以使用固定频率发送地图数据,但效率太低;方案2:可以将地图发布对象的latch设置为true ,并且发布方只发送一次数据,每当订阅者连接时,将地图数据发送给订阅者(只发送一次),提高数据发送效率


     */
    // 5.编写发布逻辑并发布数据
    std_msgs::String msg;
    //设置发布频率,专有函数调用就可以
    ros::Rate rate(0.5);
    //设置发布编号
    int count = 0;
    //发送数据之前添加一个休眠汗说,出现订阅丢失前几条数据,原因是注册第一条数据的时候,发布者还没有完成注册
    ros::Duration(3).sleep();
    //编写循环,循环发布数据
    while (ros::ok())
    {
        count++;
        //实现字符串拼接数字 两者需要拼接成一个字符串,需要包含新的头文件
        std::stringstream ss;
        ss << "hello ---> " << count;
        // msg.data = "hello";
        msg.data = ss.str();
        if(count<=10)
        {
            pub.publish(msg);
            //添加日志   先把字符串类stringstream转化为string再转化为c语言的string
            ROS_INFO("发布的数据:%s", ss.str().c_str());
        }
        //延时函数0.1s
        rate.sleep();
    }
    return 0;
}

latch 设置为true 的作用?
导航之中 以静态地图发布为例:

方案1:可以使用固定频率发送地图数据,但效率太低;

方案2:可以将地图发布对象的latch设置为true ,并且发布方只发送一次数据,每当订阅者连接时,将地图数据发送给订阅者(只发送一次),提高数据发送效率。

2.2消息订阅对象

代码如下:

#include "ros/ros.h"
#include "std_msgs/String.h"//普通文本类型信息
/**
 * @brief 订阅方实现:包含消息头文件、初始化ROS节点、创建节点句柄、创建订阅者对象、处理订阅到的数据、spin函数
 * 
 * @param argc 
 * @param argv 
 * @return int 
 */

void  doMsg(const std_msgs::String::ConstPtr &msg)
{
    ROS_INFO("订阅的数据:%s",msg->data.c_str());
}
int main(int argc, char *argv[])
{
    // 解决乱码问题
    setlocale(LC_ALL,"");
    //2.初始化ros节点  节点名称要唯一
    ros::init(argc,argv,"lisener");
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建发布者对象
    ros::Subscriber  sub;
    sub = nh.subscribe<std_msgs::String>("informations", 10, doMsg);
    /*
    作用:生成某个话题的订阅对象
    模板:nh.subscribe <订阅消息的类型> ("订阅话题", 消息队列长度,回调函数)
    参数:
        1、话题名称
        2、消息队列长度
        3、回调函数  传入该消息的共享指针,该消息不能被修改,因为可能其他订阅对象也会使用该消息
    使用:
        回调函数的作用?
            当订阅到一条消息时,自动执行回调函数(调用子函数),可将相关信息输出至屏幕。。。。
    返回:
        调用成功时,返回一个订阅者对象信息;
        调用失败时,返回空对象;
    */
   if(sub)
   {
       ROS_INFO("订阅成功");
   }else{
       ROS_INFO("订阅失败");
   }
    // 5.处理订阅到的数据
    ros::spin(); // spin函数是回旋函数,用于处理回调函数

    return 0;

}

修改话题名称根据if判断订阅对象是否成功。

2.3服务对象

代码如下:

#include "ros/ros.h"
#include "plumbing_apis/AddInts.h"

/**
 * @brief 服务端实现
 * 解析客户端提交的数据,并运算产生响应
 * 1、包含头文件
 * 2、初始化ros节点
 * 3、创建节点句柄
 * 4、创建服务对象
 * 5、处理请求并产生响应
 * 6、spin()一定要加,循环则是spinOnce
 * @param argc
 * @param argv
 * @return int
 */
bool doNums(plumbing_apis::AddInts::Request &request,
            plumbing_apis::AddInts::Response &response)
{
    // 1、处理请求
    int num1 = request.num1;
    int num2 = request.num2;
    ROS_INFO("收到的请求数据是:num1=%d,num2=%d", num1, num2);
    // 2、组织响应
    int sum = num1 + num2;
    response.sum = sum;
    ROS_INFO("求和结果sum=%d", sum);

    return true;
}
int main(int argc, char *argv[])
{
    setlocale(LC_ALL, "");
    ROS_INFO("服务端已启动");
    //  * 2、初始化ros节点
    ros::init(argc, argv, "HeiShui"); //保证名称唯一
    //  * 3、创建节点句柄
    ros::NodeHandle nh;
    //  * 4、创建服务对象 服务起了个话题名称addInts
    ros::ServiceServer server = nh.advertiseService("addInts", doNums);
    /*
    作用:该函数可以连接到 ROS master,并提供一个具有给定名称的服务对象
    模板:ros::ServiceServer service = handle.advertiseService("my_service", callback);
    参数:
        1、my_service   服务的主题名称
        2、callback     接收到请求时,需要处理请求的回调函数
    使用:
        生成服务端对象
    返回:
        成功返回 服务对象;
        失败返回 空对象;
    */
    //  * 5、处理请求并产生响应
    //  * 6、spin()一定要加,循环则是spinOnce
    ros::spin();
    return 0;
}

2.4客户端对象

代码如下:

#include "ros/ros.h"
#include "plumbing_apis/AddInts.h"

/**
 * @brief
 * 1、包含头文件
 * 2、初始化ros节点
 * 3、创建节点句柄
 * 4、创建一个客户端对象
 * 5、提交申请,并进行处理响应
 *
 * 6、添加argc、argv使用
 * argc是判断有几个元素,写入argv
 * argv[]并不是指针,只是保存c风格的字符串数组,其中argv[0]保存的是客户端的地址,因此从argv[1]开始
 *
 * 问题u:
 *      如果先启动客户端,那么会请求异常
 * 需求:
 *      如果先启动客户端,不能直接出现异常,而是挂起,等待服务器启动之后,再正常请求
 * 解决:
 *      在ros中内置了相关函数,这些函数可以让客户端启动后挂起,等待服务器的启动
 *           client.waitForExistence();
 *           ros::service::waitForService("服务话题");
 */

int main(int argc, char* argv[])
{
    setlocale(LC_ALL, "");
    if(argc!=3){
        ROS_INFO("输入错误!请重新输入!");
        return 1;
    }
    ROS_INFO("客户端启动成功");
    //  * 2、初始化ros节点
    ros::init(argc, argv, "daBao");
    //  * 3、创建节点句柄
    ros::NodeHandle nh;
    //  * 4、创建一个客户端对象
    ros::ServiceClient client = nh.serviceClient<plumbing_apis::AddInts>("addInts");
    /*
    作用:创建一个请求服务的客户端对象
    模板:nh.serviceClient <xxx.srv文件数据类型> ("服务话题名称")
    参数:
        1、服务主题名称
    使用:
        向服务端发出申请,根据服务端返回数据进行处理响应
    */
    //  * 5、提交申请,并进行处理响应
    plumbing_apis::AddInts ai;
    // 5.1 发出请求
    ai.request.num1 = atoi(argv[1]);
    ai.request.num2 = atoi(argv[2]);
    // 5.2 响应处理
    // 调用判断服务器状态的函数
    // 函数1
    // client.waitForExistence();
    /*
    作用:等待服务可用,否则一直处于阻塞状态
    模板: client.waitForExistence();
    参数:
        1、service_name          被"等待"的服务的话题名称
    使用:
        等待服务函数
    返回:
        成功返回 bool true;
        失败返回 bool false;
    */
    ros::service::waitForService("addInts");
    /*
    作用:等待服务可用,否则一直处于阻塞状态
    模板:ros::service::waitForService("服务话题名称");
    参数:
        1、service_name   被"等待"的服务的话题名称
    使用:
        等待服务函数1
    返回:
        成功返回 bool true;
        失败返回 bool false;
    */
    bool flag = client.call(ai);
    /*
    作用:发送请求
    模板:bool xxx= client.call(Service& service);
    参数:
        1、Service& service   xxx.srv文件数据类型数据
    使用:
        请求发送函数
    返回:
        成功返回 bool true;
        失败返回 bool false;
    */
    if (flag)
    {
        ROS_INFO("成功!");
        ROS_INFO("sum=%d", ai.response.sum);
    } else {
        ROS_INFO("失败。。。");
    }
    return 0;
}

3.回旋函数相关API

在ROS程序中使用 ros::spin() 和 ros::spinOnce() 两个回旋函数,用于处理回调函数。

3.1spinOnce()

处理一轮回调

* 一般应用场景:
* 在循环体内,处理所有可用的回调函数   

代码如下:

#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型信息
#include <sstream>
//字符拼接数字
//要求以10hz频率发布数据,并且文本后添加发布编号
// 1.包含头文件
int main(int argc, char *argv[])
{
    // 解决乱码问题
    setlocale(LC_ALL, "");
    /*
        作用:ROS初始化函数
        参数:
            1、argc     封装函数传入的实参个数(n+1)
            2、argv     封装参数的数组
            3、name     为节点命名   应该保证唯一性
            4、option   节点启动选择

            返回值为空
        使用:
            1、argc  argv 的使用
                如果按照ros中的特定格式传入实参,那么ros可以加以使用,比如用来设置全局参数、给节点重新命名。。。。
            2、options 的使用
                节点名称需要保证唯一,同一个节点不能重复启动
                同名节点再次启动,之前的的节点会被关闭
                需求:特定场景下,一个节点需要多次启动,需要保证两次启动都是正常运行的情况,怎么办?
                解决:设置启动项--ros::init_options::AnonymousName添加随机数
                创建ROS节点是,会在用户自定义的节点名称后缀随机数,从而避免重名问题
    */
    // 2.初始化ros节点
    ros::init(argc, argv, "talker",ros::init_options::AnonymousName);
    // 3.创建节点句柄
    ros::NodeHandle nh;
    // 4.创建发布者对象
    ros::Publisher pub;
    pub = nh.advertise<std_msgs::String>("informations", 10,true);
    /*
        作用:创建发布者对象
        模板:被发布的消息的类型
        参数:
            1、话题名称
            2、队列长度
            3、latch(可选)  如果设置为true ,会保存发布方的最后一条消息,并且新的订阅对象连接到发布方时,发布方会将这条消息发布给订阅者
        使用:
            latch 设置为true 的作用?
            导航之中 以静态地图发布为例,方案1:可以使用固定频率发送地图数据,但效率太低;方案2:可以将地图发布对象的latch设置为true ,并且发布方只发送一次数据,每当订阅者连接时,将地图数据发送给订阅者(只发送一次),提高数据发送效率


     */
    // 5.编写发布逻辑并发布数据
    std_msgs::String msg;
    //设置发布频率,专有函数调用就可以
    ros::Rate rate(0.5);
    //设置发布编号
    int count = 0;
    //发送数据之前添加一个休眠汗说,出现订阅丢失前几条数据,原因是注册第一条数据的时候,发布者还没有完成注册
    ros::Duration(3).sleep();
    //编写循环,循环发布数据
    while (ros::ok())
    {
        count++;
        //实现字符串拼接数字 两者需要拼接成一个字符串,需要包含新的头文件
        std::stringstream ss;
        ss << "hello ---> " << count;
        // msg.data = "hello";
        msg.data = ss.str();
        // if(count<=10)
        // {
        //     pub.publish(msg);
        //     //添加日志   先把字符串类stringstream转化为string再转化为c语言的string
        //     ROS_INFO("发布的数据:%s", ss.str().c_str());
        // }
        pub.publish(msg);
        //添加日志   先把字符串类stringstream转化为string再转化为c语言的string
        ROS_INFO("发布的数据:%s", ss.str().c_str()); 
        //延时函数0.1s
        rate.sleep();
        ros::spinOnce();
        ROS_INFO("一轮回调执行完毕....");
    }
    return 0;
}

3.2spin()

进入循环处理回调

* 一般应用场景:
* 程序会一直回旋调用回调函数   

代码如下:

#include "ros/ros.h"
#include "std_msgs/String.h"//普通文本类型信息
/**
 * @brief 订阅方实现:包含消息头文件、初始化ROS节点、创建节点句柄、创建订阅者对象、处理订阅到的数据、spin函数
 * 
 * @param argc 
 * @param argv 
 * @return int 
 */

void  doMsg(const std_msgs::String::ConstPtr &msg)
{
    ROS_INFO("订阅的数据:%s",msg->data.c_str());
}
int main(int argc, char *argv[])
{
    // 解决乱码问题
    setlocale(LC_ALL,"");
    //2.初始化ros节点  节点名称要唯一
    ros::init(argc,argv,"lisener");
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建发布者对象
    ros::Subscriber  sub;
    sub = nh.subscribe<std_msgs::String>("informations", 10, doMsg);
    /*
    作用:生成某个话题的订阅对象
    模板:nh.subscribe <订阅消息的类型> ("订阅话题", 消息队列长度,回调函数)
    参数:
        1、话题名称
        2、消息队列长度
        3、回调函数  传入该消息的共享指针,该消息不能被修改,因为可能其他订阅对象也会使用该消息
    使用:
        回调函数的作用?
            当订阅到一条消息时,自动执行回调函数(调用子函数),可将相关信息输出至屏幕。。。。
    返回:
        调用成功时,返回一个订阅者对象信息;
        调用失败时,返回空对象;
    */
    // 5.处理订阅到的数据
    ros::spin(); // spin函数是回旋函数,用于处理回调函数
    ROS_INFO("spin后的语句");

    return 0;

}

区别:
spinOnce()函数后面的语句可以运行,而spin()函数后面的语句并不会运行;

4.时间相关API

ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器…都与时间相关

4.1时刻

代码如下:

#include <ros/ros.h>
/**
 * @brief 
 * 需求:演示时间相关的操作(获取当前时刻+设置指定时刻)
 * 实现:
 *      1、准备(头文件、节点初始化、节点句柄创建)
 *      2、获取当前时刻
 *      3、设置指定时刻
 */
int main(int argc, char *argv[])
{
    //  *1、准备(头文件、节点初始化、节点句柄创建)
    setlocale(LC_ALL,"");
    ros::init(argc, argv, "hello_time");
    ros::NodeHandle nh;//注意:必须创建句柄,否则会导致时间API调用失败(再NodeHandle会初始化时间操作)
    //  *2、获取当前时刻
    //now 函数会将当前时刻封装并返回
    //当前时刻:now 被调用执行的那一刻
    //参考系:1970年01月01日  00:00:00
    ros::Time right_now =ros::Time::now();
    ROS_INFO("当前时刻:%.2f", right_now.toSec());//将时间转化为秒
    ROS_INFO("当前时刻:%d", right_now.sec);       //返回整型

    //  *3、设置指定时刻
    ros::Time t1(20,312345678);
    ros::Time t2(100.35);
    ROS_INFO("t1=%.2f", t1.toSec());
    ROS_INFO("t2=%.2f", t2.toSec());

    return 0;
}

参考系:1970年01月01日 00:00:00;

4.2持续时间

代码如下:

#include <ros/ros.h>
/**
 * @brief
 * 需求1:演示时间相关的操作(获取当前时刻+设置指定时刻)
 * 实现:
 *      1、准备(头文件、节点初始化、节点句柄创建)
 *      2、获取当前时刻
 *      3、设置指定时刻
 * 需求2:程序执行中停顿5秒
 * 实现:
 *      1、创建持续时间对象
 *      2、休眠
 */
int main(int argc, char *argv[])
{
    //  *1、准备(头文件、节点初始化、节点句柄创建)
    setlocale(LC_ALL,"");
    ros::init(argc, argv, "hello_time");
    ros::NodeHandle nh;//注意:必须创建句柄,否则会导致时间API调用失败(再NodeHandle会初始化时间操作)
    //  *2、获取当前时刻
    //now 函数会将当前时刻封装并返回
    //当前时刻:now 被调用执行的那一刻
    //参考系:1970年01月01日  00:00:00
    ros::Time right_now =ros::Time::now();
    ROS_INFO("当前时刻:%.2f", right_now.toSec());//将时间转化为秒
    ROS_INFO("当前时刻:%d", right_now.sec);       //返回整型

    //  *3、设置指定时刻
    ros::Time t1(20,312345678);
    ros::Time t2(100.35);
    ROS_INFO("t1=%.2f", t1.toSec());
    ROS_INFO("t2=%.2f", t2.toSec());
    //------------------------------------------------
    ROS_INFO("---------------持续时间---------------");
    ros::Time start=ros::Time::now();

    ROS_INFO("开始休眠:%.2f", start.toSec());
    ros::Duration du(4.5);
    du.sleep();
    ros::Time end = ros::Time::now();
    ROS_INFO("休眠结束:%.2f", end.toSec());

    return 0;
}

主要对一段时间进行操作;

4.3持续时间与时刻运算

代码如下:

#include <ros/ros.h>
/**
 * @brief
 * 需求1:演示时间相关的操作(获取当前时刻+设置指定时刻)
 * 实现:
 *      1、准备(头文件、节点初始化、节点句柄创建)
 *      2、获取当前时刻
 *      3、设置指定时刻
 * 需求2:程序执行中停顿5秒
 * 实现:
 *      1、创建持续时间对象
 *      2、休眠
 * 需求2:已知程序开始运行的时刻 和程序运行的时间 ,求运行结束的时刻
 * 实现:
 *      1、获取开始执行的时刻
 *      2、模拟运行时间(N秒)
 *      3、计算结束时刻 = 开始 + 持续时间
 * 注意:
 *      1、时刻与持续时间可以执行加减;
 *      2、持续时间之间也可以执行加减;
 *      3、时刻之间可以相减,不可以相加。
 */
int main(int argc, char *argv[])
{
    //  *1、准备(头文件、节点初始化、节点句柄创建)
    setlocale(LC_ALL,"");
    ros::init(argc, argv, "hello_time");
    ros::NodeHandle nh;//注意:必须创建句柄,否则会导致时间API调用失败(再NodeHandle会初始化时间操作)
    //  *2、获取当前时刻
    //now 函数会将当前时刻封装并返回
    //当前时刻:now 被调用执行的那一刻
    //参考系:1970年01月01日  00:00:00
    ros::Time right_now =ros::Time::now();
    ROS_INFO("当前时刻:%.2f", right_now.toSec());//将时间转化为秒
    ROS_INFO("当前时刻:%d", right_now.sec);       //返回整型
    //  *3、设置指定时刻
    ros::Time t1(20,312345678);
    ros::Time t2(100.35);
    ROS_INFO("t1=%.2f", t1.toSec());
    ROS_INFO("t2=%.2f", t2.toSec());
    //------------------------------------------------
    ROS_INFO("---------------持续时间---------------");
    ros::Time start=ros::Time::now();

    ROS_INFO("开始休眠:%.2f", start.toSec());
    ros::Duration du(4.5);
    du.sleep();
    ros::Time end = ros::Time::now();
    ROS_INFO("休眠结束:%.2f", end.toSec());
    //------------------------------------------------
    ROS_INFO("---------------时间运算---------------");
    //时刻与持续时间运算
    //*1、获取开始执行的时刻
    ros::Time begin = ros::Time::now();
    //*2、模拟运行时间(N秒)
    ros::Duration du1(5);
    //*3、计算结束时刻 = 开始 + 持续时间
    ros::Time stop = begin + du1;//也可以减
    ROS_INFO("开始时刻:%.2f", begin.toSec());
    ROS_INFO("结束时刻:%.2f", stop.toSec());
    //时刻与时刻运算
    // ros::Time sum = begin + stop; // error 不可以相加
    ros::Duration du2 = begin - stop;
    ROS_INFO("时刻相减:%.2f", du2.toSec());
    //持续时间与持续时间运算
    ros::Duration du3 = du1 + du2;//0
    ros::Duration du4 = du1 - du2;//10
    ROS_INFO("持续时间与持续时间相加:%.2f", du3.toSec());
    ROS_INFO("持续时间与持续时间相减:%.2f", du4.toSec());

    return 0;
}
  • 注意:
  • 1、时刻与持续时间可以执行加减;
  • 2、持续时间之间也可以执行加减;
  • 3、时刻之间可以相减,不可以相加。

4.4设置运行频率

代码如下:

    //设置发布频率,专有函数调用就可以
    ros::Rate rate(0.5);
	
	//延时函数0.1s
    rate.sleep();

4.5定时器

代码如下:

#include <ros/ros.h>
/**
 * @brief
 * 需求1:演示时间相关的操作(获取当前时刻+设置指定时刻)
 * 实现:
 *      1、准备(头文件、节点初始化、节点句柄创建)
 *      2、获取当前时刻
 *      3、设置指定时刻
 * 需求2:程序执行中停顿5秒
 * 实现:
 *      1、创建持续时间对象
 *      2、休眠
 * 需求3:已知程序开始运行的时刻 和程序运行的时间 ,求运行结束的时刻
 * 实现:
 *      1、获取开始执行的时刻
 *      2、模拟运行时间(N秒)
 *      3、计算结束时刻 = 开始 + 持续时间
 * 注意:
 *      1、时刻与持续时间可以执行加减;
 *      2、持续时间之间也可以执行加减;
 *      3、时刻之间可以相减,不可以相加。
 * 需求4:每隔1秒钟,在控制台输出一段文本
 * 实现:
 *      1、策略1: ros::Rate()
 *      2、策略2: 定时器
 * 注意:
 *      创建:nh.createTimer()
 *      參数1:时间间隔
 *      參数2:回调函数(时间事件 TimerEvent)
 *      參数3:是否只执行一次
 *      參数4:是香自动启动(当设置为 false, 需要手动调用 timer
 *
 *      定时器启动后:ros::spin()
 */
//回调函数
void cb(const ros::TimerEvent &event){
    ROS_INFO("------------");
    ROS_INFO("函数被调用的时刻:%.2f",event.current_real.toSec());
}
int main(int argc, char *argv[])
{
    //  *1、准备(头文件、节点初始化、节点句柄创建)
    setlocale(LC_ALL,"");
    ros::init(argc, argv, "hello_time");
    ros::NodeHandle nh;//注意:必须创建句柄,否则会导致时间API调用失败(再NodeHandle会初始化时间操作)
    //  *2、获取当前时刻
    //now 函数会将当前时刻封装并返回
    //当前时刻:now 被调用执行的那一刻
    //参考系:1970年01月01日  00:00:00
    ros::Time right_now =ros::Time::now();
    ROS_INFO("当前时刻:%.2f", right_now.toSec());//将时间转化为秒
    ROS_INFO("当前时刻:%d", right_now.sec);       //返回整型
    //  *3、设置指定时刻
    ros::Time t1(20,312345678);
    ros::Time t2(100.35);
    ROS_INFO("t1=%.2f", t1.toSec());
    ROS_INFO("t2=%.2f", t2.toSec());
    //------------------------------------------------
    ROS_INFO("---------------持续时间---------------");
    ros::Time start=ros::Time::now();

    ROS_INFO("开始休眠:%.2f", start.toSec());
    ros::Duration du(4.5);
    du.sleep();
    ros::Time end = ros::Time::now();
    ROS_INFO("休眠结束:%.2f", end.toSec());
    //------------------------------------------------
    ROS_INFO("---------------时间运算---------------");
    //时刻与持续时间运算
    //*1、获取开始执行的时刻
    ros::Time begin = ros::Time::now();
    //*2、模拟运行时间(N秒)
    ros::Duration du1(5);
    //*3、计算结束时刻 = 开始 + 持续时间
    ros::Time stop = begin + du1;//也可以减
    ROS_INFO("开始时刻:%.2f", begin.toSec());
    ROS_INFO("结束时刻:%.2f", stop.toSec());
    //时刻与时刻运算
    // ros::Time sum = begin + stop; // error 不可以相加
    ros::Duration du2 = begin - stop;
    ROS_INFO("时刻相减:%.2f", du2.toSec());
    //持续时间与持续时间运算
    ros::Duration du3 = du1 + du2;//0
    ros::Duration du4 = du1 - du2;//10
    ROS_INFO("持续时间与持续时间相加:%.2f", du3.toSec());
    ROS_INFO("持续时间与持续时间相减:%.2f", du4.toSec());
    //------------------------------------------------
    ROS_INFO("---------------定时器---------------");
    /* ros::Timer createTimer(ros::Duration period, //时间间隔----1s
            const ros::TimerCallback &callback,     //回调函数----封装业务
            bool oneshot = false,                   //是否是一次性  true隔1s执行一次,但只运行一次回调函数;false则循环每秒一次
            bool autostart = true)                  //自动启动    

    */
    // ros::Timer timer = nh.createTimer(ros::Duration(1),cb);
    // ros::Timer timer = nh.createTimer(ros::Duration(1),cb,true);
    ros::Timer timer = nh.createTimer(ros::Duration(1), cb,false,false);//关闭自动启动
    timer.start();//手动启动

    ros::spin();//回调函数需要回旋函数
    return 0;
}

ROS 中内置了专门的定时器,可以实现与 ros::Rate 类似的效果:。

5.其他函数API

5.1节点关闭APIros::shutdown()

循环发布消息时,循环的判断条件一般由节点状态来控制,C++中可以通过 ros::ok() 来判断节点状态是否正常,其中导致节点退出的原因主要有如下几种:

  1. 节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
  2. 同名节点启动,导致现有节点退出;
  3. 程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown());

代码如下:

#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型信息
#include <sstream>
//字符拼接数字
//要求以10hz频率发布数据,并且文本后添加发布编号
// 1.包含头文件
int main(int argc, char *argv[])
{
    // 解决乱码问题
    setlocale(LC_ALL, "");
    /*
        作用:ROS初始化函数
        参数:
            1、argc     封装函数传入的实参个数(n+1)
            2、argv     封装参数的数组
            3、name     为节点命名   应该保证唯一性
            4、option   节点启动选择

            返回值为空
        使用:
            1、argc  argv 的使用
                如果按照ros中的特定格式传入实参,那么ros可以加以使用,比如用来设置全局参数、给节点重新命名。。。。
            2、options 的使用
                节点名称需要保证唯一,同一个节点不能重复启动
                同名节点再次启动,之前的的节点会被关闭
                需求:特定场景下,一个节点需要多次启动,需要保证两次启动都是正常运行的情况,怎么办?
                解决:设置启动项--ros::init_options::AnonymousName添加随机数
                创建ROS节点是,会在用户自定义的节点名称后缀随机数,从而避免重名问题
    */
    // 2.初始化ros节点
    ros::init(argc, argv, "talker",ros::init_options::AnonymousName);
    // 3.创建节点句柄
    ros::NodeHandle nh;
    // 4.创建发布者对象
    ros::Publisher pub;
    pub = nh.advertise<std_msgs::String>("informations", 10,true);
    /*
        作用:创建发布者对象
        模板:被发布的消息的类型
        参数:
            1、话题名称
            2、队列长度
            3、latch(可选)  如果设置为true ,会保存发布方的最后一条消息,并且新的订阅对象连接到发布方时,发布方会将这条消息发布给订阅者
        使用:
            latch 设置为true 的作用?
            导航之中 以静态地图发布为例,方案1:可以使用固定频率发送地图数据,但效率太低;方案2:可以将地图发布对象的latch设置为true ,并且发布方只发送一次数据,每当订阅者连接时,将地图数据发送给订阅者(只发送一次),提高数据发送效率


     */
    // 5.编写发布逻辑并发布数据
    std_msgs::String msg;
    //设置发布频率,专有函数调用就可以
    ros::Rate rate(0.5);
    //设置发布编号
    int count = 0;
    //发送数据之前添加一个休眠汗说,出现订阅丢失前几条数据,原因是注册第一条数据的时候,发布者还没有完成注册
    ros::Duration(3).sleep();
    //编写循环,循环发布数据
    while (ros::ok())
    {
        //如果计数器>=50,那么关闭节点
        if (count >= 50)
        {
            ros::shutdown();
        }
        //------------------------
        count++;
        //实现字符串拼接数字 两者需要拼接成一个字符串,需要包含新的头文件
        std::stringstream ss;
        ss << "hello ---> " << count;
        // msg.data = "hello";
        msg.data = ss.str();
        // if(count<=10)
        // {
        //     pub.publish(msg);
        //     //添加日志   先把字符串类stringstream转化为string再转化为c语言的string
        //     ROS_INFO("发布的数据:%s", ss.str().c_str());
        // }

        pub.publish(msg);
        //添加日志   先把字符串类stringstream转化为string再转化为c语言的string
        ROS_INFO("发布的数据:%s", ss.str().c_str()); 
        //延时函数0.1s
        rate.sleep();
        ros::spinOnce();
        ROS_INFO("一轮回调执行完毕....");
    }
    return 0;
}

5.2spinOnce()

在ROS中常用的日志被划分成如下级别:

  1. DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;
  2. INFO(信息):标准消息,一般用于说明系统内正在执行的操作;
  3. WARN(警告):提醒一些异常情况,但程序仍然可以执行;
  4. ERROR(错误):提示错误信息,此类错误会影响程序运行;
  5. FATAL(严重错误):此类错误将阻止节点继续运行。

代码如下:

#include <ros/ros.h>
/**
 * @brief
 * ROS 中日志:
 *      演示不同级别日志的基本使用
 */
int main(int argc, char *argv[])
{
    setlocale(LC_ALL, "") ;
    ros::init (argc,argv,"hello_log") ;
    ros ::NodeHandle nh;
    //日志榆出
    ROS_DEBUG("调试信息");//不会打印在控制台
    ROS_INFO("一般信息");
    ROS_WARN("警告信息");
    ROS_ERROR("错误信息");
    ROS_FATAL("严重错误");
    return 0;
}


总结

  • 基本掌握ROS常用API的使用
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会是要长脑子了吧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值