|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "id": "2b7e92ce", |
| 6 | + "metadata": {}, |
| 7 | + "source": [ |
| 8 | + "This tutorial illustrates a few of the fault detection and exclusion capabilities from `algorithms/fde.py`." |
| 9 | + ] |
| 10 | + }, |
| 11 | + { |
| 12 | + "cell_type": "code", |
| 13 | + "execution_count": null, |
| 14 | + "id": "cce0e69e", |
| 15 | + "metadata": {}, |
| 16 | + "outputs": [], |
| 17 | + "source": [ |
| 18 | + "import numpy as np\n", |
| 19 | + "import gnss_lib_py as glp\n", |
| 20 | + "\n", |
| 21 | + "# load Android Google Challenge data\n", |
| 22 | + "!wget https://raw.githubusercontent.com/Stanford-NavLab/gnss_lib_py/main/data/unit_test/android_2022/device_gnss.csv --quiet -O \"device_gnss.csv\"\n", |
| 23 | + "navdata = glp.AndroidDerived2022(\"device_gnss.csv\")" |
| 24 | + ] |
| 25 | + }, |
| 26 | + { |
| 27 | + "cell_type": "markdown", |
| 28 | + "id": "76ee7e5e", |
| 29 | + "metadata": {}, |
| 30 | + "source": [ |
| 31 | + "# FDE: Fault Detection and Exclusion" |
| 32 | + ] |
| 33 | + }, |
| 34 | + { |
| 35 | + "cell_type": "markdown", |
| 36 | + "id": "b9f9b91d-0a3f-49aa-b380-fb6d57639fe6", |
| 37 | + "metadata": {}, |
| 38 | + "source": [ |
| 39 | + "Several pre-built methods exist for performing fault detection and exclusion and can be accessed through the ``solve_fde()`` function." |
| 40 | + ] |
| 41 | + }, |
| 42 | + { |
| 43 | + "cell_type": "markdown", |
| 44 | + "id": "0194ecce", |
| 45 | + "metadata": {}, |
| 46 | + "source": [ |
| 47 | + "## Greedy Euclidean Distance Matrix FDE" |
| 48 | + ] |
| 49 | + }, |
| 50 | + { |
| 51 | + "cell_type": "markdown", |
| 52 | + "id": "067ad75b-d0ff-4a25-8d3b-85bf11634af6", |
| 53 | + "metadata": {}, |
| 54 | + "source": [ |
| 55 | + "The first method is based on \"Detection and Exclusion of Multiple Faults using Euclidean Distance Matrices\" by Derek Knowles and Grace Gao from the ION GNSS+ 2023 conference." |
| 56 | + ] |
| 57 | + }, |
| 58 | + { |
| 59 | + "cell_type": "code", |
| 60 | + "execution_count": null, |
| 61 | + "id": "535130e8-c01c-4840-8939-666677ba51cb", |
| 62 | + "metadata": {}, |
| 63 | + "outputs": [], |
| 64 | + "source": [ |
| 65 | + "result = glp.solve_fde(navdata, method=\"edm\")" |
| 66 | + ] |
| 67 | + }, |
| 68 | + { |
| 69 | + "cell_type": "markdown", |
| 70 | + "id": "f566b10b-fe33-4140-9e89-69b3c6eee64c", |
| 71 | + "metadata": {}, |
| 72 | + "source": [ |
| 73 | + "After this method runs, a new row is added called \"fault_edm\" which has a 0 if no fault is predicted, 1 if a fault is predicted, and 2 for an unknown fault status (usually due to lack of necessary columns or information)." |
| 74 | + ] |
| 75 | + }, |
| 76 | + { |
| 77 | + "cell_type": "code", |
| 78 | + "execution_count": null, |
| 79 | + "id": "b775909a-ac85-4ce8-9fea-6f71cec3f19f", |
| 80 | + "metadata": {}, |
| 81 | + "outputs": [], |
| 82 | + "source": [ |
| 83 | + "result[\"fault_edm\"]" |
| 84 | + ] |
| 85 | + }, |
| 86 | + { |
| 87 | + "cell_type": "markdown", |
| 88 | + "id": "9805d5a9-5138-4588-a0af-3e5649ecef36", |
| 89 | + "metadata": {}, |
| 90 | + "source": [ |
| 91 | + "You can channge the ``threshold`` variable to be more or less sensitive based on false alarm or missed detection system requirements. Greedy EDM FDE has a range for the threshold between 0 and 1 since the detection statistic is normalized to 1. Note that if the threshold is set to the lower limit of zero, faults are detected until only four measurements remain at each timestep." |
| 92 | + ] |
| 93 | + }, |
| 94 | + { |
| 95 | + "cell_type": "code", |
| 96 | + "execution_count": null, |
| 97 | + "id": "6a144355-2564-45ff-9155-a6a4da1d0755", |
| 98 | + "metadata": {}, |
| 99 | + "outputs": [], |
| 100 | + "source": [ |
| 101 | + "result = glp.solve_fde(navdata, method=\"edm\", \n", |
| 102 | + " threshold=0)\n", |
| 103 | + "result[\"fault_edm\"]" |
| 104 | + ] |
| 105 | + }, |
| 106 | + { |
| 107 | + "cell_type": "markdown", |
| 108 | + "id": "1ed8aa60-6ce4-4ab8-bb35-4084277ba4de", |
| 109 | + "metadata": {}, |
| 110 | + "source": [ |
| 111 | + "The ``max_faults`` variable can be changed to apply a maximum number of faults detected at each timestep. In this example, the threshold is still set to zero, but we limit the faults removed with the ``max_faults`` variable." |
| 112 | + ] |
| 113 | + }, |
| 114 | + { |
| 115 | + "cell_type": "code", |
| 116 | + "execution_count": null, |
| 117 | + "id": "c0f8efa0-8be1-49aa-b323-69aa16f5952b", |
| 118 | + "metadata": {}, |
| 119 | + "outputs": [], |
| 120 | + "source": [ |
| 121 | + "result = glp.solve_fde(navdata, method=\"edm\",\n", |
| 122 | + " threshold=0, max_faults=4)\n", |
| 123 | + "result[\"fault_edm\"]" |
| 124 | + ] |
| 125 | + }, |
| 126 | + { |
| 127 | + "cell_type": "markdown", |
| 128 | + "id": "775035ec-8d48-4780-a45f-80c5766b465d", |
| 129 | + "metadata": {}, |
| 130 | + "source": [ |
| 131 | + "If the ``remove_outliers`` variable is set, then the outliers and unknown statuses will automatically be removed from the returned ``NavData`` object." |
| 132 | + ] |
| 133 | + }, |
| 134 | + { |
| 135 | + "cell_type": "code", |
| 136 | + "execution_count": null, |
| 137 | + "id": "a09f05e2-9a13-4a7d-8ef2-935b66eb1f87", |
| 138 | + "metadata": {}, |
| 139 | + "outputs": [], |
| 140 | + "source": [ |
| 141 | + "result = glp.solve_fde(navdata, method=\"edm\",\n", |
| 142 | + " threshold=0, remove_outliers=True)\n", |
| 143 | + "result[\"fault_edm\"]" |
| 144 | + ] |
| 145 | + }, |
| 146 | + { |
| 147 | + "cell_type": "markdown", |
| 148 | + "id": "6a8540d7-0652-4a1b-bdf0-d819db379e6d", |
| 149 | + "metadata": {}, |
| 150 | + "source": [ |
| 151 | + "## Greedy Residual FDE" |
| 152 | + ] |
| 153 | + }, |
| 154 | + { |
| 155 | + "cell_type": "markdown", |
| 156 | + "id": "85df2e4f-0298-4cf6-8184-9cc13361bdfc", |
| 157 | + "metadata": {}, |
| 158 | + "source": [ |
| 159 | + "This FDE method is based on \"Fast multiple fault exclusion with a large number of measurements.\" by Juan, Blanch, Todd Walter, and Per Enge from the ION GNSS+ 2015 conference." |
| 160 | + ] |
| 161 | + }, |
| 162 | + { |
| 163 | + "cell_type": "code", |
| 164 | + "execution_count": null, |
| 165 | + "id": "9d03b637-9489-45a7-b908-334ad0012874", |
| 166 | + "metadata": {}, |
| 167 | + "outputs": [], |
| 168 | + "source": [ |
| 169 | + "result = glp.solve_fde(navdata, method=\"residual\")" |
| 170 | + ] |
| 171 | + }, |
| 172 | + { |
| 173 | + "cell_type": "markdown", |
| 174 | + "id": "5c247a63-db03-4f45-b52a-ad7ede25e41f", |
| 175 | + "metadata": {}, |
| 176 | + "source": [ |
| 177 | + "After this method runs, a new row is added called \"fault_residual\" which has a 0 if no fault is predicted, 1 if a fault is predicted, and 2 for an unknown fault status (usually due to lack of necessary columns or information)." |
| 178 | + ] |
| 179 | + }, |
| 180 | + { |
| 181 | + "cell_type": "code", |
| 182 | + "execution_count": null, |
| 183 | + "id": "ebc3467e-7594-405d-85d2-5742acaf82ca", |
| 184 | + "metadata": {}, |
| 185 | + "outputs": [], |
| 186 | + "source": [ |
| 187 | + "result[\"fault_residual\"]" |
| 188 | + ] |
| 189 | + }, |
| 190 | + { |
| 191 | + "cell_type": "markdown", |
| 192 | + "id": "2ec33c02-d460-45eb-bdad-9f613461b63a", |
| 193 | + "metadata": {}, |
| 194 | + "source": [ |
| 195 | + "## Evaluate FDE" |
| 196 | + ] |
| 197 | + }, |
| 198 | + { |
| 199 | + "cell_type": "markdown", |
| 200 | + "id": "0d62f70e-3081-459b-b681-63c65ed9ca03", |
| 201 | + "metadata": {}, |
| 202 | + "source": [ |
| 203 | + "The ``evaluate_fde()`` function can be used to create overall metrics on accuracy and timing based on a ground truth fault status row. The below example shows a comparison between the default parameters for greedy EDM and greedy residual FDE." |
| 204 | + ] |
| 205 | + }, |
| 206 | + { |
| 207 | + "cell_type": "code", |
| 208 | + "execution_count": null, |
| 209 | + "id": "02263541-fa69-41d0-8834-2c7b1caacff8", |
| 210 | + "metadata": {}, |
| 211 | + "outputs": [], |
| 212 | + "source": [ |
| 213 | + "edm_metrics, _ = glp.evaluate_fde(navdata, method=\"edm\",\n", |
| 214 | + " fault_truth_row=\"MultipathIndicator\",\n", |
| 215 | + " time_fde=True)\n", |
| 216 | + "for key, value in edm_metrics.items():\n", |
| 217 | + " print(key,\":\",value)" |
| 218 | + ] |
| 219 | + }, |
| 220 | + { |
| 221 | + "cell_type": "code", |
| 222 | + "execution_count": null, |
| 223 | + "id": "bfcf48a7-efd7-4324-8446-9a70bf4e1b1b", |
| 224 | + "metadata": {}, |
| 225 | + "outputs": [], |
| 226 | + "source": [ |
| 227 | + "residual_metrics, _ = glp.evaluate_fde(navdata, method=\"residual\",\n", |
| 228 | + " fault_truth_row=\"MultipathIndicator\",\n", |
| 229 | + " time_fde=True)\n", |
| 230 | + "for key, value in residual_metrics.items():\n", |
| 231 | + " print(key,\":\",value)" |
| 232 | + ] |
| 233 | + }, |
| 234 | + { |
| 235 | + "cell_type": "markdown", |
| 236 | + "id": "cb756cd0-9382-4991-9ed0-3b41b7482841", |
| 237 | + "metadata": {}, |
| 238 | + "source": [ |
| 239 | + "Checkout the timing difference between the too methods!" |
| 240 | + ] |
| 241 | + }, |
| 242 | + { |
| 243 | + "cell_type": "code", |
| 244 | + "execution_count": null, |
| 245 | + "id": "d59bd302-d610-4463-ad1f-ab16a8899f0b", |
| 246 | + "metadata": {}, |
| 247 | + "outputs": [], |
| 248 | + "source": [ |
| 249 | + "speed_increase = residual_metrics[\"timestep_mean_ms\"] - edm_metrics[\"timestep_mean_ms\"]\n", |
| 250 | + "print(f\"Greedy EDM is {np.round(speed_increase,1)} milliseconds faster than Greedy Residual on average!\")" |
| 251 | + ] |
| 252 | + } |
| 253 | + ], |
| 254 | + "metadata": { |
| 255 | + "kernelspec": { |
| 256 | + "display_name": "Python 3 (ipykernel)", |
| 257 | + "language": "python", |
| 258 | + "name": "python3" |
| 259 | + }, |
| 260 | + "language_info": { |
| 261 | + "codemirror_mode": { |
| 262 | + "name": "ipython", |
| 263 | + "version": 3 |
| 264 | + }, |
| 265 | + "file_extension": ".py", |
| 266 | + "mimetype": "text/x-python", |
| 267 | + "name": "python", |
| 268 | + "nbconvert_exporter": "python", |
| 269 | + "pygments_lexer": "ipython3", |
| 270 | + "version": "3.8.9" |
| 271 | + } |
| 272 | + }, |
| 273 | + "nbformat": 4, |
| 274 | + "nbformat_minor": 5 |
| 275 | +} |
0 commit comments