cgse-coordinates 0.17.3__tar.gz → 0.17.4__tar.gz
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-0.17.3 → cgse_coordinates-0.17.4}/PKG-INFO +1 -1
- {cgse_coordinates-0.17.3 → cgse_coordinates-0.17.4}/pyproject.toml +3 -3
- cgse_coordinates-0.17.4/src/cgse_coordinates/settings.yaml +0 -0
- cgse_coordinates-0.17.4/src/egse/coordinates/__init__.py +229 -0
- cgse_coordinates-0.17.4/src/egse/coordinates/avoidance.py +91 -0
- cgse_coordinates-0.17.4/src/egse/coordinates/cslmodel.py +118 -0
- {cgse_coordinates-0.17.3 → cgse_coordinates-0.17.4}/src/egse/coordinates/laser_tracker_to_dict.py +16 -25
- cgse_coordinates-0.17.4/src/egse/coordinates/point.py +833 -0
- cgse_coordinates-0.17.4/src/egse/coordinates/pyplot.py +203 -0
- cgse_coordinates-0.17.4/src/egse/coordinates/reference_frame.py +1417 -0
- cgse_coordinates-0.17.4/src/egse/coordinates/refmodel.py +828 -0
- cgse_coordinates-0.17.4/src/egse/coordinates/rotation_matrix.py +95 -0
- cgse_coordinates-0.17.4/src/egse/coordinates/transform3d_addon.py +501 -0
- cgse_coordinates-0.17.3/src/cgse_coordinates/settings.yaml +0 -16
- cgse_coordinates-0.17.3/src/egse/coordinates/__init__.py +0 -536
- cgse_coordinates-0.17.3/src/egse/coordinates/avoidance.py +0 -99
- cgse_coordinates-0.17.3/src/egse/coordinates/cslmodel.py +0 -127
- cgse_coordinates-0.17.3/src/egse/coordinates/point.py +0 -707
- cgse_coordinates-0.17.3/src/egse/coordinates/pyplot.py +0 -191
- cgse_coordinates-0.17.3/src/egse/coordinates/referenceFrame.py +0 -1251
- cgse_coordinates-0.17.3/src/egse/coordinates/refmodel.py +0 -720
- cgse_coordinates-0.17.3/src/egse/coordinates/rotationMatrix.py +0 -82
- cgse_coordinates-0.17.3/src/egse/coordinates/transform3d_addon.py +0 -437
- {cgse_coordinates-0.17.3 → cgse_coordinates-0.17.4}/.gitignore +0 -0
- {cgse_coordinates-0.17.3 → cgse_coordinates-0.17.4}/README.md +0 -0
- {cgse_coordinates-0.17.3 → cgse_coordinates-0.17.4}/src/cgse_coordinates/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cgse-coordinates
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.4
|
|
4
4
|
Summary: Reference Frames and Coordinate Transofrmations for CGSE
|
|
5
5
|
Author: IvS KU Leuven
|
|
6
6
|
Maintainer-email: Rik Huygen <rik.huygen@kuleuven.be>, Sara Regibo <sara.regibo@kuleuven.be>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cgse-coordinates"
|
|
3
|
-
version = "0.17.
|
|
3
|
+
version = "0.17.4"
|
|
4
4
|
description = "Reference Frames and Coordinate Transofrmations for CGSE"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "IvS KU Leuven"}
|
|
@@ -28,8 +28,8 @@ dependencies = [
|
|
|
28
28
|
[project.entry-points."cgse.version"]
|
|
29
29
|
cgse-coordinates = 'egse.version:get_version_installed'
|
|
30
30
|
|
|
31
|
-
[project.entry-points."cgse.settings"]
|
|
32
|
-
cgse-coordinates = "cgse_coordinates:settings.yaml"
|
|
31
|
+
#[project.entry-points."cgse.settings"]
|
|
32
|
+
#cgse-coordinates = "cgse_coordinates:settings.yaml"
|
|
33
33
|
|
|
34
34
|
[tool.pytest.ini_options]
|
|
35
35
|
pythonpath = "src"
|
|
File without changes
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import logging
|
|
3
|
+
import re
|
|
4
|
+
from typing import Dict
|
|
5
|
+
from typing import List
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from typing import Union
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from egse.setup import navdict
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def dict_to_ref_model(model_def: Union[Dict, List]) -> navdict:
|
|
16
|
+
"""Creates a reference frames model from a dictionary or list of reference frame definitions.
|
|
17
|
+
|
|
18
|
+
When a list is provided, the items in the list must be ReferenceFrames.
|
|
19
|
+
|
|
20
|
+
The reference frame definitions are usually read from a YAML file or returned by a Setup, but can also be just
|
|
21
|
+
ReferenceFrame objects.
|
|
22
|
+
|
|
23
|
+
ReferenceFrame definitions have the following format:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
ReferenceFrame://(<definition>)
|
|
27
|
+
```
|
|
28
|
+
where `<definition>` has the following elements, separated by '` | `':
|
|
29
|
+
* a translation matrix
|
|
30
|
+
* a rotation matrix
|
|
31
|
+
* the name of the reference frame
|
|
32
|
+
* the name of the reference for this reference frame
|
|
33
|
+
* a dictionary of links
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
model_def (dict or list): Definition of the reference model.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Dictionary representing the reference frames model.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
ref_model = navdict()
|
|
43
|
+
ref_links = {}
|
|
44
|
+
|
|
45
|
+
from egse.coordinates.reference_frame import ReferenceFrame
|
|
46
|
+
|
|
47
|
+
def create_ref_frame(name, data) -> Union[ReferenceFrame, str]:
|
|
48
|
+
# This is a recursive function that creates a reference frame based on the given data.
|
|
49
|
+
# * When the data is already a ReferenceFrame, it just returns data
|
|
50
|
+
# * When data starts with the special string `ReferenceFrame//`, the data string is parsed
|
|
51
|
+
# and a corresponding ReferenceFrame is returned
|
|
52
|
+
# * When there is no match, the data is returned unaltered.
|
|
53
|
+
#
|
|
54
|
+
# SIDE EFFECT:
|
|
55
|
+
# * In the process, the outer ref-model and ref_links are updated.
|
|
56
|
+
|
|
57
|
+
if isinstance(data, ReferenceFrame):
|
|
58
|
+
return data
|
|
59
|
+
|
|
60
|
+
match = re.match(r"ReferenceFrame//\((.*)\)$", data)
|
|
61
|
+
if not match:
|
|
62
|
+
return data
|
|
63
|
+
|
|
64
|
+
translation, rotation, name, ref_name, links = match[1].split(" | ")
|
|
65
|
+
|
|
66
|
+
# All links are processed later
|
|
67
|
+
|
|
68
|
+
ref_links[name] = ast.literal_eval(links)
|
|
69
|
+
|
|
70
|
+
if ref_name == name == "Master":
|
|
71
|
+
ref_model.add(ref_name, ReferenceFrame.create_master())
|
|
72
|
+
return ref_model["Master"]
|
|
73
|
+
|
|
74
|
+
if ref_name not in ref_model:
|
|
75
|
+
ref_model.add(ref_name, create_ref_frame(ref_name, model_def[ref_name]))
|
|
76
|
+
|
|
77
|
+
ref_frame = ReferenceFrame.from_translation_rotation(
|
|
78
|
+
deserialize_array(translation),
|
|
79
|
+
deserialize_array(rotation),
|
|
80
|
+
name=name,
|
|
81
|
+
reference_frame=ref_model[ref_name],
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return ref_frame
|
|
85
|
+
|
|
86
|
+
# if the given model_def is a list, turn it into a dict
|
|
87
|
+
|
|
88
|
+
if isinstance(model_def, list):
|
|
89
|
+
model_def = {frame.name: frame for frame in model_def}
|
|
90
|
+
|
|
91
|
+
for key, value in model_def.items():
|
|
92
|
+
if key not in ref_model:
|
|
93
|
+
ref_model.add(key, create_ref_frame(key, value))
|
|
94
|
+
|
|
95
|
+
# Process all the links
|
|
96
|
+
|
|
97
|
+
for ref_name, link_names in ref_links.items():
|
|
98
|
+
ref = ref_model[ref_name]
|
|
99
|
+
for link_name in link_names:
|
|
100
|
+
if link_name not in ref.linked_to:
|
|
101
|
+
ref.add_link(ref_model[link_name])
|
|
102
|
+
|
|
103
|
+
return ref_model
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def ref_model_to_dict(ref_model) -> navdict:
|
|
107
|
+
"""Creates a dictionary with reference frames definitions that define a reference model.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
ref_model: A dictionary representing the reference frames model or a list of reference frames.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Dictionary of reference frame definitions.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
if isinstance(ref_model, dict):
|
|
117
|
+
ref_model = ref_model.values()
|
|
118
|
+
|
|
119
|
+
# take each key (which is a reference frame) and serialize it
|
|
120
|
+
|
|
121
|
+
model_def = {}
|
|
122
|
+
|
|
123
|
+
for ref in ref_model:
|
|
124
|
+
translation, rotation = ref.get_translation_rotation_vectors()
|
|
125
|
+
links = [ref.name for ref in ref.linked_to]
|
|
126
|
+
model_def[ref.name] = (
|
|
127
|
+
f"ReferenceFrame//("
|
|
128
|
+
f"{serialize_array(translation, precision=6)} | "
|
|
129
|
+
f"{serialize_array(rotation, precision=6)} | "
|
|
130
|
+
f"{ref.name} | "
|
|
131
|
+
f"{ref.reference_frame.name} | "
|
|
132
|
+
f"{links})"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return navdict(model_def)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def serialize_array(arr: Union[np.ndarray, list], precision: int = 4) -> str:
|
|
139
|
+
"""Returns a string representation of a numpy array.
|
|
140
|
+
|
|
141
|
+
>>> serialize_array([1,2,3])
|
|
142
|
+
'[1, 2, 3]'
|
|
143
|
+
>>> serialize_array([[1,2,3], [4,5,6]])
|
|
144
|
+
'[[1, 2, 3], [4, 5, 6]]'
|
|
145
|
+
>>> serialize_array([[1,2.2,3], [4.3,5,6]])
|
|
146
|
+
'[[1.0000, 2.2000, 3.0000], [4.3000, 5.0000, 6.0000]]'
|
|
147
|
+
>>> serialize_array([[1,2.2,3], [4.3,5,6]], precision=2)
|
|
148
|
+
'[[1.00, 2.20, 3.00], [4.30, 5.00, 6.00]]'
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
arr: One- or-two dimensional numpy array or list.
|
|
152
|
+
precision (int): number of digits of precision
|
|
153
|
+
Returns:
|
|
154
|
+
A string representing the input array.
|
|
155
|
+
"""
|
|
156
|
+
if isinstance(arr, list):
|
|
157
|
+
arr = np.array(arr)
|
|
158
|
+
msg = np.array2string(
|
|
159
|
+
arr,
|
|
160
|
+
separator=", ",
|
|
161
|
+
suppress_small=True,
|
|
162
|
+
formatter={"float_kind": lambda x: f"{x:.{precision}f}"},
|
|
163
|
+
).replace("\n", "")
|
|
164
|
+
return msg
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def deserialize_array(arr_str: str) -> Optional[np.ndarray]:
|
|
168
|
+
"""Returns a numpy array from the given string.
|
|
169
|
+
|
|
170
|
+
The input string is interpreted as a one or two-dimensional array, with commas or spaces separating the columns,
|
|
171
|
+
and semicolons separating the rows.
|
|
172
|
+
|
|
173
|
+
>>> deserialize_array('1,2,3')
|
|
174
|
+
array([1, 2, 3])
|
|
175
|
+
>>> deserialize_array('1 2 3')
|
|
176
|
+
array([1, 2, 3])
|
|
177
|
+
>>> deserialize_array('1,2,3;4,5,6')
|
|
178
|
+
array([[1, 2, 3],
|
|
179
|
+
[4, 5, 6]])
|
|
180
|
+
>>> deserialize_array("[[1,2,3], [4,5,6]]")
|
|
181
|
+
array([[1, 2, 3],
|
|
182
|
+
[4, 5, 6]])
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
arr_str: String representation of a numpy array.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
One- or two-dimensional numpy array or `None` when input string cannot be parsed into a numpy array.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
import re
|
|
192
|
+
|
|
193
|
+
arr_str = re.sub(r"\],\s*\[", "];[", arr_str)
|
|
194
|
+
try:
|
|
195
|
+
arr = np.array(_convert_from_string(arr_str))
|
|
196
|
+
return arr if ";" in arr_str else arr.flatten()
|
|
197
|
+
except ValueError as exc:
|
|
198
|
+
logger.error(f"Input string could not be parsed into a numpy array: {exc}")
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _convert_from_string(data: str) -> list[list]:
|
|
203
|
+
# This function was copied from:
|
|
204
|
+
# https://github.com/numpy/numpy/blob/v1.19.0/numpy/matrixlib/defmatrix.py#L14
|
|
205
|
+
# We include the function here because the np.matrix class is deprecated and will be removed.
|
|
206
|
+
# This function is what we actually needed from np.matrix.
|
|
207
|
+
|
|
208
|
+
# This function can be replaced with np.fromstring()
|
|
209
|
+
|
|
210
|
+
for char in "[]":
|
|
211
|
+
data = data.replace(char, "")
|
|
212
|
+
|
|
213
|
+
rows = data.split(";")
|
|
214
|
+
new_data = []
|
|
215
|
+
count = 0
|
|
216
|
+
for row in rows:
|
|
217
|
+
trow = row.split(",")
|
|
218
|
+
new_row = []
|
|
219
|
+
for col in trow:
|
|
220
|
+
temp = col.split()
|
|
221
|
+
new_row.extend(map(ast.literal_eval, temp))
|
|
222
|
+
if count == 0:
|
|
223
|
+
n_cols = len(new_row)
|
|
224
|
+
elif len(new_row) != n_cols:
|
|
225
|
+
raise ValueError("Rows not the same size.")
|
|
226
|
+
count += 1
|
|
227
|
+
new_data.append(new_row)
|
|
228
|
+
|
|
229
|
+
return new_data
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from egse.coordinates.point import Points
|
|
4
|
+
from egse.coordinates.reference_frame import ReferenceFrame
|
|
5
|
+
from egse.setup import Setup, load_setup
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def is_avoidance_ok(hexusr: ReferenceFrame, hexobj: ReferenceFrame, setup: Setup = None, verbose: bool = False):
|
|
9
|
+
"""Checks whether the FPA is outside the avoidance volume around L6.
|
|
10
|
+
|
|
11
|
+
This function is used to verify that a requested movement of the PUNA hexapod will not cause the FPA to enter the
|
|
12
|
+
avoidance volume around L6.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
hexusr (ReferenceFrame): User Reference Frame for the PUNA hexapod. Its xy-plane corresponds to the maximum
|
|
17
|
+
height of FPA_SEN. Its z-axis points away from the FPA.
|
|
18
|
+
hexobj (ReferenceFrame): Object Reference Frame for the PUNA hexapod. Its xy-plane coincides with FPA_SEN. Its
|
|
19
|
+
z-axis points towards L6.
|
|
20
|
+
setup (Setup): Setup object containing the default reference frames.
|
|
21
|
+
verbose (bool): Indicates whether to print verbose output.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
True if the FPA is outside the avoidance volume around L6; False otherwise.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
setup = setup or load_setup()
|
|
28
|
+
|
|
29
|
+
# A. HORIZONTAL AVOIDANCE
|
|
30
|
+
# Ensure that the centre of L6, materialised by HEX_USR (incl. z-direction security wrt TOU_L6) stays within a
|
|
31
|
+
# given radius of the origin of FPA_SEN
|
|
32
|
+
|
|
33
|
+
# Clearance = the tolerance in every horizontal direction (3 mm; PLATO-KUL-PL-ICD-0001 v1.2)
|
|
34
|
+
clearance_xy = setup.camera.fpa.avoidance.clearance_xy
|
|
35
|
+
|
|
36
|
+
# Projection of the origin of HEX_USR on the xy-plane of FPA_SEN
|
|
37
|
+
l6xy = hexusr.get_origin().express_in(hexobj)[:2]
|
|
38
|
+
|
|
39
|
+
# !! This is a verification of the current situation
|
|
40
|
+
# -> need to replace by a simulation of the forthcoming movement in the building block
|
|
41
|
+
horizontal_check = (l6xy[0] ** 2.0 + l6xy[1] ** 2.0) < clearance_xy * clearance_xy
|
|
42
|
+
|
|
43
|
+
# B. VERTICAL AVOIDANCE
|
|
44
|
+
# Ensure that the CCD never hits L6.
|
|
45
|
+
# - The definition of HEX_USR includes a tolerance below L6 (1.65 mm).
|
|
46
|
+
# - We include a tolerance above FPA_SEN here (0.3 mm).
|
|
47
|
+
# - We define a collection of points to act at the vertices. of the avoidance volume above the FPA
|
|
48
|
+
|
|
49
|
+
# Clearance = vertical uncertainty on the CCD location (0.3 mm; PLATO-KUL-PL-ICD-0001 v1.2)
|
|
50
|
+
clearance_z = setup.camera.fpa.avoidance.clearance_z
|
|
51
|
+
|
|
52
|
+
# Vertices = Points representing the vertices of the avoidance volume above the FPA (60)
|
|
53
|
+
vertices_nb = setup.camera.fpa.avoidance.vertices_nb
|
|
54
|
+
# All vertices are on a circle of radius 'vertices_radius' (100 mm)
|
|
55
|
+
vertices_radius = setup.camera.fpa.avoidance.vertices_radius
|
|
56
|
+
|
|
57
|
+
angles = np.linspace(0, np.pi * 2, vertices_nb, endpoint=False)
|
|
58
|
+
vertices_x = np.cos(angles) * vertices_radius
|
|
59
|
+
vertices_y = np.sin(angles) * vertices_radius
|
|
60
|
+
vertices_z = np.ones_like(angles) * clearance_z
|
|
61
|
+
|
|
62
|
+
# The collection of points defining the avoidance volume around FPA_SEN
|
|
63
|
+
|
|
64
|
+
vert_obj = Points(
|
|
65
|
+
coordinates=np.array([vertices_x, vertices_y, vertices_z]), reference_frame=hexobj, name="vert_obj"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Their coordinates in HEX_USR
|
|
69
|
+
# NB: vert_obj is a Points object, vert_usr is an array
|
|
70
|
+
vert_usr = vert_obj.express_in(hexusr)
|
|
71
|
+
|
|
72
|
+
# !! Same as above : this is verifying the current situation, not the one after a planned movement
|
|
73
|
+
# Verify that all vertices ("protecting" FPA_SEN) are below the x-y plane of HEX_USR ("protecting" L6)
|
|
74
|
+
vertical_check = np.all(vert_usr[2, :] < 0.0)
|
|
75
|
+
|
|
76
|
+
if verbose:
|
|
77
|
+
printdict = {True: "OK", False: "NOT OK"}
|
|
78
|
+
print(f"HORIZONTAL AVOIDANCE: {printdict[horizontal_check]}")
|
|
79
|
+
print(f" VERTICAL AVOIDANCE: {printdict[vertical_check]}")
|
|
80
|
+
|
|
81
|
+
if verbose > 1:
|
|
82
|
+
print(f"Points Coordinates")
|
|
83
|
+
coobj = vert_obj.coordinates
|
|
84
|
+
for i in range(vertices_nb):
|
|
85
|
+
print(f"{i} OBJ {np.round(coobj[:3, i], 6)} --> USR {np.round(vert_usr[:3, i], 6)}")
|
|
86
|
+
vert_z = vert_usr[2, :]
|
|
87
|
+
vert_zi = np.where(vert_z == np.max(vert_z))
|
|
88
|
+
print(f"#vertices at max z : {len(vert_zi[0])}")
|
|
89
|
+
print(f"First one: vertex {vert_zi[0][0]} : {np.round(vert_usr[:3, vert_zi[0][0]], 6)}")
|
|
90
|
+
|
|
91
|
+
return horizontal_check and vertical_check
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A CSL reference frame model which has knowledge about the CSL Setup, and the PUNA Hexapod model.
|
|
3
|
+
|
|
4
|
+
The CSL Reference Frame Model incorporates a Hexapod PUNA model which is represented by the
|
|
5
|
+
Reference Frames HEXUSR, HEXOBJ, HEXMEC, and HEXPLT. A number of methods are defined here that
|
|
6
|
+
assume these four reference frames exist in the model and behave like a proper hexapod simulator.
|
|
7
|
+
Those methods start with the name `hexapod_`, e.g. `hexapod_goto_zero_position()`.
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
from egse.coordinates.refmodel import ReferenceFrameModel
|
|
13
|
+
|
|
14
|
+
HEXUSR = "hexusr"
|
|
15
|
+
HEXMEC = "hexmec"
|
|
16
|
+
HEXOBJ = "hexobj"
|
|
17
|
+
HEXPLT = "hexplt"
|
|
18
|
+
HEXOBUSR = "hexobusr"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CSLReferenceFrameModel(ReferenceFrameModel):
|
|
22
|
+
"""
|
|
23
|
+
The CSL reference Frame Model is a specific reference model that adds convenience methods for manipulating the
|
|
24
|
+
Hexapod PUNA which is part of the overall CSL Setup.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
_DEGREES_DEFAULT = ReferenceFrameModel._DEGREES_DEFAULT
|
|
28
|
+
|
|
29
|
+
def _create_obusr(self) -> None:
|
|
30
|
+
"""Creates the Object User Reference Frame if it does not exist yet."""
|
|
31
|
+
|
|
32
|
+
if HEXOBUSR in self:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
hexusr = self.get_frame(HEXUSR)
|
|
36
|
+
hexobj = self.get_frame(HEXOBJ)
|
|
37
|
+
|
|
38
|
+
transformation = hexusr.get_active_transformation_to(hexobj)
|
|
39
|
+
|
|
40
|
+
self.add_frame(HEXOBUSR, transformation=transformation, reference=HEXUSR)
|
|
41
|
+
self.add_link(HEXOBUSR, HEXOBJ)
|
|
42
|
+
|
|
43
|
+
def hexapod_move_absolute(self, translation: np.ndarray, rotation: np.ndarray, degrees: bool = _DEGREES_DEFAULT):
|
|
44
|
+
"""Moves/defines the Object Coordinate System expressed in the invariant User Coordinate System.
|
|
45
|
+
|
|
46
|
+
The rotation centre coincides with the Object Coordinates System origin and the movements are controlled with
|
|
47
|
+
translation components first (Tx, Ty, tZ) and then the rotation components (Rx, Ry, Rz).
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
translation (np.ndarray): Translation vector.
|
|
51
|
+
rotation (np.ndarray): Rotation vector.
|
|
52
|
+
degrees (bool): Indicates whether the rotation angles are specified in degrees, rather than radians.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
self.move_absolute_self(HEXOBUSR, translation, rotation, degrees=degrees)
|
|
56
|
+
|
|
57
|
+
def hexapod_move_relative_object(
|
|
58
|
+
self, translation: np.ndarray, rotation: np.ndarray, degrees: bool = _DEGREES_DEFAULT
|
|
59
|
+
):
|
|
60
|
+
"""Moves the object relative to its current position and orientation
|
|
61
|
+
|
|
62
|
+
The relative movement is expressed in the object coordinate system.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
translation (np.ndarray): Translation vector.
|
|
66
|
+
rotation (np.ndarray): Rotation vector.
|
|
67
|
+
degrees (bool): Indicates whether the rotation angles are specified in degrees, rather than radians.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
self.move_relative_self(HEXOBJ, translation, rotation, degrees=degrees)
|
|
71
|
+
|
|
72
|
+
def hexapod_move_relative_user(
|
|
73
|
+
self, translation: np.ndarray, rotation: np.ndarray, degrees: bool = _DEGREES_DEFAULT
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Moves the object relative to its current object position and orientation.
|
|
76
|
+
|
|
77
|
+
The relative movement is expressed in the (invariant) user coordinate system.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
translation (np.ndarray): Translation vector.
|
|
81
|
+
rotation (np.ndarray): Rotation vector.
|
|
82
|
+
degrees (bool): Indicates whether the rotation angles are specified in degrees, rather than radians.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
self.move_relative_other_local(HEXOBJ, HEXUSR, translation, rotation, degrees=degrees)
|
|
86
|
+
|
|
87
|
+
def hexapod_configure_coordinates(
|
|
88
|
+
self,
|
|
89
|
+
usr_trans: np.ndarray,
|
|
90
|
+
usr_rot: np.ndarray,
|
|
91
|
+
obj_trans: np.ndarray,
|
|
92
|
+
obj_rot: np.ndarray,
|
|
93
|
+
) -> None:
|
|
94
|
+
"""Changes the definition of the User Coordinate System and the Object Coordinate System in the hexapod.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
usr_trans (np.ndarray): Translation vector used to define the User Coordinate System relative to the Machine Coordinate System.
|
|
98
|
+
usr_rot (np.ndarray): Rotation vector used to define the User Coordinate System relative to the Machine Coordinate System.
|
|
99
|
+
obj_trans (np.ndarray): Translation vector used to define the Object Coordinate System relative to the Platform Coordinate System.
|
|
100
|
+
obj_rot (np.ndarray): Rotation vector used to define the Object Coordinate System relative to the Platoform Coordinate System.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
self.remove_link(HEXUSR, HEXMEC)
|
|
104
|
+
self.remove_link(HEXOBJ, HEXPLT)
|
|
105
|
+
self.get_frame(HEXUSR).set_translation_rotation(usr_trans, usr_rot)
|
|
106
|
+
self.get_frame(HEXOBJ).set_translation_rotation(obj_trans, obj_rot)
|
|
107
|
+
self.add_link(HEXUSR, HEXMEC)
|
|
108
|
+
self.add_link(HEXOBJ, HEXPLT)
|
|
109
|
+
|
|
110
|
+
def hexapod_goto_zero_position(self) -> None:
|
|
111
|
+
"""Instructs the hexapod to go to its zero position"""
|
|
112
|
+
|
|
113
|
+
self.move_absolute_self(HEXPLT, translation=np.array([0, 0, 0]), rotation=np.array([0, 0, 0]))
|
|
114
|
+
|
|
115
|
+
def hexapod_goto_retracted_position(self) -> None:
|
|
116
|
+
"""Instructs the hexapod to go to its retracted position."""
|
|
117
|
+
|
|
118
|
+
self.move_absolute_self(HEXPLT, translation=np.array([0, 0, -20]), rotation=np.array([0, 0, 0]))
|
{cgse_coordinates-0.17.3 → cgse_coordinates-0.17.4}/src/egse/coordinates/laser_tracker_to_dict.py
RENAMED
|
@@ -1,28 +1,15 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Created on Sat Oct 3 11:53:23 2020
|
|
5
|
-
|
|
6
|
-
@author: pierre
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
1
|
import sys
|
|
10
2
|
import pandas
|
|
11
3
|
|
|
12
4
|
from egse.setup import Setup
|
|
13
5
|
|
|
14
6
|
|
|
15
|
-
def laser_tracker_to_dict(
|
|
16
|
-
"""
|
|
17
|
-
laser_tracker_to_dict(filexls)
|
|
18
|
-
|
|
19
|
-
INPUT
|
|
20
|
-
|
|
21
|
-
filexls : CSL - provided excell file (from a laser tracker)
|
|
22
|
-
|
|
23
|
-
OUTPUT
|
|
7
|
+
def laser_tracker_to_dict(file_xls: str, setup: Setup):
|
|
8
|
+
"""Reads the laser tracker file and returns a dictionary of reference frames.
|
|
24
9
|
|
|
25
|
-
|
|
10
|
+
Args:
|
|
11
|
+
file_xls (str): Path to the laser tracker file. This is an excell sheet provided by CSL.
|
|
12
|
+
setup (Setup): Setup object containing the default reference frames.
|
|
26
13
|
|
|
27
14
|
Known Features:
|
|
28
15
|
- no link can be included:
|
|
@@ -37,6 +24,8 @@ def laser_tracker_to_dict(filexls, setup: Setup):
|
|
|
37
24
|
- the names of the reference frames are returned lowercase, without '_'
|
|
38
25
|
("Master" is an exception)
|
|
39
26
|
|
|
27
|
+
Returns:
|
|
28
|
+
Dictionary of reference frames compatible with egse.coordinates.dict_to_ref_model.
|
|
40
29
|
"""
|
|
41
30
|
|
|
42
31
|
# Predefined model -- gliso ~ master
|
|
@@ -72,22 +61,24 @@ def laser_tracker_to_dict(filexls, setup: Setup):
|
|
|
72
61
|
|
|
73
62
|
# Read input file
|
|
74
63
|
|
|
75
|
-
pan = pandas.read_excel(
|
|
64
|
+
pan = pandas.read_excel(file_xls, sheet_name="Data", usecols="A:D", names=["desc", "x", "y", "z"])
|
|
76
65
|
|
|
77
|
-
|
|
66
|
+
num_rows = pan.shape[0]
|
|
78
67
|
|
|
79
68
|
desc = pan["desc"].values
|
|
80
69
|
colx = pan["x"].values
|
|
81
70
|
coly = pan["y"].values
|
|
82
71
|
colz = pan["z"].values
|
|
83
72
|
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
reference_frames = dict()
|
|
74
|
+
reference_frames["Master"] = (
|
|
75
|
+
"ReferenceFrame//([0.0000,0.0000,0.0000 | [0.0000,0.0000,0.0000 | Master | Master | [])"
|
|
76
|
+
)
|
|
86
77
|
|
|
87
78
|
links = "[]"
|
|
88
79
|
|
|
89
80
|
i, frame = -1, -1
|
|
90
|
-
while i <
|
|
81
|
+
while i < num_rows:
|
|
91
82
|
i += 1
|
|
92
83
|
|
|
93
84
|
try:
|
|
@@ -111,10 +102,10 @@ def laser_tracker_to_dict(filexls, setup: Setup):
|
|
|
111
102
|
else:
|
|
112
103
|
ref = "None"
|
|
113
104
|
|
|
114
|
-
|
|
105
|
+
reference_frames[name] = f"ReferenceFrame//({translation} | {rotation} | {name} | {ref} | {links})"
|
|
115
106
|
|
|
116
107
|
except:
|
|
117
108
|
print(f"Frame extraction issue after row {i} : {desc[i]}")
|
|
118
109
|
print(sys.exc_info())
|
|
119
110
|
|
|
120
|
-
return
|
|
111
|
+
return reference_frames
|