""" This module contains a collection of utility functions useful for parsing and converting the OpenSim `JointSet`
@author: Aleksi Ikkala
"""
from lxml import etree
from scipy.spatial.transform import Rotation
import matplotlib
import matplotlib.pyplot as pp
import numpy as np
import os
from myoconverter.xml.utils import str2vec, str2bool
matplotlib.use("agg")
pp.ioff()
[docs]def parse_coordinates(objects):
""" Parse OpenSim `Coordinates`.
:param objects: A list of OpenSim `Coordinate` XML elements
:return: A dictionary of parsed `Coordinates`
"""
# Return if there are no coordinates
if objects is None:
return dict()
# Parse all coordinates defined for this joint. Default value is stored as "user" key. Default value is also used for
# "_transform_value", which is used when a joint is locked
coordinates = dict()
for coordinate in objects:
default_value = coordinate.find("default_value")
coordinates[coordinate.attrib["name"]] = {
"name": coordinate.attrib["name"],
"range": str2vec(coordinate.find("range").text),
"limited": str2bool(coordinate.find("clamped").text),
"_locked": str2bool(coordinate.find("locked").text)}
if default_value is not None:
coordinates[coordinate.attrib["name"]].update({"user": float(default_value.text),
"_transform_value": float(default_value.text)})
return coordinates
[docs]def lock_joint(params, M_EQUALITY):
""" Lock a joint by adding an equality constraint.
:param params: Joint parameters
:param M_EQUALITY: Pointer to equality constraints in MuJoCo XML file
:return: None
"""
# A dependent joint/coordinate can be locked (since it's possible to toggle lock on/off), so we have to disable
# any existing constraints of this particular joint
constraint = M_EQUALITY.find(f".//*[@joint1='{params['name']}']")
if constraint is not None:
# Disable the other constraint
constraint.attrib["active"] = "false"
# Create the new constraint
etree.SubElement(M_EQUALITY, "joint",
name=params["name"] + "_locked",
active="true",
joint1=params["name"],
polycoef=f"{params['_transform_value']} 0 0 0 0",
solimp="0.9999 0.9999 0.001 0.5 2")
[docs]def estimate_axis(socket_child_frame, axis):
""" Estimate axis of MuJoCo joint.
:param socket_child_frame: Child frame socket
:param axis: Axis of the OpenSim joint element
:return: MuJoCo joint axis
"""
# Get orientation of child frame
child_orientation = str2vec(socket_child_frame.find("orientation").text)
# Turn into a rotation
child_rotation = Rotation.from_euler("XYZ", child_orientation)
# Return new axis
return child_rotation.apply(axis)