-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.py
169 lines (149 loc) · 5.92 KB
/
parser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import xml.etree.ElementTree as ET
from dataclasses import dataclass
from typing import List, Optional, Dict, Union
from pathlib import Path
@dataclass
class Joint:
name: str
joint_type: str
index: int
limits: Optional[Dict[str, float]] = None
parent: Optional[str] = None
child: Optional[str] = None
@dataclass
class Actuator:
name: str
joint: str
range: Optional[List[float]] = None
forcerange: Optional[List[float]] = None
class RobotFormatParser:
def __init__(self, file_path: Union[str, Path]):
self.file_path = Path(file_path)
self.tree = ET.parse(self.file_path)
self.root = self.tree.getroot()
self.format = self._detect_format()
def _detect_format(self) -> str:
"""Detect whether the file is URDF or MJCF format."""
root_tag = self.root.tag
if root_tag == "robot":
return "urdf"
elif root_tag == "mujoco":
return "mjcf"
else:
raise ValueError(f"Unsupported format with root tag: {root_tag}")
def parse_joints(self) -> List[Joint]:
"""Extract joint information from the robot description."""
joints = []
if self.format == "urdf":
for idx, joint in enumerate(self.root.findall(".//joint")):
limits = {
"angle": [],
"force": [],
}
limit_elem = joint.find("limit")
if limit_elem is not None:
for attr in ["lower", "upper", "effort"]:
if attr in limit_elem.attrib:
if attr == "lower":
limits["angle"].append(float(limit_elem.attrib[attr]))
if attr == "upper":
limits["angle"].append(float(limit_elem.attrib[attr]))
if attr == "effort":
limits["force"] = [
-float(limit_elem.attrib[attr]),
float(limit_elem.attrib[attr]),
]
parent = joint.find("parent").attrib["link"]
child = joint.find("child").attrib["link"]
joints.append(
Joint(
name=joint.attrib["name"],
joint_type=joint.attrib["type"],
index=idx,
limits=limits,
parent=parent,
child=child,
)
)
elif self.format == "mjcf":
for idx, joint in enumerate(self.root.findall(".//joint")):
limits = {
"angle": [],
"force": [],
}
if "range" in joint.attrib:
limits["angle"] = [float(x) for x in joint.attrib["range"].split()]
if "actuatorfrcrange" in joint.attrib:
limits["force"] = [
float(x) for x in joint.attrib["actuatorfrcrange"].split()
]
joints.append(
Joint(
name=joint.attrib["name"],
joint_type=joint.attrib.get("type", "hinge"),
index=idx,
limits=limits,
)
)
return joints
# def parse_actuators(self) -> List[Actuator]:
# """Extract actuator information from the robot description."""
# actuators = []
#
# if self.format == "urdf":
# # URDF doesn't have explicit actuator definitions
# # We can create them based on joints with limits
# joints = self.parse_joints()
# for joint in joints:
# if joint.limits:
# actuators.append(
# Actuator(
# name=f"{joint.name}_actuator",
# joint=joint.name,
# range=[
# joint.limits.get("lower", 0),
# joint.limits.get("upper", 0),
# ],
# forcerange=[
# -joint.limits.get("effort", 0),
# joint.limits.get("effort", 0),
# ],
# )
# )
#
# elif self.format == "mjcf":
# for actuator in self.root.findall(".//motor"):
# range_values = None
# if "range" in actuator.attrib:
# range_values = [float(x) for x in actuator.attrib["range"].split()]
#
# force_range = None
# if "forcerange" in actuator.attrib:
# force_range = [
# float(x) for x in actuator.attrib["forcerange"].split()
# ]
#
# actuators.append(
# Actuator(
# name=actuator.attrib["name"],
# joint=actuator.attrib["joint"],
# range=range_values,
# forcerange=force_range,
# )
# )
#
# return actuators
def get_joint_by_name(self, joint_name: str) -> Optional[Joint]:
"""Get joint information by name."""
joints = self.parse_joints()
for joint in joints:
if joint.name == joint_name:
return joint
return None
# def get_actuator_by_name(self, actuator_name: str) -> Optional[Actuator]:
# """Get actuator information by name."""
# actuators = self.parse_actuators()
# for actuator in actuators:
# if actuator.name == actuator_name:
# return actuator
# return None