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

Composition and instantiation of muscle models #31

Open
mlprt opened this issue Mar 4, 2024 · 0 comments
Open

Composition and instantiation of muscle models #31

mlprt opened this issue Mar 4, 2024 · 0 comments

Comments

@mlprt
Copy link
Owner

mlprt commented Mar 4, 2024

The composition of the models in feedbax.mechanics.muscle is the most complicated in the library.

Class diagram of feedbax.mechanics.muscle
classDiagram
  class AbstractActivationFunction {
  }
  class AbstractFLVFunction {
  }
  class AbstractForceFunction {
  }
  class AbstractMuscle {
    activation_func : AbstractVar[AbstractActivationFunction]
    force_func : AbstractVar[AbstractFLVFunction]
    n_muscles : AbstractVar[int]
    noise_func : AbstractVar[Optional[Callable[[Array, Array, Array], Array]]]
    change_n_muscles(n_muscles: int) 'AbstractMuscle'
  }
  class AbstractMuscleState {
    activation : AbstractVar[Array]
    length : AbstractVar[Array]
    tension : AbstractVar[Array]
    velocity : AbstractVar[Array]
  }
  class AbstractVirtualMuscleShortenFactor {
    c_v : Tuple[float, float]
  }
  class ActivationFilter {
    tau_act : float
    tau_deact : float
    init()*
    input_size()
    vector_field(t: None, state: Array, input: Array)
  }
  class HillShortenFactor {
  }
  class LillicrapScottForceLength {
    beta : float
    omega : float
  }
  class VirtualMuscle {
    activation_func
    bounds
    force_func
    n_muscles : int
    noise_func : Optional[Callable[[Array, Array, Array], Array]]
    init() VirtualMuscleState
  }
  class VirtualMuscleActivationFunction {
    a_f : float
    n_f : Tuple[float, float]
  }
  class VirtualMuscleFLVFunction {
    force_length
    force_passive_1
    force_passive_2
    force_velocity
  }
  class VirtualMuscleForceLength {
    beta : float
    omega : float
    rho : float
  }
  class VirtualMuscleForcePassive1 {
    c1 : float
    k1 : float
    l_r1 : float
  }
  class VirtualMuscleForcePassive2 {
    c2 : float
    k2 : float
    l_r2 : float
  }
  class VirtualMuscleForceVelocity {
    a_v : Tuple[float, float, float]
    b_v : float
    shorten_denom_factor_func
    v_max : float
  }
  class VirtualMuscleShortenFactor {
  }
  class VirtualMuscleState {
    activation : Array
    length : Array
    tension : Array
    velocity : Array
  }
  VirtualMuscle ..> VirtualMuscleState
  AbstractMuscle ..> AbstractMuscleState
  HillShortenFactor --|> AbstractVirtualMuscleShortenFactor
  LillicrapScottForceLength --|> AbstractForceFunction
  VirtualMuscle --|> AbstractMuscle
  VirtualMuscleActivationFunction --|> AbstractActivationFunction
  VirtualMuscleFLVFunction --|> AbstractFLVFunction
  VirtualMuscleForceLength --|> AbstractForceFunction
  VirtualMuscleForcePassive1 --|> AbstractForceFunction
  VirtualMuscleForcePassive2 --|> AbstractForceFunction
  VirtualMuscleForceVelocity --|> AbstractForceFunction
  VirtualMuscleShortenFactor --|> AbstractVirtualMuscleShortenFactor
  VirtualMuscleState --|> AbstractMuscleState
  AbstractActivationFunction --* VirtualMuscle : activation_func
  AbstractFLVFunction --* VirtualMuscle : force_func
  AbstractForceFunction --* VirtualMuscleFLVFunction : force_length
  AbstractForceFunction --* VirtualMuscleFLVFunction : force_velocity
  AbstractForceFunction --* VirtualMuscleFLVFunction : force_passive_1
  AbstractForceFunction --* VirtualMuscleFLVFunction : force_passive_2
  AbstractVirtualMuscleShortenFactor --* VirtualMuscleForceVelocity : shorten_denom_factor_func
Loading

For example, a muscle model typically has a force-length-velocity (FLV) function which determines how its force output relates to kinematic variables. This function is typically a composite of several different force functions; e.g. force-length, force-velocity, passive force. Sometimes, only one of these functions changes between implementations. Therefore the composition of muscle models is dependency-inverted so that it's easier to swap out specific components.

Here's an example of construction of a VirtualMuscle model:

def todorov_li_2004_virtualmuscle(
n_muscles: int = 1,
noise_func: Optional[Callable] = None,
params: PyTree[float] = TODOROV_LI_VIRTUALMUSCLE_PARAMS,
):
"""Muscle model from Todorov & Li 2004.
!!! Note ""
Simplifies the Brown et al. 1999 Virtual Muscle Model:
1. Omits the first passive element, PE1.
2. Uses averages of the fast and slow twitch parameters from Brown 1999.
Arguments:
n_muscles: The number of muscles to model.
noise_func: Generates noise to add to the muscle force.
Has the signature `noise_func(input, force, key) -> Array`, where
`input` is the input to the muscle model.
params: The parameters for the Virtual Muscle Model.
"""
return VirtualMuscle(
n_muscles,
activation_func=VirtualMuscleActivationFunction(**params["activation"]),
force_func=VirtualMuscleFLVFunction(
force_length=VirtualMuscleForceLength(**params["force_length"]),
force_velocity=VirtualMuscleForceVelocity(
**params["force_velocity"],
shorten_denom_factor_func=VirtualMuscleShortenFactor(
**params["shorten"]
),
),
force_passive_1=lambda length, velocity: 0,
force_passive_2=VirtualMuscleForcePassive2(**params["force_passive_2"]),
),
noise_func=noise_func,
)

The default parameters are defined as a dict:

"""Virtual Muscle Model parameters used by Todorov & Li, 2004."""
TODOROV_LI_VIRTUALMUSCLE_PARAMS = dict(
force_length=dict(
beta=1.93, # slow/fast avg
omega=1.03, # slow/fast avg is 1.035
rho=1.87, # slow/fast avg
),
force_velocity=dict(
a_v=(-3.12, 4.21, -2.67), # slow/fast avg
b_v=0.62, # slow/fast avg
v_max=-5.72, # slow/fast avg is -5.725
),
force_passive_2=dict( # identical for slow/fast
c2=-0.02,
k2=-18.7,
l_r2=0.79,
),
shorten=dict(
c_v=(1.38, 2.09), # slow/fast avg is (1.335, 2.085)
),
activation=dict(
n_f=(2.11, 4.16), # slow/fast avg (2.11, 4.155),
a_f=0.56,
),
#! unused
force_passive_1=dict(
c1=0.0,
k1=1.0,
l_r1=0.0,
),
)

Is there a better way to instantiate these models? Note that currently, the muscle components (like VirtualMuscleForceLength and VirtualMuscleForcePassive) do not have default values for their fields.

@mlprt mlprt added the structure label Mar 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant