Better code formatting with ``python

There is more than that here. I had a bad commit of a bunch of temporary
files, I had to reset back in time, and now I am doing a massive commit.
Sorry.
This commit is contained in:
Roger Labbe committed 2015-07-18 08:43:24 -07:00
1 parent cc24dd6291
commit 6092e06459
22 files changed
+1022 -890

No files matched your search

+32 -24
View File
@@ -750,16 +750,19 @@
"source": [
"Now let's try a randomly chosen number to scale our estimate: $\\frac{4}{10}$. Our estimate will be four tenths the measurement and the rest will be from the prediction. In other words, we are expressing a belief here, a belief that the prediction is somewhat more likely to be correct than the measurement. We compute that as\n",
"\n",
"$$ new\\_estimate = prediction + \\frac{4}{10}(measurement - prediction)\n",
"$$\n",
"$$\\mathtt{new\\_estimate} = \\mathtt{prediction} + \\frac{4}{10}(\\mathtt{measurement} - \\mathtt{prediction})$$\n",
"\n",
"The difference between the measurement and prediction is called the *residual*, which is depicted by the black vertical line in the plot above. This will become an important value to use later on, as it is an exact computation of the difference between measurements and the filter's output. Smaller residuals imply better performance.\n",
"\n",
"$$\\begin{cases}\n",
"x=0 & \\text{hi there} \\\\\n",
"x = 1 & \\text{good bye}\n",
"\\end{cases}$$\n",
"Let's code that and see the results when we test it against the series of weights from above. We have to take into account one other factor. Weight gain has units of lbs/time, so to be general we will need to add a time step $t$, which we will set to 1 (day). \n",
"\n",
"I hand generated the weight data to correspond to a true starting weight of 160 lbs, and a weight gain of 1 lb per day. In other words on the first day (day zero) the true weight is 160lbs, on the second day (day one, the first day of weighing) the true weight is 161 lbs, and so on. \n",
"\n",
"We need to make a guess for the initial weight to feed into the filter. It is too early to talk about strategies for making that initial guess/estimate, so for now I will assume 159 lbs."
"We need to make a guess for the initial weight. It is too early to talk about initialization strategies, so for now I will assume 159 lbs."
]
},
{
@@ -895,7 +898,7 @@
"\n",
"So, should we set the new gain/day to 4.4 lbs? Yesterday we though the weight gain was 1 lb, today we think it is 4.4 lbs. We have two numbers, and want to combine them somehow. Hmm, sounds like our same problem again. Let's use our same tool, and the only tool we have so far - pick a value part way between the two. This time I will use another arbitrarily chosen number, $\\frac{1}{3}$. The equation is identical as for the weight estimate except we have to incorporate time because this is a rate (gain/day):\n",
"\n",
"$$new gain = old gain + \\frac{1}{3}\\frac{measurement - predicted~weight}{1~ day}\n",
"$$\\text{new gain} = \\text{old gain} + \\frac{1}{3}\\frac{\\text{measurement - predicted weight}}{1 \\text{ day}}\n",
"$$"
]
},
@@ -964,7 +967,8 @@
"\n",
"One final point before we go on. In the prediction step I wrote the line\n",
"\n",
" gain_rate = gain_rate\n",
"```python\n",
"gain_rate = gain_rate```\n",
" \n",
"This obviously has no effect, and can be removed. I wrote this to emphasize that in the prediction step you need to predict next value for **all** variables, both *weight* and *gain_rate*. In this case we are assuming that the the gain does not vary, but when we generalize this algorithm we will remove that assumption. "
]
@@ -1385,17 +1389,18 @@
"source": [
"In the example above, I explicitly coded this to solve the weighing problem that we've been discussing throughout the chapter. For example, the variables are named \"weight_scale\", \"gain\", and so on. I did this to make the algorithm easy to follow - you can easily see that we correctly implemented each step. But, that is code written for exactly one problem, and the algorithm is the same for any problem. So let's rewrite the code to be generic - to work with any problem. Use this function signature:\n",
"\n",
" def g_h_filter(data, x0, dx, g, h, dt):\n",
" \"\"\"\n",
" Performs g-h filter on 1 state variable with a fixed g and h.\n",
"```python\n",
"def g_h_filter(data, x0, dx, g, h, dt):\n",
" \"\"\"\n",
" Performs g-h filter on 1 state variable with a fixed g and h.\n",
"\n",
" 'data' contains the data to be filtered.\n",
" 'x0' is the initial value for our state variable\n",
" 'dx' is the initial change rate for our state variable\n",
" 'g' is the g-h's g scale factor\n",
" 'h' is the g-h's h scale factor\n",
" 'dt' is the length of the time step \n",
" \"\"\"\n",
" 'data' contains the data to be filtered.\n",
" 'x0' is the initial value for our state variable\n",
" 'dx' is the initial change rate for our state variable\n",
" 'g' is the g-h's g scale factor\n",
" 'h' is the g-h's h scale factor\n",
" 'dt' is the length of the time step \n",
" \"\"\"```\n",
"\n",
"Return the data as a NumPy array, not a list. Test it by passing in the same weight data as before, plot the results, and visually determine that it works."
]
@@ -2074,18 +2079,21 @@
"\n",
"First, let's simulate the situation without a filter. We will assume that the train is currently at kilometer 23, and moving at 15 m/s. We can code this as \n",
"\n",
" pos = 23*1000\n",
" vel = 15\n",
" \n",
"```python\n",
"pos = 23*1000\n",
"vel = 15```\n",
"\n",
"Now we can compute the position of the train at some future time, *assuming* no change in velocity, with\n",
"\n",
" def compute_new_position(pos, vel, dt=1):\n",
" return pos + (vel * dt)\n",
" \n",
"```python\n",
"def compute_new_position(pos, vel, dt=1):\n",
" return pos + (vel * dt)```\n",
"\n",
"We can simulate the measurement by adding in some random noise to the position. Here our error is 500m, so the code might look like:\n",
"\n",
" def measure_position(pos):\n",
" return pos + random.randn()*500\n",
"```python\n",
"def measure_position(pos):\n",
" return pos + random.randn()*500```\n",
" \n",
"Let's put that in a cell and plot the results of 100 seconds of simulation. I will use NumPy's `asarray` function to convert the data into an NumPy array. This will allow me to divide all of the elements of the array at once by using the '/' operator."
]
@@ -2564,7 +2572,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+12 -11
View File
@@ -1761,13 +1761,13 @@
"$$P(x_i|Z) = \\frac{P(Z|x_i) P(x_i)}{P(Z)}$$\n",
"\n",
"That looks ugly, but it is actually quite simple. Let's figure out what each term on the right means. First is $P(Z|x_i)$. This is the probability for the measurement at every cell $x_i$. $P(x_i)$ is the *prior* - our belief before incorporating the measurements. We multiply those together. This is just the unnormalized multiplication in the `update()` function, where `belief` is our prior $P(x_i)$.\n",
" \n",
"\n",
" for i, val in enumerate(map_):\n",
" if val == z:\n",
" belief[i] *= correct_scale\n",
" else:\n",
" belief[i] *= 1.\n",
"```python\n",
"for i, val in enumerate(map_):\n",
" if val == z:\n",
" belief[i] *= correct_scale\n",
" else:\n",
" belief[i] *= 1.```\n",
"\n",
"I added the `else` here, which has no mathematical effect, to point out that every element in $x$ (called `belief` in the code) is multiplied by a probability. You may object that I am multiplying by a scale factor, which I am, but this scale factor is derived from the probability of the measurement being correct vs the probability being incorrect.\n",
"\n",
@@ -1792,10 +1792,11 @@
"\n",
"That equation is called the *total probability theorem*. Quoting from Wikipedia [6] \"It expresses the total probability of an outcome which can be realized via several distinct events\". I could have given you that equation and implemented `predict()`, but your chances of understanding why the equation works would be slim. As a reminder, here is the code that computes this equation\n",
"\n",
" for i in range(N):\n",
" for k in range (kN):\n",
" index = (i + (width-k) - offset) % N\n",
" result[i] += prob_dist[index] * kernel[k]"
"```python\n",
"for i in range(N):\n",
" for k in range (kN):\n",
" index = (i + (width-k) - offset) % N\n",
" result[i] += prob_dist[index] * kernel[k]```"
]
},
{
@@ -1882,7 +1883,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+13 -11
View File
@@ -907,14 +907,15 @@
"\n",
"> **Optional:** Let's remind ourselves how to look at a function stored in a file by using the *%load* magic. If you type *%load -s gaussian stats.py* into a code cell and then press CTRL-Enter, the notebook will create a new input cell and load the function into it.\n",
"\n",
" %load -s gaussian stats.py\n",
" \n",
" def gaussian(x, mean, var):\n",
" \"\"\"returns normal distribution for x given a \n",
" gaussian with the specified mean and variance. \n",
" \"\"\"\n",
" return np.exp((-0.5*(x-mean)**2)/var) / \\\n",
" np.sqrt(_two_pi*var)"
"```python\n",
"%load -s gaussian stats.py\n",
"\n",
"def gaussian(x, mean, var):\n",
" \"\"\"returns normal distribution for x given a \n",
" gaussian with the specified mean and variance. \n",
" \"\"\"\n",
" return np.exp((-0.5*(x-mean)**2)/var) / \\\n",
" np.sqrt(_two_pi*var)```"
]
},
{
@@ -1005,7 +1006,7 @@
"\n",
"The standard notation for a normal distribution for a random variable $X$ is $X \\sim\\ \\mathcal{N}(\\mu,\\sigma^2)$ where $\\sim$ means *distributed according to*. This means I can express the temperature reading of our thermometer as\n",
"\n",
"$$temp \\sim \\mathcal{N}(22,4)$$\n",
"$$\\text{temp} \\sim \\mathcal{N}(22,4)$$\n",
"\n",
"This is an **extremely important** result. Gaussians allow me to capture an infinite number of possible values with only two numbers! With the values $\\mu=22$ and $\\sigma^2=4$ I can compute the distribution of measurements for over any range."
]
@@ -1538,7 +1539,8 @@
"\n",
"The code for rand_student_t is included in `filterpy.stats`. You may use it with\n",
"\n",
" from filterpy.stats import rand_student_t"
"```python\n",
"from filterpy.stats import rand_student_t```"
]
},
{
@@ -1599,7 +1601,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+96 -83
View File
@@ -271,15 +271,6 @@
"load_style()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
@@ -360,13 +351,15 @@
"\n",
"Our model assumes that the velocity of our dog is constant, but we acknowledged that things like hills and obstacles make that very unlikely to be true. We can simulate this by adding a randomly generated value to the velocity each time we compute the dog's position.\n",
"\n",
" noisy_vel = vel + randn()*velocity_std\n",
" x = x + noisy_vel*dt\n",
"```python\n",
"noisy_vel = vel + randn()*velocity_std\n",
"x = x + noisy_vel*dt```\n",
" \n",
"\n",
"To model the sensor we need to take the current position of the dog and then add some noise to it proportional to the Gaussian of the measurement noise. This is simply\n",
"\n",
" z = x + randn()*measurement_std\n",
"```python\n",
"z = x + randn()*measurement_std```\n",
" \n",
"I don't like the bookkeeping required to keep track of the various values for position, velocity, and standard deviations so I will implement the simulation as a class. "
]
@@ -732,25 +725,28 @@
"\n",
"Recall the discrete Bayes code for adding a measurement to a preexisting belief:\n",
"\n",
" def update(pos, measure, p_hit, p_miss):\n",
" q = array(pos, dtype=float)\n",
" for i in range(len(hallway)):\n",
" if hallway[i] == measure:\n",
" q[i] = pos[i] * p_hit\n",
" else:\n",
" q[i] = pos[i] * p_miss\n",
" normalize(q)\n",
" return q\n",
"```python\n",
"def update(pos, measure, p_hit, p_miss):\n",
" q = array(pos, dtype=float)\n",
" for i in range(len(hallway)):\n",
" if hallway[i] == measure:\n",
" q[i] = pos[i] * p_hit\n",
" else:\n",
" q[i] = pos[i] * p_miss\n",
" normalize(q)\n",
" return q```\n",
" \n",
"Note that the algorithm is essentially computing:\n",
"\n",
" new_belief = prior_belief * measurement * sensor_error\n",
"```python\n",
"new_belief = prior_belief * measurement * sensor_error```\n",
" \n",
"The measurement term might not be obvious, but recall that measurement in this case was always 1 or 0, and so it was left out for convenience. Here *prior* carries the both the colloquial sense of *previoius*, but also the Bayesian meaning. In Bayesian terms the *prior* is the probability after the prediction and before the measurements have been incorporated.\n",
" \n",
"If we are implementing this with Gaussians, we might expect it to be implemented as:\n",
"\n",
" new_gaussian = measurement * old_gaussian\n",
"```python\n",
"new_gaussian = measurement * old_gaussian```\n",
" \n",
"where measurement is a Gaussian returned from the sensor. But does that make sense? Can we multiply Gaussians? If we multiply a Gaussian with a Gaussian is the result another Gaussian, or something else?"
]
@@ -1067,9 +1063,10 @@
"source": [
"Perhaps this would be clearer if we used more specific names:\n",
"\n",
" def update_dog(dog_pos, dog_variance, measurement, measurement_variance):\n",
" return multiply(dog_pos, dog_variance, \n",
" measurement, measurement_variance)"
"```python\n",
"def update_dog(dog_pos, dog_variance, measurement, measurement_variance):\n",
" return multiply(dog_pos, dog_variance, \n",
" measurement, measurement_variance)```"
]
},
{
@@ -1155,17 +1152,17 @@
"\n",
"How do we perform the predict function with Gaussians? Recall the discrete Bayes method:\n",
"\n",
" def predict(pos, move, p_correct, p_under, p_over):\n",
" n = len(pos)\n",
" result = array(pos, dtype=float)\n",
" for i in range(n):\n",
" result[i] = \\\n",
" pos[(i-move) % n] * p_correct + \\\n",
" pos[(i-move-1) % n] * p_over + \\\n",
" pos[(i-move+1) % n] * p_under \n",
" return result\n",
" \n",
" \n",
"```python\n",
"def predict(pos, move, p_correct, p_under, p_over):\n",
" n = len(pos)\n",
" result = array(pos, dtype=float)\n",
" for i in range(n):\n",
" result[i] = \\\n",
" pos[(i-move) % n] * p_correct + \\\n",
" pos[(i-move-1) % n] * p_over + \\\n",
" pos[(i-move+1) % n] * p_under \n",
" return result```\n",
"\n",
"In a nutshell, we shift the probability vector by the amount we believe the animal moved, and adjust the probability. How do we do that with Gaussians?\n",
"\n",
"It turns out that we add them. Think of the case without Gaussians. I think my dog is at 7.3 meters, and he moves 2.6 meters to right, where is he now? Obviously, 7.3 + 2.6 = 9.9. He is at 9.9 meters. Abstractly, the algorithm is `new_pos = old_pos + dist_moved`. It does not matter if we use floating point numbers or Gaussians for these values, the algorithm must be the same. \n",
@@ -1329,17 +1326,19 @@
"source": [
"Now let's walk through the code and output.\n",
"\n",
" pos = (0., 400.) # gaussian N(0, 400)\n",
"\n",
"```python\n",
"pos = (0., 400.) # gaussian N(0, 400)```\n",
" \n",
"Here we set the initial position at 0 m. We set the variance to 400 m$^2$, which is a standard deviation of 20 meters. You can think of this as saying \"I believe with 99% accuracy the position is 0 plus or minus 60 meters\". This is because with Gaussians ~99% of values fall within 3$\\sigma$ of the mean.\n",
"\n",
" velocity = 1.\n",
"```python\n",
"velocity = 1.```\n",
" \n",
"So how do I know the velocity? Magic? Consider it a prediction, or perhaps we have a secondary velocity sensor. If this is a robot then this would be a control input to the robot. In the next chapter we will learn how to handle situations where you don't have a velocity sensor or input, so please accept this simplification for now.\n",
"\n",
" process_variance = 1.\n",
" sensor_variance = 2.\n",
"```python\n",
"process_variance = 1.\n",
"sensor_variance = 2.```\n",
" \n",
"Here the variance for the predict step is `process_variance` and the variance for the sensor is `sensor_variance`. The meaning of sensor variance should be clear - it is how much variance there is in each sensor reading. The process variance is how much error there is in the prediction model. We are predicting that at each time step the dog moves forward one meter. Dogs rarely do what we expect, and of course things like hills or the whiff of a squirrel will change his progress. If this was a robot responding to digital commands the performance would be better, but not perfect. Perhaps a hand made robot would have a variance of $\\sigma^2=.1$, and an industrial robot might have $\\sigma^2=0.02$. These are not 'magic' numbers; the square root of the express the distance error in meters. It is easy to get a Kalman filter working by just plugging in numbers, but if the numbers do not reflect reality the performance of the filter will be poor."
]
@@ -1350,12 +1349,13 @@
"source": [
"Next we initialize the simulator and run it with\n",
"\n",
" dog = DogSimulation(pos[0], velocity=velocity, \n",
" measurement_variance=sensor_variance, \n",
" process_variance=process_variance)\n",
"```python\n",
"dog = DogSimulation(pos[0], velocity=velocity, \n",
" measurement_variance=sensor_variance, \n",
" process_variance=process_variance)\n",
"\n",
" # simulate dog and get measurements\n",
" zs = [dog.move_and_sense() for _ in range(N)]\n",
"# simulate dog and get measurements\n",
"zs = [dog.move_and_sense() for _ in range(N)]```\n",
" \n",
"It may seem very 'convenient' to set the simulator to the same position as our guess, and it is. Do not fret. In the next example we will see the effect of a wildly inaccurate guess for the dog's initial position. We move the dog for 10 steps forward and store the measurements in `zs` via a list comprehension.\n",
"\n",
@@ -1363,7 +1363,8 @@
"\n",
"The next code allocates an array to store the output of the filtered positions. \n",
"\n",
" positions = np.zeros(N)"
"```python\n",
"positions = np.zeros(N)```"
]
},
{
@@ -1372,9 +1373,10 @@
"source": [
"Now we enter our `predict() ... update()` loop.\n",
"\n",
" for i in range(N):\n",
" pos = predict(pos=pos[0], variance=pos[1], \n",
" movement=vel, movement_variance=process_variance)\n",
"```python\n",
"for i in range(N):\n",
" pos = predict(pos=pos[0], variance=pos[1], \n",
" movement=vel, movement_variance=process_variance)```\n",
"\n",
"We call the `predict()` function with the Gaussian for the dog's position, and another Gaussian for its movement. `pos[0], pos[1]` is the mean and variance for the position, and 'vel, process_variance' is the mean and variance for the movement. The first time through the loop we get\n",
"\n",
@@ -1384,8 +1386,9 @@
"\n",
"Then we call the update function of our filter and save the result in our `positions` array.\n",
"\n",
" pos = update(pos[0], pos[1], z, sensor_variance)\n",
" positions.append(pos[0])\n",
"```python\n",
"pos = update(pos[0], pos[1], z, sensor_variance)\n",
"positions.append(pos[0])```\n",
" \n",
"For this I get this as the result:\n",
"\n",
@@ -1401,11 +1404,12 @@
"\n",
"Before we continue I want to point out how few lines of code actually perform the filtering. Most of the code is either initialization, storing of data, simulating the dog movement, and printing results. The code that performs the filtering is the very succinct\n",
"\n",
" for i in range(N):\n",
" pos = predict(pos=pos[0], variance=pos[1], \n",
" movement=vel, movement_variance=process_variance)\n",
" pos = update(mean=pos[0], variance=pos[1],\n",
" measurement=z, measurement_variance=sensor_variance)"
"```python\n",
"for i in range(N):\n",
" pos = predict(pos=pos[0], variance=pos[1], \n",
" movement=vel, movement_variance=process_variance)\n",
" pos = update(mean=pos[0], variance=pos[1],\n",
" measurement=z, measurement_variance=sensor_variance)```"
]
},
{
@@ -1611,41 +1615,46 @@
"source": [
"For many purposes the code above suffices. However, if you write enough of these filters the functions will become a bit annoying. For example, having to write\n",
"\n",
" pos = predict(pos[0], pos[1], movement, movement_variance) \n",
" \n",
"```python\n",
"pos = predict(pos[0], pos[1], movement, movement_variance) ```\n",
"\n",
"is a bit cumbersome and error prone. Let's investigate how we might implement this in a form that makes our lives easier.\n",
"\n",
"First, values for the movement error and the measurement errors are typically constant for a given problem, so we only want to specify them once. We can store them in instance variables in the class. Second, it is annoying to have to pass in the state (pos in the code snippet above) and then remember to assign the output of the function back to that state, so the state should also be an instance variable. Our first attempt might look like:\n",
"\n",
" class KalmanFilter1D:\n",
" def __init__(self, initial_state, measurement_variance, movement_variance):\n",
" self.state = initial_state\n",
" self.measurement_variance = measurement_variance\n",
" self.movement_variance = movement_variance\n",
"```python\n",
"class KalmanFilter1D:\n",
" def __init__(self, initial_state, measurement_variance, movement_variance):\n",
" self.state = initial_state\n",
" self.measurement_variance = measurement_variance\n",
" self.movement_variance = movement_variance```\n",
"\n",
"That works, but I am going to use different naming. The Kalman filter literature uses math, not code, so it uses single letter variables, so I will start exposing you to it now. At first it seems impossibly terse, but as you become familiar with the nomenclature you'll see that the math formulas in the textbooks will have an exact one-to-one correspondence with the code. Unfortunately there is not a lot of meaning behind the naming unless you have training in control theory; you will have to memorize them. If you do not make this effort you will never be able to read the literature.\n",
"\n",
"So, we use `x` for the state (estimated value of the filter) and `P` for the variance of the state. `R` is the measurement error, and `Q` is the process (prediction) error. This gives us:\n",
"\n",
" class KalmanFilter1D:\n",
" def __init__(self, x0, P, R, Q):\n",
" self.x = x0\n",
" self.P = P\n",
" self.R = R\n",
" self.Q = Q\n",
"```python\n",
"class KalmanFilter1D:\n",
" def __init__(self, x0, P, R, Q):\n",
" self.x = x0\n",
" self.P = P\n",
" self.R = R\n",
" self.Q = Q```\n",
" \n",
"Now we can implement the `update()` and `predict()` function. In the literature the measurement is usually named either `z` or `y`; I find `y` is too easy to confuse with the y axis of a plot, so I like `z`. I like to think I can hear a `z` in *measurement*, which helps me remember what `z` stands for. So for the update method we might write:\n",
"\n",
" def update(z):\n",
" self.x = (self.P * z + self.x * self.R) / (self.P + self.R)\n",
" self.P = 1 / (1/self.P + 1/self.R)\n",
"```python\n",
"def update(z):\n",
" self.x = (self.P * z + self.x * self.R) / (self.P + self.R)\n",
" self.P = 1 / (1/self.P + 1/self.R)```\n",
"\n",
"Finally, the movement is usually called `u`, and so we will use that. So for the predict function we might write:\n",
"\n",
" def predict(self, u):\n",
" self.x += u\n",
" self.P += self.Q\n",
" \n",
"```python\n",
"def predict(self, u):\n",
" self.x += u\n",
" self.P += self.Q```\n",
"\n",
"That give us the following code. Production code would require significant comments and error checking. However, in the next chapter we will develop Kalman filter code that works for any dimension so this class will never be more than a stepping stone for us."
]
},
@@ -1785,13 +1794,15 @@
"source": [
"Modify the values of `movement_variance` and `sensor_variance` and note the effect on the filter and on the variance. Which has a larger effect on the variance convergence?. For example, which results in a smaller variance:\n",
"\n",
"```python\n",
" movement_variance = 40\n",
" sensor_variance = 2\n",
" sensor_variance = 2```\n",
" \n",
"or:\n",
"\n",
"```python\n",
" movement_variance = 2\n",
" sensor_variance = 40"
" sensor_variance = 40```"
]
},
{
@@ -2467,9 +2478,11 @@
"\n",
"Do you suppose that this filter works well or poorly with nonlinear systems?\n",
"\n",
"Implement a Kalman filter that uses the following equation to generate the measurement value for i in range(100):\n",
"Implement a Kalman filter that uses the following equation to generate the measurement value\n",
"\n",
" Z = math.sin(i/3.) * 2\n",
"```python\n",
"for i in range(100):\n",
" Z = math.sin(i/3.) * 2```\n",
" \n",
"Adjust the variance and initial positions to see the effect. What is, for example, the result of a very bad initial guess?"
]
@@ -2745,7 +2758,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+2 -2
View File
@@ -303,7 +303,7 @@
"What might a **multivariate normal distribution** be? *Multivariate* means multiple variables. Our goal is to be able to represent a normal distribution across multiple dimensions. I don't necessarily mean spatial dimensions - it could be position, velocity, and acceleration. Consider a two dimensional case. Let's say we believe that $x = 2$ and $y = 17$. This might be the *x* and *y* coordinates for the position of our dog, it might be the position and velocity of our dog on the x-axis, or the temperature and wind speed at our weather station. It doesn't really matter. We can see that for $N$ dimensions, we need $N$ means, which we will arrange in a column matrix (vector) like so:\n",
"\n",
"$$\n",
"\\mu = \\begin{bmatrix}{\\mu}_1\\\\{\\mu}_2\\\\ \\vdots \\\\{\\mu}_n\\end{bmatrix}\n",
"\\mu = \\begin{bmatrix}\\mu_1\\\\\\mu_2\\\\ \\vdots \\\\\\mu_n\\end{bmatrix}\n",
"$$\n",
"\n",
"Therefore for this example we would have\n",
@@ -1427,7 +1427,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+36 -26
View File
@@ -452,9 +452,8 @@
"\n",
"I have programmed the equations of the Kalman filter into the `KalmanFilter` class in FilterPy. You will import it with\n",
"\n",
" from filterpy.kalman import KalmanFilter\n",
" \n",
"The class contains variables **blah**"
"```python\n",
"from filterpy.kalman import KalmanFilter```"
]
},
{
@@ -680,8 +679,9 @@
"\n",
"and implemented it with\n",
"\n",
" def predict(pos, variance, movement, movement_variance):\n",
" return (pos + movement, variance + movement_variance)\n",
"```python\n",
"def predict(pos, variance, movement, movement_variance):\n",
" return (pos + movement, variance + movement_variance)```\n",
" \n",
"If the current position is 5.4 meters, and the movement is 1.1 meters over the time step, then of course the new position is 5.4 + 1.1, or 6.5 meters.\n",
"\n",
@@ -693,12 +693,14 @@
"\n",
"But we need to generalize this. The Kalman filter equations work with *any* linear system, not just Newtonian ones. Maybe the system you are filtering is the plumbing system in a chemical plant, and the amount of flow in a given pipe is determined by a linear combination of the settings of different valves. \n",
"\n",
"$$ \\mathtt{pipe_1 flow} = 0.134 (\\mathtt{valve}_1) + 0.41(\\mathtt{valve}_2 - \\mathtt{valve}_3) + .34 \\\\\n",
"$$\\mathtt{pipe_1 flow} = 0.134 (\\mathtt{valve}_1) + 0.41(\\mathtt{valve}_2 - \\mathtt{valve}_3) + .34 \\\\\n",
"\\mathtt{pipe_2 flow} = 0.21 (\\mathtt{valve}_2) - 0.6(\\mathtt{valve}_1 - \\mathtt{valve}_5) + 1.86$$\n",
"\n",
"Linear algebra has a powerful way to express systems of equations. Take this system\n",
"\n",
"$$2x+3y=8\\\\3x-y=1$$\n",
"$$\\begin{cases}\n",
"2x+3y=8\\\\3x-y=1\n",
"\\end{cases}$$\n",
"\n",
"We can put this in matrix form by writing:\n",
"\n",
@@ -1081,8 +1083,9 @@
"\n",
"We need to convert the temperature into a voltage so we can perform the subtraction. For the thermometer it might read:\n",
"\n",
" CELSIUS_TO_VOLTS = 0.21475\n",
" residual = measurement - CELSIUS_TO_VOLTS * predicted_state\n",
"```python\n",
"CELSIUS_TO_VOLTS = 0.21475\n",
"residual = measurement - CELSIUS_TO_VOLTS * predicted_state```\n",
" \n",
"The Kalman filter generalizes this problem by having you supply a **measurement function** that converts a state into a measurement. \n",
"\n",
@@ -1325,27 +1328,31 @@
"\n",
"The function `filter_dog()` implements the filter itself. Lets work through it line by line. The first line creates the simulation of the DogSensor, as we have seen in the previous chapter.\n",
"\n",
" dog = DogSensor(velocity=1, noise=noise)\n",
"```python\n",
"dog = DogSensor(velocity=1, noise=noise)```\n",
"\n",
"The next line uses our helper function to create a Kalman filter.\n",
"\n",
" dog_filter = dog_tracking_filter(R=R, Q=Q, cov=500.)\n",
" \n",
"```python\n",
"dog_filter = dog_tracking_filter(R=R, Q=Q, cov=500.)```\n",
"\n",
"We will want to plot the filtered position, the measurements, and the covariance, so we will need to store them in lists. The next three lines initialize empty lists of length *count* in a Pythonic way.\n",
"\n",
" pos = [None] * count\n",
" zs = [None] * count\n",
" cov = [None] * count\n",
" \n",
"```python\n",
"pos = [None] * count\n",
"zs = [None] * count\n",
"cov = [None] * count```\n",
"\n",
"Finally we get to the filter. All we need to do is perform the update and predict steps of the Kalman filter for each measurement. The `KalmanFilter` class provides the two functions `update()` and `predict()` for this purpose. `update()` performs the measurement update step of the Kalman filter, and so it takes a variable containing the sensor measurement. \n",
"\n",
"Absent the bookkeeping work of storing the filter's data, the for loop reads:\n",
"\n",
" for t in range(count):\n",
" z = dog.sense()\n",
" dog_filter.update (z)\n",
" dog_filter.predict()\n",
" \n",
"```python\n",
"for t in range(count):\n",
" z = dog.sense()\n",
" dog_filter.update (z)\n",
" dog_filter.predict()```\n",
"\n",
"It really cannot get much simpler than that. As we tackle more complicated problems this code will remain largely the same; all of the work goes into setting up the `KalmanFilter` variables; executing the filter is trivial.\n",
"\n",
"Now let's look at the result. Here is some code that calls `filter_track()` and then plots the result. It is fairly uninteresting code, so I will not walk through it. The `DogSimulation` class from the previous chapter has been placed in `DogSimulation.py`. I have also implemented a few plot routines to visualize the data in `mkf_internal.py`. Both of these are in the *code* subdirectory if you wish to read them.\n",
@@ -2660,16 +2667,19 @@
"\n",
"First collect your measurements into an array or list. Maybe it is in a CSV file, for example.\n",
"\n",
" zs = read_altitude_from_csv()\n",
"```python\n",
"zs = read_altitude_from_csv()```\n",
"\n",
"Or maybe you will generate it using a generator:\n",
"\n",
" zs = [some_func(i) for i in range(1000)]\n",
"```python\n",
"zs = [some_func(i) for i in range(1000)]```\n",
"\n",
"Then call the `batch_filter()` method.\n",
"\n",
" Xs, Ps, Xs_pred, Ps_pred = kfilter.batch_filter(zs)\n",
" \n",
"```python\n",
"Xs, Ps, Xs_pred, Ps_pred = kfilter.batch_filter(zs)```\n",
"\n",
"The function takes the list/array of measurements, filters it, and returns a list of state estimates (Xs), covariance matrices (Ps), and the predictions for the same (Xs_pred, Ps_pred).\n",
"\n",
"Here is a complete example."
@@ -2925,7 +2935,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+19 -17
View File
@@ -350,28 +350,30 @@
"source": [
"This is still a little hard to compare to Bayes' equation because we are dealing with probability distributions rather than probabilities. So let's cast our minds back to the discrete Bayes chapter where we computed the probability that our dog was at any given position in the hallway. It looked like this:\n",
"\n",
" def update(pos_belief, measure, p_hit, p_miss):\n",
" for i in range(len(hallway)):\n",
" if hallway[i] == measure:\n",
" pos_belief[i] *= p_hit\n",
" else:\n",
" pos_belief[i] *= p_miss\n",
"```python\n",
"def update(pos_belief, measure, p_hit, p_miss):\n",
" for i in range(len(hallway)):\n",
" if hallway[i] == measure:\n",
" pos_belief[i] *= p_hit\n",
" else:\n",
" pos_belief[i] *= p_miss\n",
"\n",
" pos_belief /= sum(pos_belief)\n",
" pos_belief /= sum(pos_belief)```\n",
"\n",
"Let's rewrite this using our newly learned terminology.\n",
"\n",
" def update(prior_probability, measure, prob_hit, prob_miss):\n",
" posterior_probability = np.zeros(len(prior_probability))\n",
" for i in range(len(hallway)):\n",
" if hallway[i] == measure:\n",
" posterior_probability[i] = prior_probability[i] * p_hit\n",
" else:\n",
" posterior_probability[i] = prior_probability[i] * p_miss\n",
"```python\n",
"def update(prior_probability, measure, prob_hit, prob_miss):\n",
" posterior_probability = np.zeros(len(prior_probability))\n",
" for i in range(len(hallway)):\n",
" if hallway[i] == measure:\n",
" posterior_probability[i] = prior_probability[i] * p_hit\n",
" else:\n",
" posterior_probability[i] = prior_probability[i] * p_miss\n",
"\n",
" return posterior_probability / sum(posterior_probability)```\n",
"\n",
" return posterior_probability / sum(posterior_probability)\n",
"\n",
" \n",
"So what is this doing? It's multiplying the old belief that the dog is at position *i* (prior probability) with the probability that the measurement is correct for that position, and then dividing by the total probability for that new event.\n",
"\n",
"Now let's look at Bayes' equation again.\n",
@@ -2910,7 +2912,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+8 -6
View File
@@ -2792,7 +2792,8 @@
"source": [
"So to create a trajectory starting at (0, 15) with a velocity of $100 \\frac{m}{s}$ and an angle of $60^\\circ$ we would write:\n",
"\n",
" traj = BallTrajectory2D(x0=0, y0=15, velocity=100, theta_deg=60)\n",
"```python\n",
"traj = BallTrajectory2D(x0=0, y0=15, velocity=100, theta_deg=60)```\n",
" \n",
"and then call `traj.position(t)` for each time step. Let's test this "
]
@@ -3052,11 +3053,12 @@
"source": [
"We already performed this step when we tested the state transition function. Recall that we computed the initial velocity for $x$ and $y$ using trigonometry, and set the value of $\\mathbf{x}$ with:\n",
"\n",
" omega = radians(omega)\n",
" vx = cos(omega) * v0\n",
" vy = sin(omega) * v0\n",
"```python\n",
"omega = radians(omega)\n",
"vx = cos(omega) * v0\n",
"vy = sin(omega) * v0\n",
"\n",
" f1.x = np.array([[x, vx, y, vy]]).T\n",
"f1.x = np.array([[x, vx, y, vy]]).T```\n",
" \n",
" \n",
"With all the steps done we are ready to implement our filter and test it. First, the implementation:"
@@ -3606,7 +3608,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+2 -2
View File
@@ -417,7 +417,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Unfortunately Gaussians are not closed under an arbitrary nonlinear function. Recall the equations of the Kalman filter - at each step of its evolution we do things like pass the covariances through our process function to get the new covariance at time $k$. Our process function was always linear, so the output was always another Gaussian. Let's look at that on a graph. I will take an arbitrary Gaussian and pass it through the function $f(x) = 2x + 1$ and plot the result. We know how to do this analytically, but lets do this with sampling. I will generate 500,000 points on the Gaussian curve, pass it through the function, and then plot the results. I will do it this way because the next example will be nonlinear, and we will have no way to compute this analytically."
"Unfortunately Gaussians are not closed under an arbitrary nonlinear function. Recall the equations of the Kalman filter - at each evolution (prediction) we pass the Gaussian representing the state through the process function to get the Gaussian at time $k$. Our process function was always linear, so the output was always another Gaussian. Let's look at that on a graph. I will take an arbitrary Gaussian and pass it through the function $f(x) = 2x + 1$ and plot the result. We know how to do this analytically, but lets do this with sampling. I will generate 500,000 points with a normal distribution, pass it through the function, and then plot the results. I do it this way because the next example will be nonlinear, and we will have no way to compute this analytically."
]
},
{
@@ -927,7 +927,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
File diff suppressed because it is too large. Load diff
+23 -18
View File
@@ -749,29 +749,33 @@
"\n",
"We start by importing the filter and creating it. There are 3 variables in `x` and only 1 measurement. At the same time we will create our radar simulator.\n",
"\n",
" from filterpy.kalman import ExtendedKalmanFilter\n",
"```python\n",
"from filterpy.kalman import ExtendedKalmanFilter\n",
"\n",
"rk = ExtendedKalmanFilter(dim_x=3, dim_z=1)\n",
"radar = RadarSim(dt, pos=0., vel=100., alt=1000.)```\n",
"\n",
" rk = ExtendedKalmanFilter(dim_x=3, dim_z=1)\n",
" radar = RadarSim(dt, pos=0., vel=100., alt=1000.)\n",
" \n",
"We will initialize the filter near the airplane's actual position\n",
"\n",
" rk.x = array([radar.pos, radar.vel-10, radar.alt+100])\n",
" \n",
"```python\n",
"rk.x = array([radar.pos, radar.vel-10, radar.alt+100])```\n",
"\n",
"We assign the system matrix using the first term of the Taylor series expansion we computed above.\n",
"\n",
" dt = 0.05\n",
" rk.F = eye(3) + array([[0, 1, 0],\n",
" [0, 0, 0],\n",
" [0, 0, 0]])*dt\n",
" \n",
"```python\n",
"dt = 0.05\n",
"rk.F = eye(3) + array([[0, 1, 0],\n",
" [0, 0, 0],\n",
" [0, 0, 0]])*dt```\n",
"\n",
"After assigning reasonable values to $\\mathbf{R}$, $\\mathbf{Q}$, and $\\mathbf{P}$ we can run the filter with a simple loop\n",
"\n",
" for i in range(int(20/dt)):\n",
" z = radar.get_range()\n",
" rk.update(array([z]), HJacobian_at, hx)\n",
" rk.predict()\n",
" \n",
"```python\n",
"for i in range(int(20/dt)):\n",
" z = radar.get_range()\n",
" rk.update(array([z]), HJacobian_at, hx)\n",
" rk.predict()```\n",
"\n",
"Putting that all together along with some boilerplate code to save the results and plot them, we get"
]
},
@@ -1448,7 +1452,8 @@
"\n",
"First, `evalf` uses a dictionary to pass in the values you want to use. For example, if your matrix contains an x and y, you can write\n",
"\n",
" M.evalf(subs={x:3, y:17})\n",
"```python\n",
" M.evalf(subs={x:3, y:17})```\n",
" \n",
"to evaluate the matrix for `x=3` and `y=17`. \n",
"\n",
@@ -1924,7 +1929,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+171 -170
View File
@@ -16,7 +16,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {
"collapsed": false
},
@@ -254,7 +254,7 @@
"<IPython.core.display.HTML object>"
]
},
"execution_count": 1,
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
@@ -315,16 +315,16 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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 truncated
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x58b8860>"
"<matplotlib.figure.Figure at 0x41394e0>"
]
},
"metadata": {},
@@ -389,7 +389,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 4,
"metadata": {
"collapsed": false
},
@@ -398,7 +398,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x8fcf400>"
"<matplotlib.figure.Figure at 0x7da0080>"
]
},
"metadata": {},
@@ -433,30 +433,28 @@
"\n",
"Let's use it to compute $\\pi$ by computing the area of a circle. This is very easy. We will create a circle with radius 1, and bound it in a square. The means that the length of a side of the box will be 2, and hence the area of the box is 4. We will generate a set of uniformly distributed random points within the box, and count how many fall inside the circle. The area of the circle is then the area of the box times the ratio of points inside the circle over the total number number of points. Finally, we know that $A = \\pi r^2$, so $\\pi = A / r^2$.\n",
"\n",
"We start by creating the points."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" N = 20000\n",
" ps = uniform(-1, 1, (N, 2))\n",
"We start by creating the points.\n",
"\n",
"```python\n",
"N = 20000\n",
"ps = uniform(-1, 1, (N, 2))```\n",
"\n",
"A point is inside the circle if it's distance from the center point (0, 0) is less than or equal to one. We can compute the distance by using `numpy.linalg.norm`. If you aren't familiar with this, the norm is the magnitude of a vector. Since vectors start at (0, 0) calling norm will compute the point's distance from the origin.\n",
"\n",
" dist = np.linalg.norm(ps, axis=1)\n",
"```python\n",
"dist = np.linalg.norm(ps, axis=1)```\n",
"\n",
"Next we compute which of this distances fit the criteria with this code, which returns a bool array that contains `True` if it meets the condition\n",
"\n",
" in_circle = dist <= 1\n",
"```python\n",
"in_circle = dist <= 1```\n",
"\n",
"All that is left is to count the points inside the circle, compute pi, and plot the results. I've put it all in one cell so you can experiment with alternative values for `N`."
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 5,
"metadata": {
"collapsed": false
},
@@ -473,7 +471,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x9771f28>"
"<matplotlib.figure.Figure at 0x7dd30f0>"
]
},
"metadata": {},
@@ -517,7 +515,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 6,
"metadata": {
"collapsed": false
},
@@ -526,7 +524,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x961d7f0>"
"<matplotlib.figure.Figure at 0x7dcc898>"
]
},
"metadata": {},
@@ -551,7 +549,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 7,
"metadata": {
"collapsed": false
},
@@ -560,7 +558,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x97c0710>"
"<matplotlib.figure.Figure at 0x82b7198>"
]
},
"metadata": {},
@@ -605,7 +603,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 8,
"metadata": {
"collapsed": true
},
@@ -645,16 +643,17 @@
"\n",
"I cannot easily modify a class definition in Jupyter Notebook, so I will show you the method in text. The entire class is in pf_internal, and we will be importing when we run the filter. \n",
"\n",
" def predict(self, u, std, dt=1.):\n",
" \"\"\" move according to control input u (heading change, velocity) \n",
" with noise std\"\"\"\n",
"```python\n",
"def predict(self, u, std, dt=1.):\n",
" \"\"\" move according to control input u (heading change, velocity) \n",
" with noise std\"\"\"\n",
"\n",
" self.particles[:, 2] += u[0] + randn(self.N) * std[0]\n",
" self.particles[:, 2] %= 2 * np.pi\n",
" self.particles[:, 2] += u[0] + randn(self.N) * std[0]\n",
" self.particles[:, 2] %= 2 * np.pi\n",
"\n",
" d = u[1]*dt + randn(self.N) * std[1]\n",
" self.particles[:, 0] += np.cos(self.particles[:, 2]) * d\n",
" self.particles[:, 1] += np.sin(self.particles[:, 2]) * d"
" d = u[1]*dt + randn(self.N) * std[1]\n",
" self.particles[:, 0] += np.cos(self.particles[:, 2]) * d\n",
" self.particles[:, 1] += np.sin(self.particles[:, 2]) * d```"
]
},
{
@@ -667,22 +666,23 @@
"\n",
"Think back to the *Discrete Bayes* chapter. In that chapter we modeled positions in a hallway as discrete and uniformly spaced. We assigned a probability to each position. When a new measurement came in we multiplied the current probability of that position by the probability that the measurement matched that location. We implemented that like this:\n",
"\n",
" def update(map_, belief, z, prob_correct):\n",
" scale = prob_correct / (1. - prob_correct)\n",
" for i, val in enumerate(map_):\n",
" if val == z:\n",
" belief[i] *= scale\n",
" normalize(belief)\n",
"```python\n",
"def update(map_, belief, z, prob_correct):\n",
" scale = prob_correct / (1. - prob_correct)\n",
" for i, val in enumerate(map_):\n",
" if val == z:\n",
" belief[i] *= scale\n",
" normalize(belief)```\n",
"\n",
"We will want to do the same thing with our particles. Each particle has a position. We can also assign it a weight or probability based on how well it matches the measurement. We will want to multiply that new probability into the current weight of the particle. If we then normalize the weights of the particles those that are closest to the robot will generally have a higher weight than ones far from the robot.\n",
"\n",
" def update(self, z):\n",
" self.weights.fill(1.)\n",
" for i, landmark in enumerate(self.landmarks):\n",
" dist = np.linalg.norm(self.particles[:, 0:2] - landmark, axis=1)\n",
" self.weights *= scipy.stats.norm(dist, self.R).pdf(z[i])\n",
" self.weights /= sum(self.weights) # normalize\n",
"\n",
"```python\n",
"def update(self, z):\n",
" self.weights.fill(1.)\n",
" for i, landmark in enumerate(self.landmarks):\n",
" dist = np.linalg.norm(self.particles[:, 0:2] - landmark, axis=1)\n",
" self.weights *= scipy.stats.norm(dist, self.R).pdf(z[i])\n",
" self.weights /= sum(self.weights) # normalize```\n",
"\n",
"In the literature this part of the algorithm is called **Sequential Importance Sampling**, or SIS, and the equation for the weights is called the **importance density**. I will give it theoretical underpinnings in a following section. For now I hope that this makes intuitive sense. If we weight the particles according to how how they match the measurements they *probably* are a good sample for the probability distribution of the system after incorporating the measurements. Theory changes 'probably are a good sample' to 'are a good sample'. Different problems will need to tackle this step in slightly different ways.\n",
"\n",
@@ -722,15 +722,16 @@
"\n",
"One way to accomplish this is to sample from the current particle set `N` times, making a new set of particles from the sample. For this to work the probability of selecting any given particle should be proportional to its weight. The easiest way to accomplish this is to use NumPy's `cumsum` function. `cumsum` computes the *cumulative* sum of an array. That is, element one is the sum of elements zero and one, element two is the sum of elements zero and one, etc. Then we generate random numbers in the range of 0.0 to 1.0 and do a binary search to find the weight that most closely matches that number. This algorithm is variously called **multinomial resampling** or **simple random resampling**. Here is my implementation:\n",
"\n",
" def resample(self):\n",
" cumulative_sum = np.cumsum(self.weights)\n",
" cumulative_sum[-1] = 1. # avoid round-off error\n",
" indexes = np.searchsorted(cumulative_sum, random(self.N))\n",
" \n",
" # resample according to indexes\n",
" self.particles = self.particles[indexes]\n",
" self.weights = self.weights[indexes]\n",
" self.weights /= np.sum(self.weights) # normalize\n",
"```python\n",
"def resample(self):\n",
" cumulative_sum = np.cumsum(self.weights)\n",
" cumulative_sum[-1] = 1. # avoid round-off error\n",
" indexes = np.searchsorted(cumulative_sum, random(self.N))\n",
"\n",
" # resample according to indexes\n",
" self.particles = self.particles[indexes]\n",
" self.weights = self.weights[indexes]\n",
" self.weights /= np.sum(self.weights) # normalize```\n",
" \n",
"There are many resampling algorithms. Each has different properties with respect to which particles are sampled, execution time, and memory required. I will share a few of them in a later section.\n",
"\n",
@@ -740,9 +741,9 @@
"\n",
"and we can implement this in Python with\n",
"\n",
" def neff(self):\n",
" return 1. / np.sum(np.square(self.weights))\n",
"\n",
"```python\n",
"def neff(self):\n",
" return 1. / np.sum(np.square(self.weights))```\n",
"\n",
"If $\\hat{N}_{eff}$ falls below some threshold it is time to resample. A useful starting point is $\\frac{1}{2}N$, but this varies by problem. It is also possible for $\\hat{N}_{eff} = N$, which means the particle set has collapsed to one point (each has equal weight). It may not be theoretically pure, but if that happens I create a new distribution of particles in the hopes of generating particles with more diversity. If this happens to you often, you may need to increase the number of particles, or otherwise adjust your filter. We will talk more of this later."
]
@@ -764,7 +765,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 9,
"metadata": {
"collapsed": false
},
@@ -776,7 +777,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 10,
"metadata": {
"collapsed": false
},
@@ -792,7 +793,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x844b780>"
"<matplotlib.figure.Figure at 0x8259b38>"
]
},
"metadata": {},
@@ -857,14 +858,15 @@
"source": [
"Most of this code is devoted to initialization and plotting. The entirety of the particle filter processing consists of these lines:\n",
"\n",
" # distance from robot to each landmark\n",
" zs = norm(landmarks - pos, axis=1) + randn(NL)*sensor_std_err\n",
"```python\n",
"# distance from robot to each landmark\n",
"zs = norm(landmarks - pos, axis=1) + randn(NL)*sensor_std_err\n",
"\n",
" # move diagonally forward to (x+1, x+1)\n",
" pf.predict(u=(0.00, 1.414), std=(.2, .05))\n",
" pf.update(z=zs)\n",
" if pf.neff() < N/2:\n",
" pf.resample()\n",
"# move diagonally forward to (x+1, x+1)\n",
"pf.predict(u=(0.00, 1.414), std=(.2, .05))\n",
"pf.update(z=zs)\n",
"if pf.neff() < N/2:\n",
" pf.resample()```\n",
"\n",
"The first line takes advantage of `numpy.linalg.norm`, which computes the Euclidian distance of a vector. In other words, we compute the distance of the robot to each landmark, and add in sensor noise so that this is a realistic simulation. Uou wouldn't add noise if this was real sensor data, of course!\n",
"\n",
@@ -879,7 +881,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 11,
"metadata": {
"collapsed": false
},
@@ -895,7 +897,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x8a8a860>"
"<matplotlib.figure.Figure at 0x8cc08d0>"
]
},
"metadata": {},
@@ -931,7 +933,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 12,
"metadata": {
"collapsed": false,
"scrolled": true
@@ -948,7 +950,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x964d320>"
"<matplotlib.figure.Figure at 0x82bab38>"
]
},
"metadata": {},
@@ -968,8 +970,9 @@
"\n",
"Another approach is to be smarter about generating the initial particle cloud. Suppose we know that the robot is near (0,0). This is not exact, as the simulation actually places the robot at (1,1), but it is close. If instead of creating a uniform cloud of particles over the entire map we made a normally distributed cloud near (0, 0) there is a much greater chance of the particles matching the robot's position. The particle filter code includes the method `create_gaussian_particles()`; feel free to alter the code above to use this function as in the code snippet below. However, we will be using this in the next section to help with a different problem, so feel free to wait.\n",
"\n",
" pf = RobotLocalizationParticleFilter(N, 20, 20, landmarks, sensor_std_err)\n",
" pf.create_gaussian_particles(mean=(0, 0), var=(10,10))\n"
"```python\n",
"pf = RobotLocalizationParticleFilter(N, 20, 20, landmarks, sensor_std_err)\n",
"pf.create_gaussian_particles(mean=(0, 0), var=(10,10))```"
]
},
{
@@ -983,7 +986,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 13,
"metadata": {
"collapsed": false
},
@@ -999,7 +1002,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0xa99af98>"
"<matplotlib.figure.Figure at 0x82f08d0>"
]
},
"metadata": {},
@@ -1022,7 +1025,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 14,
"metadata": {
"collapsed": false
},
@@ -1038,7 +1041,7 @@
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAtsAAAEWCAYAAAC37ltfAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsvXtwW+d55/99Dw6vAAiSuBCgRFuyLYmSLcmSrVjOZFU7\nTTLy1Ekn2Y6b9XQc/xHRv9lkmrTTTZtpG1LZNG1+vzTT7iTtmpxtlct6G01nmsx6Jxo3TVyOx/Ev\nEnWxZEmkZFsyRNxBEgTA68F594+jc3QA4o4D4IB8PjMYUcC5POf6ft/nfd7nYZxzDoIgCIIgCIIg\nDEdotgEEQRAEQRAEsVkhsU0QBEEQBEEQdYLENkEQBEEQBEHUCRLbBEEQBEEQBFEnSGwTBEEQBEEQ\nRJ0gsU0QBEEQBEEQdYLENkEQBEEQBEHUiaJi+y//8i9x5MgROBwOeDwefOpTn8I777yzYbmxsTFs\n27YN3d3dePrpp3H16tW6GUwQBEEQBEEQrUJRsf3v//7v+OIXv4hf/epX+MUvfgFRFPGxj30M8/Pz\n2jLf+ta38J3vfAff/e53cfbsWXg8Hnz84x9HKpWqu/EEQRAEQRAEYWZYJRUk0+k0HA4HfvrTn+K3\nfuu3wDnH4OAgfv/3fx9f/epXAQArKyvweDz49re/jZGRkboZThAEQRAEQRBmp6KY7cXFRciyjL6+\nPgDA+++/j3A4jE984hPaMp2dnTh27BjefPNNYy0lCIIgCIIgiBajIrH9pS99CYcOHcKTTz4JAAiF\nQgCAgYGBrOU8Ho/2G0EQBEEQBEFsVcRyF/zDP/xDvPnmm3jjjTfAGCu5fO4yiUSicusIgiAIgiAI\nwiQ4HI6K1ynLs/0Hf/AH+PGPf4xf/OIX2LFjh/a91+sFAITD4azlw+Gw9htBEARBEARBbFVKiu0v\nfelLmtDevXt31m87d+6E1+vFa6+9pn23srKCN954Ax/+8IeNt5YgCIIgCIIgWoiiYSRf+MIX8KMf\n/Qg/+clP4HA4tDhsu90Oq9UKxhi+/OUv45vf/CaGh4exa9cufOMb34Ddbsfzzz9fcLvVuOAJopGc\nO3cOAPD444832RKCKA3dr0SrQfcs0UrUGgpdVGz//d//PRhj+M3f/M2s78fGxvC1r30NAPCVr3wF\ny8vL+MIXvoD5+XkcPXoUr732GqxWa02GEQRBEARBEESrU1Rsy7Jc1kZGR0cxOjpqiEEEQRAEQRAE\nsVmoKPUfQRAEQRAEQRDlQ2KbIAiCIAiCIOoEiW2CIAiCIAiCqBMktgmCIAiCIAiiTpDYJgiCIAiC\nIIg6QWKbIAiCIAiCIOoEiW2CIAiCIAiCqBMktgmCIAiCIAiiTpDYJgiCIAiCIIg6QWKbIAiCIAiC\nIOoEiW2CIAiCIAiCqBMktgmCIAiCIAiiTojNNoAgCIIgCIIwB2NjY3n/JqqHxDZBEARBEMQmY2Rk\nBKdOnUJ3dzcWFhby/g4A4+PjWd+fPHlS+5vEtjGQ2CYIgiAIgthkTE5OYn19HYlEAoKgRA1zzjcs\nNzExAQA4ceLEBuFNGAPFbBMEQRAEQWwyjh07hra2NgCKyM4ntPVMTEyAMdYI07Yc5NkmCIIgCILY\nZKhe6tOnT2NpaQnr6+t5l3sZwG7d/2cAvHT370KhJkRlkGebIAiCIAhiEzI+Po6FhQW8+OKLmpc7\nl90AntJ99MJ7YmJC83gPDw/X2drNC4ltgiAIgiCITcz4+DjW1tbAOceJEycKCu9iTE9PgzGGzs7O\nOli4uSGxTRAEQRAEsUVQhbfT6axq/dXVVQiCoIWYEKUhsU0QBEEQBLHF+OIXvwhAidF+XfeZKWNd\nzjlOnz5NgrtMTCm2JVmCP+GHP+GHJEvNNocgCIIgCGJTMTY2Bs45RjjHU7rPS7plHA5HwfW9Xm/9\njdwkmC4biSRLODt7FgJT+gF3Fu/gyLYjEAXTmUoQBEEQBLFpUYvh6D3Yk5OTOHbsGGUoqQDTKdhg\nMgiBCZq4lmQJwWQQQ46hJltGEARBEASx9SBhXRumE9sEQRAEQRCtTDX5qfWl0alM+ubCdGLbZ/fh\nzuIdLVZb5jJ8dl+TrSIIgiAIgihO7oTBkZERnD59Gl6vF9evXy+67smTJ7W/SWxvLkwntkVBxJFt\nRxBMBgEo4pvitQmCIAiCaBXGx8cxMjKCU6dOYX19HYlEIqsUeltbG9bW1kyZzWN0dLTZJmw6TKli\nRUGkGG2CIAiCIFoKfdjI5ORkwRLp6+vrWeLbTJBX3XhMmfqPIAiCIAiilTl27FjR1HnFoKIxmwsS\n2wRBEARBEAYzPj6OhYUF7Nmzp6zlXwbwy7ufX3COxycmTOv9JiqDxDZBEARBEESduH79Ojjn4Jzj\nxIkTBZfbDeAp3Wf33e8ZY9qnvb0d7e3tdbaYMBoS2wRBEARBEA1gfHwchw8fbrYZRIMhsU0QBEEQ\nBNEgPvnJT1a97vr6uja5cnh4mOK6WwQS2yZAkiX4E374E34tvzhBEARBEJuPsbExLaxE/5kB8Lru\nM1NiOzMzM5icnKyvsYQhmDL131ZCkiWcnT0LgSn9njuLd3Bk2xHKLU4QBEEQOnp7ewEACwsLTbak\nPryk+5tzjqcAvFRkgiTnvN4mEQZBnu0mE0wGITABoiBCFEQITNAK+hAEQRAEcY/FxUUMDw9rn82O\n6vV2OBxwOBzaJEv1/8eOHWu2iUQZkPuUIAiCIAjTs7CwgOHhYYRCISQSCQDYkBpPTbNXqjS6ir6A\ni5mLueR685977rmsAjqEuWG8QeMQ6oMBoGSSd0mWtky59twwkrXMGrY7tkNk4qY/djNz7tw5AMDj\njz/eZEsIojR0vxKtRi337MjICCYmJipax+Fw5A0/0Yv1ZodlmMkWIptKNGw+TKfktloMsyiIOLLt\nCILJICRZwmxyFpFUBMDmP3aCIAiCqBTVo3v69OksEVSMRCIBxpipRezo6GizTSDqhOlUnD6GGbjn\n5R5yDDXZsvohCiKGHEPwJ/xoE9q21LETBEEQRKWMj49jfHy8ZIXFl3GvOAwAjDOmTURsa2url3lV\nYeYwFqI2aIIkQRAEQRAtiTphsJDoLlSVEVByVhNEIzCd2PbZfZC5DEmWIMkSZC7DZ/c126yGsJWP\nnSAIgiCqYXx8HLIsa5k7qkUtEkOFYgijMV0YiT6GGdj8EyT1tPqxb6WJrQRBEIQ5UQV3qRCTXKan\npzE9PQ0A2gTM0dFRCu8gasaUakiNYd6KtOqxb7WJrQRBEIS54ZwDIyN44x/+AVImA6B0VUaCqAem\nCyMhWhMqzkMQBEGYjvFxfESS8ProKJ5GdpVGgmgUJLYJIg+SLCG0HII/4YckS802hyAIgqiBsbEx\nLaZbnVSpFsApRFtbG8bGxiiOm6gZGuMnDMFn9+HO4h1NmLby5E5JlnA1cRUMDIFkgEJiCIIgNhlq\nru58hWRUYU0VGgmjIPVAGEKrT+7UE0wGwcC0kBjKd04QBLF1yBXZJLqJWmlNNUSYklad3EkQBEE0\nFlXAvvrqq5Ttg9j0lIzZnpycxKc+9Sls374dgiDg+9//ftbvL774IgRByPp8+MMfrpvBBFFvfHYf\nODjlOycIgigDfUxzob9zmZiYwMTEBE6ePNkwOwmiWZT0bKfTaRw4cACf+9zn8MILL2zIW8kYw8c/\n/nH88Ic/1L5rb2833lKCaBCiIGKfYx9iqzEM2gdbOiSGIAii3kxOTiIUCmn/P3XqFCYmJsAYw+7d\nuzEyMmLaUIzR0dFmm0BsAUoqiGeeeQbPPPMMAMWLnQvnHO3t7fB4PIYbRxDNQhREeLu8FBZDEARR\ngmPHjmFychKTk5MA7pVB55xrhWLUIjGMMXz+859vmq25UAgL0QhqTv3HGMMbb7yBgYEB7NmzByMj\nI4hGo0bYRhAEQRCEyRkfH8f169cBIMvDnQ/OuSa8VSitHrHZqVlsHz9+HD/84Q/xi1/8An/913+N\nX//61/joRz+KtbU1I+wjCCIPkizBn/BTHnCCIEzDsWPH8Nxzz+HEiRMVraeGnJDoJjYrjKuJJcvA\nbrfje9/7Hl544YWCywSDQdx///348Y9/jE9/+tPa94lEQvv7xo0bVZpLEIQ+DzgAcHDsc+yjuHKC\nIEzD7/zO7+D27dsFf38ZwG7d/2eQXd1xz549+NGPflQn6wiiMnbt2qX97XA4Kl7f8NbZ5/Nh+/bt\nuHnzptGbJghTIMkSYqsxAICrw9VwkRtbjWl5wPX2eLu8DbWDIOpNs581onr++Z//GQDwoQ99CPl8\nersBPFVk/enp6brYRRDNwPA3VzQaxezsLHy+wqnSHn/8caN3SxCGcu7cOQAb71VJlnB29izczA1A\nqZT56LZHGyoC/Ak/AslAltgetA/SZM4tTKH7tZUxw7NG1M7Xvva1qtP7HTlyBABw4sQJ02YzIbYG\n+uiMaigZs51Op3Hx4kVcvHgRsizj9u3buHjxIvx+P9LpNP7oj/4Ib731Fm7duoXXX38dn/rUpzAw\nMJAVQkIQm4VgMgiBCVp1SYEJWtXMRuGz+yBzmfKAb2IoJt8czxpRO2NjY+Ccax+Hw4G2traKtjEx\nMUEphYmWpqTYPnv2LA4fPozDhw9jZWUFo6OjOHz4MEZHR2GxWHDlyhX89m//Nvbs2YMXX3wRe/fu\nxa9+9StYrdZG2E8QWw5REHFk2xEM2gcxaB/EkW1HyNtXRxotfFWPbiAZQCAZwNnZs1tWcBObj4WF\nBaytrWEGwOu6z0yJ9dbX12kCJdGylGyhn3rqKciyXPD3M2fOGGoQQZgZn92HO4t3NPHTLK+yKIgb\nwkYkWdI8f1SIxxhU4SswxS9xZ/FO3Ts3eo+uakMwGdxyYUJmedY2E/qc0s3OL62fDLine truncated
"text/plain": [
"<matplotlib.figure.Figure at 0xb289cc0>"
"<matplotlib.figure.Figure at 0xaae44e0>"
]
},
"metadata": {},
@@ -1103,12 +1106,13 @@
"\n",
"When you formulate a particle filter algorithm you will have to implement this step depending on the particulars of your situation. For robot localization the best distribution to use for $q(x)$ is the particle distribution from the `predict()` step of the filter. Let's look at the code again:\n",
"\n",
" def update(self, z):\n",
" self.weights.fill(1.)\n",
" for i, landmark in enumerate(self.landmarks):\n",
" dist = np.linalg.norm(self.particles[:, 0:2] - landmark, axis=1)\n",
" self.weights *= scipy.stats.norm(dist, self.R).pdf(z[i])\n",
" self.weights /= sum(self.weights) # normalize\n",
"```python\n",
"def update(self, z):\n",
" self.weights.fill(1.)\n",
" for i, landmark in enumerate(self.landmarks):\n",
" dist = np.linalg.norm(self.particles[:, 0:2] - landmark, axis=1)\n",
" self.weights *= scipy.stats.norm(dist, self.R).pdf(z[i])\n",
" self.weights /= sum(self.weights) # normalize```\n",
" \n",
"The reason for `self.weights.fill(1.)` might have confused you; it confused me the first time I saw it. In all the Bayesian filters up to this chapter we started with the probability distribution created by the `predict` step, and this appears to discard that information by setting all of the weights to 1. Well, we are discarding the weights, but we do not discard the particles. That is a direct result of applying importance sampling - we draw from the known distribution, but weight by the unknown distribution. \n",
"\n",
@@ -1131,12 +1135,13 @@
"\n",
"FilterPy implements several of the popular algorithms. FilterPy doesn't know how your particle filter is implemented, so it doesn't make sense to have the resample algorithm generate the new samples. Instead, the algorithms create an ndarray containing the indexes of the weights and particles that are chosen; your class or code needs to perform the resampling step. For example, the robot localization class implements this with\n",
"\n",
" def resample_from_index(self, indexes):\n",
" assert len(indexes) == self.N\n",
"```python\n",
"def resample_from_index(self, indexes):\n",
" assert len(indexes) == self.N\n",
"\n",
" self.particles = self.particles[indexes]\n",
" self.weights = self.weights[indexes]\n",
" self.weights /= np.sum(self.weights)\n",
" self.particles = self.particles[indexes]\n",
" self.weights = self.weights[indexes]\n",
" self.weights /= np.sum(self.weights)```\n",
"\n",
"\n",
"### Multinomial Resampling\n",
@@ -1146,7 +1151,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 15,
"metadata": {
"collapsed": false
},
@@ -1162,7 +1167,7 @@
"data": {
"image/png": "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\nOP8CSdsd9j7kQ7EAAAAASUVORK5CYII=\n",
"text/plain": [
"<matplotlib.figure.Figure at 0xb260c88>"
"<matplotlib.figure.Figure at 0xb07b160>"
]
},
"metadata": {},
@@ -1184,17 +1189,18 @@
"\n",
"This is very easy to code using NumPy's ufunc support. `searchsorted` is NumPy's binary search algorithm. If you provide is with an array of search values it will return an array of answers; one answer for each search value. \n",
"\n",
" def multinomal_resample(weights):\n",
" cumulative_sum = np.cumsum(weights)\n",
" cumulative_sum[-1] = 1. # avoid round-off errors\n",
" return np.searchsorted(cumulative_sum, random(len(weights)))\n",
"```python\n",
"def multinomal_resample(weights):\n",
" cumulative_sum = np.cumsum(weights)\n",
" cumulative_sum[-1] = 1. # avoid round-off errors\n",
" return np.searchsorted(cumulative_sum, random(len(weights)))```\n",
" \n",
"Here is an example:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 16,
"metadata": {
"collapsed": false
},
@@ -1203,7 +1209,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0xb289be0>"
"<matplotlib.figure.Figure at 0xb062f98>"
]
},
"metadata": {},
@@ -1221,10 +1227,10 @@
"source": [
"This is an O(n log(n)) algorithm. That is not terrible, but there are O(n) resampling algorithms with better properties with respect to the uniformity of the samples. I show it to you first merely because you can understand the other algorithms as variations on this one. There is a faster implementation of this algorithm that uses the inverse of the CDF of the distribution, but since we will not be using this much, if ever, I won't cover it. You can search on the internet if you are interested.\n",
"\n",
"\n",
"You may import this from FilterPy using\n",
"\n",
" from filterpy.monte_carlo import multinomal_resample"
"```python\n",
"from filterpy.monte_carlo import multinomal_resample```"
]
},
{
@@ -1237,28 +1243,28 @@
"\n",
"However, this does not make *N* selections. To select the rest, we take the *residual*: the weights minus the integer part, which leaves the fractional part of the number. We then use a simpler sampling scheme such as multinomial, to select the rest of the particles based on the residual. In the example above the scaled weight was 3.6, so the residual will be 0.6 (3.6 - int(3.6)). This residual is very large so the particle will be likely to be sampled again. This is reasonable because the larger the residual the larger the error in the round off, and thus the particle was relatively under sampled in the integer step.\n",
"\n",
"```python\n",
"def residual_resample(weights):\n",
" N = len(weights)\n",
" indexes = np.zeros(N, 'i')\n",
"\n",
" def residual_resample(weights):\n",
" N = len(weights)\n",
" indexes = np.zeros(N, 'i')\n",
"\n",
" # take int(N*w) copies of each weight\n",
" num_copies = (N*np.asarray(weights)).astype(int)\n",
" k = 0\n",
" for i in range(N):\n",
" for _ in range(num_copies[i]): # make n copies\n",
" indexes[k] = i\n",
" k += 1\n",
" # take int(N*w) copies of each weight\n",
" num_copies = (N*np.asarray(weights)).astype(int)\n",
" k = 0\n",
" for i in range(N):\n",
" for _ in range(num_copies[i]): # make n copies\n",
" indexes[k] = i\n",
" k += 1\n",
"\n",
"\n",
" # use multinormial resample on the residual to fill up the rest.\n",
" residual = w - num_copies # get fractional part\n",
" residual /= sum(residual) # normalize\n",
" cumulative_sum = np.cumsum(residual)\n",
" cumulative_sum[-1] = 1. # avoid round-off errors: ensures sum is exactly one\n",
" indexes[k:N] = np.searchsorted(cumulative_sum, random(N-k))\n",
" # use multinormial resample on the residual to fill up the rest.\n",
" residual = w - num_copies # get fractional part\n",
" residual /= sum(residual) # normalize\n",
" cumulative_sum = np.cumsum(residual)\n",
" cumulative_sum[-1] = 1. # ensures sum is exactly one\n",
" indexes[k:N] = np.searchsorted(cumulative_sum, random(N-k))\n",
"\n",
" return indexes\n",
" return indexes```\n",
"\n",
"You may be tempted to replace the inner for loop with a slice `indexes[k:k + num_copies[i]] = i`, but very short slices are comparatively slow, and the for loop usually runs faster.\n",
"\n",
@@ -1267,7 +1273,7 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 17,
"metadata": {
"collapsed": false
},
@@ -1276,7 +1282,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x96c19e8>"
"<matplotlib.figure.Figure at 0xaac6240>"
]
},
"metadata": {},
@@ -1294,7 +1300,8 @@
"source": [
"You may import this from FilterPy using\n",
"\n",
" from filterpy.monte_carlo import residual_resample"
"```python\n",
" from filterpy.monte_carlo import residual_resample```"
]
},
{
@@ -1310,7 +1317,7 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 18,
"metadata": {
"collapsed": false
},
@@ -1319,7 +1326,7 @@
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAZMAAABCCAYAAACSCTtfAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAE5BJREFUeJzt3XtQVNcdwPHvXaAuBETELkJQUWt91BdOGiOogPGFUSIb\nazoBZ6oxTKwPJDqZSfOAxClGW5xQNTZqRjE2Tqv4IDE4qYqoPDSOChkFMSoE4oOAAbUlPsLtH7gb\n1r2ryAK70N9nhtE95+69v3t39/z2nnP2XkVVVRUhhBDCDjpHByCEEKL9k2QihBDCbpJMhBBC2E2S\niRBCCLtJMhFCCGE3SSZCCCHsJslEtIjS0lJ0Oh2zZ8+2qjt//jxGo5GAgAB0Oh06XcPbLikpCZ1O\nR3Z2dqvFdejQIXQ6He+++26rbeP/mek1PHz4sEW5TqcjIiLCQVEJR3B1dACiecLDwzl8+DClpaX0\n7Nmz1bdXWlpKnz59CAsLIysry+ZyiqJYPP7pp5+YPn06xcXFxMTE0LdvX4tlFEWxek5raIttCEty\nzP+/SDJpx9ryw2ralq1tBgYGUlxcjLe3t0X5pUuXKCoqYvLkyWzZssWibuHChbz00kv06NGjdYIW\nDlNcXIyHh4ejwxBtSJJJO6aqKm11AYNHbcfV1ZVf//rXVuWXL18GwM/Pz6rO19cXX1/flglQOBWt\n94Lo2GTMxMl89tlnjB8/noCAAPR6PQEBAYwePZrly5ebl2ncR927d2/zOETv3r3Ny4SHh6PT6bh0\n6RKpqakMHjwYd3d3oqOjAbhx4wZ/+ctfGDduHIGBgXTq1AmDwUBUVBR5eXkWMW3evJk+ffoAP49B\nmP5MYxFaYyY6nY7w8HAA0tLSzM957733ANv97QDffPMNc+fOpVevXuj1egwGA0ajkVOnTmket2vX\nrvHyyy/j5+eHh4cHwcHBVmdCTbF582bzfuXn5xMZGYmPjw86nY4bN24AUF9fz8aNGwkNDcXb2xt3\nd3eGDBnC+++/z927d63WeeTIEaKioujRowd6vR4/Pz9++9vfsnTpUovlLl++zHvvvUdoaCjdu3en\nU6dOPPnkk7z00kucPXvWar2mYx4REUFlZSVz5szBz88PT09PRo8eTW5uLgA3b95k8eLF9OzZE71e\nz+DBg0lPT3/ovufk5PDss8/i7e2Nt7c3U6ZMsXnstWiNmZhe77S0NLKysggPD6dz5854e3szdepU\niouLNddVUlLCCy+8gI+PD56enoSGhvLFF1+Y401LS2tyXKL1yJmJE1m/fj2vvvoq3bt3Z+rUqRgM\nBqqqqjhz5gwfffQRb7zxBgCJiYls3ryZsrIyFi9eTJcuXQDM/za2cOFCcnNzmTp1KtOmTcPLywuA\ns2fP8tZbbxEWFsa0adPw8fGhrKyMjIwMMjMzycjIIDIyEoDg4GDi4+NJTU0lKCiIP/zhD+b1m5KF\nSeNusMTEREpLS0lLS2P48OFMnz5d8zkPOnjwIM8//zx37txh6tSp9OvXj4qKCnbu3ElmZiZ79uxh\n4sSJ5uWrqqoICQnh0qVLhIaGMnbsWC5fvsy8efMYP3580w7+A3Jzc0lOTiY8PJy4uDiuXLmCi4sL\n9+7dw2g08vnnn9O/f39iYmLQ6/UcOnSIP/3pTxw4cIB9+/bh4uICwL59+3juuefw9vYmKiqKwMBA\nfvjhB0pKSli9ejUrV640T0g4fPgwK1asYNy4cYwYMQJPT09KSkpIT08nIyODo0ePMnz4cKtYa2pq\nCA0NxdfXl1mzZlFeXs6OHTuYMGECOTk5zJkzhzt37mA0GqmtrWXbtm3MnDmT3NxcRo4cabW+Y8eO\nsXz5ciZOnMjChQspKSlh586dZGdns3//fkaNGtWkY2irS/Tzzz9nz549TJkyhXnz5nHmzBm++OIL\nvvrqK86ePWtxtlpcXExISAg1NTU899xzDBs2jAsXLhAdHc2UKVMeuh3RxlThNEaMGKHq9Xq1srLS\nqq66utricVhYmKooilpWVqa5LlN9YGCg5jK1tbVW61RVVa2oqFADAgLUAQMGWJSXlpaqiqKoERER\nmtu7dOmSqiiKOnv2bIvyQ4cOaZarqqomJiaqiqKo2dnZ5rKamhrV19dX7datm1pUVGSxfFFRkerl\n5aUGBASot2/fNpe/8sorqqIo6qJFiyyWP3XqlPqLX/xCVRRFfffddzXjftCmTZtURVFURVHUDRs2\nWNUvW7ZMVRRFXbhwoVpfX28ur6+vV+Pi4lRFUdTVq1eby41Go6ooilpQUGC1ruvXr1s8rqysVG/d\numW1XEFBgerp6alOnjzZotx0zBVFUePj4y3qkpOTVUVRVG9vb3XGjBnq3bt3zXWffvqpqiiKGh0d\nbXPfP/zwQ4u69PR0VVEUq/eF1muoqqrme8W0rJubm3rw4EGLujfeeENVFEVduXKlRfm4ceNURVHU\nNWvWWJRnZmaaY01LS1OF40k3l5NxcXHB1dX6hLFr167NWt/rr7+uOdurc+fOmut88skneeGFFzh3\n7hwVFRXmcrWZYzOP+7wtW7Zw/fp1EhMTGTBggEXdgAEDmDt3LleuXOHAgQMA3L17l3/84x94enqa\nu89Mhg8fTmxsbLPiDg4OZu7cuRZl9fX1pKam4ufnxwcffGA1K23FihUoisInn3xitT69Xm9V5uPj\nY/H4l7/8JU888YTVckOHDiUiIoJDhw7x008/WdV7enqSnJxsUTZr1iygoYsrJSXF4j314osv4urq\nSkFBgdau069fP+bNm2dRZjQaGTVqFOfOnTN3nzXX73//e6susLi4OAC++uorc1l5eTlZWVn06dOH\nP/7xjxbLT548udlnnaJ1SDeXE4mNjWXJkiUMGjSIF198kTFjxhASEoK/v3+z1/n000/brMvJySE1\nNZW8vDy+//577ty5Y1H/3XffERgY2OxtN0dOTg4ABQUFJCUlWdWfO3cOgKKiIiIjIykuLqauro6Q\nkBCrmWQAY8eOZdOmTY8dh9ZxKykpobq6mr59+1olLhO9Xk9RUZH5cWxsLLt27WLkyJHMnDmT8PBw\nRo0aRVBQkObz9+7dy9///ndOnDhBdXU19+7dM9cpikJVVZXVZIZ+/fpZzZzq3r070ND1+eCXCZ1O\nh8FgsPiy0NiYMWM0y8eOHUteXh6nT58mJCREc5mmeOqpp6zKTO+zH374wVx2+vRpAJ555hnNrqzQ\n0FD279/f7DhEy5Jk4kQSEhIwGAysW7eOtWvX8re//Q1o+DAtX76csLCwx16nqVF50K5du5gxYwYe\nHh5MmDCBvn378sQTT6DT6cjKyiI7O5vbt2/btT/NUV1dDcDHH39scxlFUfjPf/4DQG1tLaA9W+xh\n5Y+iddxMsV24cMFmMjHFZxIdHc3evXtZtWoVaWlpbNiwAYDBgweTlJSE0Wg0L5uamkpCQgJdu3Zl\nwoQJ9OzZEw8PDxRFYdeuXRQUFGi+JlpJ1HQmolVnqm+cqBp71LE0HfPm0hrbM8Xb+MyrtV5b0Tok\nmTiZmJgYYmJiuHXrFnl5eWRkZLBhwwYiIyMpKCigX79+j7U+W4OTb7/9Nnq9nhMnTtC/f3+Luu++\n+65Vf5X+MKbG7+TJk5qDzbaWv3btmma9rfJH0Tpupm1FRUWxe/fuJq8rMjKSyMhIbt++zfHjx8nM\nzGTt2rX87ne/Iysri7Fjx3Lv3j2SkpLw9/fn5MmTVg2l6YytLTzqWNpKUC2tc+fOTYpHOAcZM3FS\nnp6eTJgwgdWrV7NkyRJ+/PFHMjMzzfWm2UJafehN8c033zBo0CCrRFJfX8/Ro0etlrd3e01l6j7R\nmi6sZeDAgbi7u1NYWEhNTY1VfUsmxYEDB9KlSxeOHTumOQX4UTp16sSYMWNITk7mr3/9K6qqmpNS\nVVUVtbW1hISEWCWSW7ducfLkyTabtXTkyBHNsS7TsQwODm6TOEzbyc/Pp76+3qpe630qHEeSiRM5\nePCgZvmVK1cALAZnTdMny8rKbK7vYY1P7969KSkpMa8bGgbLk5KSKCoqsnquabC4vLz8EXthn9mz\nZ+Pj48OyZcvIz8+3qldVlaNHj5obc1dXV2JjY7l16xbvvPOOxbKnTp1i69atLRabi4sL8fHxXLt2\njfnz51NXV2e1TFVVlcXAdnZ2tmYCfvA1NRgMeHh4cOLECXMXHjRMMIiPjzd3sbWF8+fPs27dOouy\n9PR08vPzGTBgQJOnBturR48ehIeHc/HiRT788EOLun379pknYQjnIN1cTsRoNOLp6ckzzzxDr169\nUBSF48ePc/ToUX71q18xc+ZM87ITJ05kx44dvPLKKxiNRry8vPDx8WH+/PnmZR42kyohIYFXX32V\n4OBgjEYjbm5u5OTkUFRUxLRp0/jss88slvf09CQkJITc3FyioqIIDg7Gzc2NsLAwmwO2zeHj40N6\nejrTp08nJCSEcePGMWjQINzc3CgvL+fYsWOUl5dTU1ODm5sbAMnJyRw4cIA1a9Zw8uRJxowZw9Wr\nV/nXv/7FlClTyMjIaLH43nrrLb7++ms2btzI3r17zT/6/P7777lw4QI5OTksWLCAVatWARAfH09F\nRQWjR482/wCzsLCQL7/8km7duplnMel0OhYtWsT777/PkCFDiIqK4s6dO2RlZVFTU0NERMRDr4nW\nkiZNmsRrr71GZmYmQ4YM4fz58+zcuRMPD4+HjmW1hrVr1xIaGsqiRYvYt28fw4YN4+LFi+zcuZPn\nn3+ePXv2mH+nIxxLXgUnsmLFCkaOHMnp06f56KOP+Pjjj7lx4wZJSUkcO3bM/INDgJdffpm3334b\ngA8++IB33nmHlJQUc/2jLqAYFxfHpk2b8Pf3Z8uWLWzbto1evXpx/PhxRowYofncTLine truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x8f8e400>"
"<matplotlib.figure.Figure at 0x7d75320>"
]
},
"metadata": {},
@@ -1337,25 +1344,27 @@
"source": [
"The code to perform the stratification is quite straightforward. \n",
"\n",
" def stratified_resample(weights):\n",
" N = len(weights)\n",
" # make N subdivisions, and chose a random position within each one\n",
" positions = (random(N) + range(N)) / N\n",
"```python\n",
"def stratified_resample(weights):\n",
" N = len(weights)\n",
" # make N subdivisions, and chose a random position within each one\n",
" positions = (random(N) + range(N)) / N\n",
"\n",
" indexes = np.zeros(N, 'i')\n",
" cumulative_sum = np.cumsum(weights)\n",
" i, j = 0, 0\n",
" while i < N:\n",
" if positions[i] < cumulative_sum[j]:\n",
" indexes[i] = j\n",
" i += 1\n",
" else:\n",
" j += 1\n",
" return indexes\n",
" indexes = np.zeros(N, 'i')\n",
" cumulative_sum = np.cumsum(weights)\n",
" i, j = 0, 0\n",
" while i < N:\n",
" if positions[i] < cumulative_sum[j]:\n",
" indexes[i] = j\n",
" i += 1\n",
" else:\n",
" j += 1\n",
" return indexes```\n",
"\n",
"Import it from FilterPy with\n",
"\n",
" from filterpy.monte_carlo import stratified_resample"
"```python\n",
"from filterpy.monte_carlo import stratified_resample```"
]
},
{
@@ -1369,7 +1378,7 @@
},
{
"cell_type": "code",
"execution_count": 18,
"execution_count": 19,
"metadata": {
"collapsed": false
},
@@ -1378,7 +1387,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x902b2e8>"
"<matplotlib.figure.Figure at 0x824bcf8>"
]
},
"metadata": {},
@@ -1396,26 +1405,28 @@
"source": [
"The code couldn't be simpler.\n",
"\n",
" def systematic_resample(weights):\n",
" N = len(weights)\n",
"```python\n",
"def systematic_resample(weights):\n",
" N = len(weights)\n",
"\n",
" # make N subdivisions, choose positions with a consistent random offset\n",
" positions = (np.arange(N) + random()) / N\n",
" # make N subdivisions, choose positions with a consistent random offset\n",
" positions = (np.arange(N) + random()) / N\n",
"\n",
" indexes = np.zeros(N, 'i')\n",
" cumulative_sum = np.cumsum(weights)\n",
" i, j = 0, 0\n",
" while i < N:\n",
" if positions[i] < cumulative_sum[j]:\n",
" indexes[i] = j\n",
" i += 1\n",
" else:\n",
" j += 1\n",
" return indexes\n",
" indexes = np.zeros(N, 'i')\n",
" cumulative_sum = np.cumsum(weights)\n",
" i, j = 0, 0\n",
" while i < N:\n",
" if positions[i] < cumulative_sum[j]:\n",
" indexes[i] = j\n",
" i += 1\n",
" else:\n",
" j += 1\n",
" return indexes```\n",
" \n",
"Import from FilterPy with\n",
"\n",
" from filterpy.monte_carlo import systematic_resample"
"```python\n",
" from filterpy.monte_carlo import systematic_resample```"
]
},
{
@@ -1429,7 +1440,7 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 20,
"metadata": {
"collapsed": false,
"scrolled": true
@@ -1439,7 +1450,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x960ada0>"
"<matplotlib.figure.Figure at 0x7d2a4e0>"
]
},
"metadata": {},
@@ -1449,7 +1460,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x97ed908>"
"<matplotlib.figure.Figure at 0xb09f5c0>"
]
},
"metadata": {},
@@ -1459,7 +1470,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x96fa8d0>"
"<matplotlib.figure.Figure at 0xb024710>"
]
},
"metadata": {},
@@ -1469,7 +1480,7 @@
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0xa9d5b00>"
"<matplotlib.figure.Figure at 0x7ce0940>"
]
},
"metadata": {},
@@ -1524,16 +1535,6 @@
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
@@ -1552,7 +1553,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+14 -13
View File
@@ -483,20 +483,21 @@
"\n",
"As always, the hardest part of the implementation is correctly accounting for the subscripts. A basic implementation without comments or error checking would be:\n",
"\n",
" def rts_smoother(Xs, Ps, F, Q):\n",
" n, dim_x, _ = Xs.shape\n",
" \n",
" # smoother gain\n",
" K = zeros((n,dim_x, dim_x))\n",
" x, P = Xs.copy(), Ps.copy()\n",
"```python\n",
"def rts_smoother(Xs, Ps, F, Q):\n",
" n, dim_x, _ = Xs.shape\n",
"\n",
" for k in range(n-2,-1,-1):\n",
" P_pred = dot(F, P[k]).dot(F.T) + Q\n",
" # smoother gain\n",
" K = zeros((n,dim_x, dim_x))\n",
" x, P = Xs.copy(), Ps.copy()\n",
"\n",
" K[k] = dot(P[k], F.T).dot(inv(P_pred))\n",
" x[k] += dot(K[k], x[k+1] - dot(F, x[k]))\n",
" P[k] += dot(K[k], P[k+1] - P_pred).dot(K[k].T)\n",
" return (x, P, K)\n",
" for k in range(n-2,-1,-1):\n",
" P_pred = dot(F, P[k]).dot(F.T) + Q\n",
"\n",
" K[k] = dot(P[k], F.T).dot(inv(P_pred))\n",
" x[k] += dot(K[k], x[k+1] - dot(F, x[k]))\n",
" P[k] += dot(K[k], P[k+1] - P_pred).dot(K[k].T)\n",
" return (x, P, K)```\n",
" \n",
"This implementation mirrors the implementation provided in FilterPy. It assumes that the Kalman filter is being run externally in batch mode, and the results of the state and covariances are passed in via the `Xs` and `Ps` variable.\n",
"\n",
@@ -841,7 +842,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+17 -16
View File
@@ -533,20 +533,21 @@
"source": [
"To be clear about the dimension reduction, if we wanted to track both *x* and *y* we would have written\n",
"\n",
" cvfilter = KalmanFilter(dim_x = 4, dim_z=2)\n",
" cvfilter.x = array([0., 0., 0., 0.])\n",
" cvfilter.P *= 300\n",
" cvfilter.R *= 1\n",
" cvfilter.F = array([[1, dt, 0, 0],\n",
" [0, 1, 0, 0],\n",
" [0, 0, 1, dt],\n",
" [0, 0, 0, 1]], dtype=float)\n",
" cvfilter.H = array([[1, 0, 0, 0],\n",
" [0, 0, 1, 0]], dtype=float)\n",
"```python\n",
"cvfilter = KalmanFilter(dim_x = 4, dim_z=2)\n",
"cvfilter.x = array([0., 0., 0., 0.])\n",
"cvfilter.P *= 300\n",
"cvfilter.R *= 1\n",
"cvfilter.F = array([[1, dt, 0, 0],\n",
" [0, 1, 0, 0],\n",
" [0, 0, 1, dt],\n",
" [0, 0, 0, 1]], dtype=float)\n",
"cvfilter.H = array([[1, 0, 0, 0],\n",
" [0, 0, 1, 0]], dtype=float)\n",
"\n",
" q = Q_discrete_white_noise(dim=2, dt=dt, var=0.020)\n",
" cvfilter.Q[0:2, 0:2] = q\n",
" cvfilter.Q[2:4, 2:4] = q"
"q = Q_discrete_white_noise(dim=2, dt=dt, var=0.020)\n",
"cvfilter.Q[0:2, 0:2] = q\n",
"cvfilter.Q[2:4, 2:4] = q```"
]
},
{
@@ -891,11 +892,11 @@
"\n",
"That may seem like a subtle distinction, but from the plots you see it is not. The amount of deviation in the left plot when the maneuver starts is small, but the deviation in the right tells a very different story. If the tracked object was moving according to the process model the residual plot should bounce around 0.0. This is because the measurements will be obeying the equation \n",
"\n",
"$$measurement = process\\_model(t) + noise(t)$$.\n",
"$$\\mathtt{measurement} = \\mathtt{process\\_model}(t) + \\mathtt{noise}(t)$$.\n",
"\n",
"Once the target starts maneuvering the predictions of the target behavior will not match the behavior as the equation will be \n",
"\n",
"$$measurement = process\\_model(t) + maneuver\\_delta(t) + noise(t)$$.\n",
"$$\\mathtt{measurement} = \\mathtt{process\\_model}(t) + \\mathtt{maneuver\\_delta}(t) + \\mathtt{noise}(t)$$.\n",
"\n",
"Therefore if the residuals diverge from a mean of 0.0 we know that a maneuver has commenced.\n",
"\n",
@@ -1677,7 +1678,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+45 -44
View File
@@ -360,8 +360,9 @@
"\n",
"This just says to select the sigma points from the filter's initial mean and covariance. In code this might look like\n",
"\n",
" N = 1000\n",
" sigmas = multivariate_normal(mean=x, cov=P, size=N)"
"```python\n",
"N = 1000\n",
"sigmas = multivariate_normal(mean=x, cov=P, size=N)```"
]
},
{
@@ -382,26 +383,18 @@
"\\end{aligned}\n",
"$$\n",
"\n",
"That is short and sweet, but perhaps not entirely clear. The first line passes all of the sigma points through a use supplied state transition function and then adds some noise distributed according to the $\\mathbf{Q}$ matrix. In Python we might write"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" for i, s in enumerate(sigmas):\n",
" sigmas[i] = fx(x=s, dt=0.1, u=0.)\n",
"That is short and sweet, but perhaps not entirely clear. The first line passes all of the sigma points through a use supplied state transition function and then adds some noise distributed according to the $\\mathbf{Q}$ matrix. In Python we might write\n",
"\n",
"```python\n",
"for i, s in enumerate(sigmas):\n",
" sigmas[i] = fx(x=s, dt=0.1, u=0.)\n",
"\n",
"sigmas += multivariate_normal(x, Q, N)```\n",
"\n",
" sigmas += multivariate_normal(x, Q, N)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The second line computes the mean from the sigmas. In Python we will take advantage of `numpy.mean` to do this very concisely and quickly.\n",
"\n",
" x = np.mean(sigmas, axis=0)"
"```python\n",
"x = np.mean(sigmas, axis=0)```"
]
},
{
@@ -414,10 +407,11 @@
"\n",
"$\\boldsymbol\\chi-\\mathbf{x}^-$ is a one dimensional vector, so we will use `numpy.outer` to compute the $[\\boldsymbol\\chi-\\mathbf{x}^-][\\boldsymbol\\chi-\\mathbf{x}^-]^\\mathsf{T}$ term. In Python we might write\n",
"\n",
"```python\n",
" P = 0\n",
" for s in sigmas:\n",
" P += outer(s-x, s-x)\n",
" P = P / (N-1)"
" P = P / (N-1)```"
]
},
{
@@ -460,7 +454,8 @@
"\n",
"just passes the sigma points through the measurement function $h$. We name the resulting points $\\chi_h$ to distinguish them from the sigma points. In Python we could write this as\n",
"\n",
" sigmas_h = h(sigmas, u)"
"```python\n",
"sigmas_h = h(sigmas, u)```"
]
},
{
@@ -473,7 +468,8 @@
"\n",
"In Python we can compute that with\n",
"\n",
" z_mean = np.mean(sigmas_h, axis=0)\n",
"```python\n",
"z_mean = np.mean(sigmas_h, axis=0)```\n",
" \n",
"Now that we have the mean of the measurement sigmas we can compute the covariance for every measurement sigma point, and the *cross variance* for the measurement sigma points vs the sigma points. That is expressed by these two equations\n",
"\n",
@@ -485,16 +481,17 @@
"\n",
"We can express this in Python with\n",
"\n",
" P_zz = 0\n",
" for sigma in sigmas_h:\n",
" s = sigma - z_mean\n",
" P_zz += outer(s, s)\n",
" P_zz = P_zz / (N-1) + R\n",
"```python\n",
"P_zz = 0\n",
"for sigma in sigmas_h:\n",
" s = sigma - z_mean\n",
" P_zz += outer(s, s)\n",
"P_zz = P_zz / (N-1) + R\n",
"\n",
" P_xz = 0\n",
" for i in range(N):\n",
" P_xz += outer(self.sigmas[i] - self.x, sigmas_h[i] - z_mean)\n",
" P_xz /= N-1"
"P_xz = 0\n",
"for i in range(N):\n",
" P_xz += outer(self.sigmas[i] - self.x, sigmas_h[i] - z_mean)\n",
"P_xz /= N-1```"
]
},
{
@@ -503,9 +500,10 @@
"source": [
"Computation of the Kalman gain is straightforward $\\mathbf{K} = \\mathbf{P}_{xz} \\mathbf{P}_{zz}^{-1}$.\n",
"\n",
"In Python this is the trivial\n",
"In Python this is\n",
"\n",
" K = np.dot(P_xz, inv(P_zz))\n",
"```python\n",
"K = np.dot(P_xz, inv(P_zz))```\n",
"\n",
"Next, we update the sigma points with\n",
"\n",
@@ -513,15 +511,17 @@
"\n",
"Here $\\mathbf{v}_R$ is the perturbation that we add to the sigmas. In Python we can implement this with\n",
"\n",
" v_r = multivariate_normal([0]*dim_z, R, N)\n",
" for i in range(N):\n",
" sigmas[i] += dot(K, z + v_r[i] - sigmas_h[i])\n",
"```python\n",
"v_r = multivariate_normal([0]*dim_z, R, N)\n",
"for i in range(N):\n",
" sigmas[i] += dot(K, z + v_r[i] - sigmas_h[i])```\n",
"\n",
"\n",
"Our final step is recompute the filter's mean and covariance.\n",
"\n",
" x = np.mean(sigmas, axis=0)\n",
" P = self.P - dot3(K, P_zz, K.T)"
"```python\n",
" x = np.mean(sigmas, axis=0)\n",
" P = self.P - dot3(K, P_zz, K.T)```"
]
},
{
@@ -549,12 +549,13 @@
"\n",
"The EnKF is designed for nonlinear problems, so instead of using matrices to implement the state transition and measurement functions you will need to supply Python functions. For this problem they can be written as:\n",
"\n",
" def hx(x):\n",
" return np.array([x[0]])\n",
"```python\n",
"def hx(x):\n",
" return np.array([x[0]])\n",
"\n",
"def fx(x, dt):\n",
" return np.dot(F, x)```\n",
"\n",
" def fx(x, dt):\n",
" return np.dot(F, x)\n",
" \n",
"One final thing: the EnKF code, like the UKF code, uses a single dimension for $\\mathbf{x}$, not a two dimensional column matrix as used by the linear kalman filter code.\n",
"\n",
"Without further ado, here is the code."
@@ -738,7 +739,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+4 -4
View File
@@ -53,7 +53,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 1,
"metadata": {
"collapsed": false,
"scrolled": true
@@ -61,9 +61,9 @@
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAXAAAAD7CAYAAABzGc+QAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3XlYlWX+P/D3h0UUUFxAAVdMTMUFc5lxP65jWspojTrz\ns2zRKdtNrWz66djXmb7NNT8ra7zUaiwrbUqz3EXzKGqpJZLghtsILqmoICD7/fvj5rCDBzjwnOfw\nfl3XfZ3t4TwfQd7c537u535EKQUiIjIfN6MLICKiqmGAExGZFAOciMikGOBERCbFACciMikGOBGR\nSXnU9A5EhPMUiYiqQCklFb1eKz1wpZTTt/nz5xteg6vUaYYaWSfrdPZmDw6hEBGZFAOciMikGOD5\nLBaL0SXYxQx1mqFGgHU6GuusfWLvWEuVdyCianofRESuRkSgnOEgJhEROR4DnIjIpBjgREQmxQAn\nIjIpBjgRkUkxwImITIoBTkRkUgxwIiKTYoATEZkUA5yIyKQY4EREJsUAJyIyKQY4EZFJMcCJiEyK\nAU5EZFIMcCIik2KAExGZVLUCXERai8guEYkTkVgRed5RhRERUcWqdUk1EQkEEKiUOiIivgB+BhCh\nlDpeZBteUo2IqJJq/JJqSqkrSqkj+fdTARwHEFyd9yQiIvs4bAxcRNoB6AnggKPek4iIyueQAM8f\nPvkawAv5PXEiIqphHtV9AxHxBLAWwGdKqfVlbbNgwYKC+xaLBRaLpbq7JSJyKVarFVartVJfU92D\nmALgEwBJSqmXytmGBzGJiCrJnoOY1Q3wgQD2APgFgO2NXlNKbS2yDQOciKiSajzA7SyCAU5EVEk1\nPo2QiIiMwwAnIjIpBjgRkUkxwImITIoBTkRkUgxwIiKTYoATEZkUA5yIyKQY4EREJsUAJyIyKQY4\nEZFJMcCJiEyKAU5EZFIMcCIik2KAExGZFAOciMikGOBERCbFACciMikGOBGRSTHAiYhMigFORGRS\nDHAiIpNigBMRmRQDnIjIpBjgREQmxQAnIjIpBjgRkUkxwImITIoBTkRkUgxwIiKTYoATEZkUA5yI\nyKQY4EREJlXtABeRj0XkVxE56oiCiIjIPo7ogf8bwGgHvA8REVVCtQNcKRUF4KYDaiEiokrwMLoA\nIkdTSuHWrVu4fPlysZaUlIT09HSkp6cjIyMDOTk5yMnJgZubG7y9veHj41Os+fv7o3Xr1gWtQYMG\nRv/TiIqplQBfsGBBwX2LxQKLxVIbuyUXl5eXh3PnziEuLg5xcXGIjY1FXFwcTp48iYyMDIfvr1mz\nZmjTpg26dOmC8PBwhIeHo0ePHggICHD4vqjusVqtsFqtlfoaUUpVe8ci0g7ABqVUtzJeU47YB1Fm\nZiZ++ukn7NmzB3v27MG+fftw+/btMrdt2LAhgoKCEBgYiKCgIAQFBSEgIAC+vr7w9vaGl5cXPD09\n4eHhgdzcXKSlpSE9PR1paWlIS0tDamoqrl69igsXLiAhIQEXL15EdnZ2mfsKDg5G7969MXToUIwY\nMQJhYWEQkZr8VlAdICJQSlX4H4kBTk7tzJkzWLt2LbZu3YoffvihVM86KCgIXbt2RVhYWMFt586d\n4efn59A68vLy8Ouvv+L8+fM4evQojhw5gpiYGMTExCAtLa3Yti1atMCwYcMwYsQIPPjgg+yhU5XU\nSoCLyGoAQwA0A3AVwP9VSv27yOsMcLKbUgrHjh3D2rVrsW7dOsTExBR7PSwsDIMHDy5owcHBBlWq\n5eXl4cyZM9i/fz927NiBnTt34vLlywWvu7u7Y8SIEZgyZQoiIiIc/oeFXFet9cDvUgQDnO4qOTkZ\nq1atwrJlyxAbG1vwfKNGjfDAAw8gIiICFovF6XuzSimcOHECO3bswJYtWxAZGYmcnBwAgJeXF8aM\nGYMnnngC999/P9zceB4dlY8BTk7v559/xtKlS7F69Wqkp6cD0AcLx48fj4kTJ2L48OHw8vIyuMqq\nu379OtatW4fVq1dj9+7dsP0uhIaG4oUXXsCjjz4KX19fg6skZ8QAJ6eklMLWrVuxcOFC/PjjjwXP\nDx06FE8//TQiIiLg6elpYIU149KlS/jss8/wwQcf4MKFCwCAxo0bY/r06XjppZcQFBRkcIXkTBjg\n5FSUUoiMjMT8+fMLgrtx48aYNm0a/vznP6NTp04GV1g7cnJysH79erzzzjvYt28fAMDb2xuzZs3C\nnDlz0KhRI4MrJGdgT4BDKVWjTe+C6ro9e/aoAQMGKAAKgPL391dvv/22Sk1NNbo0Qx08eFBFREQU\nfF8CAgLUe++9pzIzM40ujQyWn50V5it74FSjrl27hjlz5uCTTz4BADRt2hRz5szBs88+y7HfIvbt\n24e5c+di//79AIAOHTpg2bJlGDZsmMGVkVE4hEKGycvLw8cff4y5c+fi5s2b8PLywiuvvILZs2ej\nYcOGRpfnlJRS+Pbbb/Hqq6/i5MmTAIAnnngC//znPzn9sA5igJMhzpw5g0cffbRgfHfkyJH44IMP\nEBoaanBl5pCVlYW3334bb775JrKystCmTRusXLkSQ4cONbo0qkX2BDgnopJDffbZZwgPD8e+ffsQ\nGBiI1atXY9u2bQzvSqhXrx7+8pe/IDo6Gr1798aFCxcwbNgw/PWvf0VeXp7R5ZETYQ+cHCIjIwPP\nPfccPvzwQwDAww8/jGXLlqFJkyYGV2Zu2dnZWLRoERYuXAilFCZOnIiVK1fy+EEdwCEUqhWJiYkY\nP348Dh8+jPr162PJkiV44oknuKCTA23ZsgVTpkxBcnIyunfvjvXr1yMkJMTosqgGMcCpxp04cQKj\nRo1CQkIC2rdvj7Vr1yI8PNzoslzSyZMnMW7cOJw6dQr+/v6IjIzk99qFcQycatTBgwcxcOBAJCQk\noF+/fjh06BADpQbde++9OHDgAEaNGoXr169j+PDhpRb7orqFAU5Vsn37dgwbNgxJSUkYM2YMduzY\ngaZNmxpdlstr3LgxvvvuO4wdOxY3btxgiNdxDHCqtL1792LcuHFIS0vDI488gvXr18Pb29vosuoM\nLy8vrF27FmPHjkVSUhKGDx+OuLg4o8siA3AMnCrl2LFjGDBgAG7duoUZM2Zg6dKlXBbVIJmZmZgw\nYQI2b96M9u3b49ChQ/wU5EJ4EJMcKjExEf3790dCQgIiIiLw9ddfw93d3eiy6rQ7d+5gwIABiI6O\nxujRo7Fx40b+TFwED2KSw6Snp2PMmDFISEhA//798cUXXzAonECDBg3wzTffwN/fH1u3bsUbb7xh\ndElUi9gDN5vsbCAtDUhPr7jZs01GBpCXp5tShfdLPlYKly9dQvLNm/Dy9ESbVq3gLlLutsXex80N\n8PTUrV69sm8res2ebWz3vb0BH5/yW716gIvOTd+1axdGjhyJ3NxcbNmyBaNHjza6JKomDqE4q6ws\n4OpV4Pp1ICmpeCv53I0bQGpqYejmX56LqsDdvXSo+/oCDRsCjRrpW1sr+ris15o00X8QnMhbb72F\n1157De3atUNcXBwPLJscA7y2ZWUB587pduWKbr/+Wvr2xo2q78MWQt7ehT1O2/2KWlnbeXnp9xPR\nPWU3t+L33dxw7fp1THjoISTduoU5c+fisccfL/Z6RV8LWy89O1u3rKzS98u7rew2mZmFnzzKa47+\n4+fjAzRrVro1bVr2882aAX5++ntTA7Kzs9GnTx/ExMTg1Vdfxd///vca2Q/VDgZ4TcjNBRISgPh4\n4NQp3Wz3z5/Xr9+NuzsQEKBbWb/k/v7Fw6BRo8LQreVLjU2YMAHffPMNRo0ahS1btph7xklWVulQ\nT00Fbt8GUlL0bdFW8jnb45QU4Natqv1BcHPTvXd7gz8gAGjeHPDwsOvtDxw4gH79+sHd3R2HDx9G\nt27dKl8jOQUGeFUppXvKRcPZdv/0ad3bK4sI0K4dcM89QFAQEBgItGihm+1+YKD+xTRBEEZFRWHw\n4MHw8fHByZMn0bJlS6NLch5K6SC3DXOVHAor2WzbpKRUfl9ubvr/TnBw8dayZfHH+f+vZs6ciaVL\nl+L+++/H5s2bHf9vp1rBALeXUsCZM4DVWtguXix/+6AgoGNH3UJDC++3b6+HJVyAUgr9+vXDgQMH\nsGDBAsyfP9/oklxDdnb5gV/W81evAteu2ffenp5AUBCymzfHpsOHkZCXh4eefx5BvXoVD3o/P5c9\nmOtKGODlUQo4e7Z4YCcmFt/Gzw/o1Kl0SHfooA9kubj//Oc/mDRpEgIDAxEfH8/lS42UlaWPn1y6\nVHG7edO+92vQoHigt2mjPzW2b69v27Sxe8iGag4DvKSLF4FVq4CVK4H8S1YVaNYMGDIEGDoUsFiA\nLl1MMcxRE5RS6Nq1K44dO4Zly5ZhxowZRpdE9rhzB7h8Gbh0CVePHMFbzz+PYABPjR8P3+TkwqC/\nfbvi93F3B9q21WFeNNhtt3WgA+MMGOCA7m1v3gwsWQJERupZEYA+kGSxFAZ2WFidDeyS9u7di0GD\nBiEwMBAXLlyAZy0fOCXHmDJlCtasWVN6COz27YKgx8WL+uD7mTP6U+mZM/q5in5nAwJKh7rtflAQ\nf48chAF+8iTw4ovA1q36cb16wLhxwLRpwO9+x4+J5XjkkUewatUqzJs3D4sWLTK6HKqizZs3Y+zY\nsejZsycOHz5s/xdmZJQOddv9s2f16+WpXx8ICdGBHhqqO0a2xp57pdTtAP/oI+Dpp/VBIz8/4C9/\nAR5/XE/VonLduHEDwcHByMzMxNmzZ3nVFxPLyMhAQEAAUlNTcf78ebRt27b6b5qXp3vvJYPddlvR\nAde2bXWQd+2qW1gY0LmzHpOnUuwJcNfsgu7cCfz5z3pO9hNPAH/7m55LS3e1bt06ZLine truncated
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x7e73630>"
"<matplotlib.figure.Figure at 0x7129400>"
]
},
"metadata": {},
@@ -196,7 +196,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3"
"version": "3.4.1"
}
},
"nbformat": 4,
+1 -1
View File
@@ -238,7 +238,7 @@ def plot_monte_carlo_mean(xs, ys, f, mean_fx, label, plot_colormap=True):
plt.scatter(fxs, fys, marker='.', alpha=0.02, color='k')
plt.scatter(mean_fx[0], mean_fx[1],
marker='v', s=300, c='r', label='Linearized Mean')
marker='v', s=300, c='r', label=label)
plt.scatter(computed_mean_x, computed_mean_y,
marker='*',s=120, c='r', label='Computed Mean')
+120 -9
View File
@@ -11,7 +11,9 @@ from filterpy.stats import plot_covariance_ellipse
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse,Arrow
import math
from math import cos, sin, atan2, pi
import numpy as np
from numpy.random import randn
def _sigma_points(mean, sigma, kappa):
sigma1 = mean + math.sqrt((1+kappa)*sigma)
@@ -148,6 +150,13 @@ def show_3_sigma_points():
plt.plot(xs, ys)
plt.show()
def _plot_sigmas(s, w, alpha=0.5, **kwargs):
min_w = min(abs(w))
scale_factor = 100 / min_w
return plt.scatter(s[:, 0], s[:, 1], s=abs(w)*scale_factor,
alpha=alpha, **kwargs)
def show_sigma_selections():
ax=plt.gca()
ax.axes.get_xaxis().set_visible(False)
@@ -156,22 +165,25 @@ def show_sigma_selections():
x = np.array([2, 5])
P = np.array([[3, 1.1], [1.1, 4]])
points = MerweScaledSigmaPoints(2, .05, 2., 1.)
points = MerweScaledSigmaPoints(2, .09, 2., 1.)
sigmas = points.sigma_points(x, P)
plot_covariance_ellipse(x, P, facecolor='b', alpha=0.6, variance=[.5])
plt.scatter(sigmas[:,0], sigmas[:, 1], c='k', s=50)
Wm, Wc = points.weights()
plot_covariance_ellipse(x, P, facecolor='b', alpha=.3, variance=[.5])
_plot_sigmas(sigmas, Wc, alpha=1.0, facecolor='k')
x = np.array([5, 5])
points = MerweScaledSigmaPoints(2, .15, 2., 1.)
points = MerweScaledSigmaPoints(2, .15, 1., .15)
sigmas = points.sigma_points(x, P)
plot_covariance_ellipse(x, P, facecolor='b', alpha=0.6, variance=[.5])
plt.scatter(sigmas[:,0], sigmas[:, 1], c='k', s=50)
Wm, Wc = points.weights()
plot_covariance_ellipse(x, P, facecolor='b', alpha=0.3, variance=[.5])
_plot_sigmas(sigmas, Wc, alpha=1.0, facecolor='k')
x = np.array([8, 5])
points = MerweScaledSigmaPoints(2, .4, 2., 1.)
points = MerweScaledSigmaPoints(2, .2, 3., 10)
sigmas = points.sigma_points(x, P)
plot_covariance_ellipse(x, P, facecolor='b', alpha=0.6, variance=[.5])
plt.scatter(sigmas[:,0], sigmas[:, 1], c='k', s=50)
Wm, Wc = points.weights()
plot_covariance_ellipse(x, P, facecolor='b', alpha=0.3, variance=[.5])
_plot_sigmas(sigmas, Wc, alpha=1.0, facecolor='k')
plt.axis('equal')
plt.xlim(0,10); plt.ylim(0,10)
@@ -292,6 +304,105 @@ def plot_rts_output(xs, Ms, t):
print('Difference in position in meters:', xs[-6:-1, 0] - Ms[-6:-1, 0])
def plot_scatter_of_bearing_error():
d = 100
xs, ys = [], []
for i in range (3000):
a = math.radians(30) + randn() * math.radians(1)
xs.append(d*math.cos(a))
ys.append(d*math.sin(a))
plt.scatter(xs, ys)
def plot_scatter_moving_target():
pos = np.array([5., 5.])
for i in range(5):
pos += (0.5, 1.)
actual_angle = math.atan2(pos[1], pos[0])
d = math.sqrt(pos[0]**2 + pos[1]**2)
xs, ys = [], []
for i in range (100):
a = actual_angle + randn() * math.radians(1)
xs.append(d*math.cos(a))
ys.append(d*math.sin(a))
plt.scatter(xs, ys)
plt.axis('equal')
plt.plot([5.5, pos[0]], [6, pos[1]], c='g', linestyle='--')
def _isct(pa, pb, alpha, beta):
""" Returns the (x, y) intersections of points pa and pb
given the bearing ba for point pa and bearing bb for
point pb.
"""
B = [pb[0] - pa[0], pb[1] - pa[1]]
AB = math.sqrt((pa[0] - pb[0])**2 + (pa[1] - pb[1])**2)
ab = atan2(B[1], B[0])
a = alpha - ab
b = pi - beta - ab
p = pi - b - a
AP = (sin(b) / sin(p)) * AB
x = cos(alpha) * AP + pa[0]
y = sin(alpha) * AP + pa[1]
return x, y
def _plot_iscts(pos, sa, sb, N=4):
for i in range(N):
pos += (0.5, 1.)
actual_angle_a = math.atan2(pos[1] - sa[1], pos[0] - sa[0])
actual_angle_b = math.atan2(pos[1] - sb[1], pos[0] - sb[0])
da = math.sqrt((sa[0] - pos[0])**2 + (sa[1] - pos[1])**2)
db = math.sqrt((sb[0] - pos[0])**2 + (sb[1] - pos[1])**2)
xs, ys, xs_a, xs_b, ys_a, ys_b = [], [], [], [], [], []
for i in range (300):
a_a = actual_angle_a + randn() * math.radians(1)
a_b = actual_angle_b + randn() * math.radians(1)
x,y = _isct(sa, sb, a_a, a_b)
xs.append(x)
ys.append(y)
xs_a.append(da*math.cos(a_a) + sa[0])
ys_a.append(da*math.sin(a_a) + sa[1])
xs_b.append(db*math.cos(a_b) + sb[0])
ys_b.append(db*math.sin(a_b) + sb[1])
plt.scatter(xs, ys, c='r', marker='.')
plt.scatter(xs_a, ys_a)
plt.scatter(xs_b, ys_b)
plt.gca().set_aspect('equal')
plt.show()
def plot_iscts_two_sensors():
pos = np.array([4., 4,])
sa = [0., 2.]
sb = [8., 2.]
plt.scatter(*sa, s=100)
plt.scatter(*sb, s=100)
_plot_iscts(pos, sa, sb, N=4)
def plot_iscts_two_sensors_changed_sensors():
sa = [3, 4]
sb = [3, 7]
pos= np.array([3., 3.])
plt.scatter(*sa, s=100)
plt.scatter(*sb, s=100)
_plot_iscts(pos, sa, sb, N=5)
if __name__ == '__main__':
#show_2d_transform()
+2
View File
@@ -9,6 +9,8 @@ rm --f Kalman_and_Bayesian_Filters_in_Python.ipynb
rm --f Kalman_and_Bayesian_Filters_in_Python.toc
rm --f Kalman_and_Bayesian_Filters_in_Python.tex
rm --f short.ipynb
rm --f chapter.ipynb
rm --f chapter.pdf
rmdir ./*_files/ 2> /dev/null
+5 -1
View File
@@ -5,8 +5,12 @@ rm --f *.toc
rm --f *.aux
rm --f *.log
rm --f *.out
rm --f book.ipynb
rm --f book.toc
rm --f book.tex
rm --f chapter.ipynb
rm --f chapter.pdf
rmdir /S /Q book_files
rmdir /S /Q chapter_files
+5
View File
@@ -0,0 +1,5 @@
cp %1 chapter.ipynb
ipython nbconvert --to latex chapter.ipynb
ipython to_pdf.py chapter.tex
+8 -2
View File
@@ -1,6 +1,12 @@
import IPython.nbconvert.exporters.pdf as pdf
import sys
f = open('book.tex', 'r', encoding="iso-8859-1")
if len(sys.argv) == 2:
name = sys.argv[1]
else:
name = 'book.tex'
f = open(name, 'r', encoding="iso-8859-1")
filedata = f.read()
f.close()
@@ -11,5 +17,5 @@ f.write(newdata)
f.close()
p = pdf.PDFExporter()
p.run_latex('book.tex')
p.run_latex(name)