七、ROS通信机制进阶

 

API应用程序编程接口

1初始化:ros::init

作用:ros初始化函数

参数列表:1.argc --参数个数-封装实参的个数,调用的时候传入了n个参数,那么实际封装的个数是n+1,因为他会多一个,传入的第一个是文件自身。

                2.argv --参数列表-封装这个参数的数组,字符串数组

                3.name ---为节点命名需要保证他的唯一性

                4.options(可选的)节点启动选择,暂时没有使用过。

argc和argv是main函数中的参数是用来封装程序调用的时候传入的实参,

返回值没有是空:viod

使用细节:1.argc和argv的使用:在我们启动ros节点的时候,我们可以传递一些实参,这些实参被封装进了argc和argv中,并且在ros当中如果你是按照ros当中的某些格式传入实参的,那么在传入以后ros就可以对它加以利用,实现一些特定功能,如果按照ros中的特定格式传入实参,那么ros可以加以使用,比如可以用来设置全局参数,给节点重命名。

启动节点的时候同时为参数服务器设置参数,加一个下划线加参数名,冒号等号加一个具体值回车。

这个下划线length就给argv了。

          2.options的使用:节点名称得保证唯一性,会导致一个问题就是同一个节点不能重复启动,启动同一个节点的时候第二个启动成功第一个就被关掉了。

ros中当有重名的节点启动时,之前的节点就会被关闭掉,

需求:特定场景下,需要一个节点多次启动且能正常运行怎么办?

初始化的时候,逗号隔开,

通过它解决重名问题,他的工作机制,如果你设置了我这个启动项,那这个时候我在创建节点并给他命名的时候是使用你这个ergouzi前缀再加上一个随机数,每次启动都加上一个随机数,那么这样呢就可以避免你这个名称重名问题了。

解决:设置启动项:ros::init_options::anonymousName

              当创建ros节点时,会在用户自定义的节点名称后缀随机数,从而避免重名问题。

 Python

初始化

def init_node(name, argv=None, anonymous=False, log_level=None, disable_rostime=False, disable_rosout=False, disable_signals=False, xmlrpc_port=0, tcpros_port=0):
    """
    在ROS msater中注册节点

    @param name: 节点名称,必须保证节点名称唯一,节点名称中不能使用命名空间(不能包含 '/')
    @type  name: str

    @param anonymous: 取值为 true 时,为节点名称后缀随机编号
    @type anonymous: bool
    """

  2话题与服务相关对象

话题的发布端对象和订阅端对象

服务的服务端对象和客户端对象

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

创建发布者对象:需要先用nodehandle创建一个句柄,然后再通过这个nodehandle调用advertise函数,这个函数呢会返回一个发布者对象。主要介绍这个advertise的使用。

作用:创建发布者对象

* \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.latch是可选的,他是布尔值,如果设置为true,会保存发布方的最后一条消息,并且新的订阅的对象连接到发布方时,发布方会将这条消息发送个订阅者。如果设置为false最后一条消息就不保存了。

使用:latch设置为true的作用?

我们导航的时候,机器人导航的过程当中他可能需要一张动态地图,就是我导航的这个场地的一张地图,那么这个地图可以通过发布方来发布,如果地图是静态的,就意味着地图长时间不会变,这就涉及到发布逻辑怎么设计,可以用latch优化。

具体的策略我只发一次这个地图信息,但是我将发布方这个latch值设置为true,那么这意味着当你订阅者连接到我的时候,我就会把这个地图发送给你。反正是静态的发送一次就行你就去用就行了。然后再有新的订阅方来连接我我再发一次。

以静态地图发布为例,方案1:使用固定频率发送地图数据,但是效率低:

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

消息发布函数:

/**
* 发布消息          
*/
template <typename M>
void publish(const M& message) const
Copy

2.订阅对象

对象获取:

/**
   * \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())
Copy

3.服务对象

对象获取:

/**
* \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&))
Copy

4.客户端对象

对象获取:

/** 
  * @brief 创建一个服务客户端对象
  *
  * 当清除最后一个连接的引用句柄时,连接将被关闭。
  *
  * @param service_name 服务主题名称
  */
 template<class Service>
 ServiceClient serviceClient(const std::string& service_name, bool persistent = false, 
                             const M_string& header_values = M_string())
Copy

请求发送函数:

/**
   * @brief 发送请求
   * 返回值为 bool 类型,true,请求处理成功,false,处理失败。
   */
  template<class Service>
  bool call(Service& service)
Copy

等待服务函数1:

/**
 * 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));
Copy

等待服务函数2:

/**
* client.waitForExistence();
* \brief 等待服务可用,否则一致处于阻塞状态
* \param timeout 等待最大时常,默认为 -1,可以永久等待直至节点关闭
* \return 成功返回 true,否则返回 false。
*/
bool waitForExistence(ros::Duration timeout = ros::Duration(-1));
Copy

Python

1.发布对象

对象获取:

class Publisher(Topic):
    """
    在ROS master注册为相关话题的发布方
    """

    def __init__(self, name, data_class, subscriber_listener=None, tcp_nodelay=False, latch=False, headers=None, queue_size=None):
        """
        Constructor
        @param name: 话题名称 
        @type  name: str
        @param data_class: 消息类型

        @param latch: 如果为 true,该话题发布的最后一条消息将被保存,并且后期当有订阅者连接时会将该消息发送给订阅者
        @type  latch: bool

        @param queue_size: 等待发送给订阅者的最大消息数量
        @type  queue_size: int

        """
Copy

消息发布函数:

def publish(self, *args, **kwds):
        """
        发布消息
        """
Copy

2.订阅对象

对象获取:

class Subscriber(Topic):
    """
   类注册为指定主题的订阅者,其中消息是给定类型的。
    """
    def __init__(self, name, data_class, callback=None, callback_args=None,
                 queue_size=None, buff_size=DEFAULT_BUFF_SIZE, tcp_nodelay=False):
        """
        Constructor.

        @param name: 话题名称
        @type  name: str
        @param data_class: 消息类型
        @type  data_class: L{Message} class
        @param callback: 处理订阅到的消息的回调函数
        @type  callback: fn(msg, cb_args)

        @param queue_size: 消息队列长度,超出长度时,头部的消息将被弃用

        """
Copy

3.服务对象

对象获取:

class Service(ServiceImpl):
    """
     声明一个ROS服务

    使用示例::
      s = Service('getmapservice', GetMap, get_map_handler)
    """

    def __init__(self, name, service_class, handler,
                 buff_size=DEFAULT_BUFF_SIZE, error_handler=None):
        """

        @param name: 服务主题名称 ``str``
        @param service_class:服务消息类型

        @param handler: 回调函数,处理请求数据,并返回响应数据

        @type  handler: fn(req)->resp

        """
Copy

4.客户端对象

对象获取:

class ServiceProxy(_Service):
    """
   创建一个ROS服务的句柄

    示例用法::
      add_two_ints = ServiceProxy('add_two_ints', AddTwoInts)
      resp = add_two_ints(1, 2)
    """

    def __init__(self, name, service_class, persistent=False, headers=None):
        """
        ctor.
        @param name: 服务主题名称
        @type  name: str
        @param service_class: 服务消息类型
        @type  service_class: Service class
        """
Copy

请求发送函数:

def call(self, *args, **kwds):
        """
        发送请求,返回值为响应数据


        """
Copy

等待服务函数:

def wait_for_service(service, timeout=None):
    """
    调用该函数时,程序会处于阻塞状态直到服务可用
    @param service: 被等待的服务话题名称
    @type  service: str
    @param timeout: 超时时间
    @type  timeout: double|rospy.Duration
    """

3、回旋函数:

 

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

两个的返回值都是void返回值没有

 

1.spinOnce()

/**
 * \brief 处理一轮回调
 *
 * 一般应用场景:
 *     在循环体内,处理所有可用的回调函数
 * 
 */
ROSCPP_DECL void spinOnce();
Copy

2.spin()

/** 
 * \brief 进入循环处理回调 
 */
ROSCPP_DECL void spin();

3.二者比较

相同点:二者都用于处理回调函数;

不同点:ros::spin() 是进入了循环执行回调函数,而 ros::spinOnce() 只会执行一次回调函数(没有循环),在 ros::spin() 后的语句不会执行到,而 ros::spinOnce() 后的语句可以执行。

 

都可以处理回调函数。

理论上讲当回调函数可执行的条件被触发之后,回调函数就应该被调用,执行,但是不一定,在这里必须要有一个前提,后面代码你得有spin或者spinonce出现,

如果是spinonce的话,那么代码往下走走走到spinonce就会回头,回头过来执行这个回调函数,但是他只回调一轮,等到这个回调函数处理完以后他就会跳出,继续再往下执行别的,

 

如果是spin的话,那么就不一样了,当回调函数触发条件满足时,他不一定立马执行,而是要看后面,如果说,后面有个spin,这个spin就会去找这个spin函数,不过执行到spin的时候他和spinonce 不一样,spinonce是只执行一次,而这个spin是进入一个循环,他自己啊进入一个循环,循环去执行这个回调函数,这个循环他是不会退出的。spin不会退出,后面代码就不会执行到。

相同点:二者都用于处理回调函数;

不同点:ros::spin() 是进入了循环执行回调函数,而 ros::spinOnce() 只会执行一次回调函数(没有循环),在 ros::spin() 后的语句不会执行到,而 ros::spinOnce() 后的语句可以执行。

Python

def spin():
    """
    进入循环处理回调 
    """

 4、时间:

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

  1. 时刻:ros::Time获取时刻,或是设置指定时刻。

需求:演示时间相关操作(获取当前时刻+设置指定时刻)

实现:1.准备(头文件包含,节点初始化,nodehandle创建)

时间相关的api他的头文件在ros。h中已经包含了,不需要专门的头文件。

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. 获取当前时刻

//now函数会将当前时刻封装并返回

//当前时刻,now被调用执行的那一刻

//时间还有一个参考值。参考系:参考1970年1月1日 00:00:00 right所封装的是now执行时距离上面这个时间所逝去的时间。

ros::Time right=ros::TimeTime::now();//是一个类,他下面有一个静态函数now,获取当前时间的。获取当前时间会有一个返回值,以为是ros::Time类型的

把时刻值转换成一个秒数,right。toSec();toSec这个函数他可以将逝去的这个时间总厂转换成以秒为单位计算得,他的返回值类型是一个double类型的,浮点类型的。

  1. 设置指定时刻

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

2.持续时间:

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

作用可以设置一个时间区间间隔

前面的时刻是一个时间点,持续时间是一段时间。

需求2:程序执行中停顿5秒

实现:

  1. 创建持续时间对象
  2. 休眠

怎么创建一个持续时间对象:

ros::Duration 就可以创建这个对象,他可以接收浮点类型的。

ros::Duration du(4.5)什么意思就是我现在有一个持续时间是4.5秒。

然后我想让程序休眠就可以调用这个持续时间

du.sleep():执行到这就开始睡觉了,睡多久呢,睡4.5秒。

3.持续时间与时刻运算:

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;//异常

我们的持续时间和时刻可以进行一些加减运算的。

需求3:已知程序开始运行的时刻和程序运行的时间,求运行结束时的时刻

实现:

  1. 获取开始执行的时刻

获取当前时间:ros::Time now = ros::Time::now();

  1. 模拟一个运行的时间(n秒)

ros::Duration du1(10);

  1. 计算结束时刻=开始加+持续时间

结束时刻是ros::time类型的

ros::Time after_now = now + du1;

ros::Time before_now = now - du1;

//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;//异常

ros::Duration du5 = now - before_now;

ROS_INFO("时刻相减:%.2f",du5.toSec());

4、设置运行频率


ros::Rate rate(1);//指定频率
while (true)
{
    ROS_INFO("-----------code----------");
    rate.sleep();//休眠,休眠时间 = 1 / 频率。
}

在这使用到的api是ros::Rate()

主要实现隔某个时间间隔执行某种操作。

ros::Rate rate(1);//指定频率

while (true)

{

    ROS_INFO("-----------code----------");

    rate.sleep();//休眠,休眠时间 = 1 / 频率。

}

5、定时器

  1. 定时器:ROS 中内置了专门的定时器,可以实现与 ros::Rate 类似的效果:可以隔某个时间间隔执行某种操作。
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

 

需求4:每隔一秒种,在控制台输出一段文本,文本随意

实现:

策略1:ros:Rate()

策略2;定时器

nodehandle中提供了一个api叫createtimer是用来创建定时器的。

这个函数调用的时候我们要给他传入若干的参数,

//Timer createTimer(Duration period, //时间间隔,要求每隔一秒钟输出一次文本,这个时间间隔就可以设置成1秒

const TimerCallback& callback, //回调函数,一会要封装我们每次执行的业务。业务比较简答那就是输出文本。

bool oneshot = false,//是指你的定时器是否是一次性的,如果设置成ture就是每隔一秒钟执行一次这个回调函数,但是只执行一次,设置成false的话就是循环。

 //bool autostart = true);//意味着当前你的定时器是否是自动启动,

这个函数有四个参数,其中对于我们而言较为重要的是前面两个,后面的两个当前有默认值,

定时器的使用和ros::tate类似,它使用的时候,第一步我们要依赖nodehandle来创建一个定时器,然后再在里面传参,参数1是时间间隔,参数2是回调函数。

定时器中其他两个参数的使用

小结:创建是调用nh。createtimer

              参数1:时间间隔

              参数2:回调函数(时间事件,timerevent)

              参数3:是否只执行一次

              参数4:是否自动启动(当设置为false,需要手动调用timer。atart())

定时器启动后:要使用ros::spin()

6、定时器的回调函数:

void doSomeThing(const ros::TimerEvent &event){
    ROS_INFO("-------------");
    ROS_INFO("event:%s",std::to_string(event.current_real.toSec()).c_str());
}

python

Python

1.时刻

获取时刻,或是设置指定时刻:

# 获取当前时刻
right_now = rospy.Time.now()
rospy.loginfo("当前时刻:%.2f",right_now.to_sec())
rospy.loginfo("当前时刻:%.2f",right_now.to_nsec())
# 自定义时刻
some_time1 = rospy.Time(1234.567891011)
some_time2 = rospy.Time(1234,567891011)
rospy.loginfo("设置时刻1:%.2f",some_time1.to_sec())
rospy.loginfo("设置时刻2:%.2f",some_time2.to_sec())

# 从时间创建对象
# some_time3 = rospy.Time.from_seconds(543.21)
some_time3 = rospy.Time.from_sec(543.21) # from_sec 替换了 from_seconds
rospy.loginfo("设置时刻3:%.2f",some_time3.to_sec())
Copy

2.持续时间

设置一个时间区间(间隔):

# 持续时间相关API
rospy.loginfo("持续时间测试开始.....")
du = rospy.Duration(3.3)
rospy.loginfo("du1 持续时间:%.2f",du.to_sec())
rospy.sleep(du) #休眠函数
rospy.loginfo("持续时间测试结束.....")
Copy

3.持续时间与时刻运算

为了方便使用,ROS中提供了时间与时刻的运算:

rospy.loginfo("时间运算")
now = rospy.Time.now()
du1 = rospy.Duration(10)
du2 = rospy.Duration(20)
rospy.loginfo("当前时刻:%.2f",now.to_sec())
before_now = now - du1
after_now = now + du1
dd = du1 + du2
# now = now + now #非法
rospy.loginfo("之前时刻:%.2f",before_now.to_sec())
rospy.loginfo("之后时刻:%.2f",after_now.to_sec())
rospy.loginfo("持续时间相加:%.2f",dd.to_sec())
Copy

4.设置运行频率

# 设置执行频率
rate = rospy.Rate(0.5)
while not rospy.is_shutdown():
    rate.sleep() #休眠
    rospy.loginfo("+++++++++++++++")
Copy

5.定时器

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

#定时器设置
"""    
def __init__(self, period, callback, oneshot=False, reset=False):
    Constructor.
    @param period: 回调函数的时间间隔
    @type  period: rospy.Duration
    @param callback: 回调函数
    @type  callback: function taking rospy.TimerEvent
    @param oneshot: 设置为True,就只执行一次,否则循环执行
    @type  oneshot: bool
    @param reset: if True, timer is reset when rostime moved backward. [default: False]
    @type  reset: bool
"""
rospy.Timer(rospy.Duration(1),doMsg)
# rospy.Timer(rospy.Duration(1),doMsg,True) # 只执行一次
rospy.spin()
Copy

回调函数:

def doMsg(event):
    rospy.loginfo("+++++++++++")
    rospy.loginfo("当前时刻:%s",str(event.current_real))

 5、其他函数

主要介绍两个:

一个是和节点生命周期相关的

一个是和日志输出相关的

在发布实现时,一般会循环发布消息,循环的判断条件一般由节点状态来控制,C++中可以通过 ros::ok() 来判断节点状态是否正常,如果他们都返回ture就表示节点运行正常,我们就可以继续循环。当然如果节点退出了,这个循环就结束了。什么情况下会导致节点退出呢,主要有如下几种:

1节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;

2同名节点启动,导致现有节点退出;

3程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown(),python中是rospy.signal_shutdown())

如果计数器>=50关闭节点

ros::shutdown() 被调用且执行完毕后,该函数将会返回 false

日志相关的api,日志相关的函数也是极其常用的,在ROS中日志被划分成如下级别:

1、DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;

2、INFO(信息):标准消息,一般用于说明系统内正在执行的操作;

3、WARN(警告):提醒一些异常情况,但程序仍然可以执行;

4、ERROR(错误):提示错误信息,此类错误会影响程序运行;

5、FATAL(严重错误):此类错误将阻止节点继续运行。

日志函数

使用示例

ROS_DEBUG("hello,DEBUG"); //不会输出

ROS_INFO("hello,INFO"); //默认白色字体

ROS_WARN("Hello,WARN"); //默认黄色字体

ROS_ERROR("hello,ERROR");//默认红色字体

ROS_FATAL("hello,FATAL");//默认红色字体

1.节点状态判断

/** \brief 检查节点是否已经退出
 *
 *  ros::shutdown() 被调用且执行完毕后,该函数将会返回 false
 *
 * \return true 如果节点还健在, false 如果节点已经火化了。
 */
bool ok();
Copy

2.节点关闭函数

/*
*   关闭节点
*/
void shutdown();
Copy

3.日志函数

使用示例

ROS_DEBUG("hello,DEBUG"); //不会输出
ROS_INFO("hello,INFO"); //默认白色字体
ROS_WARN("Hello,WARN"); //默认黄色字体
ROS_ERROR("hello,ERROR");//默认红色字体
ROS_FATAL("hello,FATAL");//默认红色字体
Copy

Python

1.节点状态判断

def is_shutdown():
    """
    @return: True 如果节点已经被关闭
    @rtype: bool
    """
Copy

2.节点关闭函数

def signal_shutdown(reason):
    """
    关闭节点
    @param reason: 节点关闭的原因,是一个字符串
    @type  reason: str
    """
Copy
def on_shutdown(h):
    """
    节点被关闭时调用的函数
    @param h: 关闭时调用的回调函数,此函数无参
    @type  h: fn()
    """
Copy

3.日志函数

使用示例

rospy.logdebug("hello,debug")  #不会输出
rospy.loginfo("hello,info")  #默认白色字体
rospy.logwarn("hello,warn")  #默认黄色字体
rospy.logerr("hello,error")  #默认红色字体
rospy.logfatal("hello,fatal") #默认红色字体

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值