.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/control_systems/frequency/plot_04_nichols_chart.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code or to run this example in your browser via JupyterLite. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_control_systems_frequency_plot_04_nichols_chart.py: The Nichols chart (1947) ======================== Nathaniel Nichols, at the MIT Radiation Laboratory during the war, plotted the open-loop gain in decibels against its phase. On those axes the closed-loop gain of a unity-feedback loop, :math:`|L/(1+L)|`, is a fixed family of contours (:func:`~engineeringkit.control_systems.nichols_closed_loop`), so one curve shows both. The highest contour the locus touches is the closed-loop resonant peak :math:`M_r` (:func:`~engineeringkit.control_systems.nichols`), and reshaping the locus away from the critical point :math:`(-180^\circ, 0\text{ dB})` lowers it. The chart was published in James, Nichols and Phillips's *Theory of Servomechanisms* (1947). Here a lead compensator (:func:`~engineeringkit.control_systems.design_lead`) lifts the phase of :math:`L = 4/(s(s+1)(s+2))` near crossover and pulls the resonant peak down. .. GENERATED FROM PYTHON SOURCE LINES 21-47 .. image-sg:: /api/gallery/control_systems/frequency/images/sphx_glr_plot_04_nichols_chart_001.png :alt: Nichols chart with closed-loop M-contours :srcset: /api/gallery/control_systems/frequency/images/sphx_glr_plot_04_nichols_chart_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none uncompensated: Mr = 14.69 dB at 1.187 rad/s lead compensated: Mr = 5.54 dB at 1.930 rad/s | .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from engineeringkit.control_systems import bode, complementary_sensitivity, design_lead, nichols, transfer_function from engineeringkit.control_systems.visualizers import plot_nichols loop = transfer_function([4], [1, 3, 2, 0]) design = design_lead(loop, 50.0) w = np.logspace(-2, 2, 4000) ax = plot_nichols(loop, w, label="uncompensated") plot_nichols(design.compensated_loop, w, ax=ax, label="lead compensated", contours=None) ax.set(xlim=(-270, -60), ylim=(-30, 30), title="Nichols chart with closed-loop M-contours") for name, L in (("uncompensated", loop), ("lead compensated", design.compensated_loop)): n = nichols(L, w) # The resonant peak read from the chart is the peak of the closed-loop Bode magnitude. _, mag, _ = bode(complementary_sensitivity(L), w) print(f"{name}: Mr = {n['resonant_peak_db']:.2f} dB at {n['resonant_frequency']:.3f} rad/s") assert np.isclose(n["resonant_peak_db"], mag.max(), atol=1e-6) i = np.argmax(n["closed_gain_db"]) ax.plot(n["phase_deg"][i], n["gain_db"][i], "ks", mfc="none") before, after = nichols(loop, w)["resonant_peak_db"], nichols(design.compensated_loop, w)["resonant_peak_db"] assert after < before - 3 plt.show() .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.190 seconds) .. _sphx_glr_download_api_gallery_control_systems_frequency_plot_04_nichols_chart.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: lite-badge .. image:: images/jupyterlite_badge_logo.svg :target: ../../../../lite/lab/index.html?path=api/gallery/control_systems/frequency/plot_04_nichols_chart.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_04_nichols_chart.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_04_nichols_chart.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_04_nichols_chart.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_