10.4. Visualizing forces and other vectors#
In addition to points and frames (local coordinate systems), the Player can represent forces or other vectors using its vectors
property.
We start by loading a gait c3d file.
import kineticstoolkit.lab as ktk
import numpy as np
contents = ktk.read_c3d(
ktk.doc.download("c3d_sample.c3d"), convert_point_unit=True
)
p = ktk.Player(contents["Points"])
Let’s add a vector at the right shoulder, which could, for example, represent the joint reaction force. For now, we will simply create a dummy sinusoidal vector:
forces = ktk.TimeSeries(time=contents["Points"].time)
forces.data["ShoulderForce"] = 500 * ktk.geometry.create_vector_series(
y=np.sin(3 * forces.time)
)
Now we create a new Player that shows both points and forces:
p = ktk.Player(contents["Points"], forces)
The force is not shown yet because the Player does not know how to represent the vector “ShoulderForce”. This is configured by the Player’s vectors
property, which is a dict where:
The key is the name of the force. Here, “ShoulderForce”.
The value is a dict with:
“Origin”: The name of the point the vector originates from. Here, “r should”, as read from the C3D file.
“Scale”: Optional, the scale of the vector in meters per vector unit. It is 1 by default, but here we will set 0.001, which means in this case that the line will be one millimetre per newton.
“Color”: Optional, an RGB colour tuple.
To show the shoulder force:
p.vectors["ShoulderForce"] = {"Origin": "r should", "Scale": 0.001}
10.4.1. Force platforms#
The previous section was a dummy example to show how to represent vectors in the Player. In this same file, we also have force platform contents. For visualization, we need to downsample it so that everything is on the same sampling rate:
points = contents["Points"]
force_platforms = contents["ForcePlatforms"].resample(points.time)
We can now visualize the points and force platform data in the Player:
p = ktk.Player(points, force_platforms)
As we see, the Player draws the platforms and the ground reaction forces by default, based on the default value of its interconnections
and vector
properties
p.interconnections
{'ForcePlatforms': {'Links': [['*_Corner1', '*_Corner2'], ['*_Corner2', '*_Corner3'], ['*_Corner3', '*_Corner4'], ['*_Corner1', '*_Corner4']], 'Color': (0.5, 0.0, 1.0)}}
p.vectors
{'*Force': {'Origin': '*COP', 'Scale': 0.001, 'Color': (1.0, 1.0, 0.0)}}
In these values, wildcards *
are used to mean:
Any point ending with
_Corner1
is connected to a matching point ending with_Corner2
;Any point ending with
_Corner2
is connected to a matching point ending with_Corner3
;etc.
Any vector ending with
Force
is connected to a matching point ending withCOP
.