ROS series: Chapter 3 (1)

Series Article Directory

Chapter 1 ROS Overview and Environment Construction
Chapter 2 ROS Communication Mechanism
Chapter 3 ROS Communication Mechanism Advanced
Chapter 4 ROS Operation Management
Chapter 5 ROS Common Components
Chapter 6 Robot System Simulation
Chapter 7 Robot System Simulation
Chapter 8 Robot System Simulation
Chapter 9 Robot System Simulation
Chapter 10 Robot System Simulation



foreword

  • Used for review and review
  • The video explanation address of this note: Bilibili University
  • Notes work better with videos
  • If there are mistakes and omissions, you can send private messages and comments to correct them, and we will change them in time when we see them
  • 怕什么真理无穷,进一寸有一寸的欢喜
  • share with you

3. Advanced ROS communication mechanism

API is the acronym for Application Programming Interface in English. That is, the interface between programs. I prefer to understand API as a contract between programs.

1. Common APIs

  • API related to initialization of ROS nodes;

  • Basic usage related API of NodeHandle;

  • Topic publisher and subscriber object-related APIs;

  • Service server, client object related API;

  • Time related API;

  • Log output related API

Preparation:

  1. Create the /plumbing_apis function package under demo03_ws/src in the previous chapter

Implementation process:

  1. Right-click src and select Create Catkin Package;

  2. Enter the function package name/plumbing_apis, add the function package depends on roscpp rospy std_msgs;

  3. Create demo01_apis_pub.cpp under /plumbing_apis/src;

  4. code show as below:

    #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. Create demo02_apis_sub.cpp under /plumbing_apis/src;

  6. code show as below:

    #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. Create a new directory srv under /plumbing_apis and create a custom file /srv/AddInts.srv under the directory;

  8. code show as below:

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

  10. code show as below:

    #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. Create demo04_apis_client.cpp under /plumbing_apis/src;

  12. code show as below:

    #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. Create demo05_apis_time.cpp under /plumbing_apis/src;

  14. code show as below:

  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. Create demo06_apis_log.cpp under /plumbing_apis/src;

  17. code show as below:

  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. Add the dependent library phase under /plumbing_apis/package.xml:

    <build_depend>message_generation</build_depend>
    <exec_depend>message_runtime</exec_depend>
    
  20. Configure related files under /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.1 ROS node initialization related API

C++

The initialization API code is as follows:

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

Note - the node with the same name starts again, the last started node will automatically shut down, and then the operation node starts successfully

2. The basic usage related API of NodeHandle

In roscpp, related objects of topics and services are generally created by NodeHandle.

2.1 Message publishing object

code show as below:

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

What is the effect of setting latch to true?
Take static map publishing as an example in navigation:

Solution 1: Map data can be sent at a fixed frequency, but the efficiency is too low;

Solution 2: You can set the latch of the map publishing object to true, and the publisher only sends the data once. Whenever the subscriber connects, the map data is sent to the subscriber (only once), which improves the data sending efficiency.

2.2 Message subscription object

code show as below:

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

}

Modify the topic name and judge whether the subscription object is successful or not according to if.

2.3 Service object

code show as below:

#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 Client Object

code show as below:

#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. Convoluted function related API

In the ROS program, two spin functions, ros::spin() and ros::spinOnce(), are used to process the callback function.

3.1spinOnce()

Handle a round of callbacks

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

code show as below:

#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()

Enter the loop processing callback

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

code show as below:

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

}

Difference:
The statement after the spinOnce() function can run, but the statement after the spin() function will not run;

4. Time related API

Time-related APIs in ROS are extremely commonly used, such as: getting the current moment, setting the duration, execution frequency, sleep, timer...all related to time

4.1 moments

code show as below:

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

Reference frame: January 01, 1970 00:00:00;

4.2 Duration

code show as below:

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

Mainly operate on a period of time;

4.3 Duration and time calculation

code show as below:

#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;
}
  • Notice:
  • 1. Time and duration can be added and subtracted;
  • 2. Addition and subtraction can also be performed between durations;
  • 3. Time can be subtracted, not added.

4.4 Set the running frequency

code show as below:

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

4.5 timer

code show as below:

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

There is a special timer built into ROS, which can achieve a similar effect to ros::Rate:.

5. Other function API

5.1 Node shutdown API ros::shutdown()

When publishing messages in a loop, the judgment condition of the loop is generally controlled by the node status. In C++, ros::ok() can be used to judge whether the node status is normal. The main reasons for the node exit are as follows:

  1. The node has received shutdown information, for example, the commonly used ctrl + c shortcut key is the signal to close the node;
  2. A node with the same name starts, causing the existing node to exit;
  3. Other parts of the program call the API related to node shutdown (ros::shutdown() in C++);

code show as below:

#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()

The logs commonly used in ROS are divided into the following levels:

  1. DEBUG (debugging): only used during debugging, such messages will not be output to the console;
  2. INFO (information): standard message, generally used to describe the operation being performed in the system;
  3. WARN (warning): remind some abnormal situations, but the program can still be executed;
  4. ERROR (error): Prompt error information, such errors will affect the running of the program;
  5. FATAL (fatal error): This type of error will prevent the node from continuing to operate.

code show as below:

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


Summarize

  • Basic understanding of the use of ROS common APIs

おすすめ

転載: blog.csdn.net/TianHW103/article/details/127329721