Parse inherited actuator defaults from MuJoCo defaults tree.
The output groups are class-based defaults containing actuator-related tags
(e.g. general, adhesion, motor, position, ...), plus an apply_to section
inferred from body/joint class usage.
Source code in src/flygym/flybody/parse_flybody.py
| def parse_actuators(xml_path, yaml_path, ignore_ctrlrange=True, merge_equivalent=True):
"""Parse inherited actuator defaults from MuJoCo defaults tree.
The output groups are class-based defaults containing actuator-related tags
(e.g. general, adhesion, motor, position, ...), plus an apply_to section
inferred from body/joint class usage.
"""
tree = ET.parse(xml_path)
root = tree.getroot()
default_lookup = build_effective_default_lookup(root)
class_to_targets = _collect_class_apply_targets(root.find("worldbody"))
class_hierarchy = _collect_default_class_hierarchy(root)
actuator_tags = {
"general",
"motor",
"position",
"velocity",
"intvelocity",
"damper",
"cylinder",
"muscle",
}
representative_to_targets = {}
for class_name, targets in class_to_targets.items():
representative = _resolve_representative_class(
class_name, class_hierarchy, actuator_tags, ignore_ctrlrange
)
if representative is None:
continue
representative_to_targets.setdefault(representative, set()).update(targets)
parsed = {}
for class_name, targets in representative_to_targets.items():
if class_name == "__root__" or class_name not in default_lookup:
continue
class_params = default_lookup[class_name]
group_cfg = {}
for tag in actuator_tags:
if tag not in class_params:
continue
tag_cfg = _clean_actuator_tag_config(
tag, class_params[tag], ignore_ctrlrange
)
if tag_cfg:
group_cfg[tag] = _scale_actuator_tag_config(tag_cfg)
if not group_cfg:
continue
apply_to = sorted(targets)
if len(apply_to) == 1:
group_cfg["apply_to"] = apply_to[0]
else:
group_cfg["apply_to"] = apply_to
parsed[class_name] = group_cfg
if merge_equivalent:
parsed = _merge_equivalent_actuator_groups(parsed)
_write_yaml_file(yaml_path, parsed)
|