Bases: StaticFeature
Compute weighted kinetic energy from joint velocities.
Kinetic energy is defined here as:
\[ E_k = \frac{1}{2} \cdot \sum_{i=1}^{N} w_i \cdot \|v_i\|^2 \]
where \(w_i\) is the weight (mass) of joint \(i\) and \(v_i\) is its velocity vector.
Read more in the User Guide.
| Parameters: |
-
weights
(float or array_like, default:
1.0
)
–
Mass weights for each joint. Can be a single scalar applied to all
joints or an array of shape (N_signals,). Defaults to 1.0.
-
labels
(list of str, default:
None
)
–
Text labels for each signal, used in the joints output dictionary.
|
Source code in pyeyesweb/low_level/kinetic_energy.py
| class KineticEnergy(StaticFeature):
r"""Compute weighted kinetic energy from joint velocities.
Kinetic energy is defined here as:
$$ E_k = \frac{1}{2} \cdot \sum_{i=1}^{N} w_i \cdot \|v_i\|^2 $$
where $w_i$ is the weight (mass) of joint $i$ and $v_i$ is its velocity vector.
!!! note
Expects `frame_data` to represent velocities (e.g., from [extract_velocity_from_position](../utils/math_utils.md#extract_velocity_from_position)), not raw positions.
Read more in the [User Guide](../../user_guide/theoretical_framework/low_level/kinetic_energy.md).
Parameters
----------
weights : float or array_like, optional
Mass weights for each joint. Can be a single scalar applied to all
joints or an array of shape `(N_signals,)`. Defaults to `1.0`.
labels : list of str, optional
Text labels for each signal, used in the `joints` output dictionary.
"""
def __init__(
self,
weights: Union[float, List[float], np.ndarray] = 1.0,
labels: List[str] = None
):
super().__init__()
self.labels = labels
self.weights = weights
@property
def weights(self) -> Union[float, np.ndarray]:
return self._weights
@weights.setter
def weights(self, value: Union[float, List[float], np.ndarray]):
self._weights = self._parse_weights(value)
@property
def labels(self) -> Optional[List[str]]:
return self._labels
@labels.setter
def labels(self, value: Optional[List[str]]):
self._labels = value
def _parse_weights(self, weight_input: Union[float, List[float], np.ndarray]) -> Union[float, np.ndarray]:
if np.isscalar(weight_input):
mass_scalar = float(weight_input)
if mass_scalar < 0:
raise ValueError("Mass cannot be negative.")
return mass_scalar
mass_array = np.asarray(weight_input, dtype=float)
if np.any(mass_array < 0):
raise ValueError("Mass values cannot be negative.")
return mass_array[:, np.newaxis]
def compute(self, frame_data: np.ndarray) -> KineticEnergyResult:
r"""Compute kinetic energy for a single frame of velocities.
Parameters
----------
frame_data : numpy.ndarray of shape (N_signals, N_dims)
Snapshot of joint velocities.
Returns
-------
KineticEnergyResult
The computed energy values.
"""
velocities = np.asarray(frame_data, dtype=float)
if velocities.ndim == 1:
velocities = velocities.reshape(1, -1)
num_joints = velocities.shape[0]
# 1. Validation Fail-Safes
# Note: If these trigger, the base StaticFeature.__call__ catches the ValueError
# and safely returns a FeatureResult(is_valid=False).
if self.labels and len(self.labels) != num_joints:
raise ValueError(f"Number of labels ({len(self.labels)}) must match number of joints ({num_joints}).")
if isinstance(self.weights, np.ndarray) and self.weights.shape[0] != num_joints:
raise ValueError(
f"Weight array length ({self.weights.shape[0]}) must match number of joints ({num_joints}).")
# 2. Optimized Computation
kinetic_energy_components = 0.5 * self.weights * (velocities ** 2)
kinetic_energy_per_joint = kinetic_energy_components.sum(axis=1)
# 3. Aggregation and Dictionary Building
joint_energy_dict = {}
for i in range(num_joints):
key = self.labels[i] if self.labels else str(i)
joint_energy_dict[key] = {
"total": float(kinetic_energy_per_joint[i]),
"components": kinetic_energy_components[i].tolist()
}
return KineticEnergyResult(
total_energy=float(kinetic_energy_per_joint.sum()),
component_energy=kinetic_energy_components.sum(axis=0).tolist(),
joints=joint_energy_dict
)
|
compute(frame_data)
Compute kinetic energy for a single frame of velocities.
| Parameters: |
-
frame_data
(numpy.ndarray of shape (N_signals, N_dims))
–
Snapshot of joint velocities.
|
Source code in pyeyesweb/low_level/kinetic_energy.py
| def compute(self, frame_data: np.ndarray) -> KineticEnergyResult:
r"""Compute kinetic energy for a single frame of velocities.
Parameters
----------
frame_data : numpy.ndarray of shape (N_signals, N_dims)
Snapshot of joint velocities.
Returns
-------
KineticEnergyResult
The computed energy values.
"""
velocities = np.asarray(frame_data, dtype=float)
if velocities.ndim == 1:
velocities = velocities.reshape(1, -1)
num_joints = velocities.shape[0]
# 1. Validation Fail-Safes
# Note: If these trigger, the base StaticFeature.__call__ catches the ValueError
# and safely returns a FeatureResult(is_valid=False).
if self.labels and len(self.labels) != num_joints:
raise ValueError(f"Number of labels ({len(self.labels)}) must match number of joints ({num_joints}).")
if isinstance(self.weights, np.ndarray) and self.weights.shape[0] != num_joints:
raise ValueError(
f"Weight array length ({self.weights.shape[0]}) must match number of joints ({num_joints}).")
# 2. Optimized Computation
kinetic_energy_components = 0.5 * self.weights * (velocities ** 2)
kinetic_energy_per_joint = kinetic_energy_components.sum(axis=1)
# 3. Aggregation and Dictionary Building
joint_energy_dict = {}
for i in range(num_joints):
key = self.labels[i] if self.labels else str(i)
joint_energy_dict[key] = {
"total": float(kinetic_energy_per_joint[i]),
"components": kinetic_energy_components[i].tolist()
}
return KineticEnergyResult(
total_energy=float(kinetic_energy_per_joint.sum()),
component_energy=kinetic_energy_components.sum(axis=0).tolist(),
joints=joint_energy_dict
)
|