Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a documentation page(s) that show parameter values and figures for all bicycles and riders #112

Merged
merged 25 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1758a2b
Start at making a gallery of all the measured bicycles.
moorepants Aug 31, 2024
df54930
Added eig of rider on balance assist.
moorepants Aug 31, 2024
8cc3a1b
Added a _repr_html_ to ParameterSet.
moorepants Oct 25, 2024
963cf90
Added sphinx gallery and first example for the gallery.
moorepants Oct 25, 2024
bdcc3b1
Added gallery header file.
moorepants Oct 25, 2024
707520f
Fix plot_eigenvectors to support 1 row and show eigenvalue in plot_mo…
moorepants Oct 27, 2024
a795800
Add simulations of all modes across speeds to benchmark example.
moorepants Oct 27, 2024
d135892
Added gallery example for Balance Assist bike.
moorepants Oct 27, 2024
04108d2
Merge branch 'master' into bicycle-gallery
moorepants Oct 27, 2024
56bead4
Allow passing in axes to plot_simulation.
moorepants Oct 29, 2024
88d3538
Added control to balance assist example.
moorepants Oct 29, 2024
5e944f0
Minor cleanup.
moorepants Oct 29, 2024
28b582f
Add y labels to simulation plots.
moorepants Oct 30, 2024
3b974f6
More text for the benchmark example.
moorepants Oct 30, 2024
eb031cb
Finalized benchmark example.
moorepants Oct 31, 2024
5fd5c4a
Finished balance assist example.
moorepants Oct 31, 2024
ac105f7
Remove axes kwarg, as it wasn't fleshed out enough.
moorepants Oct 31, 2024
c12b998
Correct reference to method.
moorepants Oct 31, 2024
1efb0db
Change title of gallery.
moorepants Oct 31, 2024
2ec2262
Removed the bicycles page, using gallery instead.
moorepants Oct 31, 2024
0f67aa1
Corrected the Table of Contents.
moorepants Oct 31, 2024
2261ad0
Add sphinx gallery to the old CI setup.
moorepants Oct 31, 2024
8c4aae7
Stick with balance assist as the name.
moorepants Oct 31, 2024
0682d22
Name the example gallery directory gallery.
moorepants Oct 31, 2024
458790b
Fixed ToC.
moorepants Oct 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions bicycleparameters/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -696,19 +696,27 @@ def plot_eigenvectors(self, **parameter_overrides):
# eval_seq, evec_seq = sort_eigenmodes(eval_seq, evec_seq)

fig, axes = plt.subplots(*eval_seq.shape,
subplot_kw={'projection': 'polar'})
subplot_kw={'projection': 'polar'},
layout='constrained')
axes = np.atleast_2d(axes)
fig.set_size_inches(axes.shape[1]*3, axes.shape[0]*3)
lw = list(range(1, len(states) + 1))
lw.reverse()

for k, (evals, evecs, par_val) in enumerate(zip(eval_seq, evec_seq,
par[arr_keys[0]])):
if arr_keys:
par_vals = par[arr_keys[0]]
else:
par_vals = [0.0]

for k, (evals, evecs, par_val) in enumerate(zip(eval_seq, evec_seq.T,
par_vals)):

axes[k, 0].set_ylabel('{} = {:1.2f}'.format(arr_keys[0], par_val),
labelpad=30)
if arr_keys:
axes[k, 0].set_ylabel('{} = {:1.2f}'.format(arr_keys[0],
par_val),
labelpad=30)

for i, (eigenval, eigenvec) in enumerate(zip(evals, evecs.T)):
for i, (eigenval, eigenvec) in enumerate(zip(evals, evecs)):

max_com = np.abs(eigenvec[:2]).max()

Expand All @@ -731,8 +739,6 @@ def plot_eigenvectors(self, **parameter_overrides):
loc='upper center', bbox_to_anchor=(0.5, 1.05),
fancybox=True, shadow=True, ncol=4)

fig.tight_layout()

return axes

def simulate(self, times, initial_conditions, input_func=None,
Expand Down Expand Up @@ -933,16 +939,28 @@ def plot_mode_simulations(self, times, **parameter_overrides):

"""
results = self.simulate_modes(times, **parameter_overrides)
evals, evecs = self.calc_eigen(**parameter_overrides)

fig, axes = plt.subplots(4, 2, sharex=True)
fig, axes = plt.subplots(4, 2, sharex=True, layout='constrained')

for i, res in enumerate(results):
for i, (res, e_val) in enumerate(zip(results, evals)):
axes[i, 0].plot(times, np.rad2deg(results[i, :, :2]))
axes[i, 0].legend(['$' + lab + '$'
for lab in self.state_vars_latex[:2]])
axes[i, 0].set_ylabel('Angle\n[deg]')
axes[i, 1].plot(times, np.rad2deg(results[i, :, 2:]))
axes[i, 1].legend(['$' + lab + '$'
for lab in self.state_vars_latex[2:]])
axes[i, 1].set_ylabel('Angular Rate\n[deg/s]')
msg = r'Eigenvalue: {:1.3f}'
if e_val.real >= 0.0:
fontcolor = 'red' # red indicates unstable
else:
fontcolor = 'black'
axes[i, 0].set_title(msg.format(e_val),
fontdict={'color': fontcolor})
axes[i, 1].set_title(msg.format(e_val),
fontdict={'color': fontcolor})

axes[3, 0].set_xlabel('Time [s]')
axes[3, 1].set_xlabel('Time [s]')
Expand Down
10 changes: 10 additions & 0 deletions bicycleparameters/parameter_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,16 @@ def to_ini(self, fname):
with open(fname, 'w') as f:
f.write(text)

def _repr_html_(self):
opening = r'<table><caption>{}</caption><tr><th>Variable</th><th>Value</th></tr>'
lines = opening.format(self.parameterization)
for k, v in self.parameters.items():
latex_var = r'\({}\)'.format(self.par_strings[k])
row_str = r'<tr><td>{}</td><td>{:1.3f}</td></tr>'
lines += row_str.format(latex_var, v)
lines += r'</table>'
return lines


class Meijaard2007ParameterSet(ParameterSet):
"""Represents the parameters of the benchmark bicycle presented in
Expand Down
2 changes: 2 additions & 0 deletions conda/bicycleparameters-dev-old.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ dependencies:
- pandas ==1.3.*
- plotly
# dev
- ipython
- nose
- numpydoc ==1.2.*
- pytest
- sphinx ==4.3.*
- sphinx-gallery
1 change: 1 addition & 0 deletions conda/bicycleparameters-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ dependencies:
- numpydoc >=1.2
- pytest
- sphinx >=4.3.2
- sphinx-gallery
14 changes: 14 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@

import bicycleparameters

DOCS_CONF_PATH = os.path.realpath(__file__)
DOCS_DIR = os.path.dirname(DOCS_CONF_PATH)
REPO_DIR = os.path.realpath(os.path.join(DOCS_DIR, '..'))

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

# If your documentation needs a minimal Sphinx version, state it here.
Expand All @@ -30,9 +34,11 @@
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'matplotlib.sphinxext.plot_directive',
'sphinx.ext.mathjax',
'numpydoc',
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx_gallery.gen_gallery',
]

numpydoc_show_class_members = False
Expand Down Expand Up @@ -96,6 +102,14 @@
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# sphinx-gallery settings
sphinx_gallery_conf = {
'examples_dirs': os.path.join(REPO_DIR, 'gallery'),
'gallery_dirs': 'gallery',
'matplotlib_animations': True,
'copyfile_regex': r'.*\.svg',
'remove_config_comments': True,
}

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

Expand Down
6 changes: 6 additions & 0 deletions docs/description.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ References
The methods associated with this software were built upon these previous works,
among others.

.. [Carvallo1899] Carvallo, E. (1899). Théorie du mouvement du monocycle et de
la bicyclette. Gauthier- Villars.

.. [Whipple1899] Whipple, F. J. W. (1899). The stability of the motion of a
bicycle. Quarterly Journal of Pure and Applied Mathematics, 30, 312–348.

.. [Roland1971] Roland J R ., R. D., and Massing , D. E. A digital computer simulation of
bicycle dynamics. Calspan Report YA-3063-K-1, Cornell Aeronautical
Laboratory, Inc., Buffalo, NY, 14221, Jun 1971. Prepared for Schwinn Bicycle
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Table of Contents
description.rst
installation.rst
examples.rst
gallery/index.rst
data.rst
app.rst
bicycleparameters.rst
Expand Down
3 changes: 3 additions & 0 deletions gallery/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
===============
Example Gallery
===============
131 changes: 131 additions & 0 deletions gallery/plot_balanceassistv1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""
===========================================================
Balance Assist E-Bike with Roll Rate Feedback Steer Control
===========================================================

This example shows how to work with a model that includes a feedback controller
and how to use a simple derivative control with it. The TU Delft Bicycle Lab
developed a bicycle with a steer motor that can be controlled based on sensor
measurements from an inertial measurement unit mounted on the rear frame, a
steer angle sensor, and a speed sensor. The bicycle is based on an e-bike model
from Royal Dutch Gazelle:

.. figure:: https://objects-us-east-1.dream.io/mechmotum/balance-assist-bicycle-400x400.jpg
:align: center

Gazelle Grenoble/Arroyo E-Bike modified with a steering motor. Battery in
the downtube and electronics box on the rear rack.

"""
import numpy as np

from bicycleparameters.main import Bicycle
from bicycleparameters.io import remove_uncertainties
from bicycleparameters.parameter_sets import Meijaard2007ParameterSet
from bicycleparameters.models import Meijaard2007WithFeedbackModel

# %%
# Set Up a Model
# ==============
#
# First, load the physical parameter measurements of the bicycle from a file
# and create a
# :class:`~bicycleparameters.parameter_sets.Meijaard2007ParameterSet`.

data_dir = "../data"

bicycle = Bicycle("Balanceassistv1", pathToData=data_dir)
par = remove_uncertainties(bicycle.parameters['Benchmark'])
par['v'] = 1.0
par_set = Meijaard2007ParameterSet(par, False)
par_set

# %%
# The following plot depicts the geometry and inertial parameters with the
# inertia of the rider included.
par_set.plot_all()

# %%
# Create a :class:`~bicycleparameters.models.Meijaard2007WithFeedbackModel`.
# The parameter set does not include the feedback gain parameters, but
# :meth:`~bicycleparameters.parameter_sets.Meijaard2007WithFeedbackParameterSet.to_parameterization`
# will be used to convert the parameter set into one with the gains.
model = Meijaard2007WithFeedbackModel(par_set)
model.parameter_set

# %%
# The model shows a small self-stable speed range.
speeds = np.linspace(0.0, 10.0, num=501)
ax = model.plot_eigenvalue_parts(v=speeds)
ax.set_ylim((-10.0, 10.0))

# %%
# Add a Rider
# ===========
#
# If the data files for a rider are present in the data directory, you can add
# a rider and the package Yeadon will be used to configure a rider to sit on
# the bicycle. You can check if a rider is properly configured by plotting the
# geometry which will now include a stick figure depiction of the rider.
bicycle.add_rider('Jason', reCalc=True)
bicycle.plot_bicycle_geometry(inertiaEllipse=False)

# %%
# The inertia representation now reflects the larger inertia of the rear frame
# due to the rigid rider addition.
par = remove_uncertainties(bicycle.parameters['Benchmark'])
par['v'] = 1.0
par_set = Meijaard2007ParameterSet(par, True)
par_set.plot_all()

# %%
# The self-stable speed range begins at a higher speed and becomes wider.
model = Meijaard2007WithFeedbackModel(par_set)
ax = model.plot_eigenvalue_parts(v=speeds)
ax.set_ylim((-10.0, 10.0))

# %%
# Add Control
# ===========
#
# It turns out that controlling the steer torque with a positive feedback on
# roll angular rate, the bicycle can be stabilized over a large speed range.
# For example, setting :math:`k_{T_\delta \dot{\phi}}=-50` gives this effect:
ax = model.plot_eigenvalue_parts(v=speeds, kTdel_phid=-50.0)
ax.set_ylim((-10.0, 10.0))

# %%
# The eigenvectors at low speed show that the weave mode has a steer dominated
# high frequency natural motion. This may not be so favorable.
ax = model.plot_eigenvectors(v=2.0, kTdel_phid=-50.0)

# %%
# You can also gain schedule with respect to speed. A linear variation in the
# roll rate gain can make the weave eigenfrequency have lower magnitude than
# using simply a constant gain.
vmin, vmin_idx = 1.5, np.argmin(np.abs(speeds - 1.5))
vmax, vmax_idx = 4.7, np.argmin(np.abs(speeds - 4.7))
kappa = 10.0
kphidots = -kappa*(vmax - speeds)
kphidots[:vmin_idx] = -kappa*(vmax - vmin)/vmin*speeds[:vmin_idx]
kphidots[vmax_idx:] = 0.0
model.plot_gains(v=speeds, kTdel_phid=kphidots)

# %%
# Using the gain scheduling gives this effect to the dynamics:
ax = model.plot_eigenvalue_parts(v=speeds, kTdel_phid=kphidots)
ax.set_ylim((-10.0, 10.0))

# %%
# We can then simulate the system at a specific low speed and see the effect
# control has. First, without control:
times = np.linspace(0.0, 2.0, num=201)
x0 = np.array([0.0, 0.0, 0.5, 0.0])
speed = 3.0
ax = model.plot_simulation(times, x0, v=speed)

# %%
# And with control:
times = np.linspace(0.0, 5.0, num=501)
kphidot = kphidots[np.argmin(np.abs(speeds - speed))]
ax = model.plot_simulation(times, x0, v=speed, kTdel_phid=kphidot)
Loading