天天看點

02---控制移動底座5

在大多數時間都是依靠ROS節點來釋出恰當的Twist消息。舉個簡單的例子,假設我們想程式設計使機器人向前移動1m,旋轉180度,然後傳回開始的位置。我們會嘗試用不同的方法來完成這個任務,這些方法很好地表現了ROS不同層次的運動控制。

1、通過定時和定速估計距離和角度

我們第一個嘗試是用定時的Twist指令去讓機器人花一定時間向前移動一定的距離,旋轉180度後,然後以相同的速度進行相同時長的向前移動,不出意外的話它會回到開始的位置。最後我們會讓機器人旋轉180度回到最初的方向。

這段腳本可以在子目錄rbx1_nav/nodes下的timed_out_and_back.py中找到。

02---控制移動底座5

2、在ArbotiX模拟器上進行計時前進并傳回運動

為了保證模拟的TurtleBot回到開始的位置,按下“Ctrl-C”讓模拟的TurtleBot中正在運作的啟動檔案停止運作,然後用以下指令讓它重新運作:

roslaunch rbx1_bringup fake_turtlebot.launch      

如果你願意的話,可以用對應Pi Robot或者你自己的機器人的檔案替換掉fake_turtlebot.launch,這不會使結果有差别。

如果RViz并不是正在運作,那麼就讓它開始運作:

rosrun rviz rviz -d `rospack find      

或者按下Reset按鈕删除掉在上一部分留下的Odometry箭頭。

最後運作timed_out_and_back.py節點:

rosrun rbx1_nav timed_out_and_back.py      
02---控制移動底座5

3、計時前進并傳回運動的腳本

以下是計時前進并傳回節點的完整腳本。

02---控制移動底座5
02---控制移動底座5
#!/usr/bin/env python

import rospy
from geometry_msgs.msg import Twist
from math import pi

class OutAndBack():
    def __init__(self):
        # Give the node a name
        rospy.init_node('out_and_back', anonymous=False)

        # Set rospy to execute a shutdown function when exiting       
        rospy.on_shutdown(self.shutdown)
        
        # Publisher to control the robot's speed
        self.cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
        
        # How fast will we update the robot's movement?
        rate = 50
        
        # Set the equivalent ROS rate variable
        r = rospy.Rate(rate)
        
        # Set the forward linear speed to 0.2 meters per second 
        linear_speed = 0.2
        
        # Set the travel distance to 1.0 meters
        goal_distance = 1.0
        
        # How long should it take us to get there?
        linear_duration = goal_distance / linear_speed
        
        # Set the rotation speed to 1.0 radians per second
        angular_speed = 1.0
        
        # Set the rotation angle to Pi radians (180 degrees)
        goal_angle = pi
        
        # How long should it take to rotate?
        angular_duration = goal_angle / angular_speed
     
        # Loop through the two legs of the trip  
        for i in range(2):
            # Initialize the movement command
            move_cmd = Twist()
            
            # Set the forward speed
            move_cmd.linear.x = linear_speed
            
            # Move forward for a time to go the desired distance
            ticks = int(linear_duration * rate)
            
            for t in range(ticks):
                self.cmd_vel.publish(move_cmd)
                r.sleep()
            
            # Stop the robot before the rotation
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)
            
            # Now rotate left roughly 180 degrees  
            
            # Set the angular speed
            move_cmd.angular.z = angular_speed

            # Rotate for a time to go 180 degrees
            ticks = int(goal_angle * rate)
            
            for t in range(ticks):           
                self.cmd_vel.publish(move_cmd)
                r.sleep()
                
            # Stop the robot before the next leg
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)    
            
        # Stop the robot
        self.cmd_vel.publish(Twist())
        
    def shutdown(self):
        # Always stop the robot when shutting down the node.
        rospy.loginfo("Stopping the robot...")
        self.cmd_vel.publish(Twist())
        rospy.sleep(1)
 
if __name__ == '__main__':
    try:
        OutAndBack()
    except:
        rospy.loginfo("Out-and-Back node terminated.")      

View Code

#!/usr/bin/env python

import rospy

如果閱讀了ROS Beginner Tutorials in Python,你會知道所有ROS節點都是以這兩句開頭。第一行確定了這個腳本會被看作Python程式腳本,而第二行引用ROS的核心Python庫。

from geometry_msgs.msg import Twist

from math import pi

這裡我們引用了其他一些我們在腳本中需要用到的元件,在目前的例子中,我們需要用到ROS的geometry_msgs包中的Twist消息類型,和Python的math子產品中的常數pi。請注意一個常見的引用錯誤,即忘記在你的包中的package.xml檔案中寫上必要的ROS<run_depend>。在目前的例子中,我們的package.xml檔案必須要有這行才能正确從geometry_msgs.msg中引用Twist:

<run_depend>geometry_msgs</run_depend>

class OutAndBack():

  def __init__(self):

這裡ROS節點主要部分的開頭把它自己定義成了一個Python類,并加上一行标準的類初始化。

#Give the node a name

rospy.init_node('out_and_back',anonymous=False)

#set rospy to execute a shutdown function when exiting

rospy.on_shutdown(self.shutdown)

每個ROS節點都被要求調用rospy.init_node(),我們也可以設定一個回調函數on_shutdown(),這樣我們就可以在腳本結束的時候,比如使用者按下ctrl-C的時候,進行必要的清理操作。對于一個移動機器人,最重要的清理操作就是讓機器人停下來。我們可以在後面的腳本中看如何做到這點。

#Publisher to control the robot's speed
self.cmd_vel = rospy.Publisher('/cmd_vel',Twist)
#How fast will we update the robot's movement?
rate=50
#set the equivalent ROS rate variable      

這裡定義了我們用來釋出Twist指令給/cmd_vel話題的ROS釋出者。還設定了以每秒50次的頻率來更新機器人的運動

#Set the forward linear speed to 0.2 meters per second
linear_speed=0.2
#Set the travel distance to 1.0 meters
goal_distance=1.0
#How long should it take us to get there?      

以相對安全的0.2米/秒的速度初始化前進速度,并把目标距離設定為1.0米,接着計算運動需要多長時間。

# Set the rotation speed to 1.0 radians per second
angular_speed=1.0
#Ser the rotation angle to Pi radians(180 degrees)
goal_angle=pi
#How long should it take to rotate?      

設定旋轉速度為1.0弧度/秒,目标角度為180度或Pi弧度。

#Loop through the two legs of the trip
for i in range(2):
    #Initialize the movement command
    move_cmd=Twist()
    #Set the forward speed
    move_cmd.linear.x=linear_speed
    #Move forward for a time to go the desired distance
    ticks=int(linear_duration *rate)
    for t in range(ticks)
        self.cmd_vel.publish(move_cmd)
        r.sleep()      

正是這裡的循環使機器人運動起來,每循環一次就運動一段。因為一些機器人要求不斷釋出Twist消息來使它持續運動,是以為了讓機器人以linear_speed米/秒的速度向前移動linear_distance米,我們需要在恰當的時長内每1/rate秒釋出一次move_cmd消息。語句r.sleep()是rospy.sleep(1/rate)的簡寫。因為我們把變量指派為r=rospy.Rate(rate)。

#Now rotate left roughly 180 degrees
#Set the angular speed
move_cmd.angular.z=angular_speed
#Rotate for a time to go 180 degrees
ticks=int(goal_angle *rate)
for t in range(ticks)
    self.cmd_vel.publish(move_cmd)
    r.sleep()      

在循環的第二部分,我們讓機器人在恰當時間段内以angular_speed弧度/秒的速度旋轉,最終一共旋轉180度。

#Stop the robot      

當機器人完成整個計時前進并傳回的過程後,我們釋出一條空的Twist消息(所有項都設為0)讓它停止運動

def shutdown(self)
    #always stop the robot when shutting down the node
    rospy.loginfo("Stopping the robot...")
    self.cmd_vel.publish(Twist())
    rospy.sleep(1)      
if __name__ == '__main__'
    try
        OutAndBack()
    except rospy.ROSInterruptException:
        rospy.loginfo("Out-and-Back node terminated.")