Note

This example is available as a jupyter notebook here.

And on Google Colab here

Setup the environment if this is executed on Google Colab.

Make sure to change the runtime type to GPU. To do this go to Runtime -> Change runtime type -> GPU

Otherwise, rendering won't work in Google Colab.

import os

try:
    import google.colab
    IN_COLAB = True
except:
    IN_COLAB = False

if IN_COLAB:
    os.system("pip install -q imt-ring")
    os.system("pip install -q matplotlib")
import ring
# automatically detects colab or not
ring.utils.setup_colab_env()

import jax
import jax.numpy as jnp
import numpy as np
import matplotlib.pyplot as plt

import mediapy as media
sys_str = """
<x_xy model="knee">
<options dt="0.01" gravity="0 0 9.81"></options>
<worldbody>
<body euler="0 0 140" joint="frozen" name="femur" pos="0 0 0.5">
<geom dim="0.1" type="xyz"></geom>
<geom dim="0.05 0.4" mass="1" pos="0 0 -0.2" type="capsule"></geom>
<body joint="frozen" name="imu_femur" pos="0 .06 -.2" pos_max=".05 .08 -.1" pos_min="-.05 0 -.3">
<geom dim="0.05" type="xyz"></geom>
<geom color="orange" dim="0.05 0.02 0.05" mass="0.1" type="box"></geom>
</body>
<body damping="3" joint="rx" name="tibia" pos="0 0 -0.4">
<geom dim="0.1" type="xyz"></geom>
<geom dim="0.04 0.4" mass="1" pos="0 0 -0.2" type="capsule"></geom>
<geom dim="0.05 0.15 0.025" mass="0.1" pos="0 0.04 -0.45" type="box"></geom>
<body joint="frozen" name="imu_tibia" pos="0 0.05 -0.2" pos_max=".05 .07 -.1" pos_min="-.05 0 -.3">
<geom dim="0.05" type="xyz"></geom>
<geom color="orange" dim="0.05 0.02 0.05" mass="0.1" type="box"></geom>
</body>
</body>
</body>
</worldbody>
</x_xy>
"""  # noqa: E501
sys = ring.System.create(sys_str)
media.show_image(sys.render(width=640, height=480, camera="custom", add_cameras = {-1:'<camera euler="80 0 0" mode="fixed" name="custom" pos="0 -1.8 0.4"></camera>'})[0])
Rendering frames..: 100%|██████████| 1/1 [00:00<00:00,  3.86it/s]

No description has been provided for this image
(X, _), (_, qs, xs, _) = ring.RCMG(sys, ring.MotionConfig(T=20.0, t_min=0.3, t_max=1.5), keep_output_extras=1, add_X_imus=1).to_list()[0]
executing generators: 100%|██████████| 1/1 [00:05<00:00,  5.41s/it]

media.show_video(sys.render(xs, width=640, height=480, camera="custom", 
                            add_cameras = {-1:'<camera euler="80 0 0" mode="fixed" name="custom" pos="0 -1.8 0.4"></camera>'}, render_every_nth=4), fps=25)
Rendering frames..: 100%|██████████| 500/500 [00:04<00:00, 102.34it/s]

X.keys()
dict_keys(['femur', 'tibia'])
X["femur"].keys()
dict_keys(['acc', 'gyr'])
# number of timesteps
T = 2000
# graph
lam = (-1, 0)
# number of bodies (excluding IMUs); so tibia and femur
N = len(lam)

ringnet = ring.RING(lam, None)

X_ring = np.zeros((T, N, 10))

X_ring[:, 0, :3] = X["femur"]["acc"]
X_ring[:, 0, 3:6] = X["femur"]["gyr"]
X_ring[:, 1, :3] = X["tibia"]["acc"]
X_ring[:, 1, 3:6] = X["tibia"]["gyr"]

# time-delta between timesteps or inverse of sampling rate
X_ring[:, :, 9] = float(sys.dt)

# We could assume that we don't know the hinge joint axis direction
KNOWN_JOINT_AXIS = True
if KNOWN_JOINT_AXIS:
    # the components 6:9 store the axis direction; here it is rx-joint; Revolute x-axis
    X_ring[:, 1, 6] = 1.0

quaternions, _ = ringnet.apply(X_ring, lam=lam)
quaternions.shape

# quaternions[:, 0]
# inclination: orientation from femur to earth
# quaternioins[:, 1]
# orientation from tibia to femur
(2000, 2, 4)
ts = np.arange(0, 20.0, step=sys.dt)
plt.plot(ts, ring.maths.quat_angle_constantAxisOverTime(quaternions[:, 1]), label="knee angle prediction")
plt.plot(ts, -qs, label="knee angle truth")
plt.grid()
plt.legend()
plt.xlabel("Time [s]")
plt.ylabel("Knee Angle [rad]")
Text(0, 0.5, 'Knee Angle [rad]')
No description has been provided for this image