{
"cells": [
{
"cell_type": "markdown",
"id": "4e5048be",
"metadata": {},
"source": [
"
"
]
},
{
"cell_type": "markdown",
"id": "9a1469af",
"metadata": {},
"source": [
"# Structure from Motion\n",
"\n",
"Structure from Motion (SFM or SfM) is very similar to SLAM, except we are trying to estimate the pose of several cameras observing a set of unknown 3D landmarks.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "1b892f2b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ERROR: unknown command \"-\"\n",
"Note: you may need to restart the kernel to use updated packages.\n"
]
}
],
"source": [
"%pip -q install gtbook # also installs latest gtsam pre-release"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "5c40436d",
"metadata": {},
"outputs": [],
"source": [
"import gtsam\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"from gtsam import symbol_shorthand\n",
"L = symbol_shorthand.L\n",
"X = symbol_shorthand.X\n",
"\n",
"from gtsam.examples import SFMdata\n",
"from gtsam import (Cal3_S2, DoglegOptimizer,\n",
" GenericProjectionFactorCal3_S2, Marginals,\n",
" NonlinearFactorGraph, PinholeCameraCal3_S2, Point3,\n",
" Pose3, PriorFactorPoint3, PriorFactorPose3, Rot3, Values)\n",
"from gtsam.utils import plot\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"id": "d00bd20c",
"metadata": {},
"source": [
"Camera observations of landmarks (i.e. pixel coordinates) will be stored as Point2 (x, y).\n",
"\n",
"Each variable in the system (poses and landmarks) must be identified with a unique key.\n",
"We can either use simple integer keys (1, 2, 3, ...) or symbols (X1, X2, L1).\n",
"Here we will use Symbols\n",
"\n",
"In GTSAM, measurement functions are represented as 'factors'. Several common factors\n",
"have been provided with the library for solving robotics/SLAM/Bundle Adjustment problems.\n",
"Here we will use Projection factors to model the camera's landmark observations.\n",
"Also, we will initialize the robot at some location using a Prior factor.\n",
"\n",
"When the factors are created, we will add them to a Factor Graph. As the factors we are using\n",
"are nonlinear factors, we will need a Nonlinear Factor Graph.\n",
"\n",
"Finally, once all of the factors have been added to our factor graph, we will want to\n",
"solve/optimize to graph to find the best (Maximum A Posteriori) set of variable values.\n",
"GTSAM includes several nonlinear optimizers to perform this step. Here we will use a\n",
"trust-region method known as Powell's Degleg\n",
"\n",
"The nonlinear solvers within GTSAM are iterative solvers, meaning they linearize the\n",
"nonlinear functions around an initial linearization point, then solve the linear system\n",
"to update the linearization point. This happens repeatedly until the solver converges\n",
"to a consistent set of variable values. This requires us to specify an initial guess\n",
"for each variable, held in a Values container.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "6392d720",
"metadata": {},
"outputs": [],
"source": [
"\n",
"# Define the camera calibration parameters\n",
"K = Cal3_S2(50.0, 50.0, 0.0, 50.0, 50.0)\n",
"\n",
"# Define the camera observation noise model\n",
"measurement_noise = gtsam.noiseModel.Isotropic.Sigma(\n",
" 2, 1.0) # one pixel in u and v\n",
"\n",
"# Create the set of ground-truth landmarks\n",
"points = SFMdata.createPoints()\n",
"\n",
"# Create the set of ground-truth poses\n",
"poses = SFMdata.createPoses(K)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "e5b65c9c",
"metadata": {},
"outputs": [],
"source": [
"# Create a factor graph\n",
"graph = NonlinearFactorGraph()\n",
"\n",
"# Add a prior on pose x1. This indirectly specifies where the origin is.\n",
"# 0.3 rad std on roll,pitch,yaw and 0.1m on x,y,z\n",
"pose_noise = gtsam.noiseModel.Diagonal.Sigmas(\n",
" np.array([0.3, 0.3, 0.3, 0.1, 0.1, 0.1]))\n",
"factor = PriorFactorPose3(X(0), poses[0], pose_noise)\n",
"graph.push_back(factor)\n",
"\n",
"# Simulated measurements from each camera pose, adding them to the factor graph\n",
"for i, pose in enumerate(poses):\n",
" camera = PinholeCameraCal3_S2(pose, K)\n",
" for j, point in enumerate(points):\n",
" measurement = camera.project(point)\n",
" factor = GenericProjectionFactorCal3_S2(\n",
" measurement, measurement_noise, X(i), L(j), K)\n",
" graph.push_back(factor)"
]
},
{
"cell_type": "markdown",
"id": "9055723d",
"metadata": {},
"source": [
"Because the structure-from-motion problem has a scale ambiguity, the problem is still under-constrained\n",
"Here we add a prior on the position of the first landmark. This fixes the scale by indicating the distance\n",
"between the first camera and the first landmark. All other landmark positions are interpreted using this scale.\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "f11fbdd8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Factor Graph:\n",
"size: 66\n",
"\n",
"Factor 0: PriorFactor on x0\n",
" prior mean: R: [\n",
"\t0, 0.242536, -0.970143;\n",
"\t1, -0, 0;\n",
"\t-0, -0.970143, -0.242536\n",
"]\n",
"t: 40 0 10\n",
" noise model: diagonal sigmas [0.3; 0.3; 0.3; 0.1; 0.1; 0.1];\n",
"\n",
"Factor 1: GenericProjectionFactor, z = [\n",
"\t67.1796068;\n",
"\t37.5\n",
"]\n",
" keys = { x0 l0 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 2: GenericProjectionFactor, z = [\n",
"\t60.3077641;\n",
"\t37.5\n",
"]\n",
" keys = { x0 l1 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 3: GenericProjectionFactor, z = [\n",
"\t39.6922359;\n",
"\t37.5\n",
"]\n",
" keys = { x0 l2 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 4: GenericProjectionFactor, z = [\n",
"\t32.8203932;\n",
"\t37.5\n",
"]\n",
" keys = { x0 l3 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 5: GenericProjectionFactor, z = [\n",
"\t64.7253772;\n",
"\t67.8571429\n",
"]\n",
" keys = { x0 l4 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 6: GenericProjectionFactor, z = [\n",
"\t59.3706946;\n",
"\t56.8181818\n",
"]\n",
" keys = { x0 l5 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 7: GenericProjectionFactor, z = [\n",
"\t40.6293054;\n",
"\t56.8181818\n",
"]\n",
" keys = { x0 l6 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 8: GenericProjectionFactor, z = [\n",
"\t35.2746228;\n",
"\t67.8571429\n",
"]\n",
" keys = { x0 l7 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 9: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t37.5\n",
"]\n",
" keys = { x1 l0 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 10: GenericProjectionFactor, z = [\n",
"\t68.2217247;\n",
"\t37.5\n",
"]\n",
" keys = { x1 l1 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 11: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t37.5\n",
"]\n",
" keys = { x1 l2 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 12: GenericProjectionFactor, z = [\n",
"\t31.7782753;\n",
"\t37.5\n",
"]\n",
" keys = { x1 l3 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 13: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t71.9320653\n",
"]\n",
" keys = { x1 l4 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 14: GenericProjectionFactor, z = [\n",
"\t66.1970886;\n",
"\t61.1111111\n",
"]\n",
" keys = { x1 l5 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 15: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t55.465195\n",
"]\n",
" keys = { x1 l6 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 16: GenericProjectionFactor, z = [\n",
"\t33.8029114;\n",
"\t61.1111111\n",
"]\n",
" keys = { x1 l7 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 17: GenericProjectionFactor, z = [\n",
"\t32.8203932;\n",
"\t37.5\n",
"]\n",
" keys = { x2 l0 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 18: GenericProjectionFactor, z = [\n",
"\t67.1796068;\n",
"\t37.5\n",
"]\n",
" keys = { x2 l1 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 19: GenericProjectionFactor, z = [\n",
"\t60.3077641;\n",
"\t37.5\n",
"]\n",
" keys = { x2 l2 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 20: GenericProjectionFactor, z = [\n",
"\t39.6922359;\n",
"\t37.5\n",
"]\n",
" keys = { x2 l3 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 21: GenericProjectionFactor, z = [\n",
"\t35.2746228;\n",
"\t67.8571429\n",
"]\n",
" keys = { x2 l4 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 22: GenericProjectionFactor, z = [\n",
"\t64.7253772;\n",
"\t67.8571429\n",
"]\n",
" keys = { x2 l5 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 23: GenericProjectionFactor, z = [\n",
"\t59.3706946;\n",
"\t56.8181818\n",
"]\n",
" keys = { x2 l6 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 24: GenericProjectionFactor, z = [\n",
"\t40.6293054;\n",
"\t56.8181818\n",
"]\n",
" keys = { x2 l7 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 25: GenericProjectionFactor, z = [\n",
"\t31.7782753;\n",
"\t37.5\n",
"]\n",
" keys = { x3 l0 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 26: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t37.5\n",
"]\n",
" keys = { x3 l1 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 27: GenericProjectionFactor, z = [\n",
"\t68.2217247;\n",
"\t37.5\n",
"]\n",
" keys = { x3 l2 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 28: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t37.5\n",
"]\n",
" keys = { x3 l3 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 29: GenericProjectionFactor, z = [\n",
"\t33.8029114;\n",
"\t61.1111111\n",
"]\n",
" keys = { x3 l4 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 30: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t71.9320653\n",
"]\n",
" keys = { x3 l5 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 31: GenericProjectionFactor, z = [\n",
"\t66.1970886;\n",
"\t61.1111111\n",
"]\n",
" keys = { x3 l6 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 32: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t55.465195\n",
"]\n",
" keys = { x3 l7 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 33: GenericProjectionFactor, z = [\n",
"\t39.6922359;\n",
"\t37.5\n",
"]\n",
" keys = { x4 l0 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 34: GenericProjectionFactor, z = [\n",
"\t32.8203932;\n",
"\t37.5\n",
"]\n",
" keys = { x4 l1 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 35: GenericProjectionFactor, z = [\n",
"\t67.1796068;\n",
"\t37.5\n",
"]\n",
" keys = { x4 l2 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 36: GenericProjectionFactor, z = [\n",
"\t60.3077641;\n",
"\t37.5\n",
"]\n",
" keys = { x4 l3 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 37: GenericProjectionFactor, z = [\n",
"\t40.6293054;\n",
"\t56.8181818\n",
"]\n",
" keys = { x4 l4 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 38: GenericProjectionFactor, z = [\n",
"\t35.2746228;\n",
"\t67.8571429\n",
"]\n",
" keys = { x4 l5 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 39: GenericProjectionFactor, z = [\n",
"\t64.7253772;\n",
"\t67.8571429\n",
"]\n",
" keys = { x4 l6 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 40: GenericProjectionFactor, z = [\n",
"\t59.3706946;\n",
"\t56.8181818\n",
"]\n",
" keys = { x4 l7 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 41: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t37.5\n",
"]\n",
" keys = { x5 l0 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 42: GenericProjectionFactor, z = [\n",
"\t31.7782753;\n",
"\t37.5\n",
"]\n",
" keys = { x5 l1 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 43: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t37.5\n",
"]\n",
" keys = { x5 l2 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 44: GenericProjectionFactor, z = [\n",
"\t68.2217247;\n",
"\t37.5\n",
"]\n",
" keys = { x5 l3 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 45: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t55.465195\n",
"]\n",
" keys = { x5 l4 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 46: GenericProjectionFactor, z = [\n",
"\t33.8029114;\n",
"\t61.1111111\n",
"]\n",
" keys = { x5 l5 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 47: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t71.9320653\n",
"]\n",
" keys = { x5 l6 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 48: GenericProjectionFactor, z = [\n",
"\t66.1970886;\n",
"\t61.1111111\n",
"]\n",
" keys = { x5 l7 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 49: GenericProjectionFactor, z = [\n",
"\t60.3077641;\n",
"\t37.5\n",
"]\n",
" keys = { x6 l0 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 50: GenericProjectionFactor, z = [\n",
"\t39.6922359;\n",
"\t37.5\n",
"]\n",
" keys = { x6 l1 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 51: GenericProjectionFactor, z = [\n",
"\t32.8203932;\n",
"\t37.5\n",
"]\n",
" keys = { x6 l2 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 52: GenericProjectionFactor, z = [\n",
"\t67.1796068;\n",
"\t37.5\n",
"]\n",
" keys = { x6 l3 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 53: GenericProjectionFactor, z = [\n",
"\t59.3706946;\n",
"\t56.8181818\n",
"]\n",
" keys = { x6 l4 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 54: GenericProjectionFactor, z = [\n",
"\t40.6293054;\n",
"\t56.8181818\n",
"]\n",
" keys = { x6 l5 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 55: GenericProjectionFactor, z = [\n",
"\t35.2746228;\n",
"\t67.8571429\n",
"]\n",
" keys = { x6 l6 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 56: GenericProjectionFactor, z = [\n",
"\t64.7253772;\n",
"\t67.8571429\n",
"]\n",
" keys = { x6 l7 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 57: GenericProjectionFactor, z = [\n",
"\t68.2217247;\n",
"\t37.5\n",
"]\n",
" keys = { x7 l0 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 58: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t37.5\n",
"]\n",
" keys = { x7 l1 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 59: GenericProjectionFactor, z = [\n",
"\t31.7782753;\n",
"\t37.5\n",
"]\n",
" keys = { x7 l2 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 60: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t37.5\n",
"]\n",
" keys = { x7 l3 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 61: GenericProjectionFactor, z = [\n",
"\t66.1970886;\n",
"\t61.1111111\n",
"]\n",
" keys = { x7 l4 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 62: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t55.465195\n",
"]\n",
" keys = { x7 l5 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 63: GenericProjectionFactor, z = [\n",
"\t33.8029114;\n",
"\t61.1111111\n",
"]\n",
" keys = { x7 l6 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 64: GenericProjectionFactor, z = [\n",
"\t50;\n",
"\t71.9320653\n",
"]\n",
" keys = { x7 l7 }\n",
" noise model: unit (2) \n",
"\n",
"Factor 65: PriorFactor on l0\n",
" prior mean: [\n",
"\t10;\n",
"\t10;\n",
"\t10\n",
"]\n",
"isotropic dim=3 sigma=0.1\n",
"\n"
]
}
],
"source": [
"point_noise = gtsam.noiseModel.Isotropic.Sigma(3, 0.1)\n",
"factor = PriorFactorPoint3(L(0), points[0], point_noise)\n",
"graph.push_back(factor)\n",
"graph.print('Factor Graph:\\n')"
]
},
{
"cell_type": "markdown",
"id": "af02cdad",
"metadata": {},
"source": [
"Create the data structure to hold the initial estimate to the solution\n",
"Intentionally initialize the variables off from the ground truth"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "f2aad47d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Initial Estimates:\n",
"\n",
"Values with 16 values:\n",
"Value l0: (Eigen::Matrix)\n",
"[\n",
"\t9.86729537;\n",
"\t9.99153586;\n",
"\t9.82379831\n",
"]\n",
"\n",
"Value l1: (Eigen::Matrix)\n",
"[\n",
"\t-10.1133309;\n",
"\t10.0099529;\n",
"\t9.99434549\n",
"]\n",
"\n",
"Value l2: (Eigen::Matrix)\n",
"[\n",
"\t-9.77263557;\n",
"\t-10.0559281;\n",
"\t9.9977742\n",
"]\n",
"\n",
"Value l3: (Eigen::Matrix)\n",
"[\n",
"\t9.92443404;\n",
"\t-10.1036249;\n",
"\t10.0858625\n",
"]\n",
"\n",
"Value l4: (Eigen::Matrix)\n",
"[\n",
"\t9.86787396;\n",
"\t10.0361396;\n",
"\t-9.93586051\n",
"]\n",
"\n",
"Value l5: (Eigen::Matrix)\n",
"[\n",
"\t-10.0730792;\n",
"\t10.0340933;\n",
"\t-9.97864231\n",
"]\n",
"\n",
"Value l6: (Eigen::Matrix)\n",
"[\n",
"\t-9.84218151;\n",
"\t-9.88643437;\n",
"\t-9.89115593\n",
"]\n",
"\n",
"Value l7: (Eigen::Matrix)\n",
"[\n",
"\t9.92952336;\n",
"\t-10.0191887;\n",
"\t-10.0203568\n",
"]\n",
"\n",
"Value x0: (gtsam::Pose3)\n",
"R: [\n",
"\t-0.127007849, 0.200677098, -0.971389576;\n",
"\t0.990562318, 0.07653696, -0.113703067;\n",
"\t0.0515296036, -0.976663092, -0.208503966\n",
"]\n",
"t: 40.1786614 0.0362699781 9.93339219\n",
"\n",
"Value x1: (gtsam::Pose3)\n",
"R: [\n",
"\t-0.724310145, 0.237889273, -0.647134845;\n",
"\t0.688852791, 0.209839732, -0.693865347;\n",
"\t-0.0292685204, -0.948354354, -0.315859736\n",
"]\n",
"t: 28.2965092 28.4868545 9.91673796\n",
"\n",
"Value x2: (gtsam::Pose3)\n",
"R: [\n",
"\t-0.954617347, 0.297492994, -0.0142702135;\n",
"\t0.0977460499, 0.267675309, -0.958538282;\n",
"\t-0.281338639, -0.916432129, -0.284606258\n",
"]\n",
"t: 0.00714928917 40.1178024 10.0920309\n",
"\n",
"Value x3: (gtsam::Pose3)\n",
"R: [\n",
"\t-0.602549022, -0.352305729, 0.716111269;\n",
"\t-0.779438087, 0.0669611992, -0.622890412;\n",
"\t0.171496191, -0.933486407, -0.314947909\n",
"]\n",
"t: -28.3258023 28.3284935 9.86047817\n",
"\n",
"Value x4: (gtsam::Pose3)\n",
"R: [\n",
"\t-0.219623767, -0.217446343, 0.951042843;\n",
"\t-0.97492556, 0.0847458889, -0.205762697;\n",
"\t-0.0358546251, -0.972386354, -0.230606209\n",
"]\n",
"t: -40.1617319 0.0862596362 9.9966896\n",
"\n",
"Value x5: (gtsam::Pose3)\n",
"R: [\n",
"\t0.765780633, -0.217577404, 0.605177738;\n",
"\t-0.635682144, -0.113560325, 0.763552398;\n",
"\t-0.0974075681, -0.969414321, -0.225272368\n",
"]\n",
"t: -28.1356957 -28.4553938 9.84481954\n",
"\n",
"Value x6: (gtsam::Pose3)\n",
"R: [\n",
"\t0.987189991, 0.158904955, 0.0143226052;\n",
"\t0.0338441824, -0.296287489, 0.954498976;\n",
"\t0.155918226, -0.941787099, -0.297870052\n",
"]\n",
"t: 0.0684966063 -40.0603178 10.0001398\n",
"\n",
"Value x7: (gtsam::Pose3)\n",
"R: [\n",
"\t0.648162318, 0.16852491, -0.742620337;\n",
"\t0.755762522, -0.261869565, 0.600206082;\n",
"\t-0.0933199885, -0.950275584, -0.297098795\n",
"]\n",
"t: 28.2344487 -28.4395192 9.96461625\n",
"\n"
]
}
],
"source": [
"initial_estimate = Values()\n",
"for i, pose in enumerate(poses):\n",
" transformed_pose = pose.retract(0.1*np.random.randn(6, 1))\n",
" initial_estimate.insert(X(i), transformed_pose)\n",
"for j, point in enumerate(points):\n",
" transformed_point = point + 0.1*np.random.randn(3)\n",
" initial_estimate.insert(L(j), transformed_point)\n",
"initial_estimate.print('Initial Estimates:\\n')"
]
},
{
"cell_type": "markdown",
"id": "89bf1b01",
"metadata": {},
"source": [
"Optimize the graph and print results"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "fe9232a9",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Optimizing:\n",
"Final results:\n",
"\n",
"Values with 16 values:\n",
"Value l0: (Eigen::Matrix)\n",
"[\n",
"\t10;\n",
"\t10;\n",
"\t10\n",
"]\n",
"converged\n",
"errorThreshold: 1.75145933e-27 0\n",
"absoluteDecrease: 5.1150933011e-13 1e-05\n",
"relativeDecrease: 1 1e-05\n",
"iterations: 5 >? 100\n",
"\n",
"Value l1: (Eigen::Matrix)\n",
"[\n",
"\t-10;\n",
"\t10;\n",
"\t10\n",
"]\n",
"\n",
"Value l2: (Eigen::Matrix)\n",
"[\n",
"\t-10;\n",
"\t-10;\n",
"\t10\n",
"]\n",
"\n",
"Value l3: (Eigen::Matrix)\n",
"[\n",
"\t10;\n",
"\t-10;\n",
"\t10\n",
"]\n",
"\n",
"Value l4: (Eigen::Matrix)\n",
"[\n",
"\t10;\n",
"\t10;\n",
"\t-10\n",
"]\n",
"\n",
"Value l5: (Eigen::Matrix)\n",
"[\n",
"\t-10;\n",
"\t10;\n",
"\t-10\n",
"]\n",
"\n",
"Value l6: (Eigen::Matrix)\n",
"[\n",
"\t-10;\n",
"\t-10;\n",
"\t-10\n",
"]\n",
"\n",
"Value l7: (Eigen::Matrix)\n",
"[\n",
"\t10;\n",
"\t-10;\n",
"\t-10\n",
"]\n",
"\n",
"Value x0: (gtsam::Pose3)\n",
"R: [\n",
"\t1.06345686108e-17, 0.242535625036, -0.970142500145;\n",
"\t1, -7.01790398906e-18, 7.68207370487e-18;\n",
"\t-1.28686669842e-17, -0.970142500145, -0.242535625036\n",
"]\n",
"t: 40 3.30176431837e-20 10\n",
"\n",
"Value x1: (gtsam::Pose3)\n",
"R: [\n",
"\t-0.707106781187, 0.171498585143, -0.68599434057;\n",
"\t0.707106781187, 0.171498585143, -0.68599434057;\n",
"\t-5.74381863156e-17, -0.970142500145, -0.242535625036\n",
"]\n",
"t: 28.2842712475 28.2842712475 10\n",
"\n",
"Value x2: (gtsam::Pose3)\n",
"R: [\n",
"\t-1, 7.33763155768e-17, -1.59865365488e-16;\n",
"\t1.78698236833e-16, 0.242535625036, -0.970142500145;\n",
"\t-6.67458843965e-17, -0.970142500145, -0.242535625036\n",
"]\n",
"t: 3.7115770771e-15 40 10\n",
"\n",
"Value x3: (gtsam::Pose3)\n",
"R: [\n",
"\t-0.707106781187, -0.171498585143, 0.68599434057;\n",
"\t-0.707106781187, 0.171498585143, -0.68599434057;\n",
"\t1.48427161608e-16, -0.970142500145, -0.242535625036\n",
"]\n",
"t: -28.2842712475 28.2842712475 10\n",
"\n",
"Value x4: (gtsam::Pose3)\n",
"R: [\n",
"\t-3.06759340603e-16, -0.242535625036, 0.970142500145;\n",
"\t-1, 1.41630867686e-16, -3.05311788789e-16;\n",
"\t-5.47758684978e-17, -0.970142500145, -0.242535625036\n",
"]\n",
"t: -40 1.28166734341e-14 10\n",
"\n",
"Value x5: (gtsam::Pose3)\n",
"R: [\n",
"\t0.707106781187, -0.171498585143, 0.68599434057;\n",
"\t-0.707106781187, -0.171498585143, 0.68599434057;\n",
"\t1.10852526033e-16, -0.970142500145, -0.242535625036\n",
"]\n",
"t: -28.2842712475 -28.2842712475 10\n",
"\n",
"Value x6: (gtsam::Pose3)\n",
"R: [\n",
"\t1, 2.04858986588e-17, 2.25425184633e-17;\n",
"\t-4.87260344421e-18, -0.242535625036, 0.970142500145;\n",
"\t3.00654134731e-17, -0.970142500145, -0.242535625036\n",
"]\n",
"t: -1.25969678477e-15 -40 10\n",
"\n",
"Value x7: (gtsam::Pose3)\n",
"R: [\n",
"\t0.707106781187, 0.171498585143, -0.68599434057;\n",
"\t0.707106781187, -0.171498585143, 0.68599434057;\n",
"\t-8.69960482013e-17, -0.970142500145, -0.242535625036\n",
"]\n",
"t: 28.2842712475 -28.2842712475 10\n",
"\n",
"initial error = 1483.1626780637537\n",
"final error = 1.7514593278108246e-27\n"
]
}
],
"source": [
"params = gtsam.DoglegParams()\n",
"params.setVerbosity('TERMINATION')\n",
"optimizer = DoglegOptimizer(graph, initial_estimate, params)\n",
"print('Optimizing:')\n",
"result = optimizer.optimize()\n",
"result.print('Final results:\\n')\n",
"print('initial error = {}'.format(graph.error(initial_estimate)))\n",
"print('final error = {}'.format(graph.error(result)))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "5ddc6ca0",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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2LJlS97bCIgI9913H0eOHOFHP/rRmSQQ4IlE4dLS0sJTTz3FE088QWdnJ+985ztZu3ZtzjwtLcti27ZtVFRU5NWhqT/9/T2rqqqor68nEAiwbds2lixZMuRSc64REb7+9a+zc+dOHn744YJfyh4FnkicDrS3t/P000/z5JNP0tTUxDXXXMO6deuy5tvgbn6aMGFC3loDZoJt23R2dtLY2EhrayvV1dVMmTIlb/6eIsJ//Md/8Prrr/Poo4/m3TIwR3gicbrR1dXFM888w5NPPsmhQ4e46qqrWLduHcuXLx+VYLiViTNmzBiPNvZjxrXxW7JkCZZl5c3fU0R48MEHefnll9mwYUPefTxziCcSpzO9vb0899xzbNiwgT179nD55Zezbt06zjvvvIwEw102nDt3bl4LjzLFFYj+9vzj7e8pIvzwhz/kueee46mnnir4cvox4onEmYLribFhwwa2bt2a8sQ4//zzBwzFw+EwW7duHdRYt9Do6elhx44dLF++fNj9Mbn29/zxj3/ME088wS9+8YuCaquYIzyROBNJ98R44403TvLE8Pl8tLa2sn//fpYuXVqQu077093dza5du0bVvyPb/p6PPvooP/nJT3j22WdztvP1+eef55Of/CSWZfHhD3+Yz3/+8zl5ngzxROJMJ5FI8Jvf/IYnnniCP/7xj8ydO5d9+/bxwgsv5LUyMVPcjWXZaPAzVn/PJ598kh/84Ac8++yzOVtRsSyLefPm8etf/5qpU6eyevVqHnnkERYtWpST58sATyTOJp566im+8IUv8Pa3v50///nPrFy5krVr13L55ZcX5Lza9dnIxTb6kfp7PvPMM3z7299m48aNOZ2evfrqq9xzzz288MILAKc4So0WtyvdKChokTjjFpzzjWEY/PGPf6SqqgrLsvjDH/7AE088wRe/+EUWLVrEunXruOqqq3LemyMTOjo62Lt3b858NtINYtL9Pffu3XuKv+fzzz/PN77xDZ577rmc528aGxtP8gudOnUqf/7zn8d0TMuy0HWdeDxOb29vXrfPZxtPJLLMDTfckPpd13UuvvhiLr744pQnxuOPP87999/PnDlzWLduHddcc01echbt7e3s37+flStXjktXLU3TqK6uprq6+iR/zwcffJDf/OY3tLa28sILL4zLztJsO0i5AtHb28stt9zCXXfdxbXXXjuWIRYUZ8TumNMBTdN429vexte+9jU2bdrEP//zP7Nz506uueYabrrpJh555BG6u7vHZSxtbW3jKhD9cf0958yZw6WXXopSiptvvpnbb7+dn//85zl//mw7SOm6TiKRYP369Vx22WUpgXC9L093vJxEnkn3xNi4cSN1dXWsXbuW6667LidX1dbWVg4dOlQQTYb/8Ic/8LnPfY5nn302dZKOYV6fMaZpMm/ePF566SWmTJnC6tWr+elPf8rixYtHdJx7772Xu+66i8rKSrZt28bdd9/Nvffey8GDB9mwYQPd3d3ccsst3HjjjcMdystJeAyOUoqlS5eydOlS7rnnHvbs2cOGDRtYv349ZWVlvOc97+Hd7343dXV1Yz55WlpaOHz4MCtXrsx7efOf//xnPvvZz/KLX/zipKv4eGx48/l8fOc73+Gaa67BsizuuOOOEQsEwNy5c6msrOTEiRMsXbqUFStW8Fd/9VfcfvvtXHHFFYgIbW1tOXgF48tpEUk8/vjj3HPPPezatYvXXnuN8847L3XfYB2T/vKXv3D77bcTjUZ55zvfybe+9a2C8JHIFBHhwIEDPPHEEzz99NMEg8GUJ8bEiRNH/Fqam5s5evQoK1asyLtAvPnmm9x11108/fTTzJw5M69jGQ3pdgSPP/44n/jEJ3j11VeZNWsW+/btY+7cuQC8613v4qKLLsqkBqOwv5giMtRPQbBz507ZvXu3XHLJJfL666+nbt+xY4csW7ZMYrGYHDx4UGbPni2maYqIyOrVq+WPf/yj2LYt1157rTz33HP5Gv6YsW1bDh8+LF//+tfloosukne84x1y//33y+7du6Wvr0/C4fCQP/v375ff/va30t3dPexjc/3z6quvyrJly2Tfvn35flvHzAsvvCAiIl/4whdkxYoVsnPnThERaWxslOuuu04+9rGPZXqo4c7DvP6cFonLhQsXMn/+/FNuf/rpp7npppsIBoPMmjWLOXPm8Nprr9HU1ERPTw8XXHABSik++MEPjktCLFcopZgxYwaf+cxn+O1vf8vPfvYziouL+ehHP8qVV17JN77xDQ4ePDhg1v748eMcP36cFStW5H2L9c6dO/nIRz7CY489xpw5c/I6ltHgnjTg1MN89atfBZzcxPr167npppvYvn07FRUV/N3f/R3f/e538zncrHFaiMRgDLTe3djYSGNj40nbsc+UTkrgCMbkyZP5xCc+wUsvvcTTTz9NbW0tn/nMZ7jsssv4yle+wp49exARXnnlFZqamgpCIHbv3s0dd9zBI488MqDgnw4opVLTvMsvv5ypU6fS1dWFiPDP//zP3HjjjVxyySW0t7fzrne9K8+jzR4Fk7i88sorOXHixCm333vvvaxdu3bAvxnoyqmUOmM7KfXH9ay88847ufPOO1OeGHfffTcHDhwgGAzy4IMP5v2179+/n9tvv52f/OQno0oQFhJ33nknnZ2ddHd3c/ToUTZu3MiKFStYvHgxX/jCF7jwwguZPn16voeZVQpGJF588cUR/81g691Tp06loaHhlNvPdGpqarjjjjswDIOnnnqK97///dx///0cOXKEK6+8kuuvv55ly5aNq3ns4cOHufXWW3nooYdYvnz5uD1vtrBt+6T367Of/WzKQOiee+7hG9/4BvPnz6exsZHbb799zF3MC5JhkhYFRf/E5fbt209KXM6aNSuVuDzvvPPk1VdfTSUuN27cmK9hjztbtmyRWCyW+n9PT4888sgjsn79elm2bJl86lOfkpdffll6e3tzmqTcvXu3LF++XP70pz/l8d0YPZZlpX5/4YUXZNOmTWIYRuq2b33rW/LJT35SROSk7+UoyHtycqif00IknnzySZkyZYoEAgGpr6+Xq6++OnXfl7/8ZZk9e7bMmzfvpBWM119/XRYvXiyzZ8+Wu+66S2zbzsfQC45wOCwbNmyQW265RZYsWSJ33XWX/PrXv5aenp6sCsS+fftkxYoV8rvf/S7fL3nMfPCDH5Qbb7xRLrnkErn77rvlzTffFBGRX/3qV/KBD3wgG0+RdyEY6ue0qJPwyA2xWCzlifGXv/yFCy+8kOuvv563v/3tY0p0njhxgve///187Wtf47LLLsviiHOPyMkVn1/96lfZs2cP//Vf/8Xll19OLBbjkksu4c4778Q0Tb761a/y4IMPjvVpCzph5omEB/CWJ8aGDRt49dVXedvb3sbatWu5+OKLR1S+3draynvf+17uu+8+rr766hyOODe43cJd/vCHP7Bw4ULuueceNE3jM5/5DJdffjkXX3wxn/vc57K1UlPQInFaTDcKjV/+8pcyb948Oeecc+S+++7L93CyjmEY8tJLL8nf/d3fyeLFi+XWW2+VDRs2SEdHx5BTjKNHj8rq1auznv957LHHZNGiRaKUOmXu/2//9m9yzjnnyLx58+T5559P3f7GG2/IkiVL5JxzzpFPfOITGU03d+/eLddee63E4/GTcg/d3d2yfv16OXbsmIiI3HbbbXLPPfdk6dWJSAFMKYb68URihJimKbNnz5YDBw5IPB6XZcuWyY4dO/I9rJxhmqb89re/lb//+7+XJUuWyI033iiPPPKItLa2niQQjY2Ncv7558vPf/7zrI9hvCput23bJhdccEHqGOl8+tOflvPPP1/uvPNOec973nOSiGSBvAvBUD+ndTFVPnjttdeYM2cOs2fPJhAIcNNNN/H000/ne1g5w/XE+Na3vsWWLVv41Kc+xRtvvMEVV1zBrbfeypNPPsmJEye44YYb+Id/+IdBa1rGwnhV3C5ZsoT58+fzyiuvACfX4fyv//W/+NCHPkRZWRk/+9nP8l6cNp6cPa80S+TC1eh0QdM0zj//fM4//3xs22bTpk1s2LCBz3zmM9x9992sX79+XMfT2NjI+eefn/q/W1nr9/szrrh98cUX+dOf/oRSive///3E43H27t3LZZddlirMU0pRWVnJRz7ykZy/pkLEE4kRkn51ccl3RWM+0DSNVatWsWrVKu69994xvwf5qrj1+XxMmzaNX/7yl/T09PDyyy8TDod529vexooVK04SirMVTyRGSLZdjc4EslHBma+K20svvRSA2267DYB169bx0ksvsWHDBhKJBGvWrDmrBQJO8w1e+WD16tXs27ePQ4cOkUgkePTRR3nPe96T72GdlbznPe/h0UcfJR6Pc+jQIfbt28eaNWuYNGkSZWVl/OlPf0JE+PGPfzxkriQ98rjgggu4/vrrUUrx8MMP8+abb47HSylshslsegzAxo0bZe7cuTJ79mz58pe/nO/hnPHko+J227Zt8sADD0h3d3fWXscQ5H0FY6gfr5jKw2MQ0h2ockxBz2fO6OnGiRMnsCxrwESWh8dwnE3LnENxRkcSn/70p0kkEimHIDnLs9QeBUtBfynP6EjipptuoqenB3A2M9188818//vfz/OoMueOO+6gvr6eJUuWpG7r6OjgqquuYu7cuVx11VV0dnam7rvvvvuYM2cO8+fPT7Ww8/AYK2e0SCxcuJA33niDTZs2cf311zNjxozTqrPS7bffzvPPP3/Sbffffz9XXHEF+/bt44orruD+++8HHP/IRx99lB07dvD888/zsY99DMuy8jFsjzOMM1Ik3ClUUVERa9as4TOf+Qzz5s3jgQceGNRazLIsbNsez2EOy8UXX3xKg56nn346taZ/2223pcqNBytR9vAYK2e0SHz3u9/l6aefZtGiRXzzm98ESAmB+5je3l7A2aMwnrZuo6W5uTm1lXnSpEmpVnKDmQJ7eIyVwj8rRoFpmnzta1/j4Ycf5uGHH6axsRHTNIG3qgPdBOZTTz3F6tWrufHGG3n99dfzNuaxMlAC2kvSemSDM04k2tra+PCHP8zmzZt54YUXWLZsGSdOnEDTtAGnE+vWrePll1/mQx/6ED/4wQ/o6uoC4He/+x0AhmGc9Phdu3bl9Qo9YcIEmpqaAMcgpb6+HvDKxT1yx3BLoKcdSqkQcAmwXUQak7c9BvxURH6ulNJExFbOZfZS4KNADfAH4IPAQhzxfA1YLyK7lFJvA15P/t1NOO/bI0opXURymh1USs0EnhWRJcn/fxVoF5H7lVKfB6pF5LNKqcXAT4E1wGTgJWBursfnceZzxkUSIhITkRdcgUjSDJQk73fDiQXAp4HngRuAFcCbgF9EwsDXgU8qpdYB/w1ckPy7YyLySPJYFoBSSlM5iO2VUo8ArwLzlVINSqm/Ae4HrlJK7QOuSv4fEdkBPAbsTL6muzyB8MgGZ1wkMRhuBJH2/3rgYeDDInJUKfUU8Efg/4hIr1LqVuCrwA+BF0Xk/ymlPgB8U0RqlVJTgPOBX4tIz/i/Ig+P8eGMiyQGI10gkvQCB4DHlVL/F7gG+FNSIM4DbgXqgYdE5P8l/+Ym4ItKqcnAJ4HbgD8rpb6llCoblxfi4THOnDWRxGAopYI4EcE5wCvA5cDNOBHELGCviDyafGwXsAgoBv4L+LiIbE/mLLaJSGT8X4GHR24560WiP8kTvkxEXlRK/R1wjYhcr5RaC3xZRJYqpYqBf8IRi++JyIF8jtnDI5ecNdONTBGRP4uIa5P0EvBc8vdPABuTv1si8i9AF/AdpdTE8R2lh8f44UUSGaKUugTYCswHvgA8A7QD1wP/ISJeDbTHGYknEqNAKXU58D5gHnAf8HsRSeR3VB4eucETCQ8PjyHxchIeHh5D4omEh4fHkHgi4eHhMSSeSHh4eAyJJxIeHh5D4omEh4fHkHgi4eHhMSSeSHh4eAzJ/w+yIW2AdZ88bwAAAABJRU5ErkJggg==",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"marginals = Marginals(graph, result)\n",
"plot.plot_3d_points(1, result, marginals=marginals)\n",
"plot.plot_trajectory(1, result, marginals=marginals, scale=8)\n",
"plot.set_axes_equal(1)\n",
"plt.show()"
]
}
],
"metadata": {
"interpreter": {
"hash": "341996cd3f3db7b5e0d1eaea072c5502d80452314e72e6b77c40445f6e9ba101"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('nbdev')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}