1. 程式人生 > >ROS教程四——編寫Publisher和Subscriber節點(Python篇)

ROS教程四——編寫Publisher和Subscriber節點(Python篇)

本教程介紹如何在python中編寫釋出者和訂閱者節點

1、編寫Publisher節點 

“節點”是連線到ROS網路的可執行檔案的ROS術語。在這裡,我們將建立廣播訊息的 publisher(“talker”)節點。

將目錄更改為之前教程中建立的beginner_tutorials包:

$ roscd beginner_tutorials

  1.1 程式碼

首先,建立一個'scripts'資料夾來儲存你的Python指令碼:

$ mkdir scripts
$ cd scripts

talker.py原始碼如下:

#!/usr/bin/env python
# license removed for brevity
import rospy
from std_msgs.msg import String

def talker():
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rospy.init_node('talker', anonymous=True)
    rate = rospy.Rate(10) # 10hz
    while not rospy.is_shutdown():
        hello_str = "hello world %s" % rospy.get_time()
        rospy.loginfo(hello_str)
        pub.publish(hello_str)
        rate.sleep()

if __name__ == '__main__':
    try:
        talker()
    except rospy.ROSInterruptException:
        pass

  1.2 程式碼解釋

#!/usr/bin/env python

每個Python ROS節點都會在頂部顯示此宣告。第一行確保您的指令碼作為Python指令碼執行。

import rospy
from std_msgs.msg import String

編寫ROS節點,則需要匯入rospy。std_msgs.msg匯入是為了可以重用std_msgs / String訊息型別(一個簡單的字串容器)進行釋出。

  pub = rospy.Publisher('chatter', String, queue_size=10)
  rospy.init_node('talker', anonymous=True)

這部分程式碼定義了talker與其它ROS的介面。pub = rospy.Publisher("chatter", String, queue_size=10) 宣告節點使用String訊息型別,釋出到chatter主題。這裡的字串實際上是類std_msgs.msg.String。queue_size引數在ROS hydro中是新的型別,如果任何訂戶沒有足夠快地接收它們,則限制排隊訊息的數量。在舊的ROS發行版中,只省略了這個論點。

下一行rospy.init_node(NAME,...)非常重要,因為它釋出rospy節點的名稱 - 直到rospy有這個資訊,它才能開始與ROS 主機通訊。在這種情況下,節點將採用名稱talker。注意:名稱必須是基本名稱,即它不能包含任何斜槓“/”。

 rate = rospy.Rate(10) # 10hz

此行建立Rate物件速率。藉助其方法sleep(),它提供了一種以所需速率迴圈的便捷方式。它的引數為10,我們應該期望每秒迴圈10次(只要我們的處理時間不超過1/10秒!)

    while not rospy.is_shutdown():
        hello_str = "hello world %s" % rospy.get_time()
        rospy.loginfo(hello_str)
        pub.publish(hello_str)
        rate.sleep()

這個迴圈是一個相當標準的rospy構造:檢查rospy.is_shutdown()標誌然後做工作。檢查程式是否應該退出(例如,如果有Ctrl-C或其他)。在這種情況下,“work”是對pub.publish(hello_str)的呼叫,它將字串釋出到我們的talker主題。迴圈呼叫rate.sleep(),保持迴圈所需的速率。

也可以執行rospy.sleep(),它類似於time.sleep(),但它也適用於模擬時間(請參閱Clock)。

這個迴圈還呼叫rospy.loginfo(str),它執行三重任務:訊息被列印到螢幕,它被寫入Node的日誌檔案,並被寫入rosout。rosout對於除錯非常方便:您可以使用rqt_console來提取訊息,而不必使用Node的輸出找到控制檯視窗。

std_msgs.msg.String是一種非常簡單的訊息型別。一般的經驗法則是建構函式args的順序與.msg檔案中的順序相同。也可以不傳入引數並直接初始化欄位,例如

msg = String()
msg.data = str

或者可以初始化某些欄位,並將其餘欄位保留為預設值:

String(data=str)

最後一點:

  try:
        talker()
    except rospy.ROSInterruptException:
        pass

除了標準的Python __main__檢查之外,這還會捕獲一個rospy.ROSInterruptException異常,當按下Ctrl-C或者你的節點關閉時,rospy.sleep()和rospy.Rate.sleep()方法可以丟擲該異常。引發此異常的原因是不會在sleep()之後意外地繼續執行程式碼。

2、編寫Subscriber節點

2.1 程式碼

#!/usr/bin/env python
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
    
def listener():

    # In ROS, nodes are uniquely named. If two nodes with the same
    # node are launched, the previous one is kicked off. The
    # anonymous=True flag means that rospy will choose a unique
    # name for our 'listener' node so that multiple listeners can
    # run simultaneously.
    rospy.init_node('listener', anonymous=True)

    rospy.Subscriber("chatter", String, callback)

    # spin() simply keeps python from exiting until this node is stopped
    rospy.spin()

if __name__ == '__main__':
    listener()

2.2 程式碼解釋

    listener.py的程式碼類似於talker.py,除了引入了一個新的基於回撥的機制來訂閱訊息。

    rospy.init_node('listener', anonymous=True)

    rospy.Subscriber("chatter", String, callback)

    # spin() simply keeps python from exiting until this node is stopped
    rospy.spin()

這宣告的節點訂閱了std_msgs.msgs.String型別的chatter主題。收到新訊息時,將呼叫回撥函式,並將訊息作為第一個引數。

也稍微改變了對rospy.init_node()的呼叫。添加了anonymous = True關鍵字引數。ROS要求每個節點都有唯一的名稱。如果出現具有相同名稱的節點,則會突然顯示前一個節點。這樣可以很容易地將故障節點從網路中踢出。anonymous = True標誌告訴rospy為節點生成一個唯一的名稱,以便可以輕鬆執行多個listener.py節點。

最後新增,rospy.spin()只是讓節點退出,直到節點關閉。與roscpp不同,rospy.spin()不會影響訂閱者回調函式,因為它們有自己的執行緒。

3、編譯 nodes 

使用CMake作為構建系統,即使是Python節點也必須使用它。這是為了確保建立訊息和服務的自動生成的Python程式碼。

轉到catkin工作區並執行catkin_make

$ cd ~/catkin_ws
$ catkin_make

既然已經編寫了一個簡單的釋出者和訂閱者,讓我們來檢視ROS教程五-執行簡單的釋出者和訂閱者。