00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 from __future__ import with_statement
00039
00040 import roslib; roslib.load_manifest('elektron_calibration')
00041 import yaml
00042 import PyKDL
00043 import rospy
00044 from sensor_msgs.msg import Imu
00045 from nav_msgs.msg import Odometry
00046 from geometry_msgs.msg import Twist
00047 from elektron_calibration.msg import ScanDistAngle
00048 from math import *
00049 import threading
00050
00051
00052 def quat_to_angle(quat):
00053 rot = PyKDL.Rotation.Quaternion(quat.x, quat.y, quat.z, quat.w)
00054 return rot.GetRPY()[2]
00055
00056 def normalize_angle(angle):
00057 res = angle
00058 while res > pi:
00059 res -= 2.0*pi
00060 while res < -pi:
00061 res += 2.0*pi
00062 return res
00063
00064
00065 class CalibrateRobot:
00066 def __init__(self):
00067 self.lock = threading.Lock()
00068 self.sub_imu = rospy.Subscriber('imu/data', Imu, self.imu_cb)
00069 self.sub_odom = rospy.Subscriber('odom', Odometry, self.odom_cb)
00070 self.sub_scan = rospy.Subscriber('scan_dist_angle', ScanDistAngle, self.scan_cb)
00071 self.cmd_pub = rospy.Publisher('cmd_vel', Twist)
00072 self.imu_time = rospy.Time()
00073 self.odom_time = rospy.Time()
00074 self.scan_time = rospy.Time()
00075
00076
00077 self.inital_wall_angle = rospy.get_param("inital_wall_angle", 0.1)
00078 self.inital_wall_dist = rospy.get_param("inital_wall_angle", 3.0)
00079 self.imu_calibrate_time = rospy.get_param("imu_calibrate_time", 10.0)
00080
00081
00082
00083 def calibrate_rot(self, speed, imu_drift=0):
00084
00085 (imu_start_angle, odom_start_angle, scan_start_angle,
00086 imu_start_time, odom_start_time, scan_start_time,
00087 xx, yy, dd) = self.sync_timestamps()
00088 last_angle = odom_start_angle
00089 turn_angle = 0
00090 while turn_angle < 15.0/8*pi:
00091 if rospy.is_shutdown():
00092 return
00093 cmd = Twist()
00094 cmd.angular.z = speed
00095 self.cmd_pub.publish(cmd)
00096 rospy.sleep(0.025)
00097 with self.lock:
00098 delta_angle = normalize_angle(self.odom_angle - last_angle)
00099 turn_angle += delta_angle
00100 last_angle = self.odom_angle
00101 self.cmd_pub.publish(Twist())
00102
00103 rospy.sleep(1.0)
00104
00105 (imu_end_angle, odom_end_angle, scan_end_angle,
00106 imu_end_time, odom_end_time, scan_end_time,
00107 xx, yy, dd) = self.sync_timestamps()
00108 imu_delta = 2*pi + normalize_angle(imu_end_angle - imu_start_angle) - imu_drift*(imu_end_time - imu_start_time).to_sec()
00109 odom_delta = 2*pi + normalize_angle(odom_end_angle - odom_start_angle)
00110 scan_delta = 2*pi + normalize_angle(scan_end_angle - scan_start_angle)
00111 rospy.loginfo('Imu error: %f percent'%(100.0*((imu_delta/scan_delta)-1.0)))
00112 rospy.loginfo('Odom error: %f percent'%(100.0*((odom_delta/scan_delta)-1.0)))
00113 return (imu_delta/scan_delta, odom_delta/scan_delta)
00114
00115 def calibrate_lin(self, speed):
00116
00117 (imu_end_angle, odom_end_angle, scan_end_angle,
00118 imu_end_time, odom_end_time, scan_end_time,
00119 xx, yy, dd) = self.sync_timestamps()
00120 last_dist = dd
00121 dist = 0.0
00122 last_x = xx
00123 last_y = yy
00124 to_travel = dd - 0.5
00125 while dist < to_travel:
00126 if rospy.is_shutdown():
00127 return
00128 cmd = Twist()
00129 cmd.linear.x = speed
00130 self.cmd_pub.publish(cmd)
00131 rospy.sleep(0.025)
00132 with self.lock:
00133 dx = self.odom_x - last_x
00134 dy = self.odom_y - last_y
00135 last_x = self.odom_x
00136 last_y = self.odom_y
00137 delta_dist = sqrt(dx*dx+dy*dy)
00138 dist += delta_dist
00139 self.cmd_pub.publish(Twist())
00140
00141 rospy.sleep(1.0)
00142
00143 (imu_end_angle, odom_end_angle, scan_end_angle,
00144 imu_end_time, odom_end_time, scan_end_time,
00145 e_xx, e_yy, e_dd) = self.sync_timestamps()
00146 scan_delta = dd - e_dd
00147 sx = e_xx - xx
00148 sy = e_yy - yy
00149 odom_delta = sqrt(sx*sx+sy*sy)
00150 rospy.loginfo('Odom linear error: %f percent'%(100.0*((odom_delta/scan_delta)-1.0)))
00151 return odom_delta/scan_delta
00152
00153
00154
00155
00156
00157 def imu_drift(self):
00158
00159 rospy.loginfo('Estimating imu drift')
00160 (imu_start_angle, odom_start_angle, scan_start_angle,
00161 imu_start_time, odom_start_time, scan_start_time,
00162 xx, yy, dd) = self.sync_timestamps()
00163 rospy.sleep(self.imu_calibrate_time)
00164 (imu_end_angle, odom_end_angle, scan_end_angle,
00165 imu_end_time, odom_end_time, scan_end_time,
00166 xx, yy, dd) = self.sync_timestamps()
00167 imu_drift = normalize_angle(imu_end_angle - imu_start_angle) / ((imu_end_time - imu_start_time).to_sec())
00168 rospy.loginfo(' ... imu drift is %f degrees per second'%(imu_drift*180.0/pi))
00169 return imu_drift
00170
00171
00172 def align(self, scale = 1.0):
00173 rospy.loginfo("Aligning base with wall")
00174 with self.lock:
00175 angle = self.scan_angle
00176 cmd = Twist()
00177
00178 while angle < -self.inital_wall_angle*scale or angle > self.inital_wall_angle*scale:
00179 if rospy.is_shutdown():
00180 exit(0)
00181 if angle > 0:
00182 cmd.angular.z = -0.1*scale
00183 else:
00184 cmd.angular.z = 0.1*scale
00185 self.cmd_pub.publish(cmd)
00186 rospy.sleep(0.025)
00187 with self.lock:
00188 angle = self.scan_angle
00189
00190 def go_back(self):
00191 rospy.loginfo("Aligning base with wall")
00192 with self.lock:
00193 dist = self.scan_dist
00194 cmd = Twist()
00195
00196 while dist < self.inital_wall_dist:
00197 if rospy.is_shutdown():
00198 exit(0)
00199
00200 if (self.inital_wall_dist - dist > 0.5):
00201 cmd.linear.x = -0.15
00202 else:
00203 cmd.linear.x = -0.05
00204
00205 self.cmd_pub.publish(cmd)
00206 rospy.sleep(0.025)
00207 with self.lock:
00208 dist = self.scan_dist
00209
00210
00211
00212
00213
00214 def sync_timestamps(self, start_time=None):
00215 if not start_time:
00216 start_time = rospy.Time.now() + rospy.Duration(0.5)
00217 while not rospy.is_shutdown():
00218 rospy.sleep(0.3)
00219 with self.lock:
00220 if self.imu_time < start_time:
00221 rospy.loginfo("Still waiting for imu")
00222 elif self.odom_time < start_time:
00223 rospy.loginfo("Still waiting for odom")
00224 elif self.scan_time < start_time:
00225 rospy.loginfo("Still waiting for scan")
00226 else:
00227 return (self.imu_angle, self.odom_angle, self.scan_angle,
00228 self.imu_time, self.odom_time, self.scan_time,
00229 self.odom_x, self.odom_y, self.scan_dist)
00230 exit(0)
00231
00232
00233 def imu_cb(self, msg):
00234 with self.lock:
00235 angle = quat_to_angle(msg.orientation)
00236 self.imu_angle = angle
00237 self.imu_time = msg.header.stamp
00238
00239 def odom_cb(self, msg):
00240 with self.lock:
00241 angle = quat_to_angle(msg.pose.pose.orientation)
00242 self.odom_angle = angle
00243 self.odom_x = msg.pose.pose.position.x
00244 self.odom_y = msg.pose.pose.position.y
00245 self.odom_time = msg.header.stamp
00246
00247 def scan_cb(self, msg):
00248 with self.lock:
00249 self.scan_angle = msg.angle
00250 self.scan_dist = msg.dist
00251 self.scan_time = msg.header.stamp
00252
00253
00254
00255 def main():
00256 rospy.init_node('scan_to_angle')
00257 robot = CalibrateRobot()
00258
00259 imu_drift = robot.imu_drift()
00260 imu_corr = []
00261 odom_corr = []
00262 odom_lin_corr = []
00263 for speed in (0.3, 0.7, 1.0):
00264 robot.align()
00265 rospy.sleep(1.0)
00266 (imu, odom) = robot.calibrate_rot(speed, imu_drift)
00267 imu_corr.append(imu)
00268 odom_corr.append(odom)
00269
00270 for speed in (0.05, 0.1, 0.2):
00271 robot.align(0.3)
00272 rospy.sleep(1.0)
00273 robot.go_back()
00274 rospy.sleep(2.0)
00275 odom_lin = robot.calibrate_lin(speed)
00276 odom_lin_corr.append(odom_lin)
00277
00278 imu_res = 1.0/(sum(imu_corr)/len(imu_corr))
00279 odom_res = 1.0/(sum(odom_corr)/len(odom_corr))
00280 odom_lin_res = 1.0/(sum(odom_lin_corr)/len(odom_lin_corr))
00281 rospy.loginfo("Multiply the 'gyroscope/rot_scale' parameter with %f"%imu_res)
00282 rospy.loginfo("Multiply the 'elektron_base_node/rot_scale' parameter with %f"%odom_res)
00283 rospy.loginfo("Multiply the 'elektron_base_node/lin_scale' parameter with %f"%odom_lin_res)
00284
00285
00286 if __name__ == '__main__':
00287 main()