Overview¶
The GPSFactor family provides factors for incorporating Global Positioning System (GPS) measurements into a GTSAM factor graph. GPS typically provides measurements of position in Latitude/Longitude/Height. These GPS factors, however, assume the GPS measurement has been converted into a local Cartesian navigation frame (e.g., ENU, NED, or ECEF).
Different variants exist to handle:
State type:
Pose3orNavState.Lever arm: Whether the GPS antenna is offset from the body frame origin.
Calibration: Whether the lever arm itself is being estimated.
Factor Variants¶
For Pose3 states:
GPSFactor:Connects to:
Pose3key.Assumes: Zero lever arm (GPS measurement corresponds directly to the
Pose3origin).Measurement: 3D position (
Point3) in the navigation frame.Error:
GPSFactorArm:Connects to:
Pose3key.Assumes: Known, fixed lever arm (
bLvector in the body frame).Measurement: 3D position (
Point3) in the navigation frame.Error:
GPSFactorArmCalib:Connects to:
Pose3key,Point3key (for the lever arm).Assumes: Lever arm (
bL) is unknown and estimated.Measurement: 3D position (
Point3) in the navigation frame.Error:
For NavState states:
GPSFactor2: LikeGPSFactorbut connects to aNavStatekey.GPSFactor2Arm: LikeGPSFactorArmbut connects to aNavStatekey.GPSFactor2ArmCalib: LikeGPSFactorArmCalibbut connects to aNavStatekey and aPoint3lever arm key.
(The ‘2’ suffix historically denoted factors using NavState).
Mathematical Formulation (GPSFactorArm Example)¶
Let:
be the
Pose3state (body frame in navigation frame ).be the known lever arm vector from the body origin to the GPS antenna, expressed in the body frame.
be the measured GPS position in the navigation frame.
The predicted position of the GPS antenna in the navigation frame is:
The factor’s 3D error vector is the difference between the predicted antenna position and the measured GPS position:
The noise model reflects the uncertainty of the measurement in the navigation frame.
Key Functionality / API (Common Patterns)¶
Constructor: Takes the relevant key(s), the measured
Point3positiongpsIn(in nav frame), the noise model, and potentially theleverArm(Point3in body frame).evaluateError(...): Calculates the 3D error vector based on the connected state variable(s) and the measurement.measurementIn(): Returns the storedPoint3measurement.leverArm()(For Arm variants): Returns the storedPoint3lever arm.
Usage Example (GPSFactor and GPSFactorArm)¶
Assume we have a GPS reading converted to a local ENU frame.
import gtsam
import numpy as np
from gtsam.symbol_shorthand import X, L # Pose key, Lever arm key
# --- Setup ---
pose_key = X(0)
# GPS Measurement in local ENU frame (meters)
gps_measurement_enu = gtsam.Point3(10.5, 20.2, 5.1)
# Noise model for GPS measurement (e.g., 0.5m horizontal, 1.0m vertical sigma)
gps_sigmas = np.array([0.5, 0.5, 1.0])
gps_noise_model = gtsam.noiseModel.Diagonal.Sigmas(gps_sigmas)
# --- Scenario 1: GPSFactor (Zero Lever Arm) ---
gps_factor_zero_arm = gtsam.GPSFactor(pose_key, gps_measurement_enu, gps_noise_model)
print("Created GPSFactor (zero lever arm):")
gps_factor_zero_arm.print()
# Evaluate error: Error is difference between pose translation and measurement
test_pose1 = gtsam.Pose3(gtsam.Rot3(), gtsam.Point3(10.0, 20.0, 5.0))
error1 = gps_factor_zero_arm.evaluateError(test_pose1)
print("\nGPSFactor Error:", error1) # Expected: [0.5, 0.2, 0.1]
# --- Scenario 2: GPSFactorArm (Known Lever Arm) ---
# Assume antenna is 10cm behind and 5cm above the body origin
lever_arm_body = gtsam.Point3(-0.1, 0.0, 0.05)
gps_factor_with_arm = gtsam.GPSFactorArm(pose_key, gps_measurement_enu,
lever_arm_body, gps_noise_model)
print("\nCreated GPSFactorArm:")
gps_factor_with_arm.print()
# Evaluate error: Error is difference between (pose + R*lever_arm) and measurement
# Use the same test pose as before
predicted_antenna_pos = test_pose1.transformFrom(lever_arm_body)
error2 = gps_factor_with_arm.evaluateError(test_pose1)
print("\nGPSFactorArm Error:", error2)
print(" ( Pose: ", test_pose1.translation() , ")")
print(" ( Lever Arm: ", lever_arm_body, ")")
print(" ( Predicted Antenna Pos: ", predicted_antenna_pos, ")")
print(" ( Measured GPS Pos: ", gps_measurement_enu, ")")
# Expected: predicted_antenna_pos - gps_measurement_enu
# = [9.9, 20.0, 5.05] - [10.5, 20.2, 5.1] = [-0.6, -0.2, -0.05]
# --- Scenario 3: GPSFactorArmCalib (Example Setup - Not Evaluated) ---
lever_arm_key = L(0) # Key for the unknown lever arm
gps_factor_calib = gtsam.GPSFactorArmCalib(pose_key, lever_arm_key,
gps_measurement_enu, gps_noise_model)
print("\nCreated GPSFactorArmCalib:")
gps_factor_calib.print()Created GPSFactor (zero lever arm):
GPSFactor on x0
GPS measurement: 10.5
20.2
5.1
noise model: diagonal sigmas [0.5; 0.5; 1];
GPSFactor Error: [-0.5 -0.2 -0.1]
Created GPSFactorArm:
GPSFactorArm on x0
GPS measurement: 10.5 20.2 5.1
Lever arm: -0.1 0 0.05
noise model: diagonal sigmas [0.5; 0.5; 1];
GPSFactorArm Error: [-0.6 -0.2 -0.05]
( Pose: [10. 20. 5.] )
( Lever Arm: [-0.1 0. 0.05] )
( Predicted Antenna Pos: [ 9.9 20. 5.05] )
( Measured GPS Pos: [10.5 20.2 5.1] )
Created GPSFactorArmCalib:
GPSFactorArmCalib on x0
GPS measurement: 10.5 20.2 5.1
noise model: diagonal sigmas [0.5; 0.5; 1];
Coordinate Frames¶
It’s crucial to ensure consistency in coordinate frames:
GPS Measurement (
gpsIn): Must be provided in the chosen local Cartesian navigation frame (e.g., ENU, NED).Lever Arm (
leverArm): Must be provided in the body frame.Pose/NavState Variables: Represent the pose of the body frame in the navigation frame ().