Skip to content

Commit

Permalink
Documentation overhaul #74
Browse files Browse the repository at this point in the history
  • Loading branch information
m-lundberg committed Apr 28, 2023
1 parent afb1e80 commit d85fb32
Show file tree
Hide file tree
Showing 14 changed files with 277 additions and 277 deletions.
21 changes: 21 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.11"

# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py

# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: docs/requirements.txt
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Rename the module `PID` to `pid` to avoid the shadowing from the `PID` class
- CI migrated from Travis to GitHub Actions
- The [documentation](https://simple-pid.readthedocs.io/) has gotten an overhaul and a new look. Much of the detailed documentation in README.md has been moved to a dedicated user guide.

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018-2022 Martin Lundberg
Copyright (c) 2018-2023 Martin Lundberg

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ include README.md
recursive-exclude * __pycache__
recursive-exclude * *.py[co]

recursive-include docs *.rst conf.py Makefile requirements.txt
recursive-include docs *.rst *.md conf.py requirements.txt
recursive-include examples *.md *.txt *.py
recursive-include tests *.py

Expand Down
100 changes: 6 additions & 94 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,114 +26,26 @@ while True:
v = controlled_system.update(control)
```

Complete API documentation can be found [here](https://simple-pid.readthedocs.io/en/latest/simple_pid.html#module-simple_pid.PID).

## Installation
To install, run:
```
pip install simple-pid
```

## Usage
The `PID` class implements `__call__()`, which means that to compute a new output value, you simply call the object like this:
```python
output = pid(current_value)
```

### The basics
The PID works best when it is updated at regular intervals. To achieve this, set `sample_time` to the amount of time there should be between each update and then call the PID every time in the program loop. A new output will only be calculated when `sample_time` seconds has passed:
```python
pid.sample_time = 0.01 # Update every 0.01 seconds

while True:
output = pid(current_value)
```

To set the setpoint, ie. the value that the PID is trying to achieve, simply set it like this:
```python
pid.setpoint = 10
```

The tunings can be changed any time when the PID is running. They can either be set individually or all at once:
```python
pid.Ki = 1.0
pid.tunings = (1.0, 0.2, 0.4)
```

To use the PID in [reverse mode](http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-direction/), meaning that an increase in the input leads to a decrease in the output (like when cooling for example), you can set the tunings to negative values:

```python
pid.tunings = (-1.0, -0.1, 0)
```

Note that all the tunings should have the same sign.

In order to get output values in a certain range, and also to avoid [integral windup](https://en.wikipedia.org/wiki/Integral_windup) (since the integral term will never be allowed to grow outside of these limits), the output can be limited to a range:
```python
pid.output_limits = (0, 10) # Output value will be between 0 and 10
pid.output_limits = (0, None) # Output will always be above 0, but with no upper bound
```

### Other features
#### Auto mode
To disable the PID so that no new values are computed, set auto mode to False:
```python
pid.auto_mode = False # No new values will be computed when pid is called
pid.auto_mode = True # pid is enabled again
```
When disabling the PID and controlling a system manually, it might be useful to tell the PID controller where to start from when giving back control to it. This can be done by enabling auto mode like this:
```python
pid.set_auto_mode(True, last_output=8.0)
```
This will set the I-term to the value given to `last_output`, meaning that if the system that is being controlled was stable at that output value the PID will keep the system stable if started from that point, without any big bumps in the output when turning the PID back on.

#### Observing separate components
When tuning the PID, it can be useful to see how each of the components contribute to the output. They can be seen like this:
```python
p, i, d = pid.components # The separate terms are now in p, i, d
```

#### Proportional on measurement
To eliminate overshoot in certain types of systems, you can calculate the [proportional term directly on the measurement](http://brettbeauregard.com/blog/2017/06/introducing-proportional-on-measurement/) instead of the error. This can be enabled like this:
```python
pid.proportional_on_measurement = True
python -m pip install simple-pid
```

#### Differential on measurement
By default the [differential term is calculated on the measurement](http://brettbeauregard.com/blog/2011/04/improving-the-beginner%e2%80%99s-pid-derivative-kick/) instead of the error. This can be disabled like this:
```python
pid.differential_on_measurement = False
```

#### Error mapping
To transform the error value to another domain before doing any computations on it, you can supply an `error_map` callback function to the PID. The callback function should take one argument which is the error from the setpoint. This can be used e.g. to get a degree value error in a yaw angle control with values between [-pi, pi):
```python
import math

def pi_clip(angle):
if angle > 0:
if angle > math.pi:
return angle - 2*math.pi
else:
if angle < -math.pi:
return angle + 2*math.pi
return angle

pid.error_map = pi_clip
```
## Documentation
Documentation, including a user guide and complete API reference, can be found [here](https://simple-pid.readthedocs.io/).

#### Overriding time function
By default, the PID uses `time.monotonic()` (or if not available, `time.time()` as fallback) to get the current time on each invocation. The time function can be overridden by setting `PID.time_fn` to whichever function you want to use. For example, to use the [MicroPython time.ticks_us()](https://docs.micropython.org/en/latest/library/time.html#time.ticks_us):
```python
import time
pid.time_fn = time.ticks_us
```

## Tests
Use the following to run tests:
This project has a test suite using [`pytest`](https://docs.pytest.org/). To run the tests, install `pytest` and run:

```
pytest -v
```


## License
Licensed under the [MIT License](https://github.com/m-lundberg/simple-pid/blob/master/LICENSE.md).
20 changes: 0 additions & 20 deletions docs/Makefile

This file was deleted.

6 changes: 4 additions & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
m2r2
sphinx-rtd-theme
furo==2023.3.27
myst-parser==1.0.0
sphinx==6.2.1
sphinx-copybutton==0.5.2
147 changes: 22 additions & 125 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('../../'))
Expand All @@ -20,150 +15,52 @@
# -- Project information -----------------------------------------------------

project = 'simple-pid'
copyright = '2018-2022, Martin Lundberg'
copyright = '2018-2023, Martin Lundberg'
author = 'Martin Lundberg'

# The short X.Y version
version = ''
# The full version, including alpha/beta/rc tags
release = '1.0.1'
# Extract version from pyproject.toml
with open('../../pyproject.toml', 'r') as f:
for line in f:
if line.startswith('version'):
release = line.split('"')[1]
version = release


# -- General configuration ---------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'm2r2',
'sphinx_copybutton',
'myst_parser',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = ['.rst', '.md']

# The master toctree document.
master_doc = 'index'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
root_doc = 'index'
exclude_patterns = []

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_dark_style = 'nord'


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'

# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_theme = 'furo'
html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}


# -- Options for HTMLHelp output ---------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'simple-piddoc'


# -- Options for LaTeX output ------------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',

# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'simple-pid.tex', 'simple-pid Documentation',
'Martin Lundberg', 'manual'),
]


# -- Options for manual page output ------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'simple-pid', 'simple-pid Documentation',
[author], 1)
]


# -- Options for Texinfo output ----------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'simple-pid', 'simple-pid Documentation',
author, 'simple-pid', 'One line description of project.',
'Miscellaneous'),
]
html_title = f'{project} {release}'


# -- Extension configuration -------------------------------------------------

# autoclass_content = 'both'
autodoc_class_signature = 'separated'
autodoc_member_order = 'bysource'
autodoc_typehints = 'description'
autodoc_typehints_description_target = 'documented'

def autodoc_skip_member(app, what, name, obj, skip, options):
# include __init__ and __call__ in docs
if name in ('__init__', '__call__'):
# Include __call__ in docs
if name in ['__call__']:
return False
return skip

Expand Down
Loading

0 comments on commit d85fb32

Please sign in to comment.