Skip to content

Commit d54457f

Browse files
committed
First commit of DHFactor
not complete
1 parent 31a1326 commit d54457f

File tree

1 file changed

+311
-0
lines changed

1 file changed

+311
-0
lines changed

roboticstoolbox/robot/DHFactor.py

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
from roboticstoolbox import ETS
2+
import re
3+
import sympy
4+
import numpy as np
5+
from spatialmath import base
6+
7+
pi2 = base.pi() / 2
8+
deg = base.pi() / sympy.Integer('180')
9+
10+
class DHFactor(ETS):
11+
12+
def __init__(self, s):
13+
et_re = re.compile(r"([RT][xyz])\(([^)]*)\)")
14+
15+
super().__init__()
16+
# self.data = []
17+
18+
for axis, eta in et_re.findall(s):
19+
print(axis,eta)
20+
if eta[0] == 'q':
21+
eta = None
22+
unit = None
23+
else:
24+
# eta can be given as a variable or a number
25+
try:
26+
# first attempt to create symbolic number
27+
eta = sympy.Number(eta)
28+
except:
29+
# failing that, a symbolic variable
30+
eta = sympy.symbols(eta)
31+
if axis[0] == 'R':
32+
# convert to degrees, assumed unit in input string
33+
eta = sympy.simplify(eta * deg)
34+
35+
if axis == 'Rx':
36+
e = ETS.rx(eta)
37+
elif axis == 'Ry':
38+
e = ETS.ry(eta)
39+
elif axis == 'Rz':
40+
e = ETS.rz(eta)
41+
elif axis == 'Tx':
42+
e = ETS.tx(eta)
43+
elif axis == 'Ty':
44+
e = ETS.ty(eta)
45+
elif axis == 'Tz':
46+
e = ETS.tz(eta)
47+
48+
self.data.append(e.data[0])
49+
50+
def simplify(self):
51+
52+
self.merge()
53+
self.swap(convention)
54+
self.merge()
55+
self.float_right()
56+
self.merge()
57+
self.substitute_to_z()
58+
self.merge()
59+
for i in range(nloops):
60+
nchanges = 0
61+
62+
nchanges += self.merge()
63+
nchanges += self.swap(convention)
64+
nchanges += self.merge()
65+
nchanges += self.eliminate_y()
66+
nchanges += self.merge()
67+
68+
if nchanges == 0:
69+
# try this then
70+
nchanges += self.substitute_to_z2()
71+
if nchanges == 0:
72+
break
73+
else:
74+
# too many iterations
75+
print('too many iterations')
76+
77+
def substitute_to_z(self):
78+
# substitute all non Z joint transforms according to rules
79+
nchanges = 0
80+
out = ETS()
81+
for e in self:
82+
if e.isjoint:
83+
out *= e
84+
else:
85+
# do a substitution
86+
if e.axis == 'Rx':
87+
new = ETS.ry(pi2) * ETS.rz(e.eta) * ETS.ry(-pi2)
88+
elif e.axis == 'Ry':
89+
new = ETS.rx(-pi2) * ETS.rz(e.eta) * ETS.rx(pi2)
90+
elif e.axis == 'tx':
91+
new = ETS.ry(pi2) * ETS.tz(e.eta) * ETS.ry(-pi2)
92+
elif e.axis == 'ty':
93+
new = ETS.rx(-pi2) * ETS.tz(e.eta) * ETS.rx(pi2)
94+
else:
95+
out *= e
96+
continue
97+
out *= new
98+
nchanges += 1
99+
100+
self.data = out.data
101+
return nchanges
102+
103+
def merge(self):
104+
105+
def canmerge(prev, this):
106+
return prev.axis == this.axis and not (prev.isjoint and this.isjoint)
107+
108+
out = ETS()
109+
while len(self.data) > 0:
110+
this = self.pop(0)
111+
112+
if len(self.data) > 0:
113+
next = self[0]
114+
115+
if canmerge(this, next):
116+
new = DHFactor.add(this, next)
117+
out *= new
118+
self.pop(0) # remove next from the queue
119+
print(f"Merging {this * next} to {new}")
120+
else:
121+
out *= this
122+
123+
self.data = out.data
124+
# remove zeros
125+
126+
@staticmethod
127+
def add(this, that):
128+
if this.isjoint and not that.isjoint:
129+
out = ETS(this)
130+
if out.eta is None:
131+
out.eta = that.eta
132+
else:
133+
out.eta += that.eta
134+
elif not this.isjoint and that.isjoint:
135+
out = ETS(that)
136+
if out.eta is None:
137+
out.eta = this.eta
138+
else:
139+
out.eta += this.eta
140+
else:
141+
raise ValueError('both ET cannot be joint variables')
142+
return out
143+
144+
def eliminate_y(self):
145+
146+
nchanges = 0
147+
out = ETS()
148+
jointyet = False
149+
150+
def eliminate(prev, this):
151+
if this.isjoint or prev.isjoint:
152+
return None
153+
154+
new = None
155+
if prev.axis == 'Rx' and this.axis == 'ty': # RX.TY -> TZ.RX
156+
new = ETS.tx(prev.eta) * prev
157+
elif prev.axis == 'Rx' and this.axis == 'tz': # RX.TZ -> TY.RX
158+
new = ETS.ty(-prev.eta) * prev
159+
elif prev.axis == 'Ry' and this.axis == 'tz': # RY.TX-> TZ.RY
160+
new = ETS.tz(-prev.eta) * prev
161+
elif prev.axis == 'Ry' and this.axis == 'tz': # RY.TZ-> TX.RY
162+
new = ETS.tx(prev.eta) * prev
163+
164+
elif prev.axis == 'ty' and this.axis == 'Rx': # TY.RX -> RX.TZ
165+
new = this * ETS.tz(-this.eta)
166+
elif prev.axis == 'tx' and this.axis == 'Rz': # TX.RZ -> RZ.TY
167+
new = this * ETS.tz(this.eta)
168+
elif prev.axis == 'Ry' and this.axis == 'Rx': # RY(Q).RX -> RX.RZ(-Q)
169+
new = this * ETS.Rz(-prev.eta)
170+
elif prev.axis == 'Rx' and this.axis == 'Ry': # RX.RY -> RZ.RX
171+
new = ETS.Rz(this.eta) * prev
172+
elif prev.axis == 'Rz' and this.axis == 'Rx': # RZ.RX -> RX.RY
173+
new = this * ETS.Ry(this.eta)
174+
return new
175+
176+
for i in range(len(self)):
177+
this = self[i]
178+
jointyet = this.isjoint
179+
if i == 0 or not jointyet:
180+
continue
181+
182+
prev = self[i-1]
183+
184+
new = eliminate(prev, this)
185+
if new is not None:
186+
self[i-1:i] = new
187+
nchanges += 1
188+
189+
return nchanges
190+
191+
def __str__(self, q=None):
192+
"""
193+
Pretty prints the ETS
194+
195+
:param q: control how joint variables are displayed
196+
:type q: str
197+
:return: Pretty printed ETS
198+
:rtype: str
199+
200+
``q`` controls how the joint variables are displayed:
201+
202+
- None, format depends on number of joint variables
203+
- one, display joint variable as q
204+
- more, display joint variables as q0, q1, ...
205+
- if a joint index was provided, use this value
206+
- "", display all joint variables as empty parentheses ``()``
207+
- "θ", display all joint variables as ``(θ)``
208+
- format string with passed joint variables ``(j, j+1)``, so "θ{0}"
209+
would display joint variables as θ0, θ1, ... while "θ{1}" would
210+
display joint variables as θ1, θ2, ... ``j`` is either the joint
211+
index, if provided, otherwise a sequential value.
212+
213+
Example:
214+
215+
.. runblock:: pycon
216+
217+
>>> from roboticstoolbox import ETS
218+
>>> e = ETS.rz() * ETS.tx(1) * ETS.rz()
219+
>>> print(e[:2])
220+
>>> print(e)
221+
>>> print(e.__str__(""))
222+
>>> print(e.__str__("θ{0}")) # numbering from 0
223+
>>> print(e.__str__("θ{1}")) # numbering from 1
224+
>>> # explicit joint indices
225+
>>> e = ETS.rz(j=3) * ETS.tx(1) * ETS.rz(j=4)
226+
>>> print(e)
227+
>>> print(e.__str__("θ{0}"))
228+
229+
.. note:: Angular parameters are converted to degrees, except if they
230+
are symbolic.
231+
232+
Example:
233+
234+
.. runblock:: pycon
235+
236+
>>> from roboticstoolbox import ETS
237+
>>> from spatialmath.base import symbol
238+
>>> theta, d = symbol('theta, d')
239+
>>> e = ETS.rx(theta) * ETS.tx(2) * ETS.rx(45, 'deg') * \
240+
>>> ETS.ry(0.2) * ETS.ty(d)
241+
>>> str(e)
242+
243+
:SymPy: supported
244+
"""
245+
246+
# override this class from ETS
247+
es = []
248+
j = 0
249+
c = 0
250+
251+
if q is None:
252+
if len(self.joints()) > 1:
253+
q = "q{0}"
254+
else:
255+
q = "q"
256+
257+
# For et in the object, display it, data comes from properties
258+
# which come from the named tuple
259+
for et in self:
260+
261+
s = f"{et.axis}("
262+
if et.isjoint:
263+
if q is not None:
264+
if et.jindex is None:
265+
_j = j
266+
else:
267+
_j = et.jindex
268+
qvar = q.format(_j, _j+1) # lgtm [py/str-format/surplus-argument] # noqa
269+
else:
270+
qvar = ""
271+
if et.isflip:
272+
s += f"-{qvar}"
273+
else:
274+
s += f"{qvar}"
275+
j += 1
276+
277+
if et.eta is not None:
278+
if et.isrevolute:
279+
if base.issymbol(et.eta):
280+
if s[-1] == "(":
281+
s += f"{et.eta}"
282+
else:
283+
# adding to a previous value
284+
if str(et.eta).startswith('-'):
285+
s += f"{et.eta}"
286+
else:
287+
s += f"+{et.eta}"
288+
else:
289+
s += f"{et.eta * 180 / np.pi:.4g}°"
290+
291+
elif et.isprismatic:
292+
s += f"{et.eta}"
293+
294+
elif et.isconstant:
295+
s += f"C{c}"
296+
c += 1
297+
s += ")"
298+
299+
es.append(s)
300+
301+
return " * ".join(es)
302+
303+
if __name__ == "__main__": # pragram: no cover
304+
s = 'Rz(45) Tz(L1) Rz(q1) Ry(q2) Ty(L2) Tz(L3) Ry(q3) Tx(L4) Ty(L5) Tz(L6) Rz(q4) Ry(q5) Rz(q6)'
305+
306+
ets = DHFactor(s)
307+
print(ets)
308+
ets.substitute_to_z()
309+
print(ets)
310+
ets.merge()
311+
print(ets)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy