Optimise control and estimation
Introduce LQR, Kalman filtering, and their model assumptions. This module has 3 lessons and takes about 2h 25m. Each lesson has a practice exercise that runs real Python in your browser and a short quiz.
Start module 15 in the Learning LabLinear quadratic regulation
Balance state error and input effort with a quadratic cost.
Continuous LQR minimises the integral of x.T*Q*x+u.T*R*u for x_dot=A*x+B*u. Q is positive semidefinite and R positive definite. State and input units matter, so scale them before choosing weights.
The stabilising Riccati solution P satisfies A.T*P+P*A-P*B*R^-1*B.T*P+Q=0. The feedback is u=-K*x with K=R^-1*B.T*P. Stabilizability and detectability conditions are needed for the standard stabilising solution.
LQR does not enforce hard actuator limits. Larger state penalties often produce stronger commands. Validate the saturated system separately and compare cost, peak command, and settling rather than treating an optimal label as a universal guarantee.
See it in action
import numpy as np
from scipy.linalg import solve_continuous_are
A = np.array([[0.,1.],[0.,0.]])
B = np.array([[0.],[1.]])
Q = np.diag([4.,1.]); R = np.array([[1.]])
P = solve_continuous_are(A,B,Q,R)
K = np.linalg.solve(R,B.T@P)
print('Gain:',K,'Poles:',np.linalg.eigvals(A-B@K))
Optimal means optimal for a stated model and cost.
Kalman prediction and correction
Combine a noisy measurement with an uncertain prediction.
For the scalar random walk x[k+1]=x[k]+w[k], measurement y[k]=x[k]+v[k], let process variance be Qn and measurement variance be Rn. Prediction keeps xhat and increases uncertainty: P_prior=P+Qn.
The gain is L=P_prior/(P_prior+Rn). Correct xhat with L*(y-xhat), then update P=(1-L)*P_prior. A larger measurement variance gives less trust to the sensor. These are variances, not standard deviations.
This filter assumes a correct linear model and specified noise statistics, with uncorrelated process and measurement noise. A Gaussian assumption makes the conditional estimate fully described by mean and covariance. Bias and outliers need separate attention.
See it in action
estimate, variance = 20., 1.
process_variance, sensor_variance, measurement = 0.1, 4., 23.
prior_variance = variance+process_variance
gain = prior_variance/(prior_variance+sensor_variance)
estimate += gain*(measurement-estimate)
variance = (1-gain)*prior_variance
print('Estimate:',estimate,'Variance:',variance)
Use uncertainty to decide how strongly measurements should correct a prediction.
Practical: combine a controller and estimator
Test an LQG-style scalar loop under seeded noise.
Use x[k+1]=0.9*x[k]+0.2*u[k]+w[k], y[k]=x[k]+v[k], and u=-K*xhat. A discrete LQR computes K from a state cost and input cost. A Kalman filter estimates x from the noisy output.
For discrete LQR, P solves the discrete Riccati equation and K=(R+B.T*P*B)^-1*B.T*P*A. For the estimator, prediction uses the command actually applied: xhat_prior=A*xhat+B*u and P_prior=A*P*A.T+Qn.
The separation principle applies to the ideal linear controller-observer combination under its assumptions. Saturation, model error, and correlated noise can spoil the expected performance. Evaluate noisy measurement feedback and estimated-state feedback on identical noise records.
Your experiment plan
Use the browser editor or the downloadable notebook. All plant data are simulated. Keep a notebook with parameters, predictions, results, and units.
- Simulate 300 samples from x=2 with seeded process and sensor noise.
- Compare u=-K*y with u=-K*xhat using identical noise arrays.
- Report state RMS, command RMS, and estimation RMSE.
- Repeat with command limited to [-1,1] and explain the change.
What to hand in
- State, estimate, and command traces.
- A fair comparison table.
- A note on covariance choices and saturation.
Reflect on your results
Which assumption matters most? Describe one result that surprised you and one experiment that would test your explanation.
The automatic exercise checks the core code behaviour. Review observations and written deliverables yourself or with a mentor.
See it in action
import numpy as np
from scipy.linalg import solve_discrete_are
A=np.array([[0.9]]); B=np.array([[0.2]])
Q=np.array([[1.]]); R=np.array([[0.5]])
P=solve_discrete_are(A,B,Q,R)
K=np.linalg.solve(R+B.T@P@B,B.T@P@A)
print('Discrete LQR gain:',K[0,0])
print('Closed pole:',(A-B@K)[0,0])
Test the combined estimation and control loop under the conditions it will face.
Free, open and run by a nonprofit.
This page is part of Control Engineering, a free course of 20 modules and 60 lessons in the engineering.fit( ) Learning Lab. The Learning Lab is built and run by engineering.fit( ), a nonprofit founded by Abdullateef Opeyemi Bakare that helps African engineering students gain research experience. You do not need an account or pay a fee to learn.