Exposed ExtendedKalmanFilter to Python and added ports of easyPoint2KalmanFilter and elaboratePoint2KalmanFilter notebooks
							parent
							
								
									f401a9056e
								
							
						
					
					
						commit
						31c84311c8
					
				|  | @ -725,5 +725,22 @@ virtual class BatchFixedLagSmoother : gtsam::FixedLagSmoother { | |||
|   VALUE calculateEstimate(size_t key) const; | ||||
| }; | ||||
| 
 | ||||
| #include <gtsam/nonlinear/ExtendedKalmanFilter.h> | ||||
| template <T = {gtsam::Point2, | ||||
|                gtsam::Point3, | ||||
|                gtsam::Rot2, | ||||
|                gtsam::Rot3, | ||||
|                gtsam::Pose2, | ||||
|                gtsam::Pose3, | ||||
|                gtsam::NavState, | ||||
|                gtsam::imuBias::ConstantBias}> | ||||
| virtual class ExtendedKalmanFilter { | ||||
|   ExtendedKalmanFilter(gtsam::Key key_initial, const T& x_initial, const gtsam::noiseModel::Gaussian* P_initial); | ||||
|    | ||||
|   T predict(const gtsam::NoiseModelFactor& motionFactor); | ||||
|   T update(const gtsam::NoiseModelFactor& measurementFactor); | ||||
|    | ||||
|   gtsam::JacobianFactor::shared_ptr Density() const; | ||||
| }; | ||||
| 
 | ||||
| }  // namespace gtsam | ||||
|  |  | |||
|  | @ -0,0 +1,122 @@ | |||
| { | ||||
|  "cells": [ | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 1, | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "\"\"\"\n", | ||||
|     "Extended Kalman filter on a moving 2D point, but done using factor graphs.\n", | ||||
|     "This example uses the ExtendedKalmanFilter class to perform filtering\n", | ||||
|     "on a linear system, demonstrating the same operations as in elaboratePoint2KalmanFilter.\n", | ||||
|     "\"\"\"\n", | ||||
|     "\n", | ||||
|     "import gtsam\n", | ||||
|     "import numpy as np" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 2, | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "# Create the Kalman Filter initialization point\n", | ||||
|     "X0 = gtsam.Point2(0.0, 0.0)\n", | ||||
|     "P0 = gtsam.noiseModel.Diagonal.Sigmas(np.array([0.1, 0.1]))\n", | ||||
|     "\n", | ||||
|     "# Create Key for initial pose\n", | ||||
|     "x0 = gtsam.symbol('x', 0)\n", | ||||
|     "\n", | ||||
|     "# Create an ExtendedKalmanFilter object\n", | ||||
|     "ekf = gtsam.ExtendedKalmanFilterPoint2(x0, X0, P0)\n", | ||||
|     "\n", | ||||
|     "# For this example, we use a constant-position model where\n", | ||||
|     "# controls drive the point to the right at 1 m/s\n", | ||||
|     "# F = [1 0; 0 1], B = [1 0; 0 1], and u = [1; 0]\n", | ||||
|     "# Process noise Q = [0.1 0; 0 0.1]\n", | ||||
|     "Q = gtsam.noiseModel.Diagonal.Sigmas(np.array([0.1, 0.1]), True)\n", | ||||
|     "\n", | ||||
|     "# Measurement noise, assuming a GPS-like sensor\n", | ||||
|     "R = gtsam.noiseModel.Diagonal.Sigmas(np.array([0.25, 0.25]), True)\n", | ||||
|     "\n", | ||||
|     "# Motion model - move right by 1.0 units\n", | ||||
|     "dX = gtsam.Point2(1.0, 0.0)\n", | ||||
|     "\n", | ||||
|     "last_symbol = x0" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 3, | ||||
|    "metadata": {}, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "name": "stdout", | ||||
|      "output_type": "stream", | ||||
|      "text": [ | ||||
|       "X1 Predict: [1. 0.]\n", | ||||
|       "X1 Update: [1. 0.]\n", | ||||
|       "X2 Predict: [2. 0.]\n", | ||||
|       "X2 Update: [2. 0.]\n", | ||||
|       "X3 Predict: [3. 0.]\n", | ||||
|       "X3 Update: [3. 0.]\n", | ||||
|       "\n", | ||||
|       "Easy Final Covariance (after update):\n", | ||||
|       " [[0.0193 0.    ]\n", | ||||
|       " [0.     0.0193]]\n" | ||||
|      ] | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "for i in range(1, 4):\n", | ||||
|     "    # Create symbol for new state\n", | ||||
|     "    xi = gtsam.symbol('x', i)\n", | ||||
|     "    \n", | ||||
|     "    # Prediction step: P(x_i) ~ P(x_i|x_{i-1}) P(x_{i-1})\n", | ||||
|     "    # In Kalman Filter notation: x_{t+1|t} and P_{t+1|t}\n", | ||||
|     "    motion = gtsam.BetweenFactorPoint2(last_symbol, xi, dX, Q)\n", | ||||
|     "    Xi_predict = ekf.predict(motion)\n", | ||||
|     "    print(f\"X{i} Predict:\", Xi_predict)\n", | ||||
|     "    \n", | ||||
|     "    # Update step: P(x_i|z_i) ~ P(z_i|x_i)*P(x_i)\n", | ||||
|     "    # Assuming a measurement model h(x_{t}) = H*x_{t} + v\n", | ||||
|     "    # where H is the observation model/matrix and v is noise with covariance R\n", | ||||
|     "    measurement = gtsam.Point2(float(i), 0.0)\n", | ||||
|     "    meas_factor = gtsam.PriorFactorPoint2(xi, measurement, R)\n", | ||||
|     "    Xi_update = ekf.update(meas_factor)\n", | ||||
|     "    print(f\"X{i} Update:\", Xi_update)\n", | ||||
|     "    \n", | ||||
|     "    # Move to next state\n", | ||||
|     "    last_symbol = xi\n", | ||||
|     "\n", | ||||
|     "A = ekf.Density().getA()\n", | ||||
|     "information_matrix = A.transpose() @ A\n", | ||||
|     "covariance_matrix = np.linalg.inv(information_matrix)\n", | ||||
|     "print (\"\\nEasy Final Covariance (after update):\\n\", covariance_matrix)" | ||||
|    ] | ||||
|   } | ||||
|  ], | ||||
|  "metadata": { | ||||
|   "kernelspec": { | ||||
|    "display_name": "Python 3", | ||||
|    "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.10.12" | ||||
|   } | ||||
|  }, | ||||
|  "nbformat": 4, | ||||
|  "nbformat_minor": 2 | ||||
| } | ||||
|  | @ -0,0 +1,191 @@ | |||
| { | ||||
|  "cells": [ | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 1, | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "\"\"\"\n", | ||||
|     "Simple linear Kalman filter on a moving 2D point using factor graphs in GTSAM.\n", | ||||
|     "This example manually creates all of the needed data structures to show how\n", | ||||
|     "the Kalman filter works under the hood using factor graphs, but uses a loop\n", | ||||
|     "to handle the repetitive prediction and update steps.\n", | ||||
|     "\n", | ||||
|     "Based on the C++ example by Frank Dellaert and Stephen Williams\n", | ||||
|     "\"\"\"\n", | ||||
|     "\n", | ||||
|     "import gtsam\n", | ||||
|     "import numpy as np\n", | ||||
|     "from gtsam import Point2, noiseModel\n", | ||||
|     "from gtsam.symbol_shorthand import X" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 2, | ||||
|    "metadata": {}, | ||||
|    "outputs": [], | ||||
|    "source": [ | ||||
|     "# [code below basically does SRIF with Cholesky]\n", | ||||
|     "\n", | ||||
|     "# Setup containers for linearization points\n", | ||||
|     "linearization_points = gtsam.Values()\n", | ||||
|     "\n", | ||||
|     "# Initialize state x0 at origin\n", | ||||
|     "x_initial = Point2(0, 0)\n", | ||||
|     "p_initial = noiseModel.Isotropic.Sigma(2, 0.1)\n", | ||||
|     "\n", | ||||
|     "# Add x0 to linearization points\n", | ||||
|     "linearization_points.insert(X(0), x_initial)\n", | ||||
|     "\n", | ||||
|     "# Initial factor graph with prior on X(0)\n", | ||||
|     "gfg = gtsam.GaussianFactorGraph()\n", | ||||
|     "ordering = gtsam.Ordering()\n", | ||||
|     "ordering.push_back(X(0))\n", | ||||
|     "gfg.add(X(0), p_initial.R(), np.zeros(2), noiseModel.Unit.Create(2))\n", | ||||
|     "\n", | ||||
|     "# Common parameters for all steps\n", | ||||
|     "motion_delta = Point2(1, 0)  # Always move 1 unit to the right\n", | ||||
|     "process_noise = noiseModel.Isotropic.Sigma(2, 0.1)\n", | ||||
|     "measurement_noise = noiseModel.Isotropic.Sigma(2, 0.25)" | ||||
|    ] | ||||
|   }, | ||||
|   { | ||||
|    "cell_type": "code", | ||||
|    "execution_count": 3, | ||||
|    "metadata": {}, | ||||
|    "outputs": [ | ||||
|     { | ||||
|      "name": "stdout", | ||||
|      "output_type": "stream", | ||||
|      "text": [ | ||||
|       "X1 Predict: [1. 0.]\n", | ||||
|       "X1 Update: [1. 0.]\n", | ||||
|       "X2 Predict: [2. 0.]\n", | ||||
|       "X2 Update: [2. 0.]\n", | ||||
|       "X3 Predict: [3. 0.]\n", | ||||
|       "X3 Update: [3. 0.]\n", | ||||
|       "\n", | ||||
|       "Elaborate Final Covariance (after update):\n", | ||||
|       " [[0.0193 0.    ]\n", | ||||
|       " [0.     0.0193]]\n" | ||||
|      ] | ||||
|     } | ||||
|    ], | ||||
|    "source": [ | ||||
|     "# Current state and conditional\n", | ||||
|     "current_x = X(0)\n", | ||||
|     "current_conditional = None\n", | ||||
|     "current_result = None\n", | ||||
|     "\n", | ||||
|     "# Run three predict-update cycles\n", | ||||
|     "for step in range(1, 4):\n", | ||||
|     "    # =====================================================================\n", | ||||
|     "    # Prediction step\n", | ||||
|     "    # =====================================================================\n", | ||||
|     "    next_x = X(step)\n", | ||||
|     "    \n", | ||||
|     "    # Create new graph with prior from previous step if not the first step\n", | ||||
|     "    if step > 1:\n", | ||||
|     "        gfg = gtsam.GaussianFactorGraph()\n", | ||||
|     "        gfg.add(\n", | ||||
|     "            current_x,\n", | ||||
|     "            current_conditional.R(),\n", | ||||
|     "            current_conditional.d() - current_conditional.R() @ current_result.at(current_x),\n", | ||||
|     "            current_conditional.get_model()\n", | ||||
|     "        )\n", | ||||
|     "    \n", | ||||
|     "    # Add next state to ordering and create motion model\n", | ||||
|     "    ordering = gtsam.Ordering()\n", | ||||
|     "    ordering.push_back(current_x)\n", | ||||
|     "    ordering.push_back(next_x)\n", | ||||
|     "    \n", | ||||
|     "    # Create motion factor and add to graph\n", | ||||
|     "    motion_factor = gtsam.BetweenFactorPoint2(current_x, next_x, motion_delta, process_noise)\n", | ||||
|     "    \n", | ||||
|     "    # Add next state to linearization points if this is the first step\n", | ||||
|     "    if step == 1:\n", | ||||
|     "        linearization_points.insert(next_x, x_initial)\n", | ||||
|     "    else:\n", | ||||
|     "        linearization_points.insert(next_x, \n", | ||||
|     "                                    linearization_points.atPoint2(current_x))\n", | ||||
|     "    \n", | ||||
|     "    # Add linearized factor to graph\n", | ||||
|     "    gfg.push_back(motion_factor.linearize(linearization_points))\n", | ||||
|     "    \n", | ||||
|     "    # Solve for prediction\n", | ||||
|     "    prediction_bayes_net = gfg.eliminateSequential(ordering)\n", | ||||
|     "    next_conditional = prediction_bayes_net.back()\n", | ||||
|     "    prediction_result = prediction_bayes_net.optimize()\n", | ||||
|     "    \n", | ||||
|     "    # Extract and store predicted state\n", | ||||
|     "    next_predict = linearization_points.atPoint2(next_x) + Point2(prediction_result.at(next_x))\n", | ||||
|     "    print(f\"X{step} Predict:\", next_predict)\n", | ||||
|     "    linearization_points.update(next_x, next_predict)\n", | ||||
|     "    \n", | ||||
|     "    # =====================================================================\n", | ||||
|     "    # Update step\n", | ||||
|     "    # =====================================================================\n", | ||||
|     "    # Create new graph with prior from prediction\n", | ||||
|     "    gfg = gtsam.GaussianFactorGraph()\n", | ||||
|     "    gfg.add(\n", | ||||
|     "        next_x,\n", | ||||
|     "        next_conditional.R(),\n", | ||||
|     "        next_conditional.d() - next_conditional.R() @ prediction_result.at(next_x),\n", | ||||
|     "        next_conditional.get_model()\n", | ||||
|     "    )\n", | ||||
|     "    \n", | ||||
|     "    # Create ordering for update\n", | ||||
|     "    ordering = gtsam.Ordering()\n", | ||||
|     "    ordering.push_back(next_x)\n", | ||||
|     "    \n", | ||||
|     "    # Create measurement at correct position\n", | ||||
|     "    measurement = Point2(float(step), 0.0)\n", | ||||
|     "    meas_factor = gtsam.PriorFactorPoint2(next_x, measurement, measurement_noise)\n", | ||||
|     "    \n", | ||||
|     "    # Add measurement factor to graph\n", | ||||
|     "    gfg.push_back(meas_factor.linearize(linearization_points))\n", | ||||
|     "    \n", | ||||
|     "    # Solve for update\n", | ||||
|     "    update_bayes_net = gfg.eliminateSequential(ordering)\n", | ||||
|     "    current_conditional = update_bayes_net.back()\n", | ||||
|     "    current_result = update_bayes_net.optimize()\n", | ||||
|     "    \n", | ||||
|     "    # Extract and store updated state\n", | ||||
|     "    next_update = linearization_points.atPoint2(next_x) + Point2(current_result.at(next_x))\n", | ||||
|     "    print(f\"X{step} Update:\", next_update)\n", | ||||
|     "    linearization_points.update(next_x, next_update)\n", | ||||
|     "    \n", | ||||
|     "    # Move to next state\n", | ||||
|     "    current_x = next_x\n", | ||||
|     "\n", | ||||
|     "final_R = current_conditional.R()\n", | ||||
|     "final_information = final_R.transpose() @ final_R\n", | ||||
|     "final_covariance = np.linalg.inv(final_information)\n", | ||||
|     "print(\"\\nElaborate Final Covariance (after update):\\n\", final_covariance)" | ||||
|    ] | ||||
|   } | ||||
|  ], | ||||
|  "metadata": { | ||||
|   "kernelspec": { | ||||
|    "display_name": "Python 3", | ||||
|    "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.10.12" | ||||
|   } | ||||
|  }, | ||||
|  "nbformat": 4, | ||||
|  "nbformat_minor": 2 | ||||
| } | ||||
		Loading…
	
		Reference in New Issue