[ROS 연구 노트 9] ROS 공통 API

[ROS 연구 노트 9] ROS 공통 API

앞부분에 쓰여진 이 일련의 노트는 AutoLabor 튜토리얼을 참조하며 구체적인 프로젝트 주소는 여기에


머리말

ROS의 공통 API는 공식


1. 초기화

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

파이썬

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. 주제 및 서비스 관련 대상

C++

roscpp에서 주제와 서비스의 관련 객체는 일반적으로 NodeHandle에 의해 생성됩니다.

NodeHandle의 중요한 기능은 네임스페이스를 설정하는 데 사용할 수 있다는 점인데, 이는 이후 단계에서 중점을 두지만 이 장에서는 소개하지 않습니다.

1. 릴리즈 대상

개체 획득:

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

메시지 게시 기능:

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

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

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

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

요청 전송 기능:

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

서비스 기능 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));

서비스 기능 2 대기:

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

파이썬

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

        """

메시지 게시 기능:

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

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

        """

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

        """

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

요청 전송 기능:

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


        """

서비스 기능을 기다립니다.

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

3. 로터리 기능

C++

ROS 프로그램에서는 두 개의 스핀 함수인 ros::spin()과 ros::spinOnce()가 자주 사용되며 콜백 함수를 처리하는 데 사용할 수 있습니다.

1. 스핀원스()

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

2. 스핀()

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

3. 둘의 비교

**비슷한 점:**둘 다 콜백 함수를 처리하는 데 사용됩니다.

**차이점: **ros::spin()은 콜백 함수를 실행하기 위해 루프에 들어가는 반면, ros::spinOnce()는 콜백 함수를 한 번만 실행하고(루프 없이) ros::spin() 이후의 문을 실행합니다. does not 실행되며, ros::spinOnce() 이후의 문이 실행될 수 있습니다.


파이썬

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

4. 시간 기능

ROS의 시간 관련 API는 현재 시간 가져오기, 기간 설정, 실행 빈도, 절전 모드, 타이머 등 모두 시간과 관련하여 매우 일반적으로 사용됩니다.

C++

1. 시간

시간을 가져오거나 지정된 시간을 설정합니다.

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. 기간

시간 간격 설정(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. 기간 및 시간 운영

사용의 편의를 위해 ROS는 시간 및 시간 계산을 제공합니다.

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. 실행 빈도 설정

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

5. 타이머

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

타이머의 콜백 함수:

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

파이썬

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

2. 기간

시간 간격 설정(interval):

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

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

4. 실행 빈도 설정

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

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

콜백:

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

5. 기타 기능

구현을 해제할 때 메시지는 일반적으로 루프에서 해제되며 루프의 판단 조건은 일반적으로 노드 상태에 의해 제어됩니다.C++에서는 ros::ok()를 사용하여 노드 상태가 정상인지 판단할 수 있습니다. 그리고 python에서는 rospy.is_shutdown()을 사용할 수 있습니다.판단이 실현되고 노드가 종료되는 주된 이유는 다음과 같습니다.

  • 노드는 종료 정보를 받았습니다. 예를 들어 일반적으로 사용되는 ctrl + c 단축키는 노드를 닫는 신호입니다.
  • 이름이 같은 노드가 시작되어 기존 노드가 종료됩니다.
  • 프로그램의 다른 부분은 노드 종료와 관련된 API를 호출합니다(C++에서는 ros::shutdown(), Python에서는 rospy.signal_shutdown()).

또한 로그 관련 기능도 매우 일반적으로 사용되며 ROS에서 로그는 다음과 같은 수준으로 나뉩니다.

  • DEBUG(디버깅): 디버깅 중에만 사용되며 이러한 메시지는 콘솔에 출력되지 않습니다.
  • INFO(정보): 일반적으로 시스템에서 수행 중인 작업을 설명하는 데 사용되는 표준 메시지입니다.
  • WARN(경고): 일부 비정상적인 상황을 상기시키지만 프로그램은 여전히 ​​실행될 수 있습니다.
  • ERROR(오류): 프롬프트 오류 정보, 이러한 오류는 프로그램 실행에 영향을 미칩니다.
  • FATAL(심각한 오류): 이 유형의 오류는 노드가 계속 작동하지 못하게 합니다.

C++

1. 노드 상태 판단

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

2. 노드 종료 기능

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

3. 로그 기능

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

파이썬

1. 노드 상태 판단

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

2. 노드 종료 기능

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

3. 로그 기능

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

참조

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

추천

출처blog.csdn.net/qq_44940689/article/details/129277646