cgse-coordinates 0.17.3__py3-none-any.whl → 0.17.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cgse_coordinates/settings.yaml +0 -16
- {cgse_coordinates-0.17.3.dist-info → cgse_coordinates-0.17.4.dist-info}/METADATA +1 -1
- cgse_coordinates-0.17.4.dist-info/RECORD +16 -0
- {cgse_coordinates-0.17.3.dist-info → cgse_coordinates-0.17.4.dist-info}/entry_points.txt +0 -3
- egse/coordinates/__init__.py +27 -334
- egse/coordinates/avoidance.py +33 -41
- egse/coordinates/cslmodel.py +48 -57
- egse/coordinates/laser_tracker_to_dict.py +16 -25
- egse/coordinates/point.py +544 -418
- egse/coordinates/pyplot.py +117 -105
- egse/coordinates/reference_frame.py +1417 -0
- egse/coordinates/refmodel.py +311 -203
- egse/coordinates/rotation_matrix.py +95 -0
- egse/coordinates/transform3d_addon.py +292 -228
- cgse_coordinates-0.17.3.dist-info/RECORD +0 -16
- egse/coordinates/referenceFrame.py +0 -1251
- egse/coordinates/rotationMatrix.py +0 -82
- {cgse_coordinates-0.17.3.dist-info → cgse_coordinates-0.17.4.dist-info}/WHEEL +0 -0
egse/coordinates/refmodel.py
CHANGED
|
@@ -34,7 +34,6 @@ Functionality:
|
|
|
34
34
|
import logging
|
|
35
35
|
from typing import Dict
|
|
36
36
|
from typing import List
|
|
37
|
-
from typing import Union
|
|
38
37
|
|
|
39
38
|
import matplotlib.pyplot as plt
|
|
40
39
|
import numpy as np
|
|
@@ -44,7 +43,6 @@ from mpl_toolkits.mplot3d import Axes3D
|
|
|
44
43
|
import egse.coordinates.transform3d_addon as t3add
|
|
45
44
|
from egse.coordinates import dict_to_ref_model
|
|
46
45
|
from egse.coordinates import ref_model_to_dict
|
|
47
|
-
from egse.coordinates.referenceFrame import ReferenceFrame
|
|
48
46
|
from egse.setup import NavigableDict
|
|
49
47
|
|
|
50
48
|
LOGGER = logging.getLogger(__name__)
|
|
@@ -73,22 +71,26 @@ class ReferenceFrameModel:
|
|
|
73
71
|
|
|
74
72
|
def __init__(
|
|
75
73
|
self,
|
|
76
|
-
model:
|
|
77
|
-
|
|
74
|
+
model: Dict | List = None,
|
|
75
|
+
rotation_config: str = _ROT_CONFIG_DEFAULT,
|
|
78
76
|
use_degrees: bool = _DEGREES_DEFAULT,
|
|
79
77
|
use_active_movements: bool = _ACTIVE_DEFAULT,
|
|
80
78
|
):
|
|
81
|
-
"""
|
|
82
|
-
When the model_dict is empty or None, a new model is created with a master reference frame.
|
|
79
|
+
"""Initialisation of a reference frame model.
|
|
83
80
|
|
|
84
81
|
Args:
|
|
85
|
-
model:
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
model (Dict | List[ReferenceFrame]): List or a dictionary of reference frames that make up the model.
|
|
83
|
+
rotation_config (str): Order in which the rotation about the three axes are chained.
|
|
84
|
+
use_degrees (bool): Indicates whether the rotation angles are specified in degrees, rather than radians.
|
|
85
|
+
use_active_movements (bool): Indicates if the rotation is active (object rotates IN a fixed coordinate
|
|
86
|
+
system) or passive (coordinate system rotates AROUND a fixed object). Even if
|
|
87
|
+
two angles are zero, the match between angle orders and rot_config is still
|
|
88
|
+
critical.
|
|
88
89
|
"""
|
|
90
|
+
|
|
89
91
|
self._use_degrees = use_degrees
|
|
90
92
|
self._use_active_movements = use_active_movements
|
|
91
|
-
self._rot_config =
|
|
93
|
+
self._rot_config = rotation_config
|
|
92
94
|
|
|
93
95
|
# Keep a dictionary with all reference frames that are part of the model. The keys shall
|
|
94
96
|
# be the name of the reference frame. When the model passed is empty, create only a
|
|
@@ -99,155 +101,185 @@ class ReferenceFrameModel:
|
|
|
99
101
|
else:
|
|
100
102
|
self._model = NavigableDict({})
|
|
101
103
|
|
|
102
|
-
def __str__(self):
|
|
104
|
+
def __str__(self) -> str:
|
|
105
|
+
"""Returns a printable string representation of the reference frame.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Printable string representation of the reference frame.
|
|
109
|
+
"""
|
|
110
|
+
|
|
103
111
|
return self._model.pretty_str()
|
|
104
112
|
|
|
105
|
-
def __len__(self):
|
|
113
|
+
def __len__(self) -> int:
|
|
114
|
+
"""Returns the number of reference frames in the model."""
|
|
115
|
+
|
|
106
116
|
return len(self._model)
|
|
107
117
|
|
|
108
|
-
def __contains__(self, item):
|
|
118
|
+
def __contains__(self, item) -> bool:
|
|
119
|
+
"""Checks whether the given item is present in the model.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
item: Item for which to check whether it's present in the model.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
True if the given item is present in the model; False otherwise.
|
|
126
|
+
"""
|
|
127
|
+
|
|
109
128
|
return item in self._model
|
|
110
129
|
|
|
111
130
|
def __iter__(self):
|
|
131
|
+
"""Returns an iterator over the reference frames in the model."""
|
|
132
|
+
|
|
112
133
|
return iter(self._model.values())
|
|
113
134
|
|
|
114
|
-
def summary(self):
|
|
115
|
-
|
|
135
|
+
def summary(self) -> str:
|
|
136
|
+
"""Returns a summary of the model.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
String summary of the model.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
result = f"Number of frames: {len(self)}\n"
|
|
143
|
+
|
|
144
|
+
for reference_frame in self:
|
|
145
|
+
result += f"{reference_frame.name:>10}[{reference_frame.reference_frame.name}] --- {[link.name for link in reference_frame.linked_to]}\n"
|
|
116
146
|
|
|
117
|
-
for ref in self:
|
|
118
|
-
result += f"{ref.name:>10}[{ref.ref.name}] --- {[link.name for link in ref.linkedTo]}\n"
|
|
119
147
|
return result
|
|
120
148
|
|
|
121
149
|
@staticmethod
|
|
122
150
|
def deserialize(model_dict: dict) -> NavigableDict:
|
|
123
|
-
"""
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
references.
|
|
151
|
+
"""De-serialisation of the model.
|
|
152
|
+
|
|
153
|
+
De-serialisation means you take a serialised representation of a reference frames model and turn it into a
|
|
154
|
+
dictionary containing all the reference frames with their links and references.
|
|
127
155
|
|
|
128
156
|
Args:
|
|
129
|
-
model_dict:
|
|
157
|
+
model_dict (dict): Dictionary of serialised reference frames.
|
|
130
158
|
|
|
131
159
|
Returns:
|
|
132
|
-
|
|
160
|
+
Dictionary of reference frames that make up a model.
|
|
133
161
|
|
|
134
162
|
"""
|
|
135
163
|
return dict_to_ref_model(model_dict)
|
|
136
164
|
|
|
137
165
|
def serialize(self) -> NavigableDict:
|
|
138
|
-
"""
|
|
139
|
-
|
|
140
|
-
|
|
166
|
+
"""Serialisation of the model.
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
Serialisation of the model by serialising each of the reference frames into an object that can easily be saved
|
|
170
|
+
to a YAML or a JSON file.
|
|
141
171
|
|
|
142
172
|
Returns:
|
|
143
|
-
|
|
173
|
+
Dictionary with all the serialised reference framed.
|
|
144
174
|
"""
|
|
145
175
|
|
|
146
176
|
return ref_model_to_dict(self._model)
|
|
147
177
|
|
|
148
|
-
def add_master_frame(self):
|
|
178
|
+
def add_master_frame(self) -> None:
|
|
179
|
+
"""Adds the master reference frame to the model."""
|
|
180
|
+
|
|
149
181
|
# TODO: First check if there is not already a Master frame in the model
|
|
150
182
|
|
|
151
|
-
|
|
183
|
+
from egse.coordinates.reference_frame import ReferenceFrame
|
|
184
|
+
|
|
185
|
+
self._model["Master"] = ReferenceFrame.create_master()
|
|
152
186
|
|
|
153
187
|
def add_frame(
|
|
154
188
|
self,
|
|
155
189
|
name: str,
|
|
156
190
|
*,
|
|
157
|
-
translation:
|
|
158
|
-
rotation:
|
|
159
|
-
transformation=None,
|
|
160
|
-
|
|
161
|
-
):
|
|
162
|
-
"""
|
|
163
|
-
Add a reference frame to the model.
|
|
164
|
-
|
|
165
|
-
.. note::
|
|
166
|
-
Only the `name` parameter can be positional, all the other arguments (translation,
|
|
167
|
-
rotation, transformation, and ref) must be given as keyword arguments.
|
|
191
|
+
translation: np.ndarray = None,
|
|
192
|
+
rotation: np.ndarray = None,
|
|
193
|
+
transformation: np.ndarray = None,
|
|
194
|
+
reference: str,
|
|
195
|
+
) -> None:
|
|
196
|
+
"""Adds a reference frame to the model.
|
|
168
197
|
|
|
169
198
|
Args:
|
|
170
|
-
name:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
199
|
+
name (str): Name of the reference frame to add to the model. Only this parameter can be positional. This
|
|
200
|
+
will serve as the identifier for the reference frame in the model.
|
|
201
|
+
translation (np.ndarray): Translation vector. Ignored when `transformation` is given.
|
|
202
|
+
rotation (np.ndarray: Rotation vector. Ignored when `transformation` is given.
|
|
203
|
+
transformation (np.ndarray): Transformation matrix.
|
|
204
|
+
reference (str): Name of the reference frame that is a reference for the new reference frame, i.e. the new
|
|
205
|
+
reference frame is defined w.r.t. this one.
|
|
177
206
|
"""
|
|
178
207
|
|
|
208
|
+
from egse.coordinates.reference_frame import ReferenceFrame
|
|
209
|
+
|
|
179
210
|
if name in self._model:
|
|
180
211
|
raise KeyError("A reference frame with the name '{name} already exists in the model.")
|
|
181
212
|
|
|
182
|
-
|
|
213
|
+
reference = self._model[reference]
|
|
183
214
|
|
|
184
|
-
if transformation
|
|
215
|
+
if transformation:
|
|
185
216
|
self._model[name] = ReferenceFrame(
|
|
186
217
|
transformation,
|
|
187
|
-
|
|
218
|
+
reference_frame=reference,
|
|
188
219
|
name=name,
|
|
189
|
-
|
|
220
|
+
rotation_config=self._rot_config,
|
|
190
221
|
)
|
|
191
222
|
else:
|
|
192
|
-
self._model[name] = ReferenceFrame.
|
|
223
|
+
self._model[name] = ReferenceFrame.from_translation_rotation(
|
|
193
224
|
translation,
|
|
194
225
|
rotation,
|
|
195
226
|
name=name,
|
|
196
|
-
|
|
197
|
-
|
|
227
|
+
reference_frame=reference,
|
|
228
|
+
rotation_config=self._rot_config,
|
|
198
229
|
degrees=self._use_degrees,
|
|
199
230
|
active=self._use_active_movements,
|
|
200
231
|
)
|
|
201
232
|
|
|
202
233
|
def remove_frame(self, name: str):
|
|
203
|
-
"""
|
|
204
|
-
|
|
205
|
-
model, a warning message is logged.
|
|
234
|
+
"""Deletes the given reference frame from the model.
|
|
235
|
+
|
|
236
|
+
If the reference frame doesn't exist in the model, a warning message is logged.
|
|
206
237
|
|
|
207
238
|
Args:
|
|
208
|
-
name:
|
|
239
|
+
name (str): Name of the reference frame to remove.
|
|
209
240
|
"""
|
|
210
241
|
|
|
211
242
|
if name in self._model:
|
|
243
|
+
from egse.coordinates.reference_frame import ReferenceFrame
|
|
244
|
+
|
|
212
245
|
frame: ReferenceFrame = self._model[name]
|
|
213
246
|
|
|
214
|
-
# We need to get the links out in a list because the frame.
|
|
215
|
-
# frames from the
|
|
247
|
+
# We need to get the links out in a list because the frame.remove_link() method deletes
|
|
248
|
+
# frames from the linked_to dictionary and that is not allowed in a for loop.
|
|
216
249
|
|
|
217
|
-
links = [linked_frame for linked_frame in frame.
|
|
250
|
+
links = [linked_frame for linked_frame in frame.linked_to]
|
|
218
251
|
for link in links:
|
|
219
|
-
frame.
|
|
252
|
+
frame.remove_link(link)
|
|
220
253
|
|
|
221
254
|
del self._model[name]
|
|
222
255
|
else:
|
|
223
256
|
LOGGER.warning(f"You tried to remove a non-existing reference frame '{name}' from the model.")
|
|
224
257
|
|
|
225
|
-
def get_frame(self, name: str)
|
|
226
|
-
"""
|
|
227
|
-
Returns a frame with the given name.
|
|
258
|
+
def get_frame(self, name: str):
|
|
259
|
+
"""Returns the reference frame with the given name.
|
|
228
260
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
inconsistent model when the frame is changed outside of the scope of the reference
|
|
232
|
-
model.
|
|
261
|
+
Use this function with care since this breaks encapsulation and may lead to an inconsistent model when the frame
|
|
262
|
+
is changed outside the scope of the reference model.
|
|
233
263
|
|
|
234
|
-
|
|
235
|
-
|
|
264
|
+
Args:
|
|
265
|
+
name (str): Name of the requested reference frame.
|
|
236
266
|
|
|
237
|
-
|
|
238
|
-
|
|
267
|
+
Returns:
|
|
268
|
+
Reference frame with the given name.
|
|
239
269
|
"""
|
|
270
|
+
|
|
240
271
|
return self._model[name]
|
|
241
272
|
|
|
242
273
|
def add_link(self, source: str, target: str):
|
|
243
|
-
"""
|
|
244
|
-
Add a link between two reference frames. All links are bi-directional.
|
|
274
|
+
"""Adds a link between the two given reference frames of the model.
|
|
245
275
|
|
|
246
|
-
|
|
247
|
-
source: the source reference frame
|
|
248
|
-
target: the target reference frame
|
|
276
|
+
All links are bi-directional.
|
|
249
277
|
|
|
278
|
+
Args:
|
|
279
|
+
source (args): Name of the source reference frame.
|
|
280
|
+
target (args): Name of the target reference frame.
|
|
250
281
|
"""
|
|
282
|
+
|
|
251
283
|
if source not in self._model:
|
|
252
284
|
raise KeyError(f"There is no reference frame with the name '{source} in the model.")
|
|
253
285
|
if target not in self._model:
|
|
@@ -256,18 +288,18 @@ class ReferenceFrameModel:
|
|
|
256
288
|
source = self._model[source]
|
|
257
289
|
target = self._model[target]
|
|
258
290
|
|
|
259
|
-
source.
|
|
291
|
+
source.add_link(target)
|
|
260
292
|
|
|
261
293
|
def remove_link(self, source: str, target: str):
|
|
262
|
-
"""
|
|
263
|
-
Remove a link between two reference frames. All links are bi-directional and this method
|
|
264
|
-
removes both links.
|
|
294
|
+
"""Removes a link between two reference frames.
|
|
265
295
|
|
|
266
|
-
|
|
267
|
-
source: the source reference frame
|
|
268
|
-
target: the target reference frame
|
|
296
|
+
All links are bi-directional and this method removes both links.
|
|
269
297
|
|
|
298
|
+
Args:
|
|
299
|
+
source (args): Name of the source reference frame.
|
|
300
|
+
target (args): Name of the target reference frame.
|
|
270
301
|
"""
|
|
302
|
+
|
|
271
303
|
if source not in self._model:
|
|
272
304
|
raise KeyError(f"There is no reference frame with the name '{source} in the model.")
|
|
273
305
|
if target not in self._model:
|
|
@@ -276,39 +308,54 @@ class ReferenceFrameModel:
|
|
|
276
308
|
source = self._model[source]
|
|
277
309
|
target = self._model[target]
|
|
278
310
|
|
|
279
|
-
source.
|
|
311
|
+
source.remove_link(target)
|
|
280
312
|
|
|
281
|
-
def move_absolute_self(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
313
|
+
def move_absolute_self(
|
|
314
|
+
self, name: str, translation: np.ndarray, rotation: np.ndarray, degrees: bool = _DEGREES_DEFAULT
|
|
315
|
+
) -> None:
|
|
316
|
+
"""Applies an absolute movement to the given reference frame.
|
|
317
|
+
|
|
318
|
+
Applies an absolute movement to the given reference frame such that it occupies a given absolute position w.r.t.
|
|
319
|
+
"frame_ref" after the movement.
|
|
285
320
|
|
|
286
|
-
|
|
321
|
+
There is no hexapod equivalent.
|
|
287
322
|
|
|
288
323
|
Args:
|
|
289
|
-
|
|
324
|
+
name (str): Name of the reference frame to move.
|
|
325
|
+
translation (np.ndarray): Translation vector.
|
|
326
|
+
rotation (np.ndarray): Rotation vector.
|
|
327
|
+
degrees (bool): Indicates whether the rotation angles are specified in degrees, rather than radians.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
name (str): the name of the reference frame to move
|
|
290
331
|
"""
|
|
291
332
|
|
|
292
|
-
|
|
293
|
-
|
|
333
|
+
name = self._model[name]
|
|
334
|
+
name.set_translation_rotation(
|
|
294
335
|
translation,
|
|
295
336
|
rotation,
|
|
296
|
-
|
|
337
|
+
rotation_config=self._rot_config,
|
|
297
338
|
active=self._use_active_movements,
|
|
298
339
|
degrees=degrees,
|
|
299
|
-
|
|
340
|
+
preserve_links=True,
|
|
300
341
|
)
|
|
301
342
|
|
|
302
|
-
def move_absolute_in_other(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
343
|
+
def move_absolute_in_other(
|
|
344
|
+
self, frame: str, other: str, translation: np.ndarray, rotation: np.ndarray, degrees: bool = _DEGREES_DEFAULT
|
|
345
|
+
):
|
|
346
|
+
"""Applies an absolute movement to the given reference frame in another reference frame.
|
|
347
|
+
|
|
348
|
+
Apply an absolute movement to the ReferenceFrame "frame", such that it occupies a given absolute position
|
|
349
|
+
w.r.t. "other" after the movement.
|
|
306
350
|
|
|
307
|
-
|
|
351
|
+
Hexapod equivalent: PunaSimulator.move_absolute, setting `hexobj` w.r.t. `hexusr`.
|
|
308
352
|
|
|
309
353
|
Args:
|
|
310
|
-
frame (str):
|
|
311
|
-
other (str):
|
|
354
|
+
frame (str): Name of the reference frame to move.
|
|
355
|
+
other (str): Name of the other reference frame.
|
|
356
|
+
translation (np.ndarray): Translation vector.
|
|
357
|
+
rotation (np.ndarray): Rotation vector.
|
|
358
|
+
degrees (bool): Indicates whether the rotation angles are specified in degrees, rather than radians.
|
|
312
359
|
"""
|
|
313
360
|
|
|
314
361
|
# TODO:
|
|
@@ -318,58 +365,71 @@ class ReferenceFrameModel:
|
|
|
318
365
|
frame = self._model[frame]
|
|
319
366
|
other = self._model[other]
|
|
320
367
|
|
|
321
|
-
transformation = other.
|
|
368
|
+
transformation = other.get_active_transformation_to(frame)
|
|
322
369
|
|
|
323
|
-
|
|
370
|
+
from egse.coordinates.reference_frame import ReferenceFrame
|
|
324
371
|
|
|
325
|
-
moving_in_other
|
|
372
|
+
moving_in_other = ReferenceFrame(
|
|
373
|
+
transformation, rotation_config=self._rot_config, reference_frame=other, name="moving_in_other"
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
moving_in_other.add_link(frame)
|
|
326
377
|
|
|
327
|
-
moving_in_other.
|
|
378
|
+
moving_in_other.set_translation_rotation(
|
|
328
379
|
translation,
|
|
329
380
|
rotation,
|
|
330
|
-
|
|
381
|
+
rotation_config=self._rot_config,
|
|
331
382
|
active=self._use_active_movements,
|
|
332
383
|
degrees=degrees,
|
|
333
|
-
|
|
384
|
+
preserve_links=True,
|
|
334
385
|
)
|
|
335
386
|
|
|
336
|
-
moving_in_other.
|
|
387
|
+
moving_in_other.remove_link(frame)
|
|
337
388
|
|
|
338
389
|
del moving_in_other
|
|
339
390
|
|
|
340
|
-
def move_relative_self(
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
391
|
+
def move_relative_self(
|
|
392
|
+
self, frame: str, translation: np.ndarray, rotation: np.ndarray, degrees: bool = _DEGREES_DEFAULT
|
|
393
|
+
):
|
|
394
|
+
"""Applies a relative movement to the given reference frame.
|
|
395
|
+
|
|
396
|
+
It is assumed that the movement is expressed in that same reference frame.
|
|
344
397
|
|
|
345
|
-
|
|
398
|
+
Hexapod equivalent: PunaSimulator.move_relative_object
|
|
346
399
|
|
|
347
400
|
Args:
|
|
348
|
-
frame (str):
|
|
401
|
+
frame (str): Name of the reference frame to move.
|
|
402
|
+
translation (np.ndarray): Translation vector.
|
|
403
|
+
rotation (np.ndarray): Rotation vector.
|
|
404
|
+
degrees (bool): Indicates whether the rotation angles are specified in degrees, rather than radians.
|
|
349
405
|
"""
|
|
350
406
|
|
|
351
407
|
frame = self._model[frame]
|
|
352
|
-
frame.
|
|
408
|
+
frame.apply_translation_rotation(
|
|
353
409
|
translation,
|
|
354
410
|
rotation,
|
|
355
|
-
|
|
411
|
+
rotation_config=self._rot_config,
|
|
356
412
|
active=self._use_active_movements,
|
|
357
413
|
degrees=degrees,
|
|
358
|
-
|
|
414
|
+
preserve_links=True,
|
|
359
415
|
)
|
|
360
416
|
|
|
361
|
-
def move_relative_other(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
the
|
|
417
|
+
def move_relative_other(
|
|
418
|
+
self, frame: str, other: str, translation: np.ndarray, rotation: np.ndarray, degrees: bool = _DEGREES_DEFAULT
|
|
419
|
+
):
|
|
420
|
+
"""Applies a relative movement to the given reference frame.
|
|
365
421
|
|
|
366
|
-
The
|
|
422
|
+
The movement is expressed w.r.t. the axes of another frame. The centre of rotation is the origin of the
|
|
423
|
+
that other reference frame.
|
|
367
424
|
|
|
368
|
-
|
|
425
|
+
There is no hexapod equivalent.
|
|
369
426
|
|
|
370
427
|
Args:
|
|
371
|
-
frame (str):
|
|
372
|
-
other (str): the
|
|
428
|
+
frame (str): Name of the reference frame to move.
|
|
429
|
+
other (str): Name of the reference frame in which the movements have been defined.
|
|
430
|
+
translation (np.ndarray): Translation vector.
|
|
431
|
+
rotation (np.ndarray): Rotation vector.
|
|
432
|
+
degrees (bool): Indicates whether the rotation angles are specified in degrees, rather than radians.
|
|
373
433
|
"""
|
|
374
434
|
|
|
375
435
|
# TODO:
|
|
@@ -379,35 +439,45 @@ class ReferenceFrameModel:
|
|
|
379
439
|
frame = self._model[frame]
|
|
380
440
|
other = self._model[other]
|
|
381
441
|
|
|
382
|
-
transformation = frame.
|
|
442
|
+
transformation = frame.get_active_transformation_to(other)
|
|
443
|
+
|
|
444
|
+
from egse.coordinates.reference_frame import ReferenceFrame
|
|
383
445
|
|
|
384
|
-
moving_in_other = ReferenceFrame(
|
|
446
|
+
moving_in_other = ReferenceFrame(
|
|
447
|
+
transformation, rotation_config=self._rot_config, reference_frame=other, name="moving_in_other"
|
|
448
|
+
)
|
|
385
449
|
|
|
386
|
-
moving_in_other.
|
|
450
|
+
moving_in_other.add_link(frame)
|
|
387
451
|
|
|
388
|
-
moving_in_other.
|
|
452
|
+
moving_in_other.apply_translation_rotation(
|
|
389
453
|
translation,
|
|
390
454
|
rotation,
|
|
391
|
-
|
|
455
|
+
rotation_config=self._rot_config,
|
|
392
456
|
active=self._use_active_movements,
|
|
393
457
|
degrees=degrees,
|
|
394
|
-
|
|
458
|
+
preserve_links=True,
|
|
395
459
|
)
|
|
396
460
|
|
|
397
|
-
moving_in_other.
|
|
461
|
+
moving_in_other.remove_link(frame)
|
|
398
462
|
|
|
399
463
|
del moving_in_other # not need as local scope
|
|
400
464
|
|
|
401
|
-
def move_relative_other_local(
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
The movement is expressed wrt the axes of an external frame "other"
|
|
465
|
+
def move_relative_other_local(
|
|
466
|
+
self, frame: str, other: str, translation: np.ndarray, rotation: np.ndarray, degrees: bool = _DEGREES_DEFAULT
|
|
467
|
+
):
|
|
468
|
+
"""Applies a relative movement to the given reference frame.
|
|
406
469
|
|
|
407
|
-
The
|
|
470
|
+
The movement is expressed w.r.t. the axes of another reference frame. The centre of rotation is the origin of
|
|
471
|
+
that other reference frame.
|
|
408
472
|
|
|
409
|
-
|
|
473
|
+
Hexapod equivalent: PunaSimulator.move_relative_user
|
|
410
474
|
|
|
475
|
+
Args:
|
|
476
|
+
frame (str): Name of the reference frame to move.
|
|
477
|
+
other (str): Name of the reference frame in which the movements have been defined.
|
|
478
|
+
translation (np.ndarray): Translation vector.
|
|
479
|
+
rotation (np.ndarray): Rotation vector.
|
|
480
|
+
degrees (bool): Indicates whether the rotation angles are specified in degrees, rather than radians.
|
|
411
481
|
"""
|
|
412
482
|
|
|
413
483
|
# TODO:
|
|
@@ -418,10 +488,9 @@ class ReferenceFrameModel:
|
|
|
418
488
|
other = self._model[other]
|
|
419
489
|
|
|
420
490
|
# Represent the requested movement
|
|
491
|
+
# De-rotation of MOVING -> REF (align frame_moving axes on those of frame_ref)
|
|
421
492
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
derotation = frame.getActiveTransformationTo(other)
|
|
493
|
+
derotation = frame.get_active_transformation_to(other)
|
|
425
494
|
derotation[:3, 3] = [0, 0, 0]
|
|
426
495
|
|
|
427
496
|
# Reverse rotation (record inverse rotation, to restore the frame in the end)
|
|
@@ -436,24 +505,28 @@ class ReferenceFrameModel:
|
|
|
436
505
|
# Requested rotation matrix (already expressed wrt frame_ref)
|
|
437
506
|
|
|
438
507
|
zeros = [0, 0, 0]
|
|
439
|
-
rotation_ = t3add.
|
|
508
|
+
rotation_ = t3add.translation_rotation_to_transformation(
|
|
509
|
+
zeros, rotation, rotation_config=self._rot_config, degrees=degrees
|
|
510
|
+
)
|
|
440
511
|
|
|
441
512
|
# All translations and rotations are applied to frame_moving
|
|
442
|
-
#
|
|
443
|
-
#
|
|
444
|
-
# 1.
|
|
445
|
-
# 2.
|
|
446
|
-
# 3.
|
|
447
|
-
# 4.
|
|
513
|
+
# -> a. Need for "de-rotation" before applying the translation
|
|
514
|
+
# b. The centre of rotation is always the origin of frame_moving
|
|
515
|
+
# 1. Rotate frame_moving to align it with frame_ref (i.e. render their axes parallel)
|
|
516
|
+
# 2. Apply the translation in this frame
|
|
517
|
+
# 3. Restore the original orientation of the moving frame
|
|
518
|
+
# 4. Apply the requested rotation
|
|
448
519
|
|
|
449
520
|
transformation = derotation @ translation_ @ rerotation @ rotation_
|
|
450
521
|
|
|
451
522
|
# Apply the requested movement
|
|
452
523
|
|
|
453
|
-
frame.
|
|
524
|
+
frame.apply_transformation(transformation, preserve_links=True)
|
|
454
525
|
|
|
455
526
|
|
|
456
|
-
def plot_ref_model(model: ReferenceFrameModel):
|
|
527
|
+
def plot_ref_model(model: ReferenceFrameModel) -> None:
|
|
528
|
+
"""Plots the xz-plane of the given reference frame model."""
|
|
529
|
+
|
|
457
530
|
# figsize is in inch, 6 inch = 15.24 cm, 5 inch = 12.7 cm
|
|
458
531
|
|
|
459
532
|
fig = plt.figure(figsize=(6, 5), dpi=100)
|
|
@@ -474,7 +547,9 @@ def plot_ref_model(model: ReferenceFrameModel):
|
|
|
474
547
|
plt.show()
|
|
475
548
|
|
|
476
549
|
|
|
477
|
-
def plot_ref_model_3d(model: ReferenceFrameModel):
|
|
550
|
+
def plot_ref_model_3d(model: ReferenceFrameModel) -> None:
|
|
551
|
+
"""Plots the given reference frame model in 3D."""
|
|
552
|
+
|
|
478
553
|
fig = plt.figure(figsize=(8, 8), dpi=100)
|
|
479
554
|
ax = Axes3D(fig)
|
|
480
555
|
# ax.set_box_aspect([1, 1, 1])
|
|
@@ -521,17 +596,16 @@ def plot_ref_model_3d(model: ReferenceFrameModel):
|
|
|
521
596
|
plt.show()
|
|
522
597
|
|
|
523
598
|
|
|
524
|
-
|
|
525
|
-
|
|
599
|
+
def set_axes_equal(ax: plt.Axes) -> None:
|
|
600
|
+
"""Sets 3D plot axes to equal scale.
|
|
526
601
|
|
|
602
|
+
Make axes of 3D plot have equal scale so that spheres appear as spheres and cubes as cubes. Required since
|
|
603
|
+
`ax.axis('equal')` and `ax.set_aspect('equal')` don't work on 3D.
|
|
527
604
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
Make axes of 3D plot have equal scale so that spheres appear as
|
|
532
|
-
spheres and cubes as cubes. Required since `ax.axis('equal')`
|
|
533
|
-
and `ax.set_aspect('equal')` don't work on 3D.
|
|
605
|
+
The aspect rati0 of the plots is not equal by default. This solution was given in Stack Overflow:
|
|
606
|
+
https://stackoverflow.com/a/63625222/4609203
|
|
534
607
|
"""
|
|
608
|
+
|
|
535
609
|
limits = np.array([ax.get_xlim3d(), ax.get_ylim3d(), ax.get_zlim3d()])
|
|
536
610
|
origin = np.mean(limits, axis=1)
|
|
537
611
|
radius = 0.5 * np.max(np.abs(limits[:, 1] - limits[:, 0]))
|
|
@@ -541,23 +615,33 @@ def set_axes_equal(ax: plt.Axes):
|
|
|
541
615
|
ax.set_zlim3d([z - radius, z + radius])
|
|
542
616
|
|
|
543
617
|
|
|
544
|
-
def draw_frame_3d(ax: Axes3D, frame
|
|
618
|
+
def draw_frame_3d(ax: Axes3D, frame, **kwargs) -> None:
|
|
619
|
+
"""Draws the given frame in 3D.
|
|
620
|
+
|
|
621
|
+
Args:
|
|
622
|
+
ax (Axes3D): Axis to draw the frame in.
|
|
623
|
+
frame (ReferenceFrame): Reference frame to draw.
|
|
624
|
+
**kwargs: Keyword arguments to pass to the quiver function.
|
|
625
|
+
"""
|
|
626
|
+
|
|
545
627
|
master = frame.find_master()
|
|
546
628
|
|
|
547
|
-
f0 = frame.
|
|
548
|
-
fx = frame.
|
|
549
|
-
fy = frame.
|
|
550
|
-
fz = frame.
|
|
551
|
-
f0m = f0.
|
|
552
|
-
fxm = fx.
|
|
553
|
-
fym = fy.
|
|
554
|
-
fzm = fz.
|
|
555
|
-
|
|
556
|
-
# Origin of the
|
|
557
|
-
# Every vector independently (
|
|
629
|
+
f0 = frame.get_origin()
|
|
630
|
+
fx = frame.get_axis("x", name="fx")
|
|
631
|
+
fy = frame.get_axis("y", name="fy")
|
|
632
|
+
fz = frame.get_axis("z", name="fz")
|
|
633
|
+
f0m = f0.express_in(master)[:3]
|
|
634
|
+
fxm = fx.express_in(master)[:3]
|
|
635
|
+
fym = fy.express_in(master)[:3]
|
|
636
|
+
fzm = fz.express_in(master)[:3]
|
|
637
|
+
|
|
638
|
+
# Origin of the x, y. and z vectors (x = the 'x' coordinates of the origin of all 3 vectors)
|
|
639
|
+
# Every vector independently (-> plot in different colours)
|
|
640
|
+
|
|
558
641
|
x, y, z = np.array([f0m[0]]), np.array([f0m[1]]), np.array([f0m[2]])
|
|
559
642
|
|
|
560
|
-
# Orientation of the
|
|
643
|
+
# Orientation of the x, y, and z vectors
|
|
644
|
+
|
|
561
645
|
vecxx, vecyx, veczx = (
|
|
562
646
|
np.array([fxm[0] - f0m[0]]),
|
|
563
647
|
np.array([fym[0] - f0m[0]]),
|
|
@@ -585,7 +669,16 @@ def draw_frame_3d(ax: Axes3D, frame: ReferenceFrame, DEFAULT_AXIS_LENGTH=100, **
|
|
|
585
669
|
ax.text(f0m[0] + offset, f0m[1] + offset, f0m[2] + offset, frame.name)
|
|
586
670
|
|
|
587
671
|
|
|
588
|
-
def draw_frame(ax
|
|
672
|
+
def draw_frame(ax: plt.Axes, reference_frame, plane="xz", default_axis_length: int = 100) -> None:
|
|
673
|
+
"""Draws the given plane from the given reference frame in the given axis.
|
|
674
|
+
|
|
675
|
+
Args:
|
|
676
|
+
ax (plt.Axes): Axis to draw the plane in.
|
|
677
|
+
reference_frame (ReferenceFrame): Reference frame to draw.
|
|
678
|
+
plane (str, optional): Kind of plane to draw. Must be in ["xy", "yz", "zx"].
|
|
679
|
+
default_axis_length (int): Axis length.
|
|
680
|
+
"""
|
|
681
|
+
|
|
589
682
|
fig = ax.get_figure()
|
|
590
683
|
|
|
591
684
|
# FC : Figure coordinates (pixels)
|
|
@@ -604,8 +697,8 @@ def draw_frame(ax, frame: ReferenceFrame, plane="xz", DEFAULT_AXIS_LENGTH=100):
|
|
|
604
697
|
|
|
605
698
|
# Draw the origin
|
|
606
699
|
|
|
607
|
-
origin =
|
|
608
|
-
origin_in_master = origin.
|
|
700
|
+
origin = reference_frame.get_origin()
|
|
701
|
+
origin_in_master = origin.express_in(reference_frame.find_master())
|
|
609
702
|
|
|
610
703
|
ax.scatter([origin_in_master[x_idx]], [origin_in_master[y_idx]], color="k")
|
|
611
704
|
|
|
@@ -614,13 +707,13 @@ def draw_frame(ax, frame: ReferenceFrame, plane="xz", DEFAULT_AXIS_LENGTH=100):
|
|
|
614
707
|
origin_dc = np.array([[origin_in_master[x_idx], origin_in_master[y_idx]]])
|
|
615
708
|
|
|
616
709
|
point = dc2fc(origin_dc[0])
|
|
617
|
-
point[0] +=
|
|
710
|
+
point[0] += default_axis_length
|
|
618
711
|
target_dc = np.append(origin_dc, [fc2dc(point)], axis=0)
|
|
619
712
|
|
|
620
713
|
ax.plot(target_dc[:, 0], target_dc[:, 1], color="k")
|
|
621
714
|
|
|
622
715
|
point = dc2fc(origin_dc[0])
|
|
623
|
-
point[1] +=
|
|
716
|
+
point[1] += default_axis_length
|
|
624
717
|
target_dc = np.append(origin_dc, [fc2dc(point)], axis=0)
|
|
625
718
|
|
|
626
719
|
ax.plot(target_dc[:, 0], target_dc[:, 1], color="k")
|
|
@@ -630,17 +723,23 @@ def draw_frame(ax, frame: ReferenceFrame, plane="xz", DEFAULT_AXIS_LENGTH=100):
|
|
|
630
723
|
dx, dy = 10 / fig.dpi, 10 / fig.dpi
|
|
631
724
|
offset = ScaledTranslation(dx, dy, fig.dpi_scale_trans)
|
|
632
725
|
point = dc2ndc(origin_dc[0])
|
|
633
|
-
plt.text(point[0], point[1],
|
|
726
|
+
plt.text(point[0], point[1], reference_frame.name, transform=ax.transAxes + offset)
|
|
727
|
+
|
|
634
728
|
|
|
729
|
+
def define_the_initial_setup() -> ReferenceFrameModel:
|
|
730
|
+
"""Defines the initial setup of the reference frame model.
|
|
731
|
+
|
|
732
|
+
Returns:
|
|
733
|
+
Reference frame model with the initial setup.
|
|
734
|
+
"""
|
|
635
735
|
|
|
636
|
-
def define_the_initial_setup():
|
|
637
736
|
model = ReferenceFrameModel()
|
|
638
737
|
|
|
639
738
|
model.add_master_frame()
|
|
640
|
-
model.add_frame("A", translation=[2, 2, 2], rotation=[0, 0, 0],
|
|
641
|
-
model.add_frame("B", translation=[-2, 2, 2], rotation=[0, 0, 0],
|
|
642
|
-
model.add_frame("C", translation=[2, 2, 5], rotation=[0, 0, 0],
|
|
643
|
-
model.add_frame("D", translation=[2, 2, 2], rotation=[0, 0, 0],
|
|
739
|
+
model.add_frame("A", translation=[2, 2, 2], rotation=[0, 0, 0], reference="Master")
|
|
740
|
+
model.add_frame("B", translation=[-2, 2, 2], rotation=[0, 0, 0], reference="Master")
|
|
741
|
+
model.add_frame("C", translation=[2, 2, 5], rotation=[0, 0, 0], reference="A")
|
|
742
|
+
model.add_frame("D", translation=[2, 2, 2], rotation=[0, 0, 0], reference="B")
|
|
644
743
|
|
|
645
744
|
model.add_link("A", "B")
|
|
646
745
|
model.add_link("B", "C")
|
|
@@ -651,28 +750,37 @@ def define_the_initial_setup():
|
|
|
651
750
|
return model
|
|
652
751
|
|
|
653
752
|
|
|
654
|
-
def get_vectors(
|
|
655
|
-
"""
|
|
656
|
-
get_vectors(rf1,rf2, model)
|
|
657
|
-
:param rf1: string : name of ref. frame "from"
|
|
658
|
-
:param rf2: string : name of ref. frame "to"
|
|
659
|
-
:param model: CSLReferenceFrameModel containing rf1 and rf2
|
|
660
|
-
:return: translation and rotation vectors from rf1 to rf2
|
|
661
|
-
"""
|
|
662
|
-
return model.get_frame(rf1).getActiveTranslationRotationVectorsTo(model.get_frame(rf2))
|
|
753
|
+
def get_vectors(reference_frame_1, reference_frame_2, model: ReferenceFrameModel) -> tuple[np.ndarray, np.ndarray]:
|
|
754
|
+
"""Returns the translation and rotation vectors for the active transformation for the 1st reference frame to the 2nd.
|
|
663
755
|
|
|
756
|
+
Args:
|
|
757
|
+
reference_frame_1 (str): Name of the reference frame to get the active transformation from.
|
|
758
|
+
reference_frame_2 (str): Name of the reference frame to get the active transformation to.
|
|
759
|
+
model (ReferenceFrameModel): Model containing the reference frames with the given names.
|
|
664
760
|
|
|
665
|
-
|
|
761
|
+
Returns:
|
|
762
|
+
Translation and rotation vectors from the active transformation from the 1st reference frame to the 2nd.
|
|
666
763
|
"""
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
764
|
+
|
|
765
|
+
return model.get_frame(reference_frame_1).get_active_translation_rotation_vectors_to(
|
|
766
|
+
model.get_frame(reference_frame_2)
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def print_vectors(reference_frame_1: str, reference_frame_2: str, model: ReferenceFrameModel) -> None:
|
|
771
|
+
"""Prints the translation and rotation vectors for the active transformation for the 1st reference frame to the 2nd.
|
|
772
|
+
|
|
773
|
+
Args:
|
|
774
|
+
reference_frame_1 (str): Name of the reference frame to get the active transformation from.
|
|
775
|
+
reference_frame_2 (str): Name of the reference frame to get the active transformation to.
|
|
776
|
+
model (ReferenceFrameModel): Model containing the reference frames with the given names.
|
|
672
777
|
"""
|
|
673
|
-
|
|
778
|
+
|
|
779
|
+
trans, rot = model.get_frame(reference_frame_1).get_active_translation_rotation_vectors_to(
|
|
780
|
+
model.get_frame(reference_frame_2)
|
|
781
|
+
)
|
|
674
782
|
print(
|
|
675
|
-
f"{
|
|
783
|
+
f"{reference_frame_1:8s} -> {reference_frame_2:8s} : Trans [{trans[0]:11.4e}, {trans[1]:11.4e}, {trans[2]:11.4e}] Rot [{rot[0]:11.4e}, {rot[1]:11.4e}, {rot[2]:11.4e}]"
|
|
676
784
|
)
|
|
677
785
|
return
|
|
678
786
|
|