| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
					
						
							|  |  |  | % GTSAM Copyright 2010, Georgia Tech Research Corporation, | 
					
						
							|  |  |  | % Atlanta, Georgia 30332-0415 | 
					
						
							|  |  |  | % All Rights Reserved | 
					
						
							|  |  |  | % Authors: Frank Dellaert, et al. (see THANKS for the full author list) | 
					
						
							|  |  |  | % | 
					
						
							|  |  |  | % See LICENSE for the license information | 
					
						
							|  |  |  | % | 
					
						
							|  |  |  | % @brief Read Robotics Institute range-only Plaza2 dataset and do iSAM | 
					
						
							|  |  |  | % @author Frank Dellaert | 
					
						
							|  |  |  | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% preliminaries | 
					
						
							|  |  |  | clear | 
					
						
							|  |  |  | import gtsam.* | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% Find and load data file | 
					
						
							|  |  |  | % data available at http://www.frc.ri.cmu.edu/projects/emergencyresponse/RangeData/ | 
					
						
							|  |  |  | % Datafile format (from http://www.frc.ri.cmu.edu/projects/emergencyresponse/RangeData/log.html) | 
					
						
							|  |  |  | % GT: Groundtruth path from GPS | 
					
						
							|  |  |  | %    Time (sec)	X_pose (m)	Y_pose (m)	Heading (rad) | 
					
						
							|  |  |  | % DR: Odometry Input (delta distance traveled and delta heading change) | 
					
						
							|  |  |  | %    Time (sec)	Delta Dist. Trav. (m)	Delta Heading (rad) | 
					
						
							|  |  |  | % DRp: Dead Reckoned Path from Odometry | 
					
						
							|  |  |  | %    Time (sec)	X_pose (m)	Y_pose (m)	Heading (rad) | 
					
						
							|  |  |  | % TL: Surveyed Node Locations | 
					
						
							|  |  |  | %    Time (sec)	X_pose (m)	Y_pose (m) | 
					
						
							|  |  |  | % TD | 
					
						
							|  |  |  | %    Time (sec)	Sender / Antenna ID	Receiver Node ID	Range (m) | 
					
						
							| 
									
										
										
										
											2013-06-26 01:12:01 +08:00
										 |  |  | if true % switch between data files | 
					
						
							| 
									
										
										
										
											2013-06-21 22:17:58 +08:00
										 |  |  |   datafile = findExampleDataFile('Plaza1_.mat'); | 
					
						
							|  |  |  |   headingOffset=0; | 
					
						
							|  |  |  |   minK=200; % minimum number of range measurements to process initially | 
					
						
							| 
									
										
										
										
											2013-06-22 08:49:00 +08:00
										 |  |  |   incK=5; % minimum number of range measurements to process after | 
					
						
							| 
									
										
										
										
											2013-06-21 22:17:58 +08:00
										 |  |  | else | 
					
						
							|  |  |  |   datafile = findExampleDataFile('Plaza2_.mat'); | 
					
						
							|  |  |  |   headingOffset=pi; | 
					
						
							|  |  |  |   minK=150; % needs less for init | 
					
						
							| 
									
										
										
										
											2013-06-22 08:49:00 +08:00
										 |  |  |   incK=25; % minimum number of range measurements to process after | 
					
						
							| 
									
										
										
										
											2013-06-21 22:17:58 +08:00
										 |  |  | end | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  | load(datafile) | 
					
						
							|  |  |  | M=size(DR,1); | 
					
						
							|  |  |  | K=size(TD,1); | 
					
						
							|  |  |  | sigmaR = 100; % range standard deviation | 
					
						
							|  |  |  | sigmaInitial = 1; % draw initial landmark guess from Gaussian | 
					
						
							|  |  |  | useGroundTruth = false; | 
					
						
							|  |  |  | useRobust=true; | 
					
						
							|  |  |  | addRange=true; | 
					
						
							|  |  |  | batchInitialization=true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% Set Noise parameters | 
					
						
							|  |  |  | noiseModels.prior = noiseModel.Diagonal.Sigmas([1 1 pi]'); | 
					
						
							|  |  |  | noiseModels.pointPrior = noiseModel.Diagonal.Sigmas([1 1]'); | 
					
						
							|  |  |  | noiseModels.odometry = noiseModel.Diagonal.Sigmas([0.05 0.01 0.2]'); | 
					
						
							|  |  |  | if useRobust | 
					
						
							|  |  |  |   base = noiseModel.mEstimator.Tukey(15); | 
					
						
							|  |  |  |   noiseModels.range = noiseModel.Robust(base,noiseModel.Isotropic.Sigma(1, sigmaR)); | 
					
						
							|  |  |  | else | 
					
						
							|  |  |  |   noiseModels.range = noiseModel.Isotropic.Sigma(1, sigmaR); | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% Initialize iSAM | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  | isam = ISAM2; | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | %% Add prior on first pose | 
					
						
							| 
									
										
										
										
											2013-06-21 22:17:58 +08:00
										 |  |  | pose0 = Pose2(GT(1,2),GT(1,3),headingOffset+GT(1,4)); | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  | newFactors = NonlinearFactorGraph; | 
					
						
							| 
									
										
										
										
											2013-06-26 01:12:01 +08:00
										 |  |  | if ~addRange || ~useGroundTruth | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |   newFactors.add(PriorFactorPose2(0,pose0,noiseModels.prior)); | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | initial = Values; | 
					
						
							|  |  |  | initial.insert(0,pose0); | 
					
						
							|  |  |  | odo = Values; | 
					
						
							|  |  |  | odo.insert(0,pose0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  | %% initialize points | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  | if addRange | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |   landmarkEstimates = Values; | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |   for i=1:size(TL,1) | 
					
						
							|  |  |  |     j=TL(i,1); | 
					
						
							|  |  |  |     if useGroundTruth | 
					
						
							|  |  |  |       Lj = Point2(TL(i,2),TL(i,3)); | 
					
						
							|  |  |  |       newFactors.add(PriorFactorPoint2(symbol('L',j),Lj,noiseModels.pointPrior)); | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |       Lj = Point2(sigmaInitial*randn,sigmaInitial*randn); | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |     initial.insert(symbol('L',j),Lj); | 
					
						
							|  |  |  |     landmarkEstimates.insert(symbol('L',j),Lj); | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |   end | 
					
						
							|  |  |  |   XY = utilities.extractPoint2(initial); | 
					
						
							|  |  |  |   plot(XY(:,1),XY(:,2),'g*'); | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% Loop over odometry | 
					
						
							|  |  |  | tic | 
					
						
							|  |  |  | k = 1; % range measurement counter | 
					
						
							|  |  |  | update = false; | 
					
						
							|  |  |  | lastPose = pose0; | 
					
						
							|  |  |  | odoPose = pose0; | 
					
						
							|  |  |  | countK = 0; | 
					
						
							|  |  |  | for i=1:M % M | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   % get odometry measurement | 
					
						
							|  |  |  |   t = DR(i,1); | 
					
						
							|  |  |  |   distance_traveled = DR(i,2); | 
					
						
							|  |  |  |   delta_heading = DR(i,3); | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   % add odometry factor | 
					
						
							|  |  |  |   odometry = Pose2(distance_traveled,0,delta_heading); | 
					
						
							|  |  |  |   newFactors.add(BetweenFactorPose2(i-1, i, odometry, noiseModels.odometry)); | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   % predict pose and update odometry | 
					
						
							|  |  |  |   predictedOdo = odoPose.compose(odometry); | 
					
						
							|  |  |  |   odoPose = predictedOdo; | 
					
						
							|  |  |  |   odo.insert(i,predictedOdo); | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   % predict pose and add as initial estimate | 
					
						
							|  |  |  |   predictedPose = lastPose.compose(odometry); | 
					
						
							|  |  |  |   lastPose = predictedPose; | 
					
						
							|  |  |  |   initial.insert(i,predictedPose); | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |   landmarkEstimates.insert(i,predictedPose); | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |    | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |   % Check if there are range factors to be added | 
					
						
							|  |  |  |   while k<=K && t>=TD(k,1) | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |     j = TD(k,3); | 
					
						
							|  |  |  |     range = TD(k,4); | 
					
						
							|  |  |  |     if addRange | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |       factor = RangeFactorPosePoint2(i, symbol('L',j), range, noiseModels.range); | 
					
						
							|  |  |  |       % Throw out obvious outliers based on current landmark estimates | 
					
						
							|  |  |  |       error=factor.unwhitenedError(landmarkEstimates); | 
					
						
							|  |  |  |       if k<=minK || abs(error)<5 | 
					
						
							|  |  |  |         newFactors.add(factor); | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |     end | 
					
						
							|  |  |  |     k=k+1; countK=countK+1; update = true; | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2013-06-21 22:17:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |   % Check whether to update iSAM 2 | 
					
						
							|  |  |  |   if update && k>minK && countK>incK | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |     if batchInitialization % Do a full optimize for first minK ranges | 
					
						
							|  |  |  |       tic | 
					
						
							|  |  |  |       batchOptimizer = LevenbergMarquardtOptimizer(newFactors, initial); | 
					
						
							|  |  |  |       initial = batchOptimizer.optimize(); | 
					
						
							| 
									
										
										
										
											2013-06-21 22:17:58 +08:00
										 |  |  |       toc | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |       batchInitialization = false; % only once | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     isam.update(newFactors, initial); | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |     result = isam.calculateEstimate(); | 
					
						
							|  |  |  |     lastPose = result.at(i); | 
					
						
							|  |  |  |     % update landmark estimates | 
					
						
							|  |  |  |     if addRange | 
					
						
							|  |  |  |       landmarkEstimates = Values; | 
					
						
							|  |  |  |       for jj=1:size(TL,1) | 
					
						
							|  |  |  |         j=TL(jj,1); | 
					
						
							|  |  |  |         key = symbol('L',j); | 
					
						
							|  |  |  |         landmarkEstimates.insert(key,result.at(key)); | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |     end | 
					
						
							|  |  |  |     newFactors = NonlinearFactorGraph; | 
					
						
							|  |  |  |     initial = Values; | 
					
						
							|  |  |  |     countK = 0; | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   % visualize | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |   if mod(i,50)==0 && k>minK | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |     figure(1);clf;hold on | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     % odometry | 
					
						
							|  |  |  |     XYT = utilities.extractPose2(odo); | 
					
						
							|  |  |  |     plot(XYT(:,1),XYT(:,2),'y-'); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     % lin point | 
					
						
							|  |  |  |     lin = isam.getLinearizationPoint(); | 
					
						
							|  |  |  |     XYT = utilities.extractPose2(lin); | 
					
						
							|  |  |  |     plot(XYT(:,1),XYT(:,2),'r.'); | 
					
						
							|  |  |  |     XY = utilities.extractPoint2(lin); | 
					
						
							|  |  |  |     plot(XY(:,1),XY(:,2),'r*'); | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |     % result | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |     result = isam.calculateEstimate(); | 
					
						
							|  |  |  |     XYT = utilities.extractPose2(result); | 
					
						
							|  |  |  |     plot(XYT(:,1),XYT(:,2),'k-'); | 
					
						
							|  |  |  |     XY = utilities.extractPoint2(result); | 
					
						
							|  |  |  |     plot(XY(:,1),XY(:,2),'k*'); | 
					
						
							|  |  |  |     axis equal | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  |     %     pause | 
					
						
							| 
									
										
										
										
											2013-06-20 14:51:00 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | toc | 
					
						
							| 
									
										
										
										
											2013-06-21 03:45:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | %% Plot ground truth as well | 
					
						
							|  |  |  | plot(GT(:,2),GT(:,3),'g-'); | 
					
						
							|  |  |  | plot(TL(:,2),TL(:,3),'g*'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 |