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

Adding Composite Model Example #103

Merged
merged 5 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
252 changes: 252 additions & 0 deletions examples/06_Combining_Models.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,250 @@
"source": [
"# Combining Prognostic Models"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This section demonstrates how prognostic models can be combined. There are two times in which this is useful: \n",
"\n",
"1. When combining multiple models of different inter-related systems into one system-of-system model (i.e., [Composite Models](https://nasa.github.io/progpy/api_ref/prog_models/CompositeModel.html)), or\n",
"2. Combining multiple models of the same system to be simulated together an aggregated (i.e., [Ensemble Models](https://nasa.github.io/progpy/api_ref/prog_models/EnsembleModel.html)). This is generally done to improve the accuracy of prediction when you have multiple models that each represent part of the behavior or represent a distribution of different behaviors. \n",
teubert marked this conversation as resolved.
Show resolved Hide resolved
"\n",
"These two methods for combining models are described in the following sections."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Composite Model"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A CompositeModel is a PrognosticsModel that is composed of multiple PrognosticsModels. This is a tool for modeling system-of-systems. i.e., interconnected systems, where the behavior and state of one system affects the state of another system. The composite prognostics models are connected using defined connections between the output or state of one model, and the input of another model. The resulting CompositeModel behaves as a single model.\n",
"\n",
"To illustrate this, we will create a composite model of an aircraft's electric powertrain, combining the DCMotor, ESC, and PropellerLoad models. The Electronic Speed Controller (ESC) converts a commanded duty (i.e., throttle) to signals to the motor. The motor then acts on the signals from the ESC to spin the load, which enacts a torque on the motor (in this case from air resistence).\n",
"\n",
"First we will import the used models, and the CompositeModel class"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from progpy.models import DCMotor, ESC, PropellerLoad\n",
"from progpy import CompositeModel"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we will initiate objects of the individual models that will later create the composite powertrain model."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m_motor = DCMotor()\n",
"m_esc = ESC()\n",
"m_load = PropellerLoad()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we have to define the connections between the systems. Let's first define the connections from the DCMotor to the propeller load. For this, we'll need to look at the DCMotor states and understand how they influence the PropellerLoad inputs."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('motor states: ', m_motor.states)\n",
"print('load inputs: ', m_load.inputs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Each of the states and inputs are described in the model documentation at [DC Motor Docs](https://nasa.github.io/progpy/api_ref/prog_models/IncludedModels.html#dc-motor) and [Propeller Docs](https://nasa.github.io/progpy/api_ref/prog_models/IncludedModels.html#propellerload)\n",
"\n",
"From reading the documentation we understand that the propeller's velocity is from the motor, so we can define the first connection:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"connections = [\n",
" ('DCMotor.v_rot', 'PropellerLoad.v_rot')\n",
"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Connections are defined as couples where the first value is the input for the second value. The connection above tells the composite model to feed the DCMotor's v_rot into the PropellerLoad's input v_rot.\n",
"\n",
"Next, let's look at the connections the other direction, from the load to the motor."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('load states: ', m_load.states)\n",
"print('motor inputs: ', m_motor.inputs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We know here that the load on the motor is from the propeller load, so we can add that connection. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"connections.append(('PropellerLoad.t_l', 'DCMotor.t_l'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we will repeat the exercise with the DCMotor and ESC."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('ESC states: ', m_esc.states)\n",
"print('motor inputs: ', m_motor.inputs)\n",
"connections.append(('ESC.v_a', 'DCMotor.v_a'))\n",
"connections.append(('ESC.v_b', 'DCMotor.v_b'))\n",
"connections.append(('ESC.v_c', 'DCMotor.v_c'))\n",
"\n",
"print('motor states: ', m_motor.states)\n",
"print('ESC inputs: ', m_esc.inputs)\n",
"connections.append(('DCMotor.theta', 'ESC.theta'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we are ready to combine the models. We create a composite model with the inidividual models and the defined connections."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m_powertrain = CompositeModel(\n",
" (m_esc, m_load, m_motor), \n",
" connections=connections)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The resulting model includes two inputs, ESC voltage (from the battery) and duty (i.e., commanded throttle). These are the only two inputs not connected internally from the original three models. The states are a combination of all the states of every system. Finally, the outputs are a combination of all the outputs from each of the individual systems. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('inputs: ', m_powertrain.inputs)\n",
"print('states: ', m_powertrain.states)\n",
"print('outputs: ', m_powertrain.outputs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Frequently users only want a subset of the outputs from the original model. For example, in this case you're unlikely to be measuring the individual voltages from the ESC. Outputs can be specified when creating the composite model. For example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m_powertrain = CompositeModel(\n",
" (m_esc, m_load, m_motor), \n",
" connections=connections,\n",
" outputs={'DCMotor.v_rot', 'DCMotor.theta'})\n",
"print('outputs: ', m_powertrain.outputs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now the outputs are only DCMotor angle and velocity.\n",
"\n",
"The resulting model can be used in simulation, state estimation, and prediction the same way any other model would be, as demonstrated below:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"load = m_powertrain.InputContainer({\n",
" 'ESC.duty': 1, # 100% Throttle\n",
" 'ESC.v': 23\n",
" })\n",
"def future_loading(t, x=None):\n",
" return load\n",
"\n",
"simulated_results = m_powertrain.simulate_to(2, future_loading, dt=2.5e-5, save_freq=1e-2)\n",
"fig = simulated_results.outputs.plot(compact=False, keys=['DCMotor.v_rot'], ylabel='Velocity')\n",
"fig = simulated_results.states.plot(keys=['DCMotor.i_b', 'DCMotor.i_c', 'DCMotor.i_a'], ylabel='ESC Currents')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ensemble Model"
]
}
],
"metadata": {
Expand All @@ -15,7 +259,15 @@
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
},
"orig_nbformat": 4,
Expand Down
53 changes: 0 additions & 53 deletions examples/composite_model.py

This file was deleted.

11 changes: 11 additions & 0 deletions src/progpy/models/propeller_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ class PropellerLoad(PrognosticsModel):
This is a simple model of a propeller load. This model estimates load torque as a function of the rotational velocity. When the propeller is spinning faster, drag increases, and the propeller load on the torque increases.

This model is typically used with the esc and dcmotor models to simulate a motor and propeller system.

:term:`Events<event>`: (0)

:term:`Inputs/Loading<input>`: (1):
v_rot: Rotational Velocity (rad/sec)

:term:`States<state>`: (1)
t_l: Load Torque

:term:`Outputs<output>`: (1):
t_l: Load Torque
"""
inputs = ['v_rot']
states = ['t_l']
Expand Down
Loading