Skip to content

Commit 99517fe

Browse files
committed
collision convenience
1 parent 9c3f1e1 commit 99517fe

File tree

7 files changed

+334
-5
lines changed

7 files changed

+334
-5
lines changed

examples/neo.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env python
2+
"""
3+
@author Jesse Haviland
4+
"""
5+
6+
import roboticstoolbox as rtb
7+
import spatialmath as sm
8+
import numpy as np
9+
import qpsolvers as qp
10+
import time
11+
12+
# Launch the simulator Swift
13+
env = rtb.backend.Swift()
14+
env.launch()
15+
16+
# Create a Panda robot object
17+
panda = rtb.models.Panda()
18+
19+
# Set joint angles to ready configuration
20+
panda.q = [-0.5653, -0.1941, -1.2602, -0.7896, -2.3227, -0.3919, -2.5173]
21+
22+
# Make two obstacles with velocities
23+
s0 = rtb.Shape.Sphere(
24+
radius=0.05,
25+
base=sm.SE3(0.45, 0.4, 0.3)
26+
)
27+
s0.v = [0.01, -0.2, 0, 0, 0, 0]
28+
29+
s1 = rtb.Shape.Sphere(
30+
radius=0.05,
31+
base=sm.SE3(0.1, 0.35, 0.65)
32+
)
33+
s1.v = [0, -0.2, 0, 0, 0, 0]
34+
35+
# Make a target
36+
s2 = rtb.Shape.Sphere(
37+
radius=0.02,
38+
base=sm.SE3(0.6, -0.2, 0.0)
39+
)
40+
41+
# Add the Panda and shapes to the simulator
42+
env.add(panda)
43+
env.add(s0)
44+
env.add(s1)
45+
env.add(s2)
46+
time.sleep(1)
47+
48+
# Number of joint in the panda which we are controlling
49+
n = 7
50+
51+
# Set the desired end-effector pose to the location of s2
52+
Tep = panda.fkine()
53+
Tep.A[:3, 3] = s2.base.t
54+
55+
arrived = False
56+
57+
while not arrived:
58+
59+
# The pose of the Panda's end-effector
60+
Te = panda.fkine()
61+
62+
# Transform from the end-effector to desired pose
63+
eTep = Te.inv() * Tep
64+
65+
# Spatial error
66+
e = np.sum(np.abs(np.r_[eTep.t, eTep.rpy() * np.pi/180]))
67+
68+
# Calulate the required end-effector spatial velocity for the robot
69+
# to approach the goal. Gain is set to 1.0
70+
v, arrived = rtb.p_servo(Te, Tep, 1.0)
71+
72+
# Gain term (lambda) for control minimisation
73+
Y = 0.01
74+
75+
# Quadratic component of objective function
76+
Q = np.eye(n + 6)
77+
78+
# Joint velocity component of Q
79+
Q[:n, :n] *= Y
80+
81+
# Slack component of Q
82+
Q[n:, n:] = (1 / e) * np.eye(6)
83+
84+
# The equality contraints
85+
Aeq = np.c_[panda.jacobe(), np.eye(6)]
86+
beq = v.reshape((6,))
87+
88+
# The inequality constraints for joint limit avoidance
89+
Ain = np.zeros((n + 6, n + 6))
90+
bin = np.zeros(n + 6)
91+
92+
# The minimum angle (in radians) in which the joint is allowed to approach
93+
# to its limit
94+
ps = 0.05
95+
96+
# The influence angle (in radians) in which the velocity damper
97+
# becomes active
98+
pi = 0.9
99+
100+
# Form the joint limit velocity damper
101+
Ain[:n, :n], bin[:n] = panda.joint_velocity_damper(ps, pi, n)
102+
103+
# Linear component of objective function: the manipulability Jacobian
104+
c = np.r_[-panda.jacobm().reshape((n,)), np.zeros(6)]
105+
106+
# The lower and upper bounds on the joint velocity and slack variable
107+
lb = -np.r_[panda.qdlim[:n], 10 * np.ones(6)]
108+
ub = np.r_[panda.qdlim[:n], 10 * np.ones(6)]
109+
110+
# Solve for the joint velocities dq
111+
qd = qp.solve_qp(Q, c, Ain, bin, Aeq, beq, lb=lb, ub=ub)
112+
113+
# Apply the joint velocities to the Panda
114+
panda.qd[:n] = qd[:n]
115+
116+
# Step the simulator by 50 ms
117+
env.step(50)

roboticstoolbox/robot/ELink.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,55 @@ def friction(self, qd):
501501
tau = -np.abs(self.G) * tau
502502

503503
return tau
504+
505+
def closest_point(self, shape, inf_dist=1.0):
506+
'''
507+
closest_point(shape, inf_dist) returns the minimum euclidean
508+
distance between this link and shape, provided it is less than
509+
inf_dist. It will also return the points on self and shape in the
510+
world frame which connect the line of length distance between the
511+
shapes. If the distance is negative then the shapes are collided.
512+
513+
:param shape: The shape to compare distance to
514+
:type shape: Shape
515+
:param inf_dist: The minimum distance within which to consider
516+
the shape
517+
:type inf_dist: float
518+
:returns: d, p1, p2 where d is the distance between the shapes,
519+
p1 and p2 are the points in the world frame on the respective
520+
shapes
521+
:rtype: float, SE3, SE3
522+
'''
523+
524+
d = 10000
525+
p1 = None,
526+
p2 = None
527+
528+
for col in self.collision:
529+
td, tp1, tp2 = col.closest_point(shape, inf_dist)
530+
531+
if td is not None and td < d:
532+
d = td
533+
p1 = tp1
534+
p2 = tp2
535+
536+
if d == 10000:
537+
d = None
538+
539+
return d, p1, p2
540+
541+
def collided(self, shape):
542+
'''
543+
collided(shape) checks if this link and shape have collided
544+
545+
:param shape: The shape to compare distance to
546+
:type shape: Shape
547+
:returns: True if shapes have collided
548+
:rtype: bool
549+
'''
550+
551+
for col in self.collision:
552+
if col.collided(shape):
553+
return True
554+
555+
return False

roboticstoolbox/robot/ERobot.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,88 @@ def joint_velocity_damper(self, ps=0.05, pi=0.1, n=None, gain=1.0):
11201120

11211121
return Ain, bin
11221122

1123+
# def link_collision_damper(self, link, col, ob, q):
1124+
# dii = 5
1125+
# di = 0.3
1126+
# ds = 0.05
1127+
1128+
# ret = p.getClosestPoints(col.co, ob.co, dii)
1129+
1130+
# if len(ret) > 0:
1131+
# ret = ret[0]
1132+
# wTlp = sm.SE3(ret[5])
1133+
# wTcp = sm.SE3(ret[6])
1134+
# lpTcp = wTlp.inv() * wTcp
1135+
1136+
# d = ret[8]
1137+
1138+
# if d < di:
1139+
# n = lpTcp.t / d
1140+
# nh = np.expand_dims(np.r_[n, 0, 0, 0], axis=0)
1141+
1142+
# Je = r.jacobe(q, from_link=r.base_link, to_link=link, offset=col.base)
1143+
# n = Je.shape[1]
1144+
# dp = nh @ ob.v
1145+
# l_Ain = np.zeros((1, 13))
1146+
# l_Ain[0, :n] = nh @ Je
1147+
# l_bin = (0.8 * (d - ds) / (di - ds)) + dp
1148+
# else:
1149+
# l_Ain = None
1150+
# l_bin = None
1151+
1152+
# return l_Ain, l_bin, d, wTcp
1153+
1154+
def closest_point(self, shape, inf_dist=1.0):
1155+
'''
1156+
closest_point(shape, inf_dist) returns the minimum euclidean
1157+
distance between this robot and shape, provided it is less than
1158+
inf_dist. It will also return the points on self and shape in the
1159+
world frame which connect the line of length distance between the
1160+
shapes. If the distance is negative then the shapes are collided.
1161+
1162+
:param shape: The shape to compare distance to
1163+
:type shape: Shape
1164+
:param inf_dist: The minimum distance within which to consider
1165+
the shape
1166+
:type inf_dist: float
1167+
:returns: d, p1, p2 where d is the distance between the shapes,
1168+
p1 and p2 are the points in the world frame on the respective
1169+
shapes
1170+
:rtype: float, SE3, SE3
1171+
'''
1172+
1173+
d = 10000
1174+
p1 = None,
1175+
p2 = None
1176+
1177+
for link in self.links:
1178+
td, tp1, tp2 = link.closest_point(shape, inf_dist)
1179+
1180+
if td is not None and td < d:
1181+
d = td
1182+
p1 = tp1
1183+
p2 = tp2
1184+
1185+
if d == 10000:
1186+
d = None
1187+
1188+
return d, p1, p2
1189+
1190+
def collided(self, shape):
1191+
'''
1192+
collided(shape) checks if this robot and shape have collided
1193+
1194+
:param shape: The shape to compare distance to
1195+
:type shape: Shape
1196+
:returns: True if shapes have collided
1197+
:rtype: bool
1198+
'''
1199+
1200+
for link in self.links:
1201+
if link.collided(shape):
1202+
return True
1203+
1204+
return False
11231205

11241206
# def teach(
11251207
# self, block=True, q=None, limits=None,

roboticstoolbox/robot/Shape.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ def closest_point(self, shape, inf_dist=1.0):
207207
closest_point(shape, inf_dist) returns the minimum euclidean
208208
distance between self and shape, provided it is less than inf_dist.
209209
It will also return the points on self and shape in the world frame
210-
which connect the line of length distance between the shapes.
210+
which connect the line of length distance between the shapes. If the
211+
distance is negative then the shapes are collided.
211212
212213
:param shape: The shape to compare distance to
213214
:type shape: Shape
@@ -236,9 +237,9 @@ def closest_point(self, shape, inf_dist=1.0):
236237
ret = p.getClosestPoints(self.co, shape.co, inf_dist)
237238

238239
if len(ret) == 0:
239-
d = -1
240-
p1 = SE3()
241-
p2 = SE3()
240+
d = None
241+
p1 = None
242+
p2 = None
242243
else:
243244
ret = ret[0]
244245
d = ret[8]
@@ -247,6 +248,23 @@ def closest_point(self, shape, inf_dist=1.0):
247248

248249
return d, p1, p2
249250

251+
def collided(self, shape):
252+
'''
253+
collided(shape) checks if self and shape have collided
254+
255+
:param shape: The shape to compare distance to
256+
:type shape: Shape
257+
:returns: True if shapes have collided
258+
:rtype: bool
259+
'''
260+
261+
d, _, _ = self.closest_point(shape)
262+
263+
if d is not None and d <= 0:
264+
return True
265+
else:
266+
return False
267+
250268

251269
class Mesh(Shape):
252270
"""

tests/test_ELink.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,31 @@ def test_geometry_fail(self):
213213
with self.assertRaises(TypeError):
214214
l0.geometry = 1
215215

216+
def test_dist(self):
217+
s0 = rp.Box([1, 1, 1], sm.SE3(0, 0, 0))
218+
s1 = rp.Box([1, 1, 1], sm.SE3(3, 0, 0))
219+
p = rp.models.Panda()
220+
link = p.links[3]
221+
222+
d0, _, _ = link.closest_point(s0)
223+
d1, _, _ = link.closest_point(s1, 5)
224+
d2, _, _ = link.closest_point(s1)
225+
226+
self.assertAlmostEqual(d0, -0.5599999999995913)
227+
self.assertAlmostEqual(d1, 2.44)
228+
self.assertAlmostEqual(d2, None)
229+
230+
def test_collided(self):
231+
s0 = rp.Box([1, 1, 1], sm.SE3(0, 0, 0))
232+
s1 = rp.Box([1, 1, 1], sm.SE3(3, 0, 0))
233+
p = rp.models.Panda()
234+
link = p.links[3]
235+
c0 = link.collided(s0)
236+
c1 = link.collided(s1)
237+
238+
self.assertTrue(c0)
239+
self.assertFalse(c1)
240+
216241

217242
if __name__ == '__main__':
218243

tests/test_ERobot.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,30 @@ def test_control_type2(self):
592592
# e2 = panda.teach2(block=False, q=panda.qr)
593593
# e2.close()
594594

595+
def test_dist(self):
596+
s0 = rp.Box([1, 1, 1], sm.SE3(0, 0, 0))
597+
s1 = rp.Box([1, 1, 1], sm.SE3(3, 0, 0))
598+
p = rp.models.Panda()
599+
600+
d0, _, _ = p.closest_point(s0)
601+
d1, _, _ = p.closest_point(s1, 5)
602+
d2, _, _ = p.closest_point(s1)
603+
604+
self.assertAlmostEqual(d0, -0.5599999999995913)
605+
self.assertAlmostEqual(d1, 2.4275999999999995)
606+
self.assertAlmostEqual(d2, None)
607+
608+
def test_collided(self):
609+
s0 = rp.Box([1, 1, 1], sm.SE3(0, 0, 0))
610+
s1 = rp.Box([1, 1, 1], sm.SE3(3, 0, 0))
611+
p = rp.models.Panda()
612+
613+
c0 = p.collided(s0)
614+
c1 = p.collided(s1)
615+
616+
self.assertTrue(c0)
617+
self.assertFalse(c1)
618+
595619

596620
if __name__ == '__main__':
597621

tests/test_Shape.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def test_closest(self):
3030
self.assertAlmostEqual(d0, 0.5)
3131
self.assertAlmostEqual(d1, 4.698463840213662e-13)
3232
self.assertAlmostEqual(d2, 2.5)
33-
self.assertAlmostEqual(d3, -1)
33+
self.assertAlmostEqual(d3, None)
3434

3535
def test_to_dict(self):
3636
s1 = rp.Cylinder(1, 1)
@@ -67,3 +67,14 @@ def test_fk_dict2(self):
6767
def test_mesh(self):
6868
ur = rp.models.UR5()
6969
ur.links[1].collision[0].closest_point(ur.links[2].collision[0])
70+
71+
def test_collision(self):
72+
s0 = rp.Box([1, 1, 1], sm.SE3(0, 0, 0))
73+
s1 = rp.Box([1, 1, 1], sm.SE3(0.5, 0, 0))
74+
s2 = rp.Box([1, 1, 1], sm.SE3(3, 0, 0))
75+
76+
c0 = s0.collided(s1)
77+
c1 = s0.collided(s2)
78+
79+
self.assertTrue(c0)
80+
self.assertFalse(c1)

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