text: #!/usr/bin/env python
  
  import rospy
  from geometry_msgs.msg import Twist, Point
  from math import copysign, sqrt, pow
  import tf
  import math
  
  class CalibrateLinear():
      def __init__(self):
          # Give the node a name
          rospy.init_node('calibrate_linear', anonymous=False)
          
          # Set rospy to execute a shutdown function when terminating the script
          rospy.on_shutdown(self.shutdown)
          
          # How fast will we check the odometry values?
          self.rate = rospy.get_param('~rate', 20)
          r = rospy.Rate(self.rate)
          
          # Set the distance to travel
          self.test_distance = rospy.get_param('~test_distance', 1.0) # meters
          self.speed = rospy.get_param('~speed', 0.3) # meters per second
          self.tolerance = rospy.get_param('~tolerance', 0.01) # meters
          self.odom_linear_scale_correction = rospy.get_param('~odom_linear_scale_correction', 1.0)
          self.start_test = rospy.get_param('~start_test', True)
          
          # Publisher to control the robot's speed
          self.cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=5)
          
   
          # The base frame is base_footprint for the TurtleBot but base_link for Pi Robot
          self.base_frame = rospy.get_param('~base_frame', '/base_footprint')
  
          # The odom frame is usually just /odom
          #self.odom_frame = rospy.get_param('~odom_frame', '/odom')
          self.odom_frame = rospy.get_param('~odom_frame', '/odom_combined')
  
          # Initialize the tf listener
          self.tf_listener = tf.TransformListener()
          
          # Give tf some time to fill its buffer
          rospy.sleep(2)
          
          # Make sure we see the odom and base frames
          self.tf_listener.waitForTransform(self.odom_frame, self.base_frame, rospy.Time(), rospy.Duration(60.0))        
              
          rospy.loginfo("Bring up rqt_reconfigure to control the test.")
    
          self.position = Point()
          
          # Get the starting position from the tf transform between the odom and base frames
          self.position = self.get_position()
          
          x_start = self.position.x
          y_start = self.position.y
              
          move_cmd = Twist()
          linear_v=0
          start_slow_speed=0.15
          min_linear_speed=0.15           
          while not rospy.is_shutdown():
              # Stop the robot by default
              move_cmd = Twist()
              
              if self.start_test:
                  # Get the current position from the tf transform between the odom and base frames
                  self.position = self.get_position()
                  
                  # Compute the Euclidean distance from the target point
                  distance = sqrt(pow((self.position.x - x_start), 2) +
                                  pow((self.position.y - y_start), 2))
                                  
                  # Correct the estimated distance by the correction factor
                  distance *= self.odom_linear_scale_correction
                  
                  # How close are we?
                  error =  distance - self.test_distance
                  
                  # Are we close enough?
                  if not self.start_test or abs(error) <  self.tolerance:
                      self.start_test = False
                      params = {'start_test': False}
                      rospy.loginfo(params)
                  else:
                      # If not, move in the appropriate direction
                      #move_cmd.linear.x = copysign(self.speed, -1 * error)
                      try: 
  		    	linear_v =0.5*error;
  		    except:
  			rospy.loginfo("linear_v ji suan error")
  		    #rospy.loginfo("linear_v = "+ str(linear_v)+" self.speed="+str(self.speed))
  
                      #rospy.loginfo("self.speed "+str(self.speed))
  		    if math.fabs(linear_v) >= math.fabs(self.speed):
  			linear_v= self.speed
  		    elif math.fabs(linear_v) < math.fabs(start_slow_speed) :
  			linear_v=min_linear_speed
                      else:
                          pass 
                      #rospy.loginfo("****linear_v = "+ str(linear_v)+" self.speed="+str(self.speed))
                      move_cmd.linear.x = copysign(math.fabs(linear_v), -1 * error)
              else:
                  self.position = self.get_position()
                  x_start = self.position.x
                  y_start = self.position.y
                  
              self.cmd_vel.publish(move_cmd)
              r.sleep()
  
          # Stop the robot
          self.cmd_vel.publish(Twist())
  
      def get_position(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)
          
      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:
          CalibrateLinear()
          rospy.spin()
      except:
          rospy.loginfo("Calibration terminated.")
  