3
# Copyright 2011 Shadow Robot Company Ltd.
5
# This program is free software: you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by the Free
7
# Software Foundation, either version 2 of the License, or (at your option)
10
# This program is distributed in the hope that it will be useful, but WITHOUT
11
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15
# You should have received a copy of the GNU General Public License along
16
# with this program. If not, see <http://www.gnu.org/licenses/>.
19
import roslib; roslib.load_manifest('sr_dremmeling_wall')
26
from geometry_msgs.msg import Pose, Quaternion, PoseStamped, Point
27
from kinect_color_segmentation.srv import WallNormale, SurfaceToDremmel
28
from tf.transformations import quaternion_from_euler
29
from rospy.core import rospyerr
31
from interactive_markers.interactive_marker_server import *
32
from interactive_marker import InteractiveConnectorSelector
34
class WallDremmeler(object):
36
Controls the process of dremelling certain regions of a wall with a dremel mounted on a Denso arm
38
When the user clicks on the interactive marker, the current sequence of 3D points to be drilled is obtained, as well
39
as the normal to the surface of the wall.
41
For every point, a pose for the tool is calculated, which is inside the wall, following the normal, to ensure that the tool
42
will remove the first layer of the wall.
43
If consecutive points are farther than a set distance, an intermediate position which is above the surface of the wall is calculated,
44
so that the positions between the two points are not drilled.
46
The calculated poses are sent to the arm.
48
The tool we are using must be configured in the Denso arm's tool menu, and selected in set_tooltip(x) in DensoArm::init() (denso_arm package)
54
rospy.init_node('sr_dremmeling_wall')
56
self.trajectory_client = actionlib.SimpleActionClient('/denso_arm/trajectory', denso_msgs.msg.TrajectoryAction)
57
#self.trajectory_client.wait_for_server()
59
self.listener = tf.TransformListener()
60
self.br = tf.TransformBroadcaster()
62
self.surface_to_dremmel_server = rospy.ServiceProxy( '/kinect_segmentation/PointSequenceDetection/segment', SurfaceToDremmel)
63
self.wall_orientation_server = rospy.ServiceProxy( '/kinect_segmentation/PointSequenceDetection/get_wall_normale', WallNormale)
65
self.interactive_marker_server = InteractiveMarkerServer("dremmel_wall")
66
self.interactive_markers = InteractiveConnectorSelector(["camera_link"], self.run, self.interactive_marker_server)
68
#rospy.loginfo("go to initial pose 1")
69
#self.go_to_initial_position()
73
rospy.loginfo("go to initial pose 2")
74
self.go_to_initial_position_2()
80
#First we get the segmented points.
81
rospy.loginfo("segmenting the point cloud")
84
res = self.surface_to_dremmel_server()
85
segmented_points = res.points
86
rospy.loginfo("cloud length:" + str(len(segmented_points)))
88
rospy.logerr("Couldn't segment the point cloud")
91
#Then we get the normal for the wall
92
rospy.loginfo("Getting the wall normale")
93
wall_normale = Quaternion()
96
res = self.wall_orientation_server()
97
wall_normale = res.normale
98
wall_link = res.frame_name
100
rospy.logerr("Couldn't get the wall normale")
103
wall_normale = self.rotate_normal(segmented_points, wall_normale, wall_link)
105
#Substitution of the surface normal for the tooltip normal to test the dremmelling
106
rospy.logerr("TODO: remove this. Using tooltip normal.")
107
tooltip_pose = self.get_pose("/denso_arm/tooltip", wall_link)
108
wall_normale = tooltip_pose.orientation
110
#We build a list of poses to send to the hand.
111
list_of_poses, list_of_speeds = self.build_poses( segmented_points, wall_normale, wall_link )
113
#now we send this to the arm
114
rospy.loginfo("Sending the list of poses to the arm")
115
goal = denso_msgs.msg.TrajectoryGoal()
116
goal.trajectory = list_of_poses
117
goal.speed = list_of_speeds
119
self.trajectory_client.send_goal( goal )
120
self.trajectory_client.wait_for_result()
122
rospy.loginfo( "Finished Dremmeling the surface: " + str( self.trajectory_client.get_result() ) )
124
#move the arm out of the way
125
self.go_to_initial_position_2()
127
def rotate_normal(self, segmented_points, quaternion, rotation_link):
129
pose.position.x = segmented_points[0].x
130
pose.position.y = segmented_points[0].y
131
pose.position.z = segmented_points[0].z
132
pose.orientation = quaternion
134
normal_name = "/surface_normal"
136
pose_rotated = Pose()
137
#position is the same, we just want to rotate
138
pose_rotated.position.x = 0.0
139
pose_rotated.position.y = 0.0
140
pose_rotated.position.z = 0.0
141
rotation_tmp_array = tf.transformations.quaternion_from_euler(0.0, 0.0, 3.14159266)
142
pose_rotated.orientation.x = rotation_tmp_array[0]
143
pose_rotated.orientation.y = rotation_tmp_array[1]
144
pose_rotated.orientation.z = rotation_tmp_array[2]
145
pose_rotated.orientation.w = rotation_tmp_array[3]
147
rotated_normal_name = "/surface_normal_rotated"
149
rate = rospy.Rate(100.0)
150
pose_rotated_base = None
153
for i in range(0,100):
154
self.br.sendTransform((pose.position.x, pose.position.y, pose.position.z),
155
(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w),
156
rospy.Time.now(), normal_name, rotation_link)
158
self.br.sendTransform((pose_rotated.position.x, pose_rotated.position.y, pose_rotated.position.z),
159
(pose_rotated.orientation.x, pose_rotated.orientation.y, pose_rotated.orientation.z, pose_rotated.orientation.w),
160
rospy.Time.now(), rotated_normal_name, normal_name)
163
(trans,rot) = self.listener.lookupTransform(rotation_link, rotated_normal_name, rospy.Time())
164
pose_rotated_base = PoseStamped()
165
pose_rotated_base.pose.position.x = trans[0]
166
pose_rotated_base.pose.position.y = trans[1]
167
pose_rotated_base.pose.position.z = trans[2]
168
pose_rotated_base.pose.orientation.x = rot[0]
169
pose_rotated_base.pose.orientation.y = rot[1]
170
pose_rotated_base.pose.orientation.z = rot[2]
171
pose_rotated_base.pose.orientation.w = rot[3]
172
pose_rotated_base.header.frame_id = rotation_link
173
pose_rotated_base.header.stamp = rospy.Time.now()
178
#rospy.logerr("Could not transform from " + pose_above_pose_frame.header.frame_id + " to /base_link")
181
rospy.logerr("Could not transform from " + rotated_normal_name + " to " + rotation_link)
183
return pose_rotated_base.pose.orientation
185
def calculate_distance(self, point1, point2):
186
distance = math.sqrt((point1.x - point2.x) * (point1.x - point2.x)
187
+ (point1.y - point2.y) * (point1.y - point2.y)
188
+ (point1.z - point2.z) * (point1.z - point2.z))
192
def get_pose(self, link_name, base_link):
196
#try to get the pose of the given link_name
197
# in the base_link frame.
198
for i in range (0, 500):
200
(trans, rot) = self.listener.lookupTransform( base_link, link_name,
209
pose.position.x = trans[0]
210
pose.position.y = trans[1]
211
pose.position.z = trans[2]
212
pose.orientation.x = rot[0]
213
pose.orientation.y = rot[1]
214
pose.orientation.z = rot[2]
215
pose.orientation.w = rot[3]
219
def build_poses(self, segmented_points, quaternion, rotation_link):
222
previous_point = None
223
last_orientation_in_base_link = None
225
for index,point in enumerate(segmented_points):
226
#create a pose above the point (in the wall frame)
227
rospy.loginfo("Doing point: " + str(index))
229
send_pose_above = False
231
#Create a pose above the surface only if it's the first time, or the point is at more than x meters from the previous
232
if (previous_point == None):
233
previous_point = Point()
234
send_pose_above = True
235
elif (self.calculate_distance(previous_point, point) > 0.035):
236
send_pose_above = True
238
send_pose_above = False
240
#Save current point as previous
241
previous_point.x = point.x
242
previous_point.y = point.y
243
previous_point.z = point.z
245
pose_on_plane = Pose()
246
pose_on_plane.position.x = point.x
247
pose_on_plane.position.y = point.y
248
pose_on_plane.position.z = point.z
249
pose_on_plane.orientation = quaternion
250
pose_on_plane_name = "/pose_on_plane_" + str(index)
252
if send_pose_above == True:
254
pose_above.position.x = 0.0
255
pose_above.position.y = 0.0
256
pose_above.position.z = -0.05
257
pose_above.orientation = Quaternion(0,0,0,1)
258
pose_above_name = "/pose_above_" + str(index)
260
#then create a pose below the point (inside the wall)
262
pose_inside.position.x = -0.025
263
pose_inside.position.y = 0.02
264
pose_inside.position.z = 0.042
265
pose_inside.orientation = Quaternion(0,0,0,1)
266
pose_inside_name = "/pose_inside_" + str(index)
268
#then transform those two poses in the base_link frame
269
#print "TODO: transform from ", rotation_link, " to /base_link"
272
pose_above_base_frame = None
273
pose_inside_base_frame = None
275
rate = rospy.Rate(100.0)
277
if send_pose_above == True:
278
for i in range(0,100):
279
self.br.sendTransform((pose_on_plane.position.x, pose_on_plane.position.y, pose_on_plane.position.z),
280
(pose_on_plane.orientation.x, pose_on_plane.orientation.y, pose_on_plane.orientation.z, pose_on_plane.orientation.w),
281
rospy.Time.now(), pose_on_plane_name, rotation_link)
283
self.br.sendTransform((pose_above.position.x, pose_above.position.y, pose_above.position.z),
284
(pose_above.orientation.x, pose_above.orientation.y, pose_above.orientation.z, pose_above.orientation.w),
285
rospy.Time.now(), pose_above_name, pose_on_plane_name)
288
(trans,rot) = self.listener.lookupTransform("/base_link", pose_above_name, rospy.Time())
289
pose_above_base_frame = PoseStamped()
290
pose_above_base_frame.pose.position.x = trans[0]
291
pose_above_base_frame.pose.position.y = trans[1]
292
pose_above_base_frame.pose.position.z = trans[2]
293
pose_above_base_frame.pose.orientation.x = rot[0]
294
pose_above_base_frame.pose.orientation.y = rot[1]
295
pose_above_base_frame.pose.orientation.z = rot[2]
296
pose_above_base_frame.pose.orientation.w = rot[3]
297
pose_above_base_frame.header.frame_id = "/base_link"
298
pose_above_base_frame.header.stamp = rospy.Time.now()
303
#rospy.logerr("Could not transform from " + pose_above_pose_frame.header.frame_id + " to /base_link")
307
rospy.logerr("Could not transform from " + pose_above_name + " to /base_link")
310
for i in range(0,100):
311
self.br.sendTransform((pose_on_plane.position.x, pose_on_plane.position.y, pose_on_plane.position.z),
312
(pose_on_plane.orientation.x, pose_on_plane.orientation.y, pose_on_plane.orientation.z, pose_on_plane.orientation.w),
313
rospy.Time.now(), pose_on_plane_name, rotation_link)
315
self.br.sendTransform((pose_inside.position.x, pose_inside.position.y, pose_inside.position.z),
316
(pose_inside.orientation.x, pose_inside.orientation.y, pose_inside.orientation.z, pose_inside.orientation.w),
317
rospy.Time.now(), pose_inside_name, pose_on_plane_name)
321
(trans,rot) = self.listener.lookupTransform("/base_link", pose_inside_name, rospy.Time())
322
pose_inside_base_frame = PoseStamped()
323
pose_inside_base_frame.pose.position.x = trans[0]
324
pose_inside_base_frame.pose.position.y = trans[1]
325
pose_inside_base_frame.pose.position.z = trans[2]
326
pose_inside_base_frame.pose.orientation.x = rot[0]
327
pose_inside_base_frame.pose.orientation.y = rot[1]
328
pose_inside_base_frame.pose.orientation.z = rot[2]
329
pose_inside_base_frame.pose.orientation.w = rot[3]
330
pose_inside_base_frame.header.frame_id = "/base_link"
331
pose_inside_base_frame.header.stamp = rospy.Time.now()
335
last_orientation_in_base_link = pose_inside_base_frame.pose.orientation
339
#rospy.logerr("Could not transform from " + pose_inside_pose_frame.header.frame_id + " to /base_link")
343
rospy.logerr("Could not transform from " + pose_inside_name + " to /base_link")
346
#add those two poses to the list of poses sent to the arm
347
if send_pose_above == True:
348
if (pose_above_base_frame != None):
349
list_of_poses.append( pose_above_base_frame.pose )
350
list_of_speeds.append( 30.0 )
352
rospy.logerr("Could not convert pose above " + pose_above_name + " to /base_link")
354
if (pose_inside_base_frame != None):
355
list_of_poses.append( pose_inside_base_frame.pose )
356
list_of_speeds.append( 20.0 )
358
rospy.logerr("Could not convert pose inside " + pose_inside_name + " to /base_link")
360
#Enqueue a last position far from the target
362
pose_out.position.x = 0.644
363
pose_out.position.y = 0.279
364
pose_out.position.z = 0.581
365
pose_out.orientation = last_orientation_in_base_link
366
list_of_poses.append( pose_out )
367
list_of_speeds.append( 25.0 )
369
return list_of_poses, list_of_speeds
371
def go_to_initial_position(self):
372
#Move the arm to a fixed initial position
373
goal = denso_msgs.msg.TrajectoryGoal()
377
pose_tmp.position.x = 0.639
378
pose_tmp.position.y = -0.005
379
pose_tmp.position.z = 0.356
380
pose_tmp.orientation.x = -0.659
381
pose_tmp.orientation.y = 0.256
382
pose_tmp.orientation.z = -0.2466
383
pose_tmp.orientation.w = 0.6626
385
traj.append( pose_tmp )
388
goal.trajectory = traj
391
self.trajectory_client.send_goal( goal )
392
self.trajectory_client.wait_for_result()
393
res = self.trajectory_client.get_result()
394
if res.val != denso_msgs.msg.TrajectoryResult.SUCCESS:
395
rospy.logerr("Failed to move the arm to the given position.")
400
def go_to_initial_position_2(self):
401
#Move the arm to a fixed initial position
402
goal = denso_msgs.msg.TrajectoryGoal()
406
pose_tmp.position.x = 0.61
407
pose_tmp.position.y = 0.036
408
pose_tmp.position.z = 0.669
409
pose_tmp.orientation.x = -0.653
410
pose_tmp.orientation.y = 0.271
411
pose_tmp.orientation.z = -0.261
412
pose_tmp.orientation.w = 0.657
414
#pose_tmp.orientation.x = -0.659
415
#pose_tmp.orientation.y = 0.256
416
#pose_tmp.orientation.z = -0.2466
417
#pose_tmp.orientation.w = 0.6626
420
traj.append( pose_tmp )
423
goal.trajectory = traj
426
self.trajectory_client.send_goal( goal )
427
self.trajectory_client.wait_for_result()
428
res = self.trajectory_client.get_result()
429
if res.val != denso_msgs.msg.TrajectoryResult.SUCCESS:
430
rospy.logerr("Failed to move the arm to the given position.")