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

Add linear thrown object model #306

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/prog_models/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
from .dcmotor import DCMotor
from .esc import ESC
from .powertrain import Powertrain
from .thrown_object import ThrownObject
from .thrown_object import ThrownObject, LinearThrownObject
87 changes: 86 additions & 1 deletion src/prog_models/models/thrown_object.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright © 2021 United States Government as represented by the Administrator of the
# National Aeronautics and Space Administration. All Rights Reserved.

from .. import PrognosticsModel
from .. import PrognosticsModel, LinearModel
import numpy as np

def calc_lumped_param(params):
Expand Down Expand Up @@ -116,3 +116,88 @@ def event_state(self, x : dict) -> dict:
'falling': np.maximum(x['v']/self.parameters['throwing_speed'],0), # Throwing speed is max speed
'impact': np.maximum(x['x']/x_max,0) # then it's fraction of height
}


class LinearThrownObject(LinearModel):
"""
Model that similates an object thrown into the air without air resistance. This is a linear version of ThrownObject.

Events (2)
| falling: The object is falling
| impact: The object has hit the ground

Inputs/Loading: (0)

States: (2)
| x: Position in space (m)
| v: Velocity in space (m/s)

Outputs/Measurements: (1)
| x: Position in space (m)

Keyword Args
------------
process_noise : Optional, float or Dict[Srt, float]
Process noise (applied at dx/next_state).
Can be number (e.g., .2) applied to every state, a dictionary of values for each
state (e.g., {'x1': 0.2, 'x2': 0.3}), or a function (x) -> x
process_noise_dist : Optional, String
distribution for process noise (e.g., normal, uniform, triangular)
measurement_noise : Optional, float or Dict[Srt, float]
Measurement noise (applied in output eqn).
Can be number (e.g., .2) applied to every output, a dictionary of values for each
output (e.g., {'z1': 0.2, 'z2': 0.3}), or a function (z) -> z
measurement_noise_dist : Optional, String
distribution for measurement noise (e.g., normal, uniform, triangular)
g : Optional, float
Acceleration due to gravity (m/s^2). Default is 9.81 m/s^2 (standard gravity)
thrower_height : Optional, float
Height of the thrower (m). Default is 1.83 m
throwing_speed : Optional, float
Speed at which the ball is thrown (m/s). Default is 40 m/s
"""

inputs = [] # no inputs, no way to control
states = [
'x', # Position (m)
'v' # Velocity (m/s)
]
outputs = [
'x' # Position (m)
]
events = [
'impact' # Event- object has impacted ground
]

A = np.array([[0, 1], [0, 0]])
E = np.array([[0], [-9.81]])
C = np.array([[1, 0]])
F = None # Will override method

# The Default parameters. Overwritten by passing parameters dictionary into constructor
default_parameters = {
'thrower_height': 1.83, # m
'throwing_speed': 40, # m/s
'g': -9.81 # Acceleration due to gravity in m/s^2
}

def initialize(self, u=None, z=None):
return self.StateContainer({
'x': self.parameters['thrower_height'], # Thrown, so initial altitude is height of thrower
'v': self.parameters['throwing_speed'] # Velocity at which the ball is thrown - this guy is a professional baseball pitcher
})

# This is actually optional. Leaving thresholds_met empty will use the event state to define thresholds.
# Threshold = Event State == 0. However, this implementation is more efficient, so we included it
def threshold_met(self, x):
return {
'falling': x['v'] < 0,
'impact': x['x'] <= 0
}

def event_state(self, x):
x_max = x['x'] + np.square(x['v'])/(-self.parameters['g']*2) # Use speed and position to estimate maximum height
return {
'falling': np.maximum(x['v']/self.parameters['throwing_speed'],0), # Throwing speed is max speed
'impact': np.maximum(x['x']/x_max,0) if x['v'] < 0 else 1 # 1 until falling begins, then it's fraction of height
}