00001
00002
00003
00004 import roslib
00005 roslib.load_manifest('pose_ekf_slam')
00006 import rospy
00007 import tf
00008 from tf.transformations import euler_from_quaternion
00009 from nav_msgs.msg import Odometry
00010 from geometry_msgs.msg import PoseWithCovarianceStamped
00011 from geometry_msgs.msg import TwistWithCovarianceStamped
00012 from sensor_msgs.msg import Imu
00013
00014 from visualization_msgs.msg import Marker, MarkerArray
00015
00016
00017 from pose_ekf_slam.srv import SetPosition, SetPositionResponse, SetPositionRequest
00018 from pose_ekf_slam.srv import SetLandmark, SetLandmarkResponse
00019 from pose_ekf_slam.msg import Landmark, Map
00020
00021
00022 from numpy import delete, dot, zeros, eye, cos, sin, array, diag, sqrt
00023
00024 from numpy import matrix, asarray, squeeze, mean, var
00025 from collections import deque
00026
00027 import threading
00028 import motion_transformations as mt
00029 import PyKDL
00030 import math
00031
00032 class PoseEkfSlam :
00033 def __init__(self, name, p_var, q_var):
00034 self.name = name
00035 self.p_var = p_var
00036
00037
00038 self.odom = Odometry()
00039
00040
00041 self.imu = Imu()
00042
00043
00044 self.last_prediction = rospy.Time.now()
00045 self.lock = threading.RLock()
00046 self.listener = tf.TransformListener()
00047 self.world_frame_name = 'world'
00048 self.robot_frame_name = 'robot'
00049 self.getConfig()
00050
00051
00052 sp = SetPositionRequest()
00053 sp.position.x = 0.0
00054 sp.position.y = 0.0
00055 sp.position.z = 0.0
00056 self.initEkf(sp.position)
00057
00058
00059 self.Q = self.computeQ(q_var)
00060
00061
00062 self.is_ekf_init = False
00063 self.is_imu_init = False
00064
00065
00066 self.pub_odom = rospy.Publisher('/pose_ekf_slam/odometry', Odometry)
00067 self.pub_map = rospy.Publisher('/pose_ekf_slam/map', Map)
00068 self.pub_landmarks = rospy.Publisher('/pose_ekf_slam/landmarks',
00069 MarkerArray)
00070 self.covariance_marker = rospy.Publisher(
00071 '/pose_ekf_slam/covariance_marker', Marker)
00072
00073
00074 rospy.Subscriber('/pose_ekf_slam/pose_update',
00075 PoseWithCovarianceStamped,
00076 self.poseUpdate)
00077 rospy.Subscriber('/pose_ekf_slam/velocity_update',
00078 TwistWithCovarianceStamped,
00079 self.velocityUpdate)
00080
00081
00082 rospy.Subscriber("/pose_ekf_slam/imu_input", Imu, self.imuInput)
00083
00084
00085 self.set_position = rospy.Service('/pose_ekf_slam/set_position',
00086 SetPosition,
00087 self.setPosition)
00088
00089 self.set_landmark = rospy.Service('/pose_ekf_slam/set_landmark',
00090 SetLandmark,
00091 self.setLandmark)
00092
00093 rospy.Timer(rospy.Duration(10.0), self.searchForLandmarkUpdates)
00094
00095
00096 def searchForLandmarkUpdates(self, event):
00097
00098 topics = rospy.get_published_topics('/pose_ekf_slam/landmark_update')
00099 for t in topics:
00100 if t[1] == 'geometry_msgs/PoseWithCovarianceStamped':
00101 if t[0] not in self.landmark_update_topics:
00102 rospy.loginfo('Subscribe to landmark update: %s', t[0])
00103 self.landmark_update_topics.append(t[0])
00104 rospy.Subscriber(t[0], PoseWithCovarianceStamped,
00105 self.landmarkUpdate, t[0])
00106
00107
00108 def setPosition(self, req):
00109 self.initEkf(req.position)
00110
00111
00112 self.is_ekf_init = True
00113
00114 return SetPositionResponse()
00115
00116
00117 def setLandmark(self, req):
00118 self.lock.acquire()
00119 self.mapped_lamdmarks[req.topic_name] = self.number_of_landmarks
00120 self.landmark_values[self.number_of_landmarks] = req.topic_name
00121 self.landmark_last_update[self.number_of_landmarks] = rospy.Time().now()
00122
00123 rospy.loginfo('%s, Add feature %s',
00124 self.name, req.topic_name)
00125
00126 cov = matrix(req.landmark.covariance).reshape(6,6)
00127 self.addLandmark(req.landmark.pose.position.x,
00128 req.landmark.pose.position.y,
00129 req.landmark.pose.position.z,
00130 cov[0:3, 0:3])
00131 self.lock.release()
00132 return SetLandmarkResponse()
00133
00134
00135 def imuInput(self, data):
00136 self.imu = data
00137 tf_done = True
00138
00139 if data.header.frame_id != self.robot_frame_name:
00140
00141 if data.header.frame_id in self.tf_cache:
00142
00143 trans = self.tf_cache[data.header.frame_id][0]
00144 rot = self.tf_cache[data.header.frame_id][1]
00145
00146
00147 qt = mt.orientation(data.orientation, rot)
00148 self.imu.orientation.x = qt[0]
00149 self.imu.orientation.y = qt[1]
00150 self.imu.orientation.z = qt[2]
00151 self.imu.orientation.w = qt[3]
00152
00153
00154
00155 o_cov = mt.orientationCov(
00156 array(data.orientation_covariance).reshape(3,3),
00157 self.imu.orientation,
00158 rot)
00159
00160
00161 self.imu.orientation_covariance = o_cov.ravel()
00162
00163
00164 w_r = mt.angularVelocity(data.angular_velocity, rot)
00165 self.imu.angular_velocity.x = w_r[0]
00166 self.imu.angular_velocity.y = w_r[1]
00167 self.imu.angular_velocity.z = w_r[2]
00168
00169
00170 w_cov = mt.angularVelocityCov(
00171 array(data.angular_velocity_covariance).reshape(3,3),
00172 rot)
00173
00174
00175 self.imu.angular_velocity_covariance = w_cov.ravel()
00176
00177 else:
00178 try:
00179 self.listener.waitForTransform(self.robot_frame_name,
00180 data.header.frame_id,
00181 data.header.stamp,
00182 rospy.Duration(2.0))
00183
00184 (trans, rot) = self.listener.lookupTransform(
00185 self.robot_frame_name,
00186 data.header.frame_id,
00187 data.header.stamp)
00188
00189 self.tf_cache[data.header.frame_id] = [trans, rot]
00190
00191 except (tf.LookupException,
00192 tf.ConnectivityException,
00193 tf.ExtrapolationException):
00194
00195 tf_done = False
00196 rospy.logerr('%s, Define TF for %s input data!',
00197 self.name,
00198 data.header.frame_id)
00199
00200 if tf_done:
00201
00202
00203 self.lock.acquire()
00204 self.odom.pose.pose.orientation.x = self.imu.orientation.x
00205 self.odom.pose.pose.orientation.y = self.imu.orientation.y
00206 self.odom.pose.pose.orientation.z = self.imu.orientation.z
00207 self.odom.pose.pose.orientation.w = self.imu.orientation.w
00208 self.odom.twist.twist.angular.x = self.imu.angular_velocity.x
00209 self.odom.twist.twist.angular.y = self.imu.angular_velocity.y
00210 self.odom.twist.twist.angular.z = self.imu.angular_velocity.z
00211
00212 self.is_imu_init = True
00213
00214
00215 if self.makePrediction(data.header.stamp):
00216 self.updatePrediction()
00217 self.publishData(data.header.stamp)
00218
00219 self.lock.release()
00220
00221
00222 def poseUpdate(self, pose_msg):
00223 """ pose_msg is a geometry_msgs/PoseWithCovarianceStamped msg """
00224 tf_done = True
00225 print 'pose update:\n', pose_msg
00226 self.lock.acquire()
00227
00228
00229 if self.makePrediction(pose_msg.header.stamp):
00230 covariance_pose = self.takeCovariance(pose_msg.pose.covariance)
00231 if pose_msg.header.frame_id != self.robot_frame_name:
00232 if pose_msg.header.frame_id in self.tf_cache:
00233
00234 trans = self.tf_cache[pose_msg.header.frame_id][0]
00235 rot = self.tf_cache[pose_msg.header.frame_id][1]
00236
00237
00238 p = mt.position(pose_msg.pose.pose.position,
00239 self.imu.orientation,
00240 trans)
00241
00242
00243 covariance_pose = mt.positionCovariance(
00244 covariance_pose,
00245 array(self.imu.orientation_covariance).reshape(3,3),
00246 self.imu.orientation,
00247 trans)
00248
00249 else:
00250 tf_done = False
00251
00252 try:
00253 self.listener.waitForTransform(self.robot_frame_name,
00254 pose_msg.header.frame_id,
00255 pose_msg.header.stamp,
00256 rospy.Duration(2.0))
00257
00258 (trans, rot) = self.listener.lookupTransform(
00259 self.robot_frame_name,
00260 pose_msg.header.frame_id,
00261 pose_msg.header.stamp)
00262
00263
00264 print 'get pose TF cached!'
00265 self.tf_cache[pose_msg.header.frame_id] = [trans, rot]
00266
00267 except (tf.LookupException,
00268 tf.ConnectivityException,
00269 tf.ExtrapolationException,
00270 tf.Exception):
00271
00272 rospy.logerr('%s, Define TF for %s update!',
00273 self.name, pose_msg.header.frame_id)
00274
00275 else:
00276 p = [pose_msg.pose.pose.position.x,
00277 pose_msg.pose.pose.position.y,
00278 pose_msg.pose.pose.position.z]
00279
00280 if tf_done:
00281
00282 z, r, h, v = self.createPoseMeasures(
00283 p, covariance_pose)
00284
00285
00286 self.applyUpdate(z, r, h, v, 10.0)
00287
00288
00289 self.publishData(pose_msg.header.stamp)
00290
00291
00292
00293 self.lock.release()
00294
00295
00296 def velocityUpdate(self, twist_msg):
00297 """ twist_msg is a geometry_msgs/TwistWithCovariance msg """
00298 tf_done = True
00299 self.lock.acquire()
00300 if self.makePrediction(twist_msg.header.stamp):
00301 velocity_r = self.takeCovariance(twist_msg.twist.covariance)
00302 if twist_msg.header.frame_id != self.robot_frame_name:
00303 if twist_msg.header.frame_id in self.tf_cache:
00304
00305 trans = self.tf_cache[twist_msg.header.frame_id][0]
00306 rot = self.tf_cache[twist_msg.header.frame_id][1]
00307
00308
00309 v = mt.linearVelocity(twist_msg.twist.twist.linear,
00310 self.imu.angular_velocity,
00311 trans, rot)
00312
00313
00314 velocity_r = mt.linearVelocityCov(
00315 velocity_r,
00316 array(self.imu.angular_velocity_covariance).reshape(3,3),
00317 trans, rot)
00318 else:
00319 tf_done = False
00320 print 'waiting for ', twist_msg.header.frame_id, ' message'
00321
00322 try:
00323 self.listener.waitForTransform(self.robot_frame_name,
00324 twist_msg.header.frame_id,
00325 twist_msg.header.stamp,
00326 rospy.Duration(2.0))
00327
00328 (trans, rot) = self.listener.lookupTransform(
00329 self.robot_frame_name,
00330 twist_msg.header.frame_id,
00331 twist_msg.header.stamp)
00332
00333
00334 self.tf_cache[twist_msg.header.frame_id] = [trans, rot]
00335
00336 except (tf.LookupException,
00337 tf.ConnectivityException,
00338 tf.ExtrapolationException,
00339 tf.Exception):
00340 rospy.logerr('%s, Define TF for %s update!',
00341 self.name,
00342 twist_msg.header.frame_id)
00343 else:
00344 v = [twist_msg.twist.twist.linear.x,
00345 twist_msg.twist.twist.linear.y,
00346 twist_msg.twist.twist.linear.z]
00347
00348 if tf_done:
00349
00350 z = array([v[0], v[1], v[2]])
00351 velocity_h = self.velocityH()
00352
00353
00354 self.applyUpdate(z, velocity_r, velocity_h, eye(3), 8.0)
00355
00356
00357 self.publishData(twist_msg.header.stamp)
00358
00359 self.lock.release()
00360
00361
00362 def landmarkUpdate(self, landmark_msg, topic_name):
00363 """ landmark_msg is a geometry_msgs/PoseWithCovariance msg wrt
00364 objects frame. Then, position is (0, 0, 0) and orientation is
00365 (0, 0, 0, 0,). It is necessary to check its TF to know the object
00366 position wrt the vehicle """
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388 self.lock.acquire()
00389 if self.makePrediction(landmark_msg.header.stamp):
00390 if topic_name in self.mapped_lamdmarks:
00391 if landmark_msg.header.frame_id in self.tf_cache:
00392
00393 trans = self.tf_cache[landmark_msg.header.frame_id][0]
00394 rot = self.tf_cache[landmark_msg.header.frame_id][1]
00395
00396
00397 pose = mt.landmarkPosition(
00398 landmark_msg.pose.pose.position, rot, trans)
00399
00400
00401 z = array([pose[0], pose[1], pose[2]])
00402
00403
00404 landmark_id = self.mapped_lamdmarks[topic_name]
00405
00406 angle = tf.transformations.euler_from_quaternion(
00407 [self.imu.orientation.x, self.imu.orientation.y,
00408 self.imu.orientation.z, self.imu.orientation.w])
00409
00410 Or = PyKDL.Rotation.RPY(angle[0], angle[1], angle[2])
00411 rot = matrix([Or[0,0], Or[0,1], Or[0,2],
00412 Or[1,0], Or[1,1], Or[1,2],
00413 Or[2,0], Or[2,1], Or[2,2]]).reshape(3,3)
00414
00415 h = self.landmarkH2(landmark_id, rot.T)
00416
00417 r = self.takeCovariance(landmark_msg.pose.covariance)
00418
00419
00420 self.applyUpdate(z, r, h, eye(3), 9.0)
00421
00422
00423 self.landmark_last_update[self.mapped_lamdmarks[topic_name]] = landmark_msg.header.stamp
00424
00425
00426 self.publishData(landmark_msg.header.stamp)
00427 else:
00428
00429 [trans, rot] = self.getTF(self.robot_frame_name,
00430 landmark_msg.header.frame_id,
00431 landmark_msg.header.stamp)
00432 if trans != None:
00433 self.tf_cache[landmark_msg.header.frame_id] = [trans,
00434 rot]
00435
00436 else:
00437 if topic_name in self.candidate_landmarks:
00438 print 'landmark ', topic_name, ' is a candidate...'
00439
00440 [trans, rot] = self.getTF(self.world_frame_name,
00441 landmark_msg.header.frame_id,
00442 rospy.Time())
00443 if trans != None:
00444
00445 pose = mt.landmarkPosition(
00446 landmark_msg.pose.pose.position, rot, trans)
00447
00448 self.candidate_landmarks[topic_name].appendleft(
00449 array([pose[0], pose[1], pose[2]]))
00450
00451 if len(self.candidate_landmarks[topic_name]) > 4:
00452 print '... and have been seen more than 4 times'
00453 [pose, cov, update] = self.computeCandidate(self.candidate_landmarks[topic_name])
00454 if update:
00455 self.mapped_lamdmarks[topic_name] = self.number_of_landmarks
00456 self.landmark_values[self.number_of_landmarks] = topic_name
00457 self.landmark_last_update[self.number_of_landmarks] = landmark_msg.header.stamp
00458
00459
00460 measured_covariance = eye(3)
00461 measured_covariance[0, 0] = 0.25
00462 measured_covariance[1, 1] = 0.25
00463 measured_covariance[2, 2] = 0.25
00464
00465 self.addLandmark(pose[0], pose[1], pose[2],
00466 measured_covariance)
00467
00468 rospy.loginfo('%s, Add feature %s',
00469 self.name, topic_name)
00470 else:
00471
00472 print 'landmark ', topic_name, ' is a candidate seen by first time'
00473 [trans, rot] = self.getTF(self.world_frame_name,
00474 landmark_msg.header.frame_id,
00475 rospy.Time())
00476 if trans != None:
00477
00478 pose = mt.landmarkPosition(
00479 landmark_msg.pose.pose.position, rot, trans)
00480
00481 self.candidate_landmarks[topic_name] = deque(maxlen = 5)
00482 self.candidate_landmarks[topic_name].appendleft(array([pose[0], pose[1], pose[2]]))
00483
00484 self.lock.release()
00485
00486
00487 def getTF(self, origin, destination, stamp):
00488 try:
00489 self.listener.waitForTransform(origin,
00490 destination,
00491 stamp,
00492 rospy.Duration(0.2))
00493
00494 (trans, rot) = self.listener.lookupTransform(
00495 origin, destination, stamp)
00496
00497 return [trans, rot]
00498
00499 except (tf.LookupException,
00500 tf.ConnectivityException,
00501 tf.ExtrapolationException,
00502 tf.Exception):
00503 rospy.logerr('%s, Define TF for %s update!',
00504 self.name,
00505 destination)
00506 return [None, None]
00507
00508
00509 def computeCandidate(self, candidates):
00510 for i in candidates:
00511 print '--> ', i
00512
00513 max_cov = 0.0025
00514
00515 cov = var(candidates, axis=0)
00516 print 'cov: ', cov
00517
00518 if cov[0] < max_cov and cov[1] < max_cov and cov[2] < max_cov:
00519 return [mean(candidates, axis=0), cov, True]
00520 else:
00521 return[candidates[0], cov, False]
00522
00523
00524
00525 def applyUpdate(self, z, r, h, v, d):
00526
00527
00528
00529
00530
00531
00532
00533
00534 distance = self.mahalanobisDistance(z, h, r)
00535
00536 if self.filter_updates < 100 or distance < d:
00537 self.lock.acquire()
00538 innovation = z - dot(h, self._x_).T
00539 temp_K = dot(dot(h, self._P_), h.T) + dot(dot(v, r), v.T)
00540 inv_S = squeeze(asarray(matrix(temp_K).I))
00541 K = dot(dot(self._P_, h.T), inv_S)
00542 self.x = self._x_ + dot(K, innovation)
00543 self.P = dot((eye(6 + 3*self.number_of_landmarks)-dot(K, h)),
00544 self._P_)
00545
00546
00547 p_diag = self.P.diagonal()
00548 for i in range(len(p_diag)):
00549 if p_diag[i] <= 0.0:
00550 self.P[i,i] = 0.01
00551 rospy.logfatal('%s, NEGATIVE VALUES IN THE DIAGONAL OF P!', self.name)
00552
00553
00554 self.filter_updates = self.filter_updates + 1
00555 self.lock.release()
00556 else:
00557 rospy.loginfo('%s, Invalid update. Mahalanobis distance = %s > %s',
00558 self.name, distance, d)
00559
00560
00561 def makePrediction(self, now):
00562 if self.is_ekf_init and self.is_imu_init:
00563
00564 orientation = euler_from_quaternion([self.imu.orientation.x,
00565 self.imu.orientation.y,
00566 self.imu.orientation.z,
00567 self.imu.orientation.w])
00568 u = list(orientation)
00569
00570
00571 t = (now - self.last_prediction).to_sec()
00572
00573 self.last_prediction = now
00574 if t > 0.0 and t < 1.0:
00575
00576 self.lock.acquire()
00577 self.prediction(u, t)
00578 self.lock.release()
00579 return True
00580
00581
00582 elif t > -0.1:
00583 self._x_ = self.x
00584 self._P_ = self.P
00585 return True
00586 else:
00587 rospy.logerr('Invalid time: %s', t)
00588 return False
00589 else:
00590 return False
00591
00592
00593 def publishData(self, now):
00594 if self.is_ekf_init:
00595 self.lock.acquire()
00596
00597
00598 self.odom.header.stamp = now
00599 self.odom.header.frame_id = self.world_frame_name
00600 self.odom.child_frame_id = ''
00601
00602
00603 self.odom.pose.pose.position.x = self.x[0]
00604 self.odom.pose.pose.position.y = self.x[1]
00605 self.odom.pose.pose.position.z = self.x[2]
00606
00607
00608 p = self.P[0:3,0:3].tolist()
00609 self.odom.pose.covariance[0:3] = p[0]
00610 self.odom.pose.covariance[6:9] = p[1]
00611 self.odom.pose.covariance[12:15] = p[2]
00612 self.odom.pose.covariance[21:24] = self.imu.orientation_covariance[0:3]
00613 self.odom.pose.covariance[27:30] = self.imu.orientation_covariance[3:6]
00614 self.odom.pose.covariance[33:36] = self.imu.orientation_covariance[6:9]
00615
00616
00617 self.odom.twist.twist.linear.x = self.x[3]
00618 self.odom.twist.twist.linear.y = self.x[4]
00619 self.odom.twist.twist.linear.z = self.x[5]
00620
00621
00622 p = self.P[3:6,3:6].tolist()
00623 self.odom.twist.covariance[0:3] = p[0]
00624 self.odom.twist.covariance[6:9] = p[1]
00625 self.odom.twist.covariance[12:15] = p[2]
00626 self.odom.twist.covariance[21:24] = self.imu.angular_velocity_covariance[0:3]
00627 self.odom.twist.covariance[27:30] = self.imu.angular_velocity_covariance[3:6]
00628 self.odom.twist.covariance[33:36] = self.imu.angular_velocity_covariance[6:9]
00629
00630
00631 self.pub_odom.publish(self.odom)
00632
00633
00634 br = tf.TransformBroadcaster()
00635 br.sendTransform((self.x[0], self.x[1], self.x[2]),
00636 (self.odom.pose.pose.orientation.x,
00637 self.odom.pose.pose.orientation.y,
00638 self.odom.pose.pose.orientation.z,
00639 self.odom.pose.pose.orientation.w),
00640 self.odom.header.stamp,
00641 self.robot_frame_name,
00642 self.world_frame_name)
00643
00644
00645 marker = Marker()
00646 marker.header.stamp = self.odom.header.stamp
00647 marker.header.frame_id = self.world_frame_name
00648
00649 marker.ns = self.robot_frame_name + '_cov'
00650 marker.id = 0
00651 marker.type = 2
00652 marker.action = 0
00653 marker.pose.position.x = self.odom.pose.pose.position.x
00654 marker.pose.position.y = self.odom.pose.pose.position.y
00655 marker.pose.position.z = self.odom.pose.pose.position.z
00656 marker.scale.x = math.sqrt(self.odom.pose.covariance[0])
00657 marker.scale.y = math.sqrt(self.odom.pose.covariance[7])
00658 marker.scale.z = math.sqrt(self.odom.pose.covariance[14])
00659 marker.color.r = 0.7
00660 marker.color.g = 0.2
00661 marker.color.b = 0.2
00662 marker.color.a = 0.7
00663 marker.lifetime = rospy.Duration(2.0)
00664 marker.frame_locked = False
00665 self.covariance_marker.publish(marker)
00666
00667
00668 if self.number_of_landmarks > 0:
00669 map = Map()
00670 map.header.stamp = rospy.Time.now()
00671 map.header.frame_id = self.world_frame_name
00672 marker_array = MarkerArray()
00673
00674 for i in range(self.number_of_landmarks):
00675
00676 landmark = Landmark()
00677 landmark.position.x = self.x[6 + i*3]
00678 landmark.position.y = self.x[7 + i*3]
00679 landmark.position.z = self.x[8 + i*3]
00680 landmark.landmark_id = self.landmark_values[i]
00681 landmark.last_update = self.landmark_last_update[i]
00682 p = self.P[6 + i*3:9 + i*3, 6 + i*3:9 + i*3].tolist()
00683 landmark.position_covariance[0:3] = p[0]
00684 landmark.position_covariance[3:6] = p[1]
00685 landmark.position_covariance[6:9] = p[2]
00686
00687 map.landmark.append(landmark)
00688
00689
00690 marker = Marker()
00691 marker.header.frame_id = self.world_frame_name
00692 marker.header.stamp = self.landmark_last_update[i]
00693 marker.ns = self.landmark_values[i]
00694 marker.id = 0
00695 marker.type = 1
00696 marker.action = 0
00697 marker.pose.position.x = landmark.position.x
00698 marker.pose.position.y = landmark.position.y
00699 marker.pose.position.z = landmark.position.z
00700 marker.scale.x = 0.5
00701 marker.scale.y = 0.5
00702 marker.scale.z = 0.5
00703 marker.color.r = 0.1
00704 marker.color.g = 0.1
00705 marker.color.b = 1.0
00706 marker.color.a = 0.6
00707 marker.lifetime = rospy.Duration(2.0)
00708 marker.frame_locked = False
00709 marker_array.markers.append(marker)
00710
00711 self.pub_map.publish(map)
00712 self.pub_landmarks.publish(marker_array)
00713
00714 self.lock.release()
00715
00716
00717 def f(self, x_1, u, t):
00718 """ The model takes as state 3D position (x, y, z) and linear
00719 velocity (vx, vy, vz). The input is the orientation
00720 (roll, pitch yaw) and the linear accelerations (ax, ay, az). """
00721
00722 roll = u[0]
00723 pitch = u[1]
00724 yaw = u[2]
00725 x1 = x_1[0]
00726 y1 = x_1[1]
00727 z1 = x_1[2]
00728 vx1 = x_1[3]
00729 vy1 = x_1[4]
00730 vz1 = x_1[5]
00731
00732 x = list(x_1)
00733
00734
00735 x[0] = x1 + cos(pitch)*cos(yaw)*(vx1*t) - cos(roll)*sin(yaw)*(vy1*t) + sin(roll)*sin(pitch)*cos(yaw)*(vy1*t) + sin(roll)*sin(yaw)*(vz1*t) + cos(roll)*sin(pitch)*cos(yaw)*(vz1*t)
00736 x[1] = y1 + cos(pitch)*sin(yaw)*(vx1*t) + cos(roll)*cos(yaw)*(vy1*t) + sin(roll)*sin(pitch)*sin(yaw)*(vy1*t) - sin(roll)*cos(yaw)*(vz1*t) + cos(roll)*sin(pitch)*sin(yaw)*(vz1*t)
00737 x[2] = z1 - sin(pitch)*(vx1*t) + sin(roll)*cos(pitch)*(vy1*t) + cos(roll)*cos(pitch)*(vz1*t)
00738 x[3] = vx1
00739 x[4] = vy1
00740 x[5] = vz1
00741 return x
00742
00743
00744 def computeA(self, u, t):
00745 """ A is the jacobian matrix of f(x) """
00746
00747 roll = u[0]
00748 pitch = u[1]
00749 yaw = u[2]
00750
00751 A = eye(6 + 3*self.number_of_landmarks)
00752 A[0,3] = cos(pitch)*cos(yaw)*t
00753 A[0,4] = -cos(roll)*sin(yaw)*t + sin(roll)*sin(pitch)*cos(yaw)*t
00754 A[0,5] = sin(roll)*sin(yaw)*t + cos(roll)*sin(pitch)*cos(yaw)*t
00755
00756 A[1,3] = cos(pitch)*sin(yaw)*t
00757 A[1,4] = cos(roll)*cos(yaw)*t + sin(roll)*sin(pitch)*sin(yaw)*t
00758 A[1,5] = -sin(roll)*cos(yaw)*t + cos(roll)*sin(pitch)*sin(yaw)*t
00759
00760 A[2,3] = -sin(pitch)*t
00761 A[2,4] = sin(roll)*cos(pitch)*t
00762 A[2,5] = cos(roll)*cos(pitch)*t
00763
00764 return A
00765
00766
00767 def computeW(self, u, t):
00768 """ The noise in the system is a term added to the acceleration:
00769 e.g. x[0] = x1 + cos(pitch)*cos(yaw)*(vx1*t + Eax) *t^2/2)-..
00770 then, dEax/dt of x[0] = cos(pitch)*cos(yaw)*t^2/2 """
00771
00772 roll = u[0]
00773 pitch = u[1]
00774 yaw = u[2]
00775 t2 = (t**2)/2
00776
00777 W = zeros((6 + 3*self.number_of_landmarks, 3))
00778 W[0,0] = cos(pitch)*cos(yaw)*t2
00779 W[0,1] = -cos(roll)*sin(yaw)*t2 + sin(roll)*sin(pitch)*cos(yaw)*t2
00780 W[0,2] = sin(roll)*sin(yaw)*t2 + cos(roll)*sin(pitch)*cos(yaw)*t2
00781
00782 W[1,0] = cos(pitch)*sin(yaw)*t2
00783 W[1,1] = cos(roll)*cos(yaw)*t2 + sin(roll)*sin(pitch)*sin(yaw)*t2
00784 W[1,2] = -sin(roll)*cos(yaw)*t2 + cos(roll)*sin(pitch)*sin(yaw)*t2
00785
00786 W[2,0] = -sin(pitch)*t2
00787 W[2,1] = sin(roll)*cos(pitch)*t2
00788 W[2,2] = cos(roll)*cos(pitch)*t2
00789
00790 W[3,0] = t
00791 W[4,1] = t
00792 W[5,2] = t
00793 return W
00794
00795
00796 def computeQ(self, q_var):
00797 Q = eye(3)
00798 return Q*q_var
00799
00800
00801 def createPoseMeasures(self, pose, covariance):
00802
00803
00804 j = 0
00805 m = 0
00806
00807
00808 z = []
00809 r_diag = []
00810 h = zeros((3, 6 + 3*self.number_of_landmarks))
00811
00812 for i in range(3):
00813 if covariance[i, i] < 999:
00814 z.append(pose[j])
00815 r_diag.append(covariance[i, i])
00816 h[m, j] = 1.0
00817 m = m + 1
00818 else:
00819 h = delete(h,j,0)
00820 j = j + 1
00821 r = eye(len(z))*r_diag
00822 v = eye(len(z))
00823 return z, r, h, v
00824
00825
00826 def velocityH(self):
00827 velocity_h = zeros((3, 6 + 3*self.number_of_landmarks))
00828 velocity_h[0, 3] = 1.0
00829 velocity_h[1, 4] = 1.0
00830 velocity_h[2, 5] = 1.0
00831 return velocity_h
00832
00833
00834 def landmarkH2(self, landmark_id, rot):
00835
00836 landmark_h = zeros((3, 6 + 3*self.number_of_landmarks))
00837 if landmark_id < self.number_of_landmarks:
00838 landmark_h[0, 0:3] = -1.0*rot[0,0:3]
00839 landmark_h[1, 0:3] = -1.0*rot[1,0:3]
00840 landmark_h[2, 0:3] = -1.0*rot[2,0:3]
00841 landmark_h[0, 6 + landmark_id*3:9 + landmark_id*3] = rot[0,0:3]
00842 landmark_h[1, 6 + landmark_id*3:9 + landmark_id*3] = rot[1,0:3]
00843 landmark_h[2, 6 + landmark_id*3:9 + landmark_id*3] = rot[2,0:3]
00844 else:
00845 rospy.loginfo('%s, Invalid landmark update. Out of range.',
00846 self.name)
00847 return landmark_h
00848
00849
00850 def landmarkH(self, landmark_id):
00851
00852
00853 landmark_h = zeros((3, 6 + 3*self.number_of_landmarks))
00854 if landmark_id < self.number_of_landmarks:
00855
00856
00857
00858 landmark_h[0, 6 + landmark_id*3] = 1.0
00859 landmark_h[1, 7 + landmark_id*3] = 1.0
00860 landmark_h[2, 8 + landmark_id*3] = 1.0
00861 else:
00862 rospy.loginfo('%s, Invalid landmark update. Out of range.',
00863 self.name)
00864 return landmark_h
00865
00866
00867 def takeCovariance(self, covariance):
00868 c = array(covariance).reshape(6, 6)
00869 return c[0:3, 0:3]
00870
00871
00872 def initEkf(self, position):
00873 self.lock.acquire()
00874 rospy.loginfo("%s, Init POSE_EKF_SLAM", self.name)
00875
00876
00877 self.x = zeros(6)
00878 self.x[0] = position.x
00879 self.x[1] = position.y
00880 self.x[2] = position.z
00881 self._x_ = array(self.x)
00882
00883
00884 self.P = eye((6))
00885 self.P[0, 0] = self.p_var[0]
00886 self.P[1, 1] = self.p_var[1]
00887 self.P[2, 2] = self.p_var[2]
00888 self.P[3, 3] = self.p_var[3]
00889 self.P[4, 4] = self.p_var[4]
00890 self.P[5, 5] = self.p_var[5]
00891 self._P_ = array(self.P)
00892
00893
00894 self.number_of_landmarks = 0
00895 self.mapped_lamdmarks = {}
00896 self.candidate_landmarks = {}
00897 self.landmark_values = {}
00898 self.landmark_last_update = {}
00899 self.filter_updates = 0
00900 self.landmark_update_topics = []
00901 self.tf_cache = dict()
00902
00903 rospy.loginfo('Init x: %s', self.x)
00904 rospy.loginfo('Init P: %s', self.P)
00905 self.lock.release()
00906
00907
00908 def addLandmark(self, x, y, z, measured_covariance):
00909 self.lock.acquire()
00910 print 'add landmark at position: ', x, y, z
00911 print 'with covariance: ', measured_covariance
00912 print 'vehicle uncertainty P: ', self.P
00913
00914
00915 new_P = eye(len(self.x) + 3)
00916 new_P[0:len(self.x), 0:len(self.x)] = self.P
00917
00918 new_P[6 + 3*self.number_of_landmarks: 9 + 3*self.number_of_landmarks, 0:3] = self.P[0:3, 0:3]
00919 new_P[0:3, 6 + 3*self.number_of_landmarks: 9 + 3*self.number_of_landmarks] = self.P[0:3, 0:3]
00920
00921 angle = tf.transformations.euler_from_quaternion(
00922 [self.imu.orientation.x, self.imu.orientation.y,
00923 self.imu.orientation.z, self.imu.orientation.w])
00924
00925 R = PyKDL.Rotation.RPY(angle[0], angle[1], angle[2])
00926 rot_m = matrix([R[0,0], R[0,1], R[0,2], R[1,0], R[1,1], R[1,2], R[2,0], R[2,1], R[2,2]]).reshape(3,3)
00927
00928 m = dot(dot(rot_m, measured_covariance), rot_m.T) + self.P[0:3, 0:3]
00929
00930 new_P[6 + 3*self.number_of_landmarks: 9 + 3*self.number_of_landmarks, 6 + 3*self.number_of_landmarks: 9 + 3*self.number_of_landmarks] = m
00931 print 'Old P matrix : ', diag(self.P)
00932 print 'New P matrix : ', diag(new_P)
00933
00934 self.P = new_P
00935
00936
00937 new_x = zeros(len(self.x) + 3)
00938 new_x[0:len(self.x)] = self.x
00939 new_x[-3] = x
00940 new_x[-2] = y
00941 new_x[-1] = z
00942 self.x = new_x
00943
00944
00945 self.number_of_landmarks = self.number_of_landmarks + 1
00946
00947 self.lock.release()
00948
00949
00950 def prediction(self, u, t):
00951 A = self.computeA(u, t)
00952 W = self.computeW(u, t)
00953 self._x_ = self.f(self.x, u, t)
00954 self._P_ = dot(dot(A, self.P), A.T) + dot(dot(W, self.Q), W.T)
00955
00956
00957
00958
00959
00960 def updatePrediction(self):
00961 self.lock.acquire()
00962 self.x = self._x_
00963 self.P = self._P_
00964 self.lock.release()
00965
00966
00967 def getStateVector(self):
00968 return self.x
00969
00970
00971 def getConfig(self):
00972 if rospy.has_param('pose_ekf_slam/world_frame_name') :
00973 self.world_frame_name = rospy.get_param(
00974 'pose_ekf_slam/world_frame_name')
00975 else:
00976 rospy.logfatal('pose_ekf_slam/world_frame_name')
00977
00978 if rospy.has_param('pose_ekf_slam/robot_frame_name') :
00979 self.robot_frame_name = rospy.get_param(
00980 'pose_ekf_slam/robot_frame_name')
00981 else:
00982 rospy.logfatal('pose_ekf_slam/robot_frame_name')
00983
00984
00985 def mahalanobisDistance(self, z, h, r):
00986
00987
00988
00989
00990
00991
00992 v = z - dot(h, self.x)
00993
00994 S = matrix(dot(dot(h, self.P), h.T) + r)
00995
00996 d = dot(dot(v.T, S.I), v)
00997
00998 return sqrt(d[0,0])
00999
01000
01001 if __name__ == '__main__':
01002 try:
01003
01004 rospy.init_node('pose_ekf_slam')
01005 pose_3d_ekf = PoseEkfSlam(rospy.get_name(),
01006 [0.01, 0.01, 0.5, 0.02, 0.02, 0.02],
01007 [0.05, 0.05, 0.05])
01008
01009
01010
01011
01012
01013
01014
01015
01016 rospy.spin()
01017 except rospy.ROSInterruptException:
01018 pass