11"""Movement smoothness analysis module.
22
3- This module provides tools for quantifying the smoothness of movement signals
3+ This module provides tools for quantifying the smoothness of movement velocity signals
44using multiple metrics including SPARC (Spectral Arc Length) and Jerk RMS.
5- Designed for real time analysis of motion capture or sensor data.
5+ Designed for real-time analysis of motion capture or sensor data.
6+
7+ IMPORTANT: This module expects velocity signals as input, not position signals.
8+ Velocity should be computed from position data before analysis (for example velocity = sqrt(dx^2 + dy^2) / dt).
69
710Smoothness metrics are important indicators of movement quality in:
8111. Motor control assessment
2023
2124
2225class Smoothness :
23- """Compute movement smoothness metrics from signal data.
26+ """Compute movement smoothness metrics from velocity signal data.
2427
2528 This class analyzes movement smoothness using SPARC (Spectral Arc Length)
26- and Jerk RMS metrics. It can optionally apply Savitzky-Golay filtering
29+ and Jerk RMS metrics. This class expects velocity signals as input,
30+ not position signals. It can optionally apply Savitzky-Golay filtering
2731 to reduce noise before analysis.
2832
2933 SPARC implementation is based on Balasubramanian et al. (2015) "On the analysis
@@ -54,16 +58,17 @@ class Smoothness:
5458 >>> from pyeyesweb.data_models.sliding_window import SlidingWindow
5559 >>> import numpy as np
5660 >>>
57- >>> # Generate sample movement data (simulated velocity profile)
61+ >>> # Generate sample velocity data
62+ >>> # For example, from tracking hand movement: velocity = sqrt(dx^2 + dy^2) / dt
5863 >>> t = np.linspace(0, 2, 200)
59- >>> movement_data = np.sin(2 * np.pi * t) + 0.1 * np.random.randn(200)
64+ >>> velocity_data = np.sin(2 * np.pi * t) + 0.1 * np.random.randn(200)
6065 >>>
6166 >>> smooth = Smoothness(rate_hz=100.0, use_filter=True)
6267 >>> window = SlidingWindow(max_length=200, n_columns=1)
6368 >>>
64- >>> # Add movement data
65- >>> for value in movement_data :
66- ... window.append([value ])
69+ >>> # Add velocity data to the window
70+ >>> for velocity in velocity_data :
71+ ... window.append([velocity ])
6772 >>>
6873 >>> result = smooth(window)
6974 >>> print(f"SPARC: {result['sparc']:.3f}, Jerk RMS: {result['jerk_rms']:.3f}")
@@ -103,20 +108,21 @@ def _filter_signal(self, signal):
103108 return apply_savgol_filter (signal , self .rate_hz )
104109
105110 def __call__ (self , sliding_window : SlidingWindow ):
106- """Compute smoothness metrics from windowed signal data.
111+ """Compute smoothness metrics from windowed velocity signal data.
107112
108113 Parameters
109114 ----------
110115 sliding_window : SlidingWindow
111- Buffer containing signal data to analyze.
116+ Buffer containing velocity signal data to analyze.
117+ Input should be velocity values, not position values.
112118
113119 Returns
114120 -------
115121 dict
116122 Dictionary containing:
117123 - 'sparc': Spectral Arc Length (closer to 0 = smoother).
118124 Returns NaN if insufficient data.
119- - 'jerk_rms': RMS of jerk (third derivative ).
125+ - 'jerk_rms': RMS of jerk (computed from velocity input ).
120126 Returns NaN if insufficient data.
121127 """
122128 if len (sliding_window ) < 5 :
@@ -132,6 +138,7 @@ def __call__(self, sliding_window: SlidingWindow):
132138 normalized = normalize_signal (filtered )
133139
134140 sparc = compute_sparc (normalized , self .rate_hz )
135- jerk = compute_jerk_rms (filtered , self .rate_hz )
141+ # Note: Since the smoothness module expects velocity input (as shown in examples), we specify signal_type='velocity' to compute proper jerk
142+ jerk = compute_jerk_rms (filtered , self .rate_hz , signal_type = 'velocity' )
136143
137144 return {"sparc" : sparc , "jerk_rms" : jerk }
0 commit comments