Source code for myoconverter.xml.forces.CoordinateActuator

""" Contains the `CoordinateActuator` parser.

@author: Aleksi Ikkala
"""

from lxml import etree

from loguru import logger

from myoconverter.xml.parsers import IParser
from myoconverter.xml.utils import val2str, filter_nan_values, filter_keys
from myoconverter.xml import config as cfg


[docs]class CoordinateActuator(IParser): """ This class parses and converts the OpenSim `CoordinateActuator` XML element to MuJoCo. """
[docs] def parse(self, xml): """ This function handles the actual parsing and converting. :param xml: OpenSim `CoordinateActuator` XML element :return: None """ # Collect params/attributes params = {"name": xml.attrib["name"]} # Get min/max control min_control = xml.find("min_control") min_control = "-inf" if min_control is None else min_control.text.lower() max_control = xml.find("max_control") max_control = "inf" if max_control is None else max_control.text.lower() # If either is -inf/inf set ctrllimited=false, not sure if only one can be inf? if min_control == "-inf" or max_control == "inf": params["ctrllimited"] = False else: params["ctrllimited"] = True params["ctrlrange"] = min_control + " " + max_control # Find out which joint is actuated params["joint"] = xml.find("coordinate").text # Make sure the joint exists joint = cfg.M_WORLDBODY.find(f".//joint[@name='{params['joint']}']") if joint is None: logger.critical(f"Joint {params['joint']} was not found in the converted MuJoCo model, but it is needed for " f"CoordinateActuator {xml.attrib['name']}") # TODO how does optimal_force parameter relate to mujoco parameters? gear, gainprm, dynprm, biasprm? params["_optimal_force"] = xml.find("optimal_force") # Use default motor parameters (gain slightly higher) params["class"] = "motor" # Add motor to MuJoCo model; must be located before muscles in the xml file # Find first muscle element, and add the motor just before that. This keeps the order of the actuators the same first_muscle = cfg.M_ACTUATOR.find("muscle") idx = len(cfg.M_ACTUATOR.getchildren()) if first_muscle is None else cfg.M_ACTUATOR.index(first_muscle) cfg.M_ACTUATOR.insert(idx, etree.Element("motor", attrib=val2str(filter_nan_values(filter_keys(params)))))