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

Implement a tire model and compute the normal force #128

Merged
merged 75 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
913cc51
Take auxiliary speed into account when merging systems
tjstienstra Mar 15, 2024
e6a5144
Use a different computation of the nonholonomic constraints to preven…
tjstienstra Mar 19, 2024
b741fef
Add utility to ignore point warnings
tjstienstra Mar 19, 2024
9083230
Ignore point warnings in tests
tjstienstra Mar 19, 2024
a523103
Revert "Use a different computation of the nonholonomic constraints t…
tjstienstra Mar 20, 2024
ed8c57a
Implement a utility to compute noncontributing loads
tjstienstra Mar 21, 2024
220d766
Implement and use a custom velocity computation for points
tjstienstra Mar 21, 2024
d93023f
Add tests for auxiliary forces handler
tjstienstra Mar 21, 2024
2be67b9
Rename and fix test
tjstienstra Mar 21, 2024
ba1616e
Implement from_system classmethod
tjstienstra Mar 21, 2024
fe38159
Add comment to clarify data structure of the position tree
tjstienstra Mar 21, 2024
5f6c47c
Refactor docstrings
tjstienstra Mar 21, 2024
f50520e
Rename test module to match src module
tjstienstra Mar 21, 2024
8686927
Implement attribute to specify whether a model is the root
tjstienstra Mar 21, 2024
b461f6f
Remove unclear note
tjstienstra Mar 21, 2024
98b6dd8
Integrate auxiliary speed handler
tjstienstra Mar 21, 2024
1a1a776
Implement support for System.velocity_constraints
tjstienstra Mar 21, 2024
914621d
Implement option to compute the normal force for the NonHolonomicTire
tjstienstra Mar 21, 2024
7ddbd46
Add test for computing the normal force of a bicycle
tjstienstra Mar 21, 2024
da346de
Auto add auxiliary speeds to the root model
tjstienstra Mar 21, 2024
b5d962a
Move _on_ground declaration to init as it is a property not an object
tjstienstra Mar 22, 2024
7b4b802
Use Point.vel because it performs better on the Whipple bicycle bench…
tjstienstra Mar 22, 2024
14e58a8
Delete compute_velocity such that is does not have to be maintained
tjstienstra Mar 22, 2024
d80d381
Add a brief explanation to the auxiliary data handler
tjstienstra Mar 22, 2024
fb33802
Always adjust constraints for auxiliary speeds
tjstienstra Mar 22, 2024
39bdc7d
Add normal force tests for only one tire
tjstienstra Mar 22, 2024
ff5d4bd
Reimplement compute_velocity using the opposite cross-product resulti…
tjstienstra Mar 23, 2024
07a658b
Only use ICR assumption if that does not infer with the position tree
tjstienstra Mar 23, 2024
db7e9cb
Use create_model_from_connection utility
tjstienstra Mar 23, 2024
f684ffc
Fix failing test
tjstienstra Mar 23, 2024
3b3bfba
Add tests on defining the upward radial axis
tjstienstra Mar 23, 2024
235aacb
Add longitudinal and lateral axis properties to possibly simplify equ…
tjstienstra Mar 23, 2024
aac65ad
Define longitudinal and lateral axis of the rear wheel
tjstienstra Mar 23, 2024
0d5debe
Explain main condition for the auxiliary data handler to work correctly
tjstienstra Mar 23, 2024
ecd9044
Add auxiliary data handler to brim.core init
tjstienstra Mar 23, 2024
5ede94a
Implement an initial version of the generic InContactTire
tjstienstra Mar 23, 2024
3a1e74a
Correct auto axes computations and make them independent
tjstienstra Mar 23, 2024
2a76435
Add tests for auto axes computations
tjstienstra Mar 23, 2024
50d58ed
Only create normal force symbol if normal force will be computed
tjstienstra Mar 23, 2024
d1f63c9
Implement a camber and slip angle property
tjstienstra Mar 24, 2024
b2d383d
Move auxiliary speeds to uaux property
tjstienstra Mar 24, 2024
c722863
Take auxiliary velocity of the ground into account
tjstienstra Mar 24, 2024
a7f6a70
Integrate the handling of noncontributing forces in the implementatio…
tjstienstra Mar 24, 2024
3e658ac
Add a separate section on how to use the auxiliary data handler
tjstienstra Mar 24, 2024
4157428
Remove autouse of fixtures because not every test uses it
tjstienstra Mar 25, 2024
c22795d
Add type checking init tests
tjstienstra Mar 25, 2024
e9ff63d
Remove tree validation option as errors will already have been triggered
tjstienstra Mar 25, 2024
bdc3cb7
Add extract tree tests
tjstienstra Mar 25, 2024
4d017ae
Add get_parent tests
tjstienstra Mar 25, 2024
4e624c9
Compute parent velocity if unknown in _compute_velocity
tjstienstra Mar 25, 2024
f4681f2
Raise ValueError if point is not connected in _compute_velocity
tjstienstra Mar 25, 2024
023c8de
Add _compute_velocity tests
tjstienstra Mar 25, 2024
3b21f82
Reach full line coverage of apply_speeds
tjstienstra Mar 25, 2024
5ffb91c
Test get_auxiliary_velocity
tjstienstra Mar 25, 2024
17f2a71
Rename to match src module
tjstienstra Mar 25, 2024
6da5499
Make InContactTire public
tjstienstra Mar 25, 2024
cc8ee4c
Specify tire's longitudinal and lateral axis in RollingDisc if possible
tjstienstra Mar 25, 2024
2c6a773
Add auxiliary speeds to automatic get descriptions test
tjstienstra Mar 25, 2024
f64cf9a
Fix InContactTire descriptions property
tjstienstra Mar 25, 2024
f473585
Add InContactTire tests
tjstienstra Mar 25, 2024
a528ffe
Bump versions and adjust for new linting rules
tjstienstra Mar 25, 2024
b66d8fe
Rename uaux to u_aux to match System
tjstienstra Mar 25, 2024
39f526d
Remove yaw frame to reduce the number of operations in the EoMs
tjstienstra Mar 26, 2024
d7872c3
Reduce number of operations in the EoMs by using v2pt_theory formulation
tjstienstra Mar 26, 2024
fec6226
Correct sign of the slip angle
tjstienstra Mar 26, 2024
7f01de8
Add notes on symbol reservation in the InContactTire docstring
tjstienstra Mar 28, 2024
1d25b0c
Add explanation why having a separate yaw frame is less efficient
tjstienstra Mar 28, 2024
659832d
Make InContactTire public
tjstienstra Mar 28, 2024
a5d63cd
Update ruff linting configuration
tjstienstra Mar 28, 2024
e0b2063
Add property to specify tire force equations after the define kinemat…
tjstienstra Mar 28, 2024
d165d83
Add test for when compute_velocity has to fall back to taking the der…
tjstienstra Mar 28, 2024
2953ec8
Update sympy
tjstienstra Mar 29, 2024
93e71e9
Revert accidental changes
tjstienstra Mar 29, 2024
5001471
Add shared frame to get full line coverage
tjstienstra Mar 29, 2024
ae02851
Disable cache to always use the newest SymPy version
tjstienstra Mar 29, 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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
init-shell: >-
bash
powershell
cache-environment: true
cache-environment: false
post-cleanup: 'all'
- name: Deactivate the tutorials environment
run: |
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.262
rev: v0.3.4
hooks:
- id: ruff
args: [ --fix, --show-fixes, --exit-non-zero-on-fix ]
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
sys.path.insert(0, os.path.dirname(__file__))
sys.path.insert(0, os.path.abspath("../src"))

from process_tutorials import main as process_tutorials # noqa: E402
from process_tutorials import main as process_tutorials

# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
Expand Down
98 changes: 81 additions & 17 deletions docs/guides/component_implementation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ runtime. The following simple class shows how to specify the required submodels.

The property created for ``"ground"`` will be like the following: ::

@property
def ground(self) -> GroundBase:
"""Submodel of the ground."""
return self._ground
Expand All @@ -106,7 +107,7 @@ The property created for ``"ground"`` will be like the following: ::
)
self._ground = model

Connections should be specified similarly using the class property
Connections should be specified similarly with the class property
``required_connections``, using :class:`brim.core.requirement.ConnectionRequirement`. ::

class MyModel(ModelBase):
Expand Down Expand Up @@ -136,19 +137,27 @@ adding a load group to a component. An example is shown below. ::
model.add_load_group(load_group)
assert load_group.parent is model

class MyModel2(ModelBase):
"""Some other model."""

model2 = MyModel2("my_model2")
load_group = MyLoadGroup("my_load_group")
model2.add_load_group(load_group) # Raises an error.

Implementation Define Steps
---------------------------

To implement the "define" steps in a model, connection, or load group, a leading
underscore is added to the method name. For example, ``_define_<step>``. These methods
solely implement the "define" step for the component itself without traversing the
submodels and load groups. :class:`brim.core.base_classes.BrimBase` contains the
implementation of the "define" methods, including traversal, which should be called
by the user. These methods follow the format ``define_<step>``.
submodels and load groups. The base classes, like
:class:`brim.core.base_classes.BrimBase`, contain the implementation of the "define"
methods, including traversal, which should be called by the user. These methods follow
the format ``define_<step>``.

We have established several helping guidelines for each define steps. The subsections
below discuss each of these per define step, and provide general code examples of the
expected implementation.
We have established several helping guidelines for each of the define steps. The
subsections below discuss each of these per define step, and provide general coding
examples of the expected implementation.

Define Connections
~~~~~~~~~~~~~~~~~~
Expand All @@ -161,7 +170,6 @@ Define Connections
super()._define_connections()
self.connection.submodel = self.submodel


Define Objects
~~~~~~~~~~~~~~

Expand All @@ -172,14 +180,15 @@ Define Objects
with a string as key and the (dynamic)symbol as value.
- Generalized coordinates must be set/added to the mutable ``self.q`` matrix.
- Generalized speeds must be set/added to the mutable ``self.u`` matrix.
- Auxiliary speeds must be set/added to the mutable ``self.uaux`` matrix.
- The name of each symbol, generalized coordinate, and generalized speed must be created
using :meth:`brim.core.base_classes.BrimBase._add_prefix`. This method puts the name
of the component in front of the symbol name, such that the symbol name is unique.
- Each symbol, generalized coordinate, and generalized speed must have a description in
the :meth:`brim.core.base_classes.BrimBase.descriptions` property.
- The define objects step for each connection should be called manually because there
could be dependencies between the define step of a connection and its parent model,
since it is utility after all.
it is utility after all.
- Other component specific objects, such as bodies and reference frames, must be defined
in this stage, but they should not be oriented or positioned yet. ::

Expand All @@ -189,19 +198,23 @@ Define Objects
return {
**super().descriptions,
self.symbols["symbol_name"]: "Description of the symbol.",
self.symbols["f_noncontrib"]: "Description of the noncontributing force.",
self.q[0]: f"First generalized coordinate of {self.name}.",
self.q[1]: f"Second generalized coordinate of {self.name}.",
self.u[0]: f"First generalized speed of {self.name}.",
self.u[1]: f"Second generalized speed of {self.name}.",
self.uaux[0]: f"Auxiliary speed of {self.name}.",
}

def _define_objects(self) -> None:
"""Define the objects of the system."""
super()._define_objects()
# Create symbols and generalized coordinates and speeds.
self.symbols["symbol_name"] = symbols(self._add_prefix("symbol"))
self.symbols["f_noncontrib"] = symbols(self._add_prefix("f_noncontrib"))
self.q = MutableMatrix([dynamicsymbols(self._add_prefix("q1:3"))])
self.u = MutableMatrix([dynamicsymbols(self._add_prefix("u1:3"))])
self.uaux = MutableMatrix([dynamicsymbols(self._add_prefix("uaux"))])
# Instantiate system.
self._system = System()
# Call define objects of connections.
Expand All @@ -213,14 +226,17 @@ Define Kinematics
~~~~~~~~~~~~~~~~~

- It is generally best to first orient the reference frames in this step, and the
position the reference frames. Next, one can optimize the definition of the
velocities. Parent models must do this between their submodels.
position of the points. Next, one can optimize the definition of the velocities. With
the introduction of the auxiliary data handler it is best practise to define the
velocity of a point based on the point w.r.t. which it has been positioned.
Parent models have to orient and define the submodels w.r.t. each other.
- The kinematical differential equations, generalized coordinates, and generalized
speeds must be added to ``self.system``.
- Possibly one can also use joints for the above.
- Again the define kinematics step of each connection should be called manually.
- Generally, make sure to define the velocity of at least one point in the model's or
connection's system. ::
connection's system.
- Noncontributing forces can be added to the auxiliary data handler. ::

def _define_kinematics(self) -> None:
"""Define the kinematics of the system."""
Expand All @@ -238,15 +254,17 @@ Define Kinematics
self.system.add_joints(...)
# Call define kinematics of connections.
self.connection.define_kinematics() # Without leading underscore!
# Add noncontributing force to the auxiliary data handler.
self.auxiliary_data_handler.add_noncontributing_force(
self.point, self.frame.x, self.uaux[0], self.symbols["f_noncontrib"])

Define Loads
~~~~~~~~~~~~

- As all points and reference frames have already been defined and positioned, this step
only requires computation of the forces and torques and adding the to the
``self.system``.
- In case non-contributing should be computed, one ought to define and use the auxiliary
speeds already in the define objects and define kinematics step.
- Noncontributing forces are fully handled by the auxiliary data handler.
- Again the define loads step of each connection should be called manually. ::

def _define_loads(self) -> None:
Expand Down Expand Up @@ -274,12 +292,58 @@ Define Constraints
susceptible to have multiple possible velocity definitions. Therefore, it is advised
to compute the velocity based on the position graph of the points and the orientation
and angular velocity graph of the reference frames. A good example of this is in the
:class:`brim.bicycle.tires.NonHolonomicTire` class. ::
:class:`brim.bicycle.tires.NonHolonomicTire` class.
- To support usage of the object in a system with noncontributing forces it is also
necessary to account for the auxiliary speeds. This can be done by specifically
requesting the auxiliary velocity of a point and adding that to the constraint. Do
also note that the ``velocity_constraints`` attribute is set to modify the velocity
constraint resulting from the holonomic constraint. ::

def _define_constraints(self) -> None:
"""Define the constraints of the system."""
super()._define_constraints()
self.add_holonomic_constraints(...)
self.add_nonholonomic_constraints(...)
self.system.add_holonomic_constraints(...)
self.system.add_nonholonomic_constraints(...)
# Overwrite the velocity constraints to include the auxiliary velocity.
self.system.velocity_constraints = [
self.system.holonomic_constraints[0].diff(dynamicsymbols._t) +
self.auxiliary_handler.get_auxiliary_velocity(self.point).dot(...),
*self.system.nonholonomic_constraints
]
# Call define constraints of connections.
self.connection.define_constraints() # Without leading underscore!

Auxiliary Data Handler
----------------------

The :class:`brim.core.auxiliary.AuxiliaryDataHandler` is a utility class that is used to
compute noncontributing forces and optimize the computation of the velocity of points.
An instance of the auxiliary data handler is automatically created at the end of the
``define_objects`` step. This instance is shared by the root model, i.e. the uppermost
parent model, with all submodels, connections, and load groups. This makes the auxiliary
data handler accessible from all components through the ``self.auxiliary_handler``
attribute.

In the ``define_kinematics`` step modelers can register noncontributing forces that
should be computed using the
:meth:`brim.core.auxiliary.AuxiliaryDataHandler.add_noncontributing_force` method. This
method requires the point, the axis of the force, the auxiliary speed, and the force
symbol as arguments. From this information the auxiliary data handler can do the rest.
When defining the other kinematics it is best practise to define the velocity of a point
based on the point w.r.t. which it has been positioned. This is because the auxiliary
data handler propagates the auxiliary velocities of points to other points based on how
points are defined w.r.t. to each other.

At the end of the ``define_kinematics`` step the auxiliary data handler automatically
computes the velocity of each point in the inertial frame, while adding the auxiliary
velocity. The auxiliary speed is also automatically added to the root model's system
instance.
At the end of the ``define_loads`` step the noncontributing forces are automatically
added to the root model's system instance.

When computing the constraints in the ``define_constraints`` step it is important to
take the auxiliary speeds into account, even if you didn't define any in you component.
In many cases it is possible that other components may have defined auxiliary speeds
that do affect your constraints. To get the auxiliary velocity of a point of intereset
you can use the :meth:`brim.core.auxiliary.AuxiliaryDataHandler.get_auxiliary_velocity`
method.
2 changes: 1 addition & 1 deletion docs/tutorials/four_bar_linkage.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.18"
"version": "3.11.7"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/my_first_bicycle.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.18"
"version": "3.11.8"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
TUTORIALS_DIR = os.path.dirname(__file__)


def download_parametrization_data(data_dir: str = None) -> None:
def download_parametrization_data(data_dir: str | None = None) -> None:
"""Download the bicycle and rider data from the BicycleParameters repository."""
if data_dir is None:
data_dir = os.path.join(TUTORIALS_DIR, "data")
Expand Down
Loading
Loading