motornet.effectors

class motornet.effector.CompliantTendonArm26(timestep=0.0002, skeleton=None, muscle_kwargs: dict = {}, **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.skeleton.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.effector.Effector(skeleton, muscle, name: str = 'Effector', n_ministeps: int = 1, timestep: float = 0.01, integration_method: str = 'euler', damping: float = 0.0, pos_lower_bound: float | list | tuple = None, pos_upper_bound: float | list | tuple = None, vel_lower_bound: float | list | tuple = None, vel_upper_bound: float | list | tuple = None)

Bases: Module

Base class for Effector objects.

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

  • muscle – A motornet.muscle.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).

  • n_ministepsInteger, number of integration ministeps per timestep. This assumes the action input is constant across ministeps.

  • 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.

  • dampingFloat, the damping coefficient applied at each joint, proportional to joint velocity. This value should be positive to reduce joint torques proportionally to joint velocity.

  • 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) None

Adds a muscle to the effector.

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(batch_size: int, position: Tensor | ndarray, velocity: Tensor | ndarray | None = None) Tensor

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

Parameters:
  • batch_sizeInteger, the desired batch size.

  • 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).

Returns:

A tensor containing batch_size joint states.

draw_random_uniform_states(batch_size: int) Tensor

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: Tensor) Tensor

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 effector) plus two for musculotendon length and musculotendon velocity. However, note that how many states and what they represent may vary depending on the Effector subclass. This should be available via the geometry_state_name 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_muscle_cfg() dict

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() dict

Gets the effector object’s configuration as a dictionary.

Returns:

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

integrate(action: Tensor, endpoint_load: Tensor, joint_load: Tensor) None

Integrates the effector 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, and updates the states to the resulting values.

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

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

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

integration_step(dt: float, state_derivative: dict[str, Tensor], states: dict[str, Tensor]) dict[str, Tensor]

Performs one numerical integration step for the motornet.muscle.Muscle object class or subclass, and then for the motornet.skeleton.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: Tensor) Tensor

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.

ode(action: Tensor, states: dict[str, Tensor], endpoint_load: Tensor, joint_load: Tensor) dict[str, Tensor]

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

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

  • statesDictionary contains the joint, muscle, and geometry states as Tensor, 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 dictionary containing the derivatives of the the joint, muscle, and geometry states as Tensor, mapped to a “joint”, “muscle”, and “geometry” key, respectively.

print_muscle_wrappings() None

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

reset(*, seed: int | None = None, options: dict[str, Any] | None = None) None

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

Parameters:
  • seedInteger, the seed that is used to initialize the environment’s PRNG (np_random). If the environment does not already have a PRNG and seed=None (the default option) is passed, a seed will be chosen from some source of entropy (e.g. timestamp or /dev/urandom). However, if the environment already has a PRNG and seed=None is passed, the PRNG will not be reset. If you pass an integer, the PRNG will be reset even if it already exists. Usually, you want to pass an integer right after the environment has been initialized and then never again.

  • optionsDictionary, optional kwargs. This is mainly useful to pass batch_size and joint_state kwargs if desired, as described below.

Options:
  • batch_size: Integer, the desired batch size. Default: 1.

  • 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. Default: None.

setattr(name: str, value: Any) None

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.

step(action: Tensor | ndarray, **kwargs) None

Advances the effector by one timestep.

Parameters:
  • actionTensor or numpy.ndarray, the motor command input to the muscles.

  • **kwargs – Optional keyword arguments passed to integrate(). Useful for passing endpoint_load or joint_load tensors.

to(*args, **kwargs)

Move and/or cast the parameters and buffers.

This can be called as

to(device=None, dtype=None, non_blocking=False)
to(dtype, non_blocking=False)
to(tensor, non_blocking=False)
to(memory_format=torch.channels_last)

Its signature is similar to torch.Tensor.to(), but only accepts floating point or complex dtypes. In addition, this method will only cast the floating point or complex parameters and buffers to dtype (if given). The integral parameters and buffers will be moved device, if that is given, but with dtypes unchanged. When non_blocking is set, it tries to convert/move asynchronously with respect to the host if possible, e.g., moving CPU Tensors with pinned memory to CUDA devices.

See below for examples.

Note

This method modifies the module in-place.

Parameters:
  • device (torch.device) – the desired device of the parameters and buffers in this module

  • dtype (torch.dtype) – the desired floating point or complex dtype of the parameters and buffers in this module

  • tensor (torch.Tensor) – Tensor whose dtype and device are the desired dtype and device for all parameters and buffers in this module

  • memory_format (torch.memory_format) – the desired memory format for 4D parameters and buffers in this module (keyword only argument)

Returns:

self

Return type:

Module

Examples:

>>> # xdoctest: +IGNORE_WANT("non-deterministic")
>>> linear = nn.Linear(2, 2)
>>> linear.weight
Parameter containing:
tensor([[ 0.1913, -0.3420],
        [-0.5113, -0.2325]])
>>> linear.to(torch.double)
Linear(in_features=2, out_features=2, bias=True)
>>> linear.weight
Parameter containing:
tensor([[ 0.1913, -0.3420],
        [-0.5113, -0.2325]], dtype=torch.float64)
>>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_CUDA1)
>>> gpu1 = torch.device("cuda:1")
>>> linear.to(gpu1, dtype=torch.half, non_blocking=True)
Linear(in_features=2, out_features=2, bias=True)
>>> linear.weight
Parameter containing:
tensor([[ 0.1914, -0.3420],
        [-0.5112, -0.2324]], dtype=torch.float16, device='cuda:1')
>>> cpu = torch.device("cpu")
>>> linear.to(cpu)
Linear(in_features=2, out_features=2, bias=True)
>>> linear.weight
Parameter containing:
tensor([[ 0.1914, -0.3420],
        [-0.5112, -0.2324]], dtype=torch.float16)

>>> linear = nn.Linear(2, 2, bias=None).to(torch.cdouble)
>>> linear.weight
Parameter containing:
tensor([[ 0.3741+0.j,  0.2382+0.j],
        [ 0.5593+0.j, -0.4443+0.j]], dtype=torch.complex128)
>>> linear(torch.ones(3, 2, dtype=torch.cdouble))
tensor([[0.6122+0.j, 0.1150+0.j],
        [0.6122+0.j, 0.1150+0.j],
        [0.6122+0.j, 0.1150+0.j]], dtype=torch.complex128)
class motornet.effector.FreePointMass24(muscle, skeleton=None, timestep=0.01, muscle_kwargs: dict = {}, **kwargs)

Bases: Effector

A four-muscle planar point-mass effector with constant moment arms, compatible with any muscle type.

Unlike ReluPointMass24, this class bypasses path-based geometry and uses a fixed moment arm matrix to drive a motornet.skeleton.PointMass in two dimensions. Musculotendon lengths and velocities are set to zero, decoupling the model from force-length/velocity effects.

The four muscles pull the mass along the cardinal directions of the plane:

  • r: rightward actuator (moment arm: x = -1, y = 0)

  • u: upward actuator (moment arm: x = 0, y = -1)

  • l: leftward actuator (moment arm: x = +1, y = 0)

  • d: downward actuator (moment arm: x = 0, y = +1)

The sign convention follows the motornet ODE: generalized_forces = -forces * moments, so a negative moment arm produces a positive generalized force (i.e., movement in the positive axis direction).

If no skeleton is provided, this object will use a motornet.skeleton.PointMass with space_dim=2 and mass=1.0. The default workspace bounds are [-0.6, -0.6] to [0.6, 0.6] metres.

Parameters:
class motornet.effector.Reacher(muscle, skeleton=None, timestep=0.01, muscle_kwargs: dict = {}, **kwargs)

Bases: Effector

A simplified four-muscle arm model with constant moment arms, compatible with any muscle type.

Unlike anatomy-driven effectors, this class bypasses the default geometry calculation and uses a fixed moment arm matrix that does not vary with joint angle. Musculotendon lengths and velocities are set to zero, decoupling the model from force-length/velocity effects. This makes it useful for studying control strategies independently of complex musculoskeletal geometry.

The four muscles represent functional groups acting on the shoulder and elbow joints:

  • sf: shoulder flexor (moment arm: shoulder = -1, elbow = 0)

  • se: shoulder extensor (moment arm: shoulder = +1, elbow = 0)

  • ef: elbow flexor (moment arm: shoulder = 0, elbow = -1)

  • ee: elbow extensor (moment arm: shoulder = 0, elbow = +1)

If no skeleton is provided, this object will use a motornet.skeleton.TwoDofArm with the following parameters:

  • 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 0, and the default upper limits are 135 and 155 degrees, respectively.

Parameters:
  • muscle – A motornet.muscle.Muscle object class or subclass. Defines the muscle type used for all four muscles.

  • skeleton – A motornet.skeleton.Skeleton object class or subclass. Defaults to a motornet.skeleton.TwoDofArm as described above if not provided.

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

  • muscle_kwargsDictionary, muscle parameters passed to the motornet.muscle.Muscle.build() method. If max_isometric_force is not included, it defaults to [1000, 1000, 1000, 1000].

  • **kwargs – All contents are passed to the parent Effector class.

class motornet.effector.ReluPointMass24(timestep: float = 0.01, max_isometric_force: float = 500, mass: float = 1, **kwargs)

Bases: Effector

This object implements a 2D point-mass skeleton attached to 4 motornet.muscle.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_isometric_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.Effector class.

class motornet.effector.RigidTendonArm26(muscle, skeleton=None, timestep=0.01, muscle_kwargs: dict = {}, **kwargs)

Bases: Effector

This pre-built effector 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.skeleton.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 Effector 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 – A motornet.muscle.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.skeleton.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).

  • muscle_kwargsDictionary, contains the muscle parameters to be passed to the motornet.muscle.Muscle.build() method.

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