69 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
		
		
			
		
	
	
			69 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
|  | from __future__ import print_function | ||
|  | import gtsam | ||
|  | import math | ||
|  | import numpy as np | ||
|  | 
 | ||
|  | def Vector3(x, y, z): return np.array([x, y, z]) | ||
|  | 
 | ||
|  | # 1. Create a factor graph container and add factors to it | ||
|  | graph = gtsam.NonlinearFactorGraph() | ||
|  | 
 | ||
|  | # 2a. Add a prior on the first pose, setting it to the origin | ||
|  | # A prior factor consists of a mean and a noise model (covariance matrix) | ||
|  | priorNoise = gtsam.noiseModel.Diagonal.Sigmas(Vector3(0.3, 0.3, 0.1)) | ||
|  | graph.add(gtsam.PriorFactorPose2(1, gtsam.Pose2(0, 0, 0), priorNoise)) | ||
|  | 
 | ||
|  | # For simplicity, we will use the same noise model for odometry and loop closures | ||
|  | model = gtsam.noiseModel.Diagonal.Sigmas(Vector3(0.2, 0.2, 0.1)) | ||
|  | 
 | ||
|  | # 2b. Add odometry factors | ||
|  | # Create odometry (Between) factors between consecutive poses | ||
|  | graph.add(gtsam.BetweenFactorPose2(1, 2, gtsam.Pose2(2, 0, 0), model)) | ||
|  | graph.add(gtsam.BetweenFactorPose2(2, 3, gtsam.Pose2(2, 0, math.pi / 2), model)) | ||
|  | graph.add(gtsam.BetweenFactorPose2(3, 4, gtsam.Pose2(2, 0, math.pi / 2), model)) | ||
|  | graph.add(gtsam.BetweenFactorPose2(4, 5, gtsam.Pose2(2, 0, math.pi / 2), model)) | ||
|  | 
 | ||
|  | # 2c. Add the loop closure constraint | ||
|  | # This factor encodes the fact that we have returned to the same pose. In real | ||
|  | # systems, these constraints may be identified in many ways, such as appearance-based | ||
|  | # techniques with camera images. We will use another Between Factor to enforce this constraint: | ||
|  | graph.add(gtsam.BetweenFactorPose2(5, 2, gtsam.Pose2(2, 0, math.pi / 2), model)) | ||
|  | graph.print("\nFactor Graph:\n")  # print | ||
|  | 
 | ||
|  | # 3. Create the data structure to hold the initialEstimate estimate to the | ||
|  | # solution. For illustrative purposes, these have been deliberately set to incorrect values | ||
|  | initialEstimate = gtsam.Values() | ||
|  | initialEstimate.insert(1, gtsam.Pose2(0.5, 0.0, 0.2)) | ||
|  | initialEstimate.insert(2, gtsam.Pose2(2.3, 0.1, -0.2)) | ||
|  | initialEstimate.insert(3, gtsam.Pose2(4.1, 0.1, math.pi / 2)) | ||
|  | initialEstimate.insert(4, gtsam.Pose2(4.0, 2.0, math.pi)) | ||
|  | initialEstimate.insert(5, gtsam.Pose2(2.1, 2.1, -math.pi / 2)) | ||
|  | initialEstimate.print("\nInitial Estimate:\n")  # print | ||
|  | 
 | ||
|  | # 4. Optimize the initial values using a Gauss-Newton nonlinear optimizer | ||
|  | # The optimizer accepts an optional set of configuration parameters, | ||
|  | # controlling things like convergence criteria, the type of linear | ||
|  | # system solver to use, and the amount of information displayed during | ||
|  | # optimization. We will set a few parameters as a demonstration. | ||
|  | parameters = gtsam.GaussNewtonParams() | ||
|  | 
 | ||
|  | # Stop iterating once the change in error between steps is less than this value | ||
|  | parameters.relativeErrorTol = 1e-5 | ||
|  | # Do not perform more than N iteration steps | ||
|  | parameters.maxIterations = 100 | ||
|  | # Create the optimizer ... | ||
|  | optimizer = gtsam.GaussNewtonOptimizer(graph, initialEstimate, parameters) | ||
|  | # ... and optimize | ||
|  | result = optimizer.optimize() | ||
|  | result.print("Final Result:\n") | ||
|  | 
 | ||
|  | # 5. Calculate and print marginal covariances for all variables | ||
|  | marginals = gtsam.Marginals(graph, result) | ||
|  | print("x1 covariance:\n", marginals.marginalCovariance(1)) | ||
|  | print("x2 covariance:\n", marginals.marginalCovariance(2)) | ||
|  | print("x3 covariance:\n", marginals.marginalCovariance(3)) | ||
|  | print("x4 covariance:\n", marginals.marginalCovariance(4)) | ||
|  | print("x5 covariance:\n", marginals.marginalCovariance(5)) | ||
|  | 
 | ||
|  | 
 |