[ROS study notes 9] ROS common API

[ROS study notes 9] ROS common API

Written in the front, this series of notes refers to the tutorial of AutoLabor, the specific project address is here


foreword

Common APIs of ROS, please refer to the official website


1. Initialization

Cpp

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

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. Topics and service-related objects

C++

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

An important function of NodeHandle is that it can be used to set the namespace, which is the focus of the later stage, but it will not be introduced in this chapter.

1. Release object

Object acquisition:

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

Message publishing function:

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

2. Subscribe to

Object acquisition:

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

3. Service object

Object acquisition:

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

4. Client object

Object acquisition:

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

Request sending function:

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

Wait for service function 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));

Wait for service function 2:

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

Python

1. Release object

Object acquisition:

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

        """

Message publishing function:

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

2. Subscribe to

Object acquisition:

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: 消息队列长度,超出长度时,头部的消息将被弃用

        """

3. Service object

Object acquisition:

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

        """

4. Client object

Object acquisition:

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
        """

Request sending function:

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


        """

Wait for the service function:

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

3. Rotary function

C++

In ROS programs, the two spin functions ros::spin() and ros::spinOnce() are frequently used, which can be used to process callback functions.

1、spinOnce()

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

2、spin()

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

3. Comparison between the two

**Similar points:**Both are used to handle callback functions;

**Difference: **ros::spin() enters the loop to execute the callback function, while ros::spinOnce() will only execute the callback function once (without loop), and the statement after ros::spin() does not It will be executed, and the statement after ros::spinOnce() can be executed.


Python

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

4. Time function

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

C++

1. Time

Get the time, or set the specified time:

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

2. Duration

Set a time interval (interval):

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

3. Operation of duration and time

For the convenience of use, ROS provides the calculation of time and time:

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

4. Set the running frequency

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

5. Timer

ROS has built-in special timers, which can achieve similar effects to 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

The callback function of the timer:

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

Python

1. Time

Get the time, or set the specified time:

# 获取当前时刻
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())

2. Duration

Set a time interval (interval):

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

3. Operation of duration and time

For the convenience of use, ROS provides the calculation of time and time:

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

4. Set the running frequency

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

5. Timer

ROS has built-in special timers, which can achieve similar effects to 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()

Callback:

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

5. Other functions

When releasing the implementation, the message is generally released in a loop, and the judgment condition of the loop is generally controlled by the node status. In C++, you can use ros::ok() to judge whether the node status is normal, and in Python, you can use rospy.is_shutdown(). The main reasons for the realization of the judgment and the exit of the node are as follows:

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

In addition, log-related functions are also extremely commonly used. In ROS, logs are divided into the following levels:

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

C++

1. Node status judgment

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

2. Node shutdown function

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

3. Log function

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

Python

1. Node status judgment

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

2. Node shutdown function

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

3. Log function

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

Reference

http://www.autolabor.com.cn/book/ROSTutorials/di-2-zhang-ros-jia-gou-she-ji/23-fu-wu-tong-xin/224-fu-wu-tong-xin-zi-ding-yi-srv-diao-yong-b-python.html

Guess you like

Origin blog.csdn.net/qq_44940689/article/details/129277646