1. 程式人生 > >【模擬】Ros by example1 控制底座、導航、路徑規劃、SLAM程式碼註釋

【模擬】Ros by example1 控制底座、導航、路徑規劃、SLAM程式碼註釋

一、控制底座

效果:讓機器人花一段時間向前移動1m,再旋轉180度,最後返回原點。

1、基於定時的timed_out_and_back.py

# -*- coding: utf-8 -*-    有中文註釋
#!/usr/bin/env python
import rospy
from geometry_msgs.msg import Twist
from math import pi

class OutAndBack():
    def __init__(self):
        #1、初始化:給出節點名字,設定回撥函式
        rospy.init_node('out_and_back', anonymous=False)
        rospy.on_shutdown(self.shutdown)
        
        #2、釋出話題,機器人運動速度
        self.cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
        rate = 50
        r = rospy.Rate(rate)
        #3、設定前進速度、距離;轉動速度、距離
        linear_speed = 0.2
        goal_distance = 1.0
        linear_duration = goal_distance / linear_speed
        angular_speed = 1.0
        goal_angle = pi
        angular_duration = goal_angle / angular_speed
     
        #往返兩次行程 
        for i in range(2):
            #4、前進1m
            move_cmd = Twist()
            move_cmd.linear.x = linear_speed
            ticks = int(linear_duration * rate)
            
            for t in range(ticks):
                self.cmd_vel.publish(move_cmd)
                r.sleep()
            
            # 旋轉之前停下來
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)
            
            # 5、轉動180度
            move_cmd.angular.z = angular_speed
            ticks = int(goal_angle * rate)
            
            for t in range(ticks):           
                self.cmd_vel.publish(move_cmd)
                r.sleep()
                
            # 下一步之前停下來
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)    
            
        # 釋出空的Twist訊息讓機器人停下來
        self.cmd_vel.publish(Twist())
        
    def shutdown(self):
        # 釋出空的Twist訊息讓機器人停下來
        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.")

OutAndBack類主要分為5部分:1、初始化:給出節點名字,設定回撥函式;2、釋出話題,機器人運動速度;3、設定前進速度、距離;轉動速度、距離;4、迴圈部分,前進1m,旋轉180度;5、主函式

接下來將詳細介紹5部分內容

1、def __init__(self) 標準的類初始化,self類似於C++中this指標。

2、self.cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=5) 釋出Twist命令給/cmd_vel話題,且每秒50次更新機器人運動

4、前進1m

            # 初始化運動命令       
            move_cmd = Twist()
            # 設定前進速度
            move_cmd.linear.x = linear_speed
            # 在前進1米的這段時間控制命令更新的次數
            ticks = int(linear_duration * rate)
            for t in range(ticks):
                self.cmd_vel.publish(move_cmd)
                r.sleep()

 r.sleep()是rospy.sleep(1/rate)的簡單寫法。

r=rospy.Rate(rate) 保持一定的速率來進行迴圈。

我們需要每1/rate秒釋出一次move_cmd訊息。一共釋出linear_duration*rate次訊息。

5、標準程式塊,例項化OutAndBack類執行指令碼。

if __name__ == '__main__':
    try:
        OutAndBack()
    except:
        rospy.loginfo("Out-and-Back node terminated.")

 

2、基於測量的odomz-out_and_back.py

#!/usr/bin/env python
import rospy
from geometry_msgs.msg import Twist, Point, Quaternion
import tf
from rbx1_nav.transform_utils import quat_to_angle, normalize_angle
from math import radians, copysign, sqrt, pow, pi

class OutAndBack():
    def __init__(self):
        # 1、初始化:給出節點名稱,設定回撥函式
        rospy.init_node('out_and_back', anonymous=False)      
        rospy.on_shutdown(self.shutdown)
        # 2、釋出話題、前進速度和旋轉速度
        self.cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=5)
        rate = 20
        r = rospy.Rate(rate)
        linear_speed = 0.15
        goal_distance = 1.0
        angular_speed = 0.5
        angular_tolerance = radians(1.0)
        goal_angle = pi
        # 3、新增的一系列功能:tf、odom、base_footprint、Point
        # 初始化tf監聽器,填補緩衝區
        self.tf_listener = tf.TransformListener()
        rospy.sleep(2)
        # 設定odom座標系
        self.odom_frame = '/odom'
        # 詢問機器人使用/base_link座標系還是/base_footprint座標系
        try:
            self.tf_listener.waitForTransform(self.odom_frame, '/base_footprint', rospy.Time(), rospy.Duration(1.0))
            self.base_frame = '/base_footprint'
        except (tf.Exception, tf.ConnectivityException, tf.LookupException):
            try:
                self.tf_listener.waitForTransform(self.odom_frame, '/base_link', rospy.Time(), rospy.Duration(1.0))
                self.base_frame = '/base_link'
            except (tf.Exception, tf.ConnectivityException, tf.LookupException):
                rospy.loginfo("Cannot find transform between /odom and /base_link or /base_footprint")
                rospy.signal_shutdown("tf Exception")         
        # 初始化Point型別的變數
        position = Point()
        # 4、迴圈,前進1m,轉動180度
        for i in range(2):
            # 前進1m
            move_cmd = Twist()
            move_cmd.linear.x = linear_speed
            # 得到開始的位姿資訊
            (position, rotation) = self.get_odom()            
            x_start = position.x
            y_start = position.y  
            # 追蹤行駛距離
            distance = 0  
            # 迴圈
            while distance < goal_distance and not rospy.is_shutdown():
                # 釋出一次訊息,睡眠      
                self.cmd_vel.publish(move_cmd)
                r.sleep()
                # 得到當前位姿
                (position, rotation) = self.get_odom()
                # 計算位移
                distance = sqrt(pow((position.x - x_start), 2) + 
                                pow((position.y - y_start), 2))
            # 轉動前停止
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)
            
            # 給旋轉配置運動命令
            move_cmd.angular.z = angular_speed
            # 跟蹤最後角度
            last_angle = rotation
            # 跟蹤轉動的角度
            turn_angle = 0  
            while abs(turn_angle + angular_tolerance) < abs(goal_angle) and not rospy.is_shutdown():
                # 釋出一次訊息,睡眠        
                self.cmd_vel.publish(move_cmd)
                r.sleep()
                # 得到當前位姿,計算角度變換
                (position, rotation) = self.get_odom()
                delta_angle = normalize_angle(rotation - last_angle)
                # 新增到合計中
                turn_angle += delta_angle
                last_angle = rotation          
            # 轉動前停止
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)   
        # 停止機器人
        self.cmd_vel.publish(Twist())
        
    def get_odom(self):
        # 得到現測量值與基框架間轉換
        try:
            (trans, rot)  = self.tf_listener.lookupTransform(self.odom_frame, self.base_frame, rospy.Time(0))
        except (tf.Exception, tf.ConnectivityException, tf.LookupException):
            rospy.loginfo("TF Exception")
            return

        return (Point(*trans), quat_to_angle(Quaternion(*rot)))
        
    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.")

在上一節基礎上增加了

3、新增的一系列功能:tf、odom、base_footprint、Point;4、對於前進直線和旋轉的記錄。


1、self.tf_listener = tf.TransformListener() 

      rospy.sleep(2)

我們建立一個TransformListener物件來監視座標系轉換(frame transforms)。 注意 tf需要一些時間去填補監聽者(listener)的緩衝區,所以我們需要呼叫 rospy.sleep(2)

2、get_odom()

(position, rotation) = self.get_odom()
    def get_odom(self):
        # Get the current transform between the odom and base frames
        try:
            (trans, rot)  = self.tf_listener.lookupTransform(self.odom_frame, self.base_frame, rospy.Time(0))
        except (tf.Exception, tf.ConnectivityException, tf.LookupException):
            rospy.loginfo("TF Exception")
            return

        return (Point(*trans), quat_to_angle(Quaternion(*rot)))

首先使用tf_listener物件查詢當前 odom 座標系和 base(基礎)座標系之間的變換。正常的話返回位置和旋轉(四元數表示)

其中:return (Point(*trans), quat_to_angle(Quaternion(*rot))) 這段語句。在 python 中使用 “ * ” 修飾的transrot 變數,表示傳給一個函式的形參是一個列表。我們這裡的 transx、y、z 軸座標值;rotx、y、z、w 四元數值。


總結:

上一篇文章核心思想是:通過前進1m這段時間控制命令更新的次數。

ticks = int(linear_duration * rate)
for t in range(ticks):
      self.cmd_vel.publish(move_cmd)
        r.sleep()

本文使用TransformListener 來訪問 odom資訊,使用 tf 去監視 /odom 座標系與 /base_link 座標系之間的轉換要比依賴 /odom 話題要安全的多。