gyro_perf.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #AUTHOR: Younghun Ju <yhju@yujinrobot.com>, <yhju83@gmail.com>
00003 
00004 import roslib; roslib.load_manifest('kobuki_testsuite')
00005 import rospy
00006 
00007 import time
00008 from math import pi
00009 from kobuki_msgs.msg import ButtonEvent
00010 from kobuki_msgs.msg import Sound
00011 from geometry_msgs.msg import Twist
00012 
00013 from sensor_msgs.msg import Imu
00014 from geometry_msgs.msg import Quaternion
00015 from geometry_msgs.msg import Pose2D
00016 
00017 from tf.transformations import euler_from_quaternion
00018 from math import degrees, radians
00019 from numpy import mod
00020 
00021 #from turtlebot_calibration.msg import ScanAngle
00022 from kobuki_testsuite.msg import ScanAngle
00023 
00024 def wrap_to_pi(x):
00025     a = mod(mod(x,2*pi)+2*pi, 2*pi)
00026     if (a > pi):
00027       a -= 2*pi
00028     return a
00029 
00030 class Tester(object):
00031   def __init__(self):
00032     rospy.init_node("gyro_perf")
00033     self.state='INIT'
00034     self.done = False
00035     self.reset_angle = True
00036     self.triggered = False
00037 
00038     self.debug = rospy.get_param('~debug', False)
00039 
00040     self.command_vx = rospy.get_param('~command_vx', 0.1) # In [m/s], default is 0.1 m/s
00041     self.command_wz = radians(rospy.get_param('~command_wz', 5.0)) # In [deg/s], default is 5 deg/s
00042     # convert into [rad/s]
00043     if self.debug: print self.command_vx, self.command_wz
00044 
00045     self.init_imu = True
00046     self.init_angle = 0.0
00047     self.angle = 0.0
00048     self.angle_rate = 0.0
00049     self.diff_angle = 0.0
00050     self.accumulated_angle = 0.0
00051 
00052     self.init_time = rospy.Time.now().to_time()
00053     if self.debug: print 'init time: {0:2.4f}'.format(self.init_time)
00054 
00055     self.test_angle = rospy.get_param('~test_angle', 360.0) # in [deg], default is 360 deg
00056     self.max_sample = rospy.get_param('~max_sample', 500) # in count, default is 500 sample
00057 
00058     self.use_button = rospy.get_param('~use_button', False)
00059 
00060     self.sub_angle = rospy.Subscriber("angle_abs", ScanAngle, self.angleCallback) # Absolute angle from laser scanner
00061     self.sub_gyro = rospy.Subscriber("imu_data", Imu, self.imuCallback)   # /mobile_base/sensors/imu_data
00062     self.sub_button = rospy.Subscriber("button", ButtonEvent, self.buttonCallback)
00063     self.pub_velocity = rospy.Publisher("cmd_vel", Twist)                 # /mobile_base/commands/velocity
00064     self.pub_sound = rospy.Publisher("sound", Sound)
00065 
00066     if self.debug: print 'state:', self.state
00067     self.state='WAIT_CONNECTION'
00068     if self.debug: print 'state:', self.state
00069     rospy.Timer(rospy.Duration.from_sec(0.02), self.timerCallback) # 50 Hz
00070 
00071   def ending_condition(self):
00072     cond = abs(self.accumulated_angle) >= abs(radians(self.test_angle))
00073     return cond
00074 
00075   def timerCallback(self, event):
00076     if rospy.is_shutdown(): return
00077     if self.done: return
00078 
00079     self.elapsed_time = event.current_real.to_time()-self.init_time
00080     if event.last_real == None:
00081       event.last_real = rospy.Time.from_sec(0.0)
00082       event.last_expected = rospy.Time.from_sec(0.0)
00083       event.last_duration = 0.0
00084 
00085     if self.state == 'WAIT_CONNECTION':
00086       if self.sub_angle.get_num_connections() > 0 and\
00087         self.sub_gyro.get_num_connections() > 0 and\
00088         self.pub_velocity.get_num_connections() > 0:
00089           if self.debug: print 'connection ready.'
00090 
00091           self.state = 'ALIGNING'
00092           if self.debug: print 'state:', self.state
00093           return
00094       else:
00095         if self.debug: print 'not ready yet.'
00096         return
00097 
00098     if self.state == "ALIGNING":
00099       scan_angle = self.scan_angle
00100       if abs(scan_angle) > radians(1.0):
00101         cmd_vel = Twist()
00102         cmd_vel.angular.z = -0.33*scan_angle/abs(scan_angle)
00103         self.pub_velocity.publish(cmd_vel)
00104         self.init_time = rospy.Time.now().to_time()
00105         if self.debug: print 'state:', self.state, ':', scan_angle, '-->', cmd_vel.angular.z
00106         return
00107       else:
00108         cmd_vel = Twist()
00109         cmd_vel.linear.x = 0.0  # in [m/s]
00110         cmd_vel.angular.z = 0.0 # in [rad/s]
00111         self.pub_velocity.publish(cmd_vel)
00112 
00113         if self.elapsed_time > 5.0:
00114           if self.debug: print 'alining ready.'
00115 
00116           self.pub_sound.publish(Sound(value=Sound.RECHARGE))
00117           self.state = 'WAIT_TRIGGER'
00118           if self.debug: print 'state:', self.state
00119           return
00120         else:
00121           if self.debug: print 'state:', self.state
00122           return
00123 
00124     if self.state == 'WAIT_TRIGGER':
00125       if self.triggered or not self.use_button:
00126           self.pub_sound.publish(Sound(value=Sound.CLEANINGEND))
00127           self.state = 'CALC_ANGLE_PRERUN'
00128           self.reset_angle = True
00129           self.angle_count = 0
00130           if self.debug: print 'state:', self.state
00131           return
00132       else:
00133           return
00134 
00135     if self.state == 'CALC_ANGLE_PRERUN':
00136       if self.angle_count >= self.max_sample:
00137         self.angle_prerun = self.angle_avg
00138         if self.debug: print 'angle prerun: ', self.angle_prerun
00139 
00140         self.pub_sound.publish(Sound(value=Sound.RECHARGE))
00141 
00142         self.state = 'RUNNING'
00143         self.init_time = rospy.Time.now().to_time()
00144         if self.debug: print 'state:', self.state
00145 
00146         self.init_imu = True
00147         self.init_angle = 0.0
00148         self.angle = 0.0
00149         self.angle_rate = 0.0
00150         self.diff_angle = 0.0
00151         self.accumulated_angle = 0.0
00152 
00153         return
00154       else:
00155         if self.debug: print 'state:', self.state, ':', self.angle_count, self.scan_angle, self.angle_sum, self.angle_avg
00156         return
00157 
00158     if self.state == 'RUNNING':
00159       if self.ending_condition():
00160         self.state = 'STOP'
00161         cmd_vel = Twist()
00162         cmd_vel.linear.x = 0.0  # in [m/s]
00163         cmd_vel.angular.z = 0.0 # in [rad/s]
00164         self.pub_velocity.publish(cmd_vel)
00165         self.running_time = self.elapsed_time
00166         self.init_time = rospy.Time.now().to_time()
00167         if self.debug: print 'state:', self.state
00168         return
00169       else:
00170         cmd_vel = Twist()
00171         cmd_vel.linear.x = self.command_vx  # In [m/s]
00172         cmd_vel.angular.z = self.command_wz # In [rad/s]
00173         self.pub_velocity.publish(cmd_vel)
00174         if self.debug: print 'state:', self.state, ':', degrees(self.accumulated_angle), self.test_angle
00175         return
00176 
00177     if self.state == 'STOP':
00178       if self.elapsed_time > 5.0:
00179         self.reset_angle = True
00180         self.angle_count = 0
00181         self.state = 'CALC_ANGLE_POSTRUN'
00182         if self.debug: print 'state:', self.state
00183         return
00184       else:
00185         if self.debug: print 'state:', self.state
00186         return
00187 
00188     if self.state == 'CALC_ANGLE_POSTRUN':
00189       if self.angle_count >= self.max_sample:
00190         self.angle_postrun = self.angle_avg
00191 
00192         angle_gyro = self.accumulated_angle
00193         sign = angle_gyro/abs(angle_gyro)
00194         angle_laser = sign*radians(self.test_angle)+wrap_to_pi(self.angle_postrun - self.angle_prerun)
00195 
00196         rospy.loginfo('test_angle:    {0} deg'.format(self.test_angle))
00197         rospy.loginfo('test_command:  {0} m/s {1} deg/s'.format(self.command_vx, degrees(self.command_wz)))
00198         rospy.loginfo('running_time:  {0} s'.format(self.running_time))
00199         rospy.loginfo('')
00200         rospy.loginfo('angle_prerun:  {0} deg'.format(degrees(self.angle_prerun)))
00201         rospy.loginfo('angle_postrun: {0} deg'.format(degrees(self.angle_postrun)))
00202         rospy.loginfo('angle_gyro:    {0} deg'.format(degrees(angle_gyro)))
00203         rospy.loginfo('angle_laser:   {0} deg'.format(degrees(angle_laser)))
00204         rospy.loginfo('')
00205         rospy.loginfo('error:         {0} deg/rev'.format(angle_gyro/angle_laser*360.0-360.0))
00206         rospy.loginfo('Done')
00207         self.pub_sound.publish(Sound(value=Sound.CLEANINGSTART))
00208 
00209         self.state = 'DONE'
00210         self.done = True
00211         if self.debug: print 'state:', self.state
00212         rospy.signal_shutdown('jobs_done')
00213         time.sleep(5.0)
00214         return
00215       else:
00216         if self.debug: print 'state:', self.state, ':', self.angle_count, self.scan_angle, self.angle_sum, self.angle_avg
00217         return
00218 
00219   def imuCallback(self,data):
00220     if rospy.is_shutdown(): return
00221     quat = data.orientation
00222     q = [quat.x, quat.y, quat.z, quat.w]
00223     roll, pitch, yaw = euler_from_quaternion(q)
00224 
00225     if self.init_imu:
00226       self.init_angle = yaw
00227       self.init_imu = False
00228 
00229     self.diff_angle = wrap_to_pi(yaw - self.init_angle - self.angle)
00230     self.accumulated_angle += self.diff_angle
00231     self.angle = wrap_to_pi(yaw - self.init_angle)
00232     self.angle_rate = data.angular_velocity.z
00233 
00234   def angleCallback(self,data):
00235     if rospy.is_shutdown(): return
00236 
00237     self.scan_angle = data.scan_angle
00238 
00239     if self.reset_angle:
00240       if self.debug: print 'Resetted'
00241       self.angle_count = 0
00242       self.angle_sum = 0
00243       self.scan_angle = 0
00244       self.reset_angle = False
00245 
00246     if self.angle_count >= self.max_sample:
00247       return
00248 
00249     self.angle_count += 1
00250     self.angle_sum += data.scan_angle
00251     self.angle_avg = self.angle_sum / self.angle_count
00252 
00253   def buttonCallback(self,data):
00254     if rospy.is_shutdown(): return
00255     if data.button == ButtonEvent.Button0 and data.state == ButtonEvent.RELEASED:
00256       #if self.debug: print 'button0 pressed'
00257       self.triggered=True
00258       return
00259     #if data.button == ButtonEvent.Button1 and data.state == ButtonEvent.RELEASED:
00260     #  if self.debug: print 'button1 pressed'; return
00261     #if data.button == ButtonEvent.Button2 and data.state == ButtonEvent.RELEASED:
00262     #  if self.debug: print 'button2 pressed'; return
00263 
00264 if __name__ == '__main__':
00265   print
00266   print "It test gyro on kobuki and print gyro drift errors."
00267   print
00268   try:
00269     instance = Tester()
00270     rospy.spin()
00271   except rospy.ROSInterruptException: pass


kobuki_testsuite
Author(s): Jorge Santos Simon
autogenerated on Wed Sep 16 2015 04:35:48