motornet.plants#

Module contents#

This package contains the classes and subclasses used to create the plants that a model will be trained to control. Plants are implemented as subclasses of the motornet.plants.plants.Plant object. They all contain a single motornet.plants.skeletons.Skeleton and motornet.plants.muscles.Muscle subclass defining the biomechanical properties of that Plant object. The Plant object itself implements the numerical integration routines, some of the geometry state calculation routines (in conjunction with the Skeleton object), applies moment arms to produce generalized forces, and generally handles communication between the Skeleton and Muscle objects.

Note

While Plant objects are technically implemented in the motornet.plants.plants module, it is possible (and recommended) to call them using the motornet.plants.Plant path, which is more concise and strictly equivalent.

motornet.plants.muscles#

motornet.plants.skeletons#

motornet.plants#

class motornet_tf.plants.plants.CompliantTendonArm26(timestep=0.0002, skeleton=None, **kwargs)#

Bases: RigidTendonArm26

This is the compliant-tendon version of the RigidTendonArm26 class. Note that the default integration method is Runge-Kutta 4, instead of Euler.

Parameters:
  • timestepFloat, size of a single timestep (in sec).

  • skeleton – A motornet.plants.skeletons.Skeleton object class or subclass. This defines the type of skeleton that the muscles will wrap around. If no skeleton is passed, this will default to the skeleton used in the parent RigidTendonArm26 class.

  • **kwargs – All contents are passed to the parent RigidTendonArm26 class. This also allows for some backward compatibility.

class motornet_tf.plants.plants.Plant(skeleton, muscle_type, timestep: float = 0.01, integration_method: str = 'euler', excitation_noise_sd: float = 0.0, proprioceptive_delay: float | None = None, visual_delay: float | None = None, pos_lower_bound: float | list | tuple | None = None, pos_upper_bound: float | list | tuple | None = None, vel_lower_bound: float | list | tuple | None = None, vel_upper_bound: float | list | tuple | None = None, name: str = 'Plant')#

Bases: object

Base class for Plant objects.

Parameters:
  • skeleton – A motornet.plants.skeletons.Skeleton object class or subclass. This defines the type of skeleton that the muscles will wrap around.

  • muscle_type – A motornet.plants.muscles.Muscle object class or subclass. This defines the type of muscle that will be added each time the add_muscle() method is called.

  • nameString, the name of the object instance.

  • timestepFloat, size of a single timestep (in sec).

  • integration_methodString, “euler” to specify that numerical integration should be done using the Euler method, or “rk4”, “rungekutta4”, “runge-kutta4”, or “runge-kutta-4” to specify the Runge-Kutta 4 method instead. This argument is case-insensitive.

  • excitation_noise_sdFloat, defines the amount of stochastic noise that will be added to the excitation input during the call(). Specifically, this value represents the standard deviation of a gaussian distribution centred around zero, from which a noise term is drawn randomly at every timestep.

  • proprioceptive_delayFloat, the delay between a proprioceptive signal being produced by the plant and the network receiving it as input. If this is not a multiple of the timestep size, this will be rounded up to match the closest multiple value. What qualifies as “proprioceptive feedback” is defined at the network level.

  • visual_delayFloat, the delay between a visual signal being produced by the plant and the network receiving it as input. If this is not a multiple of the timestep size, this will be rounded up to match the closest multiple value. What qualifies as “visual feedback” is defined at the network level.

  • pos_upper_boundFloat, list or tuple, indicating the upper boundary of the skeleton’s joint position. This should be a n-elements vector or list, with n the number of joints of the skeleton. For instance, for a two degrees-of-freedom arm, we would have n=2.

  • pos_lower_boundFloat, list or tuple, indicating the lower boundary of the skeleton’s joint position. This should be a n-elements vector or list, with n the number of joints of the skeleton. For instance, for a two degrees-of-freedom arm, we would have n=2.

  • vel_upper_boundFloat, list or tuple, indicating the upper boundary of the skeleton’s joint velocity. This should be a n-elements vector or list, with n the number of joints of the skeleton. For instance, for a two degrees-of-freedom arm, we would have n=2.

  • vel_lower_boundFloat, list or tuple, indicating the lower boundary of the skeleton’s joint velocity. This should be a n-elements vector or list, with n the number of joints of the skeleton. For instance, for a two degrees-of-freedom arm, we would have n=2.

add_muscle(path_fixation_body: list, path_coordinates: list, name: str | None = None, **kwargs)#

Adds a muscle to the plant.

Parameters:
  • path_fixation_bodyList, containing the index of the fixation body (or fixation bone) for each fixation point in the muscle. The index 0 always stands for the worldspace, i.e. a fixation point outside of the skeleton.

  • path_coordinates – A List of lists. There should be as many lists in the main list as there are fixation points for that muscle. Each nested list within the main list should contain a series of n coordinate float values, with n being the dimensionality of the worldspace. For instance, in a 2D environment, we would need two coordinate values. The coordinate system of each bone is centered on that bone’s origin point. Its first dimension is always alongside the length of the bone, and the next dimension(s) proceed(s) from there on orthogonally to that first dimension.

  • nameString, the name to give to the muscle being added. If None is given, the name defaults to “muscle_m”, with m being a counter for the number of muscles.

  • **kwargs – This is used to pass the set of properties required to build the type of muscle specified at initialization. What it should contain varies depending on the muscle type being used. A TypeError will be raised by this method if a muscle property pertaining to the muscle type specified is missing.

Raises:

TypeError – If an argument is missing to build the type of muscle specified at initialization.

draw_fixed_states(position, velocity=None, batch_size=1)#

Creates a joint state tensor corresponding to the specified position, tiled batch_size times.

Parameters:
  • position – The position to tile in the state tensor.

  • velocity – The velocity to tile in the state tensor. If None, this will default to 0 (null velocity).

  • batch_sizeInteger, the desired batch size.

Returns:

A tensor containing batch_size joint states.

draw_random_uniform_states(batch_size: int = 1)#

Draws joint states according to a random uniform distribution, bounded by the position and velocity boundary attributes defined at initialization.

Parameters:

batch_sizeInteger, the desired batch size.

Returns:

A tensor containing batch_size joint states.

get_geometry(joint_state)#

Computes the geometry state from the joint state. Computes the geometry state from the joint state. Geometry state dimensionality is [n_batch, n_timesteps, n_states, n_muscles]. By default, there are as many states as there are moments (that is, one per degree of freedom in the plant) plus two for musculotendon length and musculotendon velocity. However, note that how many states and what they represent may vary depending on the Plant subclass. This should be available via the geometry_state_names attribute.

Parameters:

joint_stateTensor, the joint state from which the geometry state is computed.

Returns:

The geometry state corresponding to the joint state provided.

get_initial_state(batch_size=1, joint_state=None)#

Gets initial states (joint, cartesian, muscle, geometry) that are biomechanically compatible with each other.

Parameters:
  • batch_sizeInteger, the desired batch size.

  • joint_state – The joint state from which the other state values are inferred. If None, random initial joint states are drawn, from which the other state values are inferred.

Returns:

A list containing the joint state, and cartesian, muscle, and geometry states, in that order. If a joint_state input was provided, the joint state in the returned list will be the same as the input.

Raises:

ValueError – If the batch_size value is not specified and either the joint_state input is None or the size of joint_state input’s first dimension is equal to 1.

get_muscle_cfg()#

Gets the wrapping configuration of muscles added through the add_muscle() method.

Returns:

A dictionary containing a key for each muscle name, associated to a nested dictionary containing information fo that muscle.

get_save_config()#

Gets the plant object’s configuration as a dictionary.

Returns:

A dictionary containing the skeleton and muscle configurations as nested dictionary objects, and parameters of the plant’s configuration. Specifically, the size of the timestep (sec), the name of each muscle added via the add_muscle() method, the number of muscles, the visual and proprioceptive delay, the standard deviation of the excitation noise, and the muscle wrapping configuration as returned by get_muscle_cfg().

integrate(muscle_input, joint_state, muscle_state, geometry_state, endpoint_load, joint_load)#

Integrates the plant over one timestep. To do so, it first calls the update_ode() method to obtain state derivatives from evaluation of the Ordinary Differential Equations. Then it performs the numerical integration over one timestep using the integration_step() method.

Parameters:
  • muscle_inputTensor, the input to the muscles (motor command). Typically, this should be the output of the controller network’s forward pass.

  • joint_stateTensor, the initial state for joint configuration.

  • muscle_stateTensor, the initial state for muscle configuration.

  • geometry_stateTensor, the initial state for geometry configuration.

  • endpoint_loadTensor, the load(s) to apply at the skeleton’s endpoint.

  • joint_loadTensor, the load(s) to apply at the joints.

Returns:

A list containing the new joint, muscle, and geometry states following integration, in that order.

integration_step(dt, state_derivative, states)#

Performs one numerical integration step for the motornet.plants.muscles.Muscle object class or subclass, and then for the motornet.plants.skeletons.Skeleton object class or subclass.

Parameters:
  • dtFloat, size of a single timestep (in sec).

  • state_derivativeDictionary, contains the derivatives of the joint, muscle, and geometry states as tensor arrays, mapped to a “joint”, “muscle”, and “geometry” key, respectively. This is usually obtained using the update_ode() method.

  • states – A Dictionary containing the joint, muscle, and geometry states as Tensor, mapped to a “joint”, “muscle”, and “geometry” key, respectively.

Returns:

A dictionary containing the updated joint, muscle, and geometry states following integration.

joint2cartesian(joint_state)#

Computes the cartesian state given the joint state.

Parameters:

joint_stateTensor, the current joint configuration.

Returns:

The current cartesian configuration (position, velocity) as a tensor.

print_muscle_wrappings()#

Prints the wrapping configuration of the muscles added using the add_muscle() method in a readable format.

setattr(name: str, value)#

Changes the value of an attribute held by this object.

Parameters:
  • nameString, attribute to set to a new value.

  • value – Value that the attribute should take.

state2target(state, n_timesteps: int = 1)#

Converts a tensor formatted as a state to a tensor formatted as a target that can be passed to a tensorflow.keras.Model.fit() call.

Parameters:
  • stateTensor, the state to be converted to a target.

  • n_timestepsInteger, the number of timesteps desired for creating the target tensor.

Returns:

A tensor that can be used as a target input to the tensorflow.keras.Model.fit() method.

update_ode(excitation, states, endpoint_load, joint_load)#

Computes state derivatives by evaluating the Ordinary Differential Equations of the motornet.plants.muscles.Muscle object class or subclass, and then of the motornet.plants.skeletons.Skeleton object class or subclass.

Parameters:
  • excitationTensor, the input to the muscles (motor command). Typically, this should be the output of the controller network’s forward pass.

  • statesDictionary, contains the joint, muscle, and geometry states as tensor arrays, mapped to a “joint”, “muscle”, and “geometry” key, respectively.

  • endpoint_loadTensor, the load(s) to apply at the skeleton’s endpoint.

  • joint_loadTensor, the load(s) to apply at the joints.

Returns:

A list containing the new joint, muscle, and geometry states following integration, in that order.

class motornet_tf.plants.plants.ReluPointMass24(timestep: float = 0.01, max_isometric_force: float = 500, mass: float = 1, **kwargs)#

Bases: Plant

This object implements a 2D point-mass skeleton attached to 4 motornet.plants.muscles.ReluMuscle muscles in a “X” configuration. The outside attachement points are the corners of a (2, 2) -> (2, -2) -> (-2, -2) -> (-2, 2) frame, and the point-mass is constrained to a (1, 1) -> (1, -1) -> (-1, -1) -> (-1, 1) space.

Parameters:
  • timestepFloat, size of a single timestep (in sec).

  • max_isometic_forceFloat, the maximum force (N) that each muscle can produce.

  • massFloat, the mass (kg) of the point-mass.

  • **kwargs – The kwargs inputs are passed as-is to the parent motornet.plants.Plant class.

class motornet_tf.plants.plants.RigidTendonArm26(muscle_type, skeleton=None, timestep=0.01, **kwargs)#

Bases: Plant

This pre-built plant class is an implementation of a 6-muscles, “lumped-muscle” model from [1]. Because lumped-muscle models are functional approximations of biological reality, this class’ geometry does not rely on the default geometry methods, but on its own, custom-made geometry. The moment arm approximation is based on a set of polynomial functions. The default integration method is Euler.

If no skeleton input is provided, this object will use a motornet.plants.skeletons.TwoDofArm skeleton, with the following parameters (from [1]):

  • m1 = 1.82

  • m2 = 1.43

  • l1g = 0.135

  • l2g = 0.165

  • i1 = 0.051

  • i2 = 0.057

  • l1 = 0.309

  • l2 = 0.333

The default shoulder and elbow lower limits are defined as 0, and their default upper limits as 135 and 155 degrees, respectively.

The kwargs inputs are passed as-is to the parent Plant class.

References

[1] Nijhof, E.-J., & Kouwenhoven, E. Simulation of Multijoint Arm Movements (2000). In J. M. Winters & P. E. Crago, Biomechanics and Neural Control of Posture and Movement (pp. 363–372). Springer New York. doi: 10.1007/978-1-4612-2104-3_29

Parameters:
  • muscle_type – A motornet.plants.muscles.Muscle object class or subclass. This defines the type of muscle that will be added each time the add_muscle() method is called.

  • skeleton – A motornet.plants.skeletons.Skeleton object class or subclass. This defines the type of skeleton that the muscles will wrap around. See above for details on what this argument defaults to if no argument is passed.

  • timestepFloat, size of a single timestep (in sec).

  • **kwargs – All contents are passed to the parent Plant class. Also allows for some backward compatibility.