pose_ekf_slam.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # ROS imports
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 # from geometry_msgs.msg import Point
00014 from visualization_msgs.msg import Marker, MarkerArray
00015 
00016 # Custom msgs
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 # More imports
00022 from numpy import delete, dot, zeros, eye, cos, sin, array, diag, sqrt
00023 # from numpy import linalg
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         # Output odometry
00038         self.odom = Odometry()
00039         
00040         # Input last Imu
00041         self.imu = Imu()
00042         
00043         # Global vars
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         # Init state, P, Landmarks & TFs
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         # Init Q
00059         self.Q = self.computeQ(q_var)
00060          
00061         # Filter status
00062         self.is_ekf_init = False
00063         self.is_imu_init = False
00064         
00065         # Create publisher
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         # Create Subscriber Updates (z)
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         # Create Subscriber Inputs (u)
00082         rospy.Subscriber("/pose_ekf_slam/imu_input", Imu, self.imuInput)
00083  
00084         # Create Service SetPosition
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         # rospy.loginfo("%s: Search for new landmark updates ...", self.name)
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         # Init filter
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             # Better publish imu in robot_frame! 
00141             if data.header.frame_id in self.tf_cache:
00142                 # Recover TF from cached TF                
00143                 trans = self.tf_cache[data.header.frame_id][0]
00144                 rot = self.tf_cache[data.header.frame_id][1]
00145             
00146                 # Transform orientations
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                 # Transform orientation covariance
00154                 # TODO: To be check and slow! 
00155                 o_cov = mt.orientationCov(
00156                     array(data.orientation_covariance).reshape(3,3),
00157                     self.imu.orientation,
00158                     rot)
00159                     
00160                 # TODO: Check that!
00161                 self.imu.orientation_covariance = o_cov.ravel()                
00162                 
00163                 # Transform angular_velocity
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                 # Transform angular velocity covariance
00170                 w_cov = mt.angularVelocityCov(
00171                     array(data.angular_velocity_covariance).reshape(3,3), 
00172                     rot)
00173                     
00174                 # TODO: Check that!
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             # Copy input orientation/angular_velocity 
00202             # into odometry message to be published
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             # Make a prediction
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         # Try to make a prediction if 'u' is present and the filter is init
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                     # Recover TF from cached TF                
00234                     trans = self.tf_cache[pose_msg.header.frame_id][0]
00235                     rot = self.tf_cache[pose_msg.header.frame_id][1]
00236                     
00237                     # Transform position
00238                     p = mt.position(pose_msg.pose.pose.position, 
00239                                     self.imu.orientation, 
00240                                     trans)
00241                                     
00242                     # Transform position covariance       
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                     # Wait for the tf between the pose sensor and the robot
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                         # Pre cache TF
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                 # Create measurement z and matrices r & h
00282                 z, r, h, v = self.createPoseMeasures(
00283                                   p, covariance_pose)
00284     
00285                 # Compute Filter Update
00286                 self.applyUpdate(z, r, h, v, 10.0)
00287                 
00288                 # Publish Data
00289                 self.publishData(pose_msg.header.stamp)
00290         # else:
00291         #     rospy.logerr('%s, Impossible to apply pose_update!', self.name)
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                     # Recover TF from cached TF                
00305                     trans = self.tf_cache[twist_msg.header.frame_id][0]
00306                     rot = self.tf_cache[twist_msg.header.frame_id][1]
00307                     
00308                     # Transform linear velocity
00309                     v = mt.linearVelocity(twist_msg.twist.twist.linear, 
00310                                           self.imu.angular_velocity, 
00311                                           trans, rot)
00312                                           
00313                     # Transform linear velocity covariance     
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                     # Wait for the tf between the velocity sensor and the robot
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                         # Pre cache TF
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                 # Create measurement z and R matrix
00350                 z = array([v[0], v[1], v[2]])
00351                 velocity_h = self.velocityH()
00352                 
00353                 # Compute Filter Update
00354                 self.applyUpdate(z, velocity_r, velocity_h, eye(3), 8.0)
00355          
00356                 # Publish Data           
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         # print 'Received message: ', landmark_msg
00369         # print 'from: ', topic_name
00370         
00371         ################## Check landmark orientation #######################
00372         #                                                                   #
00373         # angle = tf.transformations.euler_from_quaternion([landmark_msg.pose.pose.orientation.x,
00374         #                                                   landmark_msg.pose.pose.orientation.y,
00375         #                                                   landmark_msg.pose.pose.orientation.z,
00376         #                                                   landmark_msg.pose.pose.orientation.w])        
00377         # for i in [0,2]:
00378         #     if abs(angle[i]) < deg2rad(35):
00379         #         pass # Around 0 degrees [-35, 35]
00380         #     elif abs(abs(angle[i]) - math.pi) < deg2rad(35):
00381         #         pass # Around 180 degress [145, 215]
00382         #     else:
00383         #         print 'invalid landmark ', topic_name
00384         #         return False
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                     # print 'landmark ', topic_name, ' mapped and TF cached'
00393                     trans = self.tf_cache[landmark_msg.header.frame_id][0]
00394                     rot = self.tf_cache[landmark_msg.header.frame_id][1]
00395                     
00396                     # Transform landmark pose to vehicle frame
00397                     pose = mt.landmarkPosition(
00398                         landmark_msg.pose.pose.position, rot, trans)                    
00399                     
00400                     # Create measurement z and R matrix
00401                     z = array([pose[0], pose[1], pose[2]])
00402                     # print 'measured z: ', z
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                     # Compute Filter Update
00420                     self.applyUpdate(z, r, h, eye(3), 9.0)
00421                     
00422                     # Save last update time
00423                     self.landmark_last_update[self.mapped_lamdmarks[topic_name]] = landmark_msg.header.stamp
00424                     
00425                     # Publish Data           
00426                     self.publishData(landmark_msg.header.stamp)
00427                 else:
00428                     # print 'landmark ', topic_name, ' waiting for TF...'
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                     # Add candidate into list
00440                     [trans, rot] = self.getTF(self.world_frame_name,
00441                                           landmark_msg.header.frame_id,
00442                                           rospy.Time())
00443                     if trans != None:
00444                         ## Transform landmark pose to world frame
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                                 # Get landmark covariance
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                     # Add first candidate into list
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                         ## Transform landmark pose to world frame
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, #rospy.Time(), 
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         # if d == 6.0:
00528             # print ' =========== Apply update: ===================\n \n \n \n'       
00529             # print 'z: ', z
00530             # print 'd: ', d
00531             # print '_P_: ', self._P_
00532             # print 'P: ', diag(self.P)
00533         
00534         distance = self.mahalanobisDistance(z, h, r)
00535         # print ' --> distance: ', distance
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             # Check covariabce matrix integrity
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             # Build input array
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             # Take current time/period
00571             t = (now - self.last_prediction).to_sec()
00572             
00573             self.last_prediction = now
00574             if t > 0.0 and t < 1.0:
00575                 # Make a prediction                
00576                 self.lock.acquire()
00577                 self.prediction(u, t)
00578                 self.lock.release()
00579                 return True
00580             # All the messages receiverd during the last 50 ms use the 
00581             # same prediction
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             # Create header
00598             self.odom.header.stamp = now
00599             self.odom.header.frame_id = self.world_frame_name
00600             self.odom.child_frame_id = '' # self.world_frame_name
00601             
00602             # Pose
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             # Pose covariance
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             # Twist
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             # Twist covariance
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             # Publish Localization
00631             self.pub_odom.publish(self.odom)
00632             
00633             # Publish TF
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             # Publish covariance marker
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 # SPHERE
00652             marker.action = 0 # Add/Modify an object
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             # Publish Mapping
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                     # Create Map
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                     # Create Markers
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 # CUBE
00696                     marker.action = 0 # Add/Modify an object
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         # Compute Prediction Model with constant velocity
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         # TODO: WARNING! This functions only uses the diagonal values 
00803         # in the covariance matrix. We can lose information!
00804         j = 0 # counter
00805         m = 0 # counter 2
00806         
00807         # Init values
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         # print 'landmark_id: ', landmark_id
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         # print 'landmark_id: ', landmark_id
00852         
00853         landmark_h = zeros((3, 6 + 3*self.number_of_landmarks))
00854         if landmark_id < self.number_of_landmarks:
00855             #landmark_h[0, 0] = 1.0
00856             #landmark_h[1, 1] = 1.0
00857             #landmark_h[2, 2] = 1.0
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         # Init state vector
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         # Init P 
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         # Init landmarks, TFs and others        
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         # Increase P matrix
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        # Increase state vector
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         # Increase number of landmarks registered
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         # print diag(dot(dot(W, self.Q), W.T))
00956         # print random.multivariate_normal(zeros(6), dot(dot(W, self.Q), W.T))
00957             
00958         # print diag(self._P_) - diag(self.P) < 0
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 #        print 'x: ', self.x
00987 #        print 'P: ', self.P
00988 #        print 'h: ', h
00989 #        print 'z: ', z
00990 #        print 'r: ', r
00991         
00992         v = z - dot(h, self.x)
00993         # print 'v: ', v
00994         S = matrix(dot(dot(h, self.P), h.T) + r)
00995         # print 'S: ', S
00996         d = dot(dot(v.T, S.I), v)
00997         # print 'Mahalanobis distance: ', d
00998         return sqrt(d[0,0])
00999     
01000     
01001 if __name__ == '__main__':
01002     try:
01003         # Init node
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         # Initialize vehicle pose (should be done by the "navigator" node) 
01010 #        position = Point()
01011 #        position.x = 0.
01012 #        position.y = 0.
01013 #        position.z = 0.
01014 #        pose_3d_ekf.initEkf(position)
01015         
01016         rospy.spin()
01017     except rospy.ROSInterruptException: 
01018         pass


pose_ekf_slam
Author(s): Narcis palomeras
autogenerated on Mon Jan 6 2014 11:32:43