• 【ROS进阶篇】第一讲 常用API介绍


    【ROS进阶篇】第一讲 常用API介绍

    在这里插入图片描述

    前言

    在先前的博客中,我们通过十一节的教程实现了对于ROS的基础入门教学,从ROS的诞生、作用到基础的使用方法,我们通过C++的编程和命令行两种方式对ROS的通讯机制进行了深入的分析,并且介绍了诸如TF坐标变换、参数使用、launch文件等功能包和扩展库的使用方法,进阶篇则主要目的是脱离先前的角度,从宏观的角度考虑问题,更加清晰的掌握ROS的生态系统,并重点研究几个工具的使用教程,本节内容主要是对于ROS中常用API的介绍与总结。包括关于初始化、通讯、循环、时间和一些节点状态和日志输出的使用方法。

    在这里插入图片描述

    常用API

    1. 初始化

    • ROS节点的初始化相关API
    /** @brief ROS初始化函数。
     *
     * 该函数可以解析并使用节点启动时传入的参数(通过参数设置节点名称、命名空间...) 
     *
     * 该函数有多个重载版本,如果使用NodeHandle建议调用该版本。 
     *
     * \param argc 参数个数
     * \param argv 参数列表
     * \param name 节点名称,需要保证其唯一性,不允许包含命名空间
     * \param options 节点启动选项,被封装进了ros::init_options
     *
     */
    void init(int &argc, char **argv, const std::string& name, uint32_t options = 0);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2. 通讯

    话题通讯

    • 发布者:
    /**
    * \brief 根据话题生成发布对象
    *
    * 在 ROS master 注册并返回一个发布者对象,该对象可以发布消息
    *
    * 使用示例如下:
    *
    *   ros::Publisher pub = handle.advertise<std_msgs::Empty>("my_topic", 1);
    *
    * \param topic 发布消息使用的话题
    *
    * \param queue_size 等待发送给订阅者的最大消息数量
    *
    * \param latch (optional) 如果为 true,该话题发布的最后一条消息将被保存,并且后期当有订阅者连接时会将该消息发送给订阅者
    *
    * \return 调用成功时,会返回一个发布对象
    *
    *
    */
    template <class M>
    Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 发布消息
    /**
    * 发布消息          
    */
    template <typename M>
    void publish(const M& message) const
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 订阅者
    /**
       * \brief 生成某个话题的订阅对象
       *
       * 该函数将根据给定的话题在ROS master 注册,并自动连接相同主题的发布方,每接收到一条消息,都会调用回调
       * 函数,并且传入该消息的共享指针,该消息不能被修改,因为可能其他订阅对象也会使用该消息。
       * 
       * 使用示例如下:
    
    void callback(const std_msgs::Empty::ConstPtr& message)
    {
    }
    
    ros::Subscriber sub = handle.subscribe("my_topic", 1, callback);
    
       *
    * \param M [template] M 是指消息类型
    * \param topic 订阅的话题
    * \param queue_size 消息队列长度,超出长度时,头部的消息将被弃用
    * \param fp 当订阅到一条消息时,需要执行的回调函数
    * \return 调用成功时,返回一个订阅者对象,失败时,返回空对象
    * 
    
    void callback(const std_msgs::Empty::ConstPtr& message){...}
    ros::NodeHandle nodeHandle;
    ros::Subscriber sub = nodeHandle.subscribe("my_topic", 1, callback);
    if (sub) // Enter if subscriber is valid
    {
    ...
    }
    
    */
    template<class M>
    Subscriber subscribe(const std::string& topic, uint32_t queue_size, void(*fp)(const boost::shared_ptr<M const>&), const TransportHints& transport_hints = TransportHints())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    服务通讯

    • 服务器
    /**
    * \brief 生成服务端对象
    *
    * 该函数可以连接到 ROS master,并提供一个具有给定名称的服务对象。
    *
    * 使用示例如下:
    \verbatim
    bool callback(std_srvs::Empty& request, std_srvs::Empty& response)
    {
    return true;
    }
    
    ros::ServiceServer service = handle.advertiseService("my_service", callback);
    \endverbatim
    *
    * \param service 服务的主题名称
    * \param srv_func 接收到请求时,需要处理请求的回调函数
    * \return 请求成功时返回服务对象,否则返回空对象:
    \verbatim
    bool Foo::callback(std_srvs::Empty& request, std_srvs::Empty& response)
    {
    return true;
    }
    ros::NodeHandle nodeHandle;
    Foo foo_object;
    ros::ServiceServer service = nodeHandle.advertiseService("my_service", callback);
    if (service) // Enter if advertised service is valid
    {
    ...
    }
    \endverbatim
    
    */
    template<class MReq, class MRes>
    ServiceServer advertiseService(const std::string& service, bool(*srv_func)(MReq&, MRes&))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 客户端
    /** 
      * @brief 创建一个服务客户端对象
      *
      * 当清除最后一个连接的引用句柄时,连接将被关闭。
      *
      * @param service_name 服务主题名称
      */
     template<class Service>
     ServiceClient serviceClient(const std::string& service_name, bool persistent = false, 
                                 const M_string& header_values = M_string())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 发送请求
    /**
       * @brief 发送请求
       * 返回值为 bool 类型,true,请求处理成功,false,处理失败。
       */
      template<class Service>
      bool call(Service& service)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 等待服务
    /**
     * ros::service::waitForService("addInts");
     * \brief 等待服务可用,否则一致处于阻塞状态
     * \param service_name 被"等待"的服务的话题名称
     * \param timeout 等待最大时常,默认为 -1,可以永久等待直至节点关闭
     * \return 成功返回 true,否则返回 false。
     */
    ROSCPP_DECL bool waitForService(const std::string& service_name, ros::Duration timeout = ros::Duration(-1));
    
    /**
    * client.waitForExistence();
    * \brief 等待服务可用,否则一致处于阻塞状态
    * \param timeout 等待最大时常,默认为 -1,可以永久等待直至节点关闭
    * \return 成功返回 true,否则返回 false。
    */
    bool waitForExistence(ros::Duration timeout = ros::Duration(-1));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3. 循环

    • 目的:处理回调函数
    • spinOnce()
    /**
     * \brief 处理一轮回调
     *
     * 一般应用场景:
     *     在循环体内,处理所有可用的回调函数
     * 
     */
    ROSCPP_DECL void spinOnce();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • spin()
    /** 
     * \brief 进入循环处理回调 
     */
    ROSCPP_DECL void spin();
    
    • 1
    • 2
    • 3
    • 4
    • 区别:
    1. ros::spin()是进入了循环执行回调函数,在 ros::spin() 后的语句不会执行
    2. ros::spinOnce() 只会执行一次回调函数(没有循环),而 ros::spinOnce() 后的语句可以执行。

    4. 时间

    • 获取时刻/设置指定时刻:
    	ros::init(argc,argv,"hello_time");
    	ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调用失败
    	ros::Time right_now = ros::Time::now();//将当前时刻封装成对象
    	ROS_INFO("当前时刻:%.2f",right_now.toSec());//获取距离 1970年01月01日 00:00:00 的秒数
    	ROS_INFO("当前时刻:%d",right_now.sec);//获取距离 1970年01月01日 00:00:00 的秒数
    	
    	ros::Time someTime(100,100000000);// 参数1:秒数  参数2:纳秒
    	ROS_INFO("时刻:%.2f",someTime.toSec()); //100.10
    	ros::Time someTime2(100.3);//直接传入 double 类型的秒数
    	ROS_INFO("时刻:%.2f",someTime2.toSec()); //100.30
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 设置时间间隔:
    	ROS_INFO("当前时刻:%.2f",ros::Time::now().toSec());
    	ros::Duration du(10);//持续10秒钟,参数是double类型的,以秒为单位
    	du.sleep();//按照指定的持续时间休眠
    	ROS_INFO("持续时间:%.2f",du.toSec());//将持续时间换算成秒
    	ROS_INFO("当前时刻:%.2f",ros::Time::now().toSec());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 时间运算:
    	ROS_INFO("时间运算");
    	ros::Time now = ros::Time::now();
    	ros::Duration du1(10);
    	ros::Duration du2(20);
    	ROS_INFO("当前时刻:%.2f",now.toSec());
    	//1.time 与 duration 运算
    	ros::Time after_now = now + du1;
    	ros::Time before_now = now - du1;
    	ROS_INFO("当前时刻之后:%.2f",after_now.toSec());
    	ROS_INFO("当前时刻之前:%.2f",before_now.toSec());
    	
    	//2.duration 之间相互运算
    	ros::Duration du3 = du1 + du2;
    	ros::Duration du4 = du1 - du2;
    	ROS_INFO("du3 = %.2f",du3.toSec());
    	ROS_INFO("du4 = %.2f",du4.toSec());
    	//PS: time 与 time 不可以运算
    	// ros::Time nn = now + before_now;//异常
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 设置运行频率:
    ros::Rate rate(1);//指定频率
    while (true)
    {
        ROS_INFO("-----------code----------");
        rate.sleep();//休眠,休眠时间 = 1 / 频率。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 定时器:
    ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调用失败
    
     // ROS 定时器
     /**
    * \brief 创建一个定时器,按照指定频率调用回调函数。
    *
    * \param period 时间间隔
    * \param callback 回调函数
    * \param oneshot 如果设置为 true,只执行一次回调函数,设置为 false,就循环执行。
    * \param autostart 如果为true,返回已经启动的定时器,设置为 false,需要手动启动。
    */
     //Timer createTimer(Duration period, const TimerCallback& callback, bool oneshot = false,
     //                bool autostart = true) const;
    
     // ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing);
     ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing,true);//只执行一次
    
     // ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing,false,false);//需要手动启动
     // timer.start();
     ros::spin(); //必须 spin
    
    // 回调函数
    void doSomeThing(const ros::TimerEvent &event){
        ROS_INFO("-------------");
        ROS_INFO("event:%s",std::to_string(event.current_real.toSec()).c_str());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    5. 节点

    • 节点的状态可以控制循环的条件,在常见的使用中常利用ros::ok()来判断,即节点是否关闭(ctrl+c)或者由于同名起动导致退出
    /** \brief 检查节点是否已经退出
     *
     *  ros::shutdown() 被调用且执行完毕后,该函数将会返回 false
     *
     * \return true 如果节点还健在, false 如果节点已经火化了。
     */
    bool ok();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 调用了节点关闭API:shutdown()
    /*
    *   关闭节点
    */
    void shutdown();
    
    • 1
    • 2
    • 3
    • 4

    6. 日志

    • DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;
      ROS_DEBUG("hello,DEBUG"); //不会输出
    • INFO(信息):标准消息,一般用于说明系统内正在执行的操作;
      ROS_INFO("hello,INFO"); //默认白色字体
    • WARN(警告):提醒一些异常情况,但程序仍然可以执行;
      ROS_WARN("Hello,WARN"); //默认黄色字体
    • ERROR(错误):提示错误信息,此类错误会影响程序运行;
      ROS_ERROR("hello,ERROR");//默认红色字体
    • FATAL(严重错误):此类错误将阻止节点继续运行;
      ROS_FATAL("hello,FATAL");//默认红色字体

    总结

    • 声明:本节博客部分参考了CSDN用户赵虚左的ROS教程,关于API的参考网站也可以参考如下:
    1. http://wiki.ros.org/APIs
    2. https://docs.ros.org/en/api/roscpp/html/

    在这里插入图片描述

  • 相关阅读:
    滴滴6月或发布造车计划;英特尔顶级专家Mike Burrows跳槽AMD;Android 13开发者预览版2发布|极客头条
    完全背包问题 —— 贪心优化 DP 范围
    1330_硬件测试中的BCI测试
    ASP.NET Web 应用 Docker踩坑历程——续
    【深度学习实验】循环神经网络(四):基于 LSTM 的语言模型训练
    红队专题-工具Nuclei
    消除两个inline-block元素之间的间隔
    CSS3基础:字体和文本调整
    跟着AIGC学Linux:简介(一)
    Vue项目实战之电商后台管理系统(四) 权限角色管理模块
  • 原文地址:https://blog.csdn.net/lc1852109/article/details/125455153