<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>LQG | Saikiran Juttu | Robotics Portfolio</title><link>https://juttu-s.github.io/saikiran_juttu.github.io/tags/lqg/</link><atom:link href="https://juttu-s.github.io/saikiran_juttu.github.io/tags/lqg/index.xml" rel="self" type="application/rss+xml"/><description>LQG</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Tue, 05 Dec 2023 00:00:00 +0000</lastBuildDate><image><url>https://juttu-s.github.io/saikiran_juttu.github.io/media/icon_hu7729264130191091259.png</url><title>LQG</title><link>https://juttu-s.github.io/saikiran_juttu.github.io/tags/lqg/</link></image><item><title>Control Strategies for Quadrotor Position and Altitude</title><link>https://juttu-s.github.io/saikiran_juttu.github.io/project/quadrotor-control/</link><pubDate>Tue, 05 Dec 2023 00:00:00 +0000</pubDate><guid>https://juttu-s.github.io/saikiran_juttu.github.io/project/quadrotor-control/</guid><description>&lt;p>Three optimal-control formulations on the same quadrotor plant, compared on the same test, in MATLAB and Simulink. The interesting part turned out not to be which controller settles fastest — it was reading the control-effort axes afterwards and realising what the fastest one was spending to get there.&lt;/p>
&lt;p>Coursework for &lt;strong>ME5659: Control Systems Engineering&lt;/strong> at Northeastern.&lt;/p>
&lt;hr>
&lt;h2 id="plant">Plant&lt;/h2>
&lt;p>A 6-DOF rigid body from the Newton–Euler equations, following &lt;a href="https://www.diva-portal.org/smash/get/diva2:860649/FULLTEXT01.pdf" target="_blank" rel="noopener">Sabatino (2015)&lt;/a>:&lt;/p>
$$m(\omega_B \wedge v_B + \dot{v}_B) = f_B, \qquad I\dot{\omega}_B + \omega_B \wedge (I\omega_B) = m_B$$&lt;p>Four rotors give four control inputs — one collective thrust and three body torques:&lt;/p>
$$u = [f_t \quad \tau_x \quad \tau_y \quad \tau_z]^\mathsf{T}, \qquad \tau_x = bl(\Omega_3^2 - \Omega_1^2)$$&lt;p>Simulation constants: &lt;strong>m = 0.2 kg&lt;/strong>, &lt;strong>Ix = Iy = 0.1&lt;/strong>, &lt;strong>Iz = 0.15 kg·m²&lt;/strong>, sample time &lt;strong>0.01 s&lt;/strong>. Inertia is treated as diagonal, which assumes the body frame&amp;rsquo;s origin and axes coincide with the barycentre and principal axes.&lt;/p>
&lt;h3 id="linearisation">Linearisation&lt;/h3>
&lt;p>Three stacked simplifications get from the nonlinear model to something &lt;code>lqr()&lt;/code> will accept:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Euler rates identified with body rates&lt;/strong> — $[\dot\phi \; \dot\theta \; \dot\psi]^\mathsf{T} = [p \; q \; r]^\mathsf{T}$, valid for small angles.&lt;/li>
&lt;li>&lt;strong>Small oscillations&lt;/strong> — sines replaced by their arguments, cosines by unity.&lt;/li>
&lt;li>&lt;strong>Hover equilibrium&lt;/strong> — $\bar{u} = [mg \; 0 \; 0 \; 0]^\mathsf{T}$, the thrust that exactly cancels weight.&lt;/li>
&lt;/ol>
&lt;p>What comes out is fully decoupled: three double-integrator chains on attitude, three on position, with gravity coupling pitch into $\dot{u}$ and roll into $\dot{v}$. Twelve states, four inputs, and with $C = I_{12}$ (all states assumed measurable) both the controllability and observability matrices come back full rank 12.&lt;/p>
&lt;p>The honest caveat on all of this: validity is bounded by the small-angle assumption and never quantified. A wind-disturbance matrix $D \in \mathbb{R}^{12 \times 6}$ is derived from the rotor and body-force channels — and then never exercised, since every simulation runs with &lt;code>D = zeros&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="controllers">Controllers&lt;/h2>
&lt;p>&lt;strong>LQR&lt;/strong> — state feedback minimising $\int (x^\mathsf{T}Qx + u^\mathsf{T}Ru)\,dt$. Weights were left at $Q = I_{12}$, $R = I_4$; no weight tuning was performed, so all three controllers share the same underlying gain.&lt;/p>
&lt;p>&lt;strong>LQR-PI&lt;/strong> — PI action in the forward path, LQR state feedback around it, with an explicit injection point for unmodelled dynamics between the PI output and the plant. Integral action is what kills the steady-state error LQR leaves behind. Final hand-tune: &lt;code>Kp = [500, 2, 200, 1]&lt;/code>, &lt;code>Ki = [1, 1, 1, 1]&lt;/code>.&lt;/p>
&lt;p>&lt;strong>LQG&lt;/strong> — LQR plus a Kalman estimator, for the case where states are inferred rather than measured. Process noise covariance $10^{-3}$ on the six rate/velocity states, measurement noise $10^{-4}$.&lt;/p>
&lt;h2 id="test">Test&lt;/h2>
&lt;p>Not a step response — a &lt;strong>regulation-to-origin decay&lt;/strong>. The quadrotor starts displaced at (x, y, z) = &lt;strong>(5.5, 6, 7.5) m&lt;/strong> with zero attitude and zero rates, reference at the origin, and each controller drives it home.&lt;/p>
&lt;p>The study&amp;rsquo;s summary comparison:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Controller&lt;/th>
&lt;th>Max overshoot&lt;/th>
&lt;th>Settling time&lt;/th>
&lt;th>Parameter sensitivity&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>LQR&lt;/td>
&lt;td>12%&lt;/td>
&lt;td>2.5 s&lt;/td>
&lt;td>High&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LQR-PI&lt;/td>
&lt;td>5%&lt;/td>
&lt;td>1.8 s&lt;/td>
&lt;td>Moderate&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LQG&lt;/td>
&lt;td>3%&lt;/td>
&lt;td>1.2 s&lt;/td>
&lt;td>Low&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="re-deriving-it">Re-deriving it&lt;/h3>
&lt;p>Rebuilding the plant from the constants above and re-solving the LQR reproduces the original run exactly where it can be checked. Both the controllability and observability matrices come back rank 12; the open-loop eigenvalues are all zero — marginally stable, six pure integrator pairs, which is what you expect from a decoupled double-integrator model and worth noting because it means the plant is &lt;em>not&lt;/em> asymptotically stable before feedback. Closing the loop puts every eigenvalue at Re ≤ −1.0. And the initial control effort comes out at&lt;/p>
$$u(0) = [-7.5,\; -6.0,\; 5.5,\; 0]$$&lt;p>matching the report&amp;rsquo;s printed console output digit for digit — good evidence the model, weights and initial condition are transcribed correctly.&lt;/p>
&lt;p>What doesn&amp;rsquo;t reproduce is the table. With $Q = I_{12}$, $R = I_4$ and that initial condition, the position channels decay &lt;strong>monotonically with no overshoot at all&lt;/strong>, settling to 2% in about &lt;strong>4.4 s&lt;/strong> — not 12% overshoot in 2.5 s. The response plots in the report agree with the re-derivation rather than with its own summary table: all three position traces are smooth monotone decays reaching zero around 4–5 s.&lt;/p>
&lt;p>So I&amp;rsquo;d treat the overshoot and settling-time figures as unreliable, and the ordinal sensitivity ranking as qualitative judgement rather than measurement. What follows below is the part that &lt;em>is&lt;/em> visible in the data.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="flex justify-center ">
&lt;div class="w-100" >&lt;img alt="LQR-PI response: position decay, body moments, thrust and power perturbation" srcset="
/saikiran_juttu.github.io/project/quadrotor-control/lqr-pi-response_hu2407646783812282393.webp 400w,
/saikiran_juttu.github.io/project/quadrotor-control/lqr-pi-response_hu6685818478649532550.webp 760w,
/saikiran_juttu.github.io/project/quadrotor-control/lqr-pi-response_hu13583573080454341919.webp 1200w"
src="https://juttu-s.github.io/saikiran_juttu.github.io/saikiran_juttu.github.io/project/quadrotor-control/lqr-pi-response_hu2407646783812282393.webp"
width="760"
height="380"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;em>LQR-PI. Positions decay cleanly — but look at the vertical axes in the right two columns.&lt;/em>&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="flex justify-center ">
&lt;div class="w-100" >&lt;img alt="LQR response on the same test and the same layout" srcset="
/saikiran_juttu.github.io/project/quadrotor-control/lqr-response_hu8600440175981309337.webp 400w,
/saikiran_juttu.github.io/project/quadrotor-control/lqr-response_hu15163642574668778384.webp 760w,
/saikiran_juttu.github.io/project/quadrotor-control/lqr-response_hu10311598550682387049.webp 1200w"
src="https://juttu-s.github.io/saikiran_juttu.github.io/saikiran_juttu.github.io/project/quadrotor-control/lqr-response_hu8600440175981309337.webp"
width="760"
height="380"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;em>LQR, same test, same panel layout. Same position traces, wildly different actuator numbers.&lt;/em>&lt;/p>
&lt;hr>
&lt;h2 id="what-the-settling-time-cost">What the settling time cost&lt;/h2>
&lt;p>Put those two figures side by side and the position columns are nearly indistinguishable — both controllers bring x, y and z to zero inside about five seconds with no visible overshoot. The difference is entirely in the actuator channels:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>LQR&lt;/th>
&lt;th>LQR-PI&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Thrust perturbation, peak&lt;/td>
&lt;td>≈ −6&lt;/td>
&lt;td>≈ −3500&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Pitch moment, peak&lt;/td>
&lt;td>≈ +4&lt;/td>
&lt;td>≈ +1000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Power perturbation, peak&lt;/td>
&lt;td>≈ 1.5&lt;/td>
&lt;td>≈ 10&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>LQR-PI pays roughly &lt;strong>500×&lt;/strong> the peak thrust perturbation for a position response that is visually indistinguishable. That is a direct consequence of &lt;code>Kp = [500, 2, 200, 1]&lt;/code> — the aggressive entries sit on exactly the thrust and pitch-torque channels where the blow-up appears. And unlike the settling times, this one is unambiguous: it&amp;rsquo;s printed on the axes of the report&amp;rsquo;s own figures.&lt;/p>
&lt;p>On a real airframe those commands are far outside what any rotor can deliver, so the controller would saturate immediately and whatever settling-time advantage it has on paper would evaporate.&lt;/p>
&lt;p>This is the part of the exercise that actually taught me something: a settling-time comparison with unbounded actuators isn&amp;rsquo;t a comparison of controllers, it&amp;rsquo;s a comparison of how hard each one is willing to push. Judge on overshoot and settling time alone and LQR-PI looks like a free improvement — plot the control effort and it obviously isn&amp;rsquo;t. Actuator limits belong &lt;em>in&lt;/em> the problem formulation, which is the argument for constrained optimal control rather than a discovery you make afterwards in the plots.&lt;/p>
&lt;p>Two further limits worth stating plainly. &lt;strong>Yaw was never exercised&lt;/strong> — the yaw moment sits on a $\times 10^{-15}$ axis in both runs, i.e. numerically zero, because initial yaw is zero and the linearised yaw channel is fully decoupled. And the &lt;strong>LQG run had noise switched off&lt;/strong> (&lt;code>NL = 0&lt;/code>), so that row shows the estimator in the loop but does not demonstrate the noise rejection that is the entire reason to prefer LQG. This is a simulation-only study; there was no hardware.&lt;/p>
&lt;hr>
&lt;h2 id="the-test-couldnt-show-what-lqr-pi-is-for">The test couldn&amp;rsquo;t show what LQR-PI is for&lt;/h2>
&lt;p>Rebuilding the three loops to check the numbers turned up something the study missed
entirely, and it&amp;rsquo;s the most useful thing I took away from the project.&lt;/p>
&lt;p>On this test, &lt;strong>plain LQR already drives the state to zero.&lt;/strong> Final position error comes out
around 10⁻⁴ m, which is numerical integration error, not a control deficiency. There is no
steady-state error, no persistent disturbance, no model mismatch. So integral action has
nothing to remove — and adding it can only contribute phase lag. Re-running with proper
integral action confirms exactly that: settling gets &lt;em>worse&lt;/em> (5.9 s against LQR&amp;rsquo;s 4.4 s) and
overshoot appears where there was none.&lt;/p>
&lt;p>Which means the standard justification for LQR-PI — that it eliminates steady-state error — is
true, but &lt;strong>the test cannot demonstrate it.&lt;/strong> A regulation-to-origin decay on an undisturbed
linear model is precisely the case where PI has no work to do.&lt;/p>
&lt;p>The fix is to disturb it. Adding a constant wind force of 0.3 N and 0.2 N — using the
disturbance matrix the report derives and then never exercises — separates them immediately:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Controller&lt;/th>
&lt;th>Steady-state position error under constant wind&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>LQR&lt;/td>
&lt;td>&lt;strong>1.24 m&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LQR-PI&lt;/td>
&lt;td>&lt;strong>0.00 m&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>That&amp;rsquo;s the comparison the study should have run. It costs one extra term in the simulation, it
uses a matrix already derived on page 13, and it turns &amp;ldquo;LQR-PI settles 0.7 s faster&amp;rdquo; — a claim
that doesn&amp;rsquo;t survive re-derivation — into a difference that is unambiguous and large.&lt;/p>
&lt;h2 id="what-id-do-differently">What I&amp;rsquo;d do differently&lt;/h2>
&lt;p>Drop the linearisation. The plant is meaningfully nonlinear away from hover, and the natural next step is &lt;strong>nonlinear model-predictive control&lt;/strong> on the full model — which also gets you actuator constraints in the formulation rather than discovered afterwards in the plots, and lets you track a planned trajectory through a set of waypoints instead of regulating to a single point. Tuning $Q$ and $R$ rather than leaving them at identity would be the cheaper first improvement.&lt;/p>
&lt;h2 id="stack">Stack&lt;/h2>
&lt;p>MATLAB (&lt;code>ss&lt;/code>, &lt;code>ctrb&lt;/code>, &lt;code>obsv&lt;/code>, &lt;code>lqr&lt;/code>), Simulink, three models for the three controllers.&lt;/p></description></item></channel></rss>