1
1
PACKAGE = 'herbpy'
2
2
import logging
3
3
import openravepy
4
+ import numbers
5
+ import numpy
4
6
import prpy
5
7
import prpy .rave , prpy .util
6
8
from prpy .base .barretthand import BarrettHand
7
9
from prpy .base .wam import WAM
8
10
from prpy .base .robot import Robot
9
- from prpy .exceptions import PrPyException
11
+ from prpy .controllers import RewdOrTrajectoryController
12
+ from prpy .exceptions import PrPyException , TrajectoryNotExecutable
10
13
from prpy .planning .base import UnsupportedPlanningError
11
14
from herbbase import HerbBase
12
15
from herbpantilt import HERBPantilt
16
+ from ros_control_client_py import ControllerManagerClient
13
17
14
18
logger = logging .getLogger ('herbpy' )
15
19
@@ -30,6 +34,31 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
30
34
31
35
Robot .__init__ (self , robot_name = 'herb' )
32
36
37
+
38
+ # Controller setup
39
+ # TODO load controller names from same config as ros_control?
40
+ self .controller_manager = None # TODO verify never used in sim
41
+ self .controllers_always_on = []
42
+ controllers_manip = []
43
+
44
+ # TODO how to deal with sim on controller manager?
45
+ self .full_controller_sim = (left_arm_sim and right_arm_sim and
46
+ left_ft_sim and right_ft_sim and
47
+ left_hand_sim and right_hand_sim and
48
+ head_sim )
49
+ if not self .full_controller_sim :
50
+ # any non-simulation requires ros and the ros_control stack
51
+ print 'CLINT: something not sim'
52
+ import rospy
53
+ if not rospy .core .is_initialized ():
54
+ rospy .init_node ('herbpy' )
55
+
56
+ self .controller_manager = ControllerManagerClient ()
57
+ self .controllers_always_on .append ('joint_state_controller' )
58
+ else :
59
+ print 'CLINT: all sim'
60
+
61
+
33
62
# Convenience attributes for accessing self components.
34
63
self .left_arm = self .GetManipulator ('left' )
35
64
self .right_arm = self .GetManipulator ('right' )
@@ -38,14 +67,14 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
38
67
self .right_arm .hand = self .right_arm .GetEndEffector ()
39
68
self .left_hand = self .left_arm .hand
40
69
self .right_hand = self .right_arm .hand
41
- self .manipulators = [ self .left_arm , self .right_arm , self .head ]
70
+ self .manipulators = [self .left_arm , self .right_arm , self .head ]
42
71
43
72
# Dynamically switch to self-specific subclasses.
44
73
prpy .bind_subclass (self .left_arm , WAM , sim = left_arm_sim , namespace = '/left' )
45
74
prpy .bind_subclass (self .right_arm , WAM , sim = right_arm_sim , namespace = '/right' )
46
75
prpy .bind_subclass (self .head , HERBPantilt , sim = head_sim , owd_namespace = '/head/owd' )
47
76
prpy .bind_subclass (self .left_arm .hand , BarrettHand , sim = left_hand_sim , manipulator = self .left_arm ,
48
- bhd_namespace = '/left' , ft_sim = right_ft_sim )
77
+ bhd_namespace = '/left' , ft_sim = left_ft_sim )
49
78
prpy .bind_subclass (self .right_arm .hand , BarrettHand , sim = right_hand_sim , manipulator = self .right_arm ,
50
79
bhd_namespace = '/right' , ft_sim = right_ft_sim )
51
80
self .base = HerbBase (sim = segway_sim , robot = self )
@@ -56,7 +85,54 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
56
85
accel_limits [self .left_arm .GetArmIndices ()] = [ 2. ] * self .left_arm .GetArmDOF ()
57
86
accel_limits [self .right_arm .GetArmIndices ()] = [ 2. ] * self .right_arm .GetArmDOF ()
58
87
self .SetDOFAccelerationLimits (accel_limits )
59
-
88
+
89
+
90
+ # Determine always-on controllers
91
+
92
+ # hand controllers
93
+ if not left_hand_sim :
94
+ self .controllers_always_on .append ('left_hand_controller' )
95
+
96
+ if not right_hand_sim :
97
+ self .controllers_always_on .append ('right_hand_controller' )
98
+
99
+ # force/torque controllers
100
+ if not left_ft_sim or not right_ft_sim :
101
+ self .controllers_always_on .append ('force_torque_sensor_controller' )
102
+
103
+ if not left_ft_sim :
104
+ self .controllers_always_on .append ('left_tare_controller' )
105
+
106
+ if not right_ft_sim :
107
+ self .controllers_always_on .append ('right_tare_controller' )
108
+
109
+ # Set default manipulator controllers
110
+ # NOTE: head is ignored until TODO new Schunk head integrated
111
+ if not left_arm_sim :
112
+ controllers_manip .append (
113
+ 'left_gravity_compensation_controller' )
114
+ else :
115
+ self .left_arm .sim_controller = self .AttachController (name = self .left_arm .GetName (),
116
+ args = 'IdealController' ,
117
+ dof_indices = self .left_arm .GetArmIndices (),
118
+ affine_dofs = 0 ,
119
+ simulated = True )
120
+
121
+ if not right_arm_sim :
122
+ controllers_manip .append (
123
+ 'right_gravity_compensation_controller' )
124
+ else :
125
+ self .right_arm .sim_controller = self .AttachController (name = self .right_arm .GetName (),
126
+ args = 'IdealController' ,
127
+ dof_indices = self .right_arm .GetArmIndices (),
128
+ affine_dofs = 0 ,
129
+ simulated = True )
130
+
131
+ # load and activate initial controllers
132
+ if self .controller_manager is not None :
133
+ self .controller_manager .request (self .controllers_always_on +
134
+ controllers_manip ).switch ()
135
+
60
136
# Support for named configurations.
61
137
import os .path
62
138
self .configurations .add_group ('left_arm' , self .left_arm .GetArmIndices ())
@@ -66,7 +142,7 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
66
142
self .configurations .add_group ('right_hand' , self .right_hand .GetIndices ())
67
143
68
144
configurations_path = FindCatkinResource ('herbpy' , 'config/configurations.yaml' )
69
-
145
+
70
146
try :
71
147
self .configurations .load_yaml (configurations_path )
72
148
except IOError as e :
@@ -78,7 +154,7 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
78
154
for hand in [ self .left_hand , self .right_hand ]:
79
155
hand .configurations = ConfigurationLibrary ()
80
156
hand .configurations .add_group ('hand' , hand .GetIndices ())
81
-
157
+
82
158
if isinstance (hand , BarrettHand ):
83
159
hand_configs_path = FindCatkinResource ('herbpy' , 'config/barrett_preshapes.yaml' )
84
160
try :
@@ -88,7 +164,7 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
88
164
hand_configs_path ))
89
165
else :
90
166
logger .warn ('Unrecognized hand class. Not loading named configurations.' )
91
-
167
+
92
168
# Initialize a default planning pipeline.
93
169
from prpy .planning import (
94
170
FirstSupported ,
@@ -140,7 +216,7 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
140
216
if not (self .trajopt_planner or self .chomp_planner ):
141
217
raise PrPyException ('Unable to load both CHOMP and TrajOpt. At'
142
218
' least one of these packages is required.' )
143
-
219
+
144
220
actual_planner = Sequence (
145
221
# First, try the straight-line trajectory.
146
222
self .snap_planner ,
@@ -151,13 +227,13 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
151
227
self .trajopt_planner or self .chomp_planner
152
228
)
153
229
self .planner = FirstSupported (
154
- Sequence (actual_planner ,
230
+ Sequence (actual_planner ,
155
231
TSRPlanner (delegate_planner = actual_planner ),
156
232
self .cbirrt_planner ),
157
233
# Special purpose meta-planner.
158
234
NamedPlanner (delegate_planner = actual_planner ),
159
235
)
160
-
236
+
161
237
from prpy .planning .retimer import HauserParabolicSmoother
162
238
self .smoother = HauserParabolicSmoother (do_blend = True , do_shortcut = True )
163
239
self .retimer = HauserParabolicSmoother (do_blend = True , do_shortcut = False ,
@@ -220,14 +296,13 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
220
296
# Initialize herbpy ROS Node
221
297
import rospy
222
298
if not rospy .core .is_initialized ():
223
- rospy .init_node ('herbpy' , anonymous = True )
299
+ rospy .init_node ('herbpy' )
224
300
logger .debug ('Started ROS node with name "%s".' , rospy .get_name ())
225
301
226
302
import talker .msg
227
303
from actionlib import SimpleActionClient
228
304
self ._say_action_client = SimpleActionClient ('say' , talker .msg .SayAction )
229
305
230
-
231
306
def CloneBindings (self , parent ):
232
307
from prpy import Cloned
233
308
super (HERBRobot , self ).CloneBindings (parent )
@@ -242,35 +317,179 @@ def CloneBindings(self, parent):
242
317
self .planner = parent .planner
243
318
self .base_planner = parent .base_planner
244
319
245
- def ExecuteTrajectory (self , traj , * args , ** kwargs ):
246
- from prpy .exceptions import TrajectoryAborted
320
+ def _ExecuteTrajectory (self , traj , defer = False , timeout = None , period = 0.01 ,
321
+ ** kwargs ):
322
+ # Don't execute trajectories that don't have at least one waypoint.
323
+ if traj .GetNumWaypoints () <= 0 :
324
+ raise ValueError ('Trajectory must contain at least one waypoint.' )
325
+
326
+ # Check if this trajectory contains both affine and joint DOFs
327
+ cspec = traj .GetConfigurationSpecification ()
328
+ needs_base = prpy .util .HasAffineDOFs (cspec )
329
+ needs_joints = prpy .util .HasJointDOFs (cspec )
330
+ if needs_base and needs_joints :
331
+ raise ValueError ('Trajectories with affine and joint DOFs are not supported' )
332
+
333
+ # Check that the current configuration of the robot matches the
334
+ # initial configuration specified by the trajectory.
335
+ if not prpy .util .IsAtTrajectoryStart (self , traj ):
336
+ raise TrajectoryNotExecutable (
337
+ 'Trajectory started from different configuration than robot.' )
338
+
339
+ # If there was only one waypoint, at this point we are done!
340
+ if traj .GetNumWaypoints () == 1 :
341
+ if defer is True :
342
+ import trollius
343
+ future = trollius .Future ()
344
+ future .set_result (traj )
345
+ return future
346
+ else :
347
+ return traj
348
+
349
+ # Verify that the trajectory is timed by checking whether the first
350
+ # waypoint has a valid deltatime value.
351
+ if not prpy .util .IsTimedTrajectory (traj ):
352
+ raise ValueError ('Trajectory cannot be executed, it is not timed.' )
353
+
354
+ # Verify that the trajectory has non-zero duration.
355
+ if traj .GetDuration () <= 0.0 :
356
+ import warnings
357
+ warnings .warn ('Executing zero-length trajectory. Please update the'
358
+ ' function that produced this trajectory to return a'
359
+ ' single-waypoint trajectory.' , FutureWarning )
360
+
361
+ traj_manipulators = self .GetTrajectoryManipulators (traj )
362
+ controllers_manip = []
363
+ active_controllers = []
364
+ if self .head in traj_manipulators :
365
+ # TODO head after Schunk integration
366
+ import warnings
367
+ warnings .warn ('The head is currently disabled under ros_control.' )
368
+
369
+ if (self .right_arm in traj_manipulators and
370
+ self .left_arm in traj_manipulators ):
371
+ controllers_manip .append ('dual_arm_trajectory_controller' )
372
+ joints = []
373
+ joints .extend (self .right_arm .GetJointNames ())
374
+ joints .extend (self .left_arm .GetJointNames ())
375
+ active_controllers .append (
376
+ RewdOrTrajectoryController (self , '' ,
377
+ 'dual_arm_trajectory_controller' ,
378
+ joints ))
379
+ else :
380
+ if self .right_arm in traj_manipulators :
381
+ if not self .right_arm .IsSimulated ():
382
+ controllers_manip .append ('right_trajectory_controller' )
383
+ joints = []
384
+ joints .extend (self .right_arm .GetJointNames ())
385
+ active_controllers .append (
386
+ RewdOrTrajectoryController (self , '' ,
387
+ 'right_trajectory_controller' ,
388
+ joints ))
389
+ else :
390
+ active_controllers .append (self .right_arm .sim_controller )
391
+
392
+ if self .left_arm in traj_manipulators :
393
+ if not self .left_arm .IsSimulated ():
394
+ controllers_manip .append ('left_trajectory_controller' )
395
+ joints = []
396
+ joints .extend (self .left_arm .GetJointNames ())
397
+ active_controllers .append (
398
+ RewdOrTrajectoryController (self , '' ,
399
+ 'left_trajectory_controller' ,
400
+ joints ))
401
+ else :
402
+ active_controllers .append (self .left_arm .sim_controller )
403
+
404
+ # load and activate controllers
405
+ if not self .full_controller_sim :
406
+ self .controller_manager .request (self .controllers_always_on +
407
+ controllers_manip ).switch ()
408
+
409
+ if needs_base :
410
+ if (hasattr (self , 'base' ) and hasattr (self .base , 'controller' ) and
411
+ self .base .controller is not None ):
412
+ active_controllers .append (self .base .controller )
413
+ else :
414
+ logger .warning (
415
+ 'Trajectory includes the base, but no base controller is'
416
+ ' available. Is self.base.controller set?' )
247
417
248
- active_manipulators = self .GetTrajectoryManipulators (traj )
418
+ for controller in active_controllers :
419
+ controller .SetPath (traj )
249
420
250
- for manipulator in active_manipulators :
251
- manipulator .ClearTrajectoryStatus ()
421
+ if defer is True :
422
+ import time
423
+ import trollius
252
424
253
- value = super (HERBRobot , self ).ExecuteTrajectory (traj , * args , ** kwargs )
425
+ @trollius .coroutine
426
+ def do_poll ():
427
+ time_stop = time .time () + (timeout if timeout else numpy .inf )
254
428
255
- for manipulator in active_manipulators :
256
- status = manipulator .GetTrajectoryStatus ()
257
- if status == 'aborted' :
258
- raise TrajectoryAborted ()
429
+ while time .time () <= time_stop :
430
+ is_done = all (controller .IsDone ()
431
+ for controller in active_controllers )
432
+ if is_done :
433
+ raise trollius .Return (traj )
434
+
435
+ yield trollius .From (trollius .sleep (period ))
436
+
437
+ raise trollius .Return (None )
438
+
439
+ return trollius .async (do_poll ())
440
+ elif defer is False :
441
+ prpy .util .WaitForControllers (active_controllers , timeout = timeout )
442
+ return traj
443
+ else :
444
+ raise ValueError ('Received unexpected value "{:s}" for defer.'
445
+ .format (str (defer )))
446
+
447
+ def ExecuteTrajectory (self , traj , * args , ** kwargs ):
448
+ # from prpy.exceptions import TrajectoryAborted
449
+
450
+ value = self ._ExecuteTrajectory (traj , * args , ** kwargs )
451
+
452
+ # TODO meaningful to do this check here?
453
+ # if so can be done on value future
454
+ #
455
+ # for manipulator in active_manipulators:
456
+ # status = manipulator.GetTrajectoryStatus()
457
+ # if status == 'aborted':
458
+ # raise TrajectoryAborted()
259
459
260
460
return value
261
461
262
462
# Inherit docstring from the parent class.
263
463
ExecuteTrajectory .__doc__ = Robot .ExecuteTrajectory .__doc__
264
464
265
- def SetStiffness (self , stiffness ):
465
+ def SetStiffness (self , stiffness , manip = None ):
266
466
"""Set the stiffness of HERB's arms and head.
267
- Zero is gravity compensation, one is position control. Stifness values
268
- between zero and one are experimental .
269
- @param stiffness value between zero and one
467
+ Stiffness False/0 is gravity compensation and stiffness True/(>0) is position
468
+ control .
469
+ @param stiffness boolean or decimal value 0.0 to 1.0
270
470
"""
271
- self .head .SetStiffness (stiffness )
272
- self .left_arm .SetStiffness (stiffness )
273
- self .right_arm .SetStiffness (stiffness )
471
+ if isinstance (stiffness , numbers .Number ) and not (0 <= stiffness <= 1 ):
472
+ raise Exception ('Stiffness must be boolean or decimal in the range [0, 1];'
473
+ 'got {}.' .format (stiffness ))
474
+ # TODO head after Schunk integration
475
+ new_manip_controllers = []
476
+ if stiffness :
477
+ if not self .left_arm_sim and (manip is None or manip is self .left_arm ):
478
+ new_manip_controllers .append (
479
+ 'left_position_controller' )
480
+ if not self .right_arm_sim and (manip is None or manip is self .right_arm ):
481
+ new_manip_controllers .append (
482
+ 'right_position_controller' )
483
+ else :
484
+ if not self .left_arm_sim and (manip is None or manip is self .left_arm ):
485
+ new_manip_controllers .append (
486
+ 'left_gravity_compensation_controller' )
487
+ if not self .right_arm_sim and (manip is None or manip is self .right_arm ):
488
+ new_manip_controllers .append (
489
+ 'right_gravity_compensation_controller' )
490
+
491
+ self .controller_manager .request (self .controllers_always_on +
492
+ new_manip_controllers ).switch ()
274
493
275
494
def Say (self , words , block = True ):
276
495
"""Speak 'words' using talker action service or espeak locally in simulation"""
0 commit comments