Skip to content

Commit 950cecf

Browse files
committed
full migration to ros_control, minus head
1 parent c7a3795 commit 950cecf

File tree

2 files changed

+249
-29
lines changed

2 files changed

+249
-29
lines changed

src/herbpy/herbpantilt.py

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ def MoveTo(self, target_dof_values, execute=True, **kw_args):
9595
@param **kw_args keyword arguments passed to \p robot.ExecuteTrajectory
9696
@return pantilt trajectory
9797
"""
98+
raise NotImplementedError('The head is currently disabled under ros_control.')
9899
# Update the controllers to get new joint values.
99100
robot = self.GetRobot()
100101
with robot.GetEnv():

src/herbpy/herbrobot.py

+248-29
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
PACKAGE = 'herbpy'
22
import logging
33
import openravepy
4+
import numbers
5+
import numpy
46
import prpy
57
import prpy.rave, prpy.util
68
from prpy.base.barretthand import BarrettHand
79
from prpy.base.wam import WAM
810
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
1013
from prpy.planning.base import UnsupportedPlanningError
1114
from herbbase import HerbBase
1215
from herbpantilt import HERBPantilt
16+
from ros_control_client_py import ControllerManagerClient
1317

1418
logger = logging.getLogger('herbpy')
1519

@@ -30,6 +34,31 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
3034

3135
Robot.__init__(self, robot_name='herb')
3236

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+
3362
# Convenience attributes for accessing self components.
3463
self.left_arm = self.GetManipulator('left')
3564
self.right_arm = self.GetManipulator('right')
@@ -38,14 +67,14 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
3867
self.right_arm.hand = self.right_arm.GetEndEffector()
3968
self.left_hand = self.left_arm.hand
4069
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]
4271

4372
# Dynamically switch to self-specific subclasses.
4473
prpy.bind_subclass(self.left_arm, WAM, sim=left_arm_sim, namespace='/left')
4574
prpy.bind_subclass(self.right_arm, WAM, sim=right_arm_sim, namespace='/right')
4675
prpy.bind_subclass(self.head, HERBPantilt, sim=head_sim, owd_namespace='/head/owd')
4776
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)
4978
prpy.bind_subclass(self.right_arm.hand, BarrettHand, sim=right_hand_sim, manipulator=self.right_arm,
5079
bhd_namespace='/right', ft_sim=right_ft_sim)
5180
self.base = HerbBase(sim=segway_sim, robot=self)
@@ -56,7 +85,54 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
5685
accel_limits[self.left_arm.GetArmIndices()] = [ 2. ] * self.left_arm.GetArmDOF()
5786
accel_limits[self.right_arm.GetArmIndices()] = [ 2. ] * self.right_arm.GetArmDOF()
5887
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+
60136
# Support for named configurations.
61137
import os.path
62138
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,
66142
self.configurations.add_group('right_hand', self.right_hand.GetIndices())
67143

68144
configurations_path = FindCatkinResource('herbpy', 'config/configurations.yaml')
69-
145+
70146
try:
71147
self.configurations.load_yaml(configurations_path)
72148
except IOError as e:
@@ -78,7 +154,7 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
78154
for hand in [ self.left_hand, self.right_hand ]:
79155
hand.configurations = ConfigurationLibrary()
80156
hand.configurations.add_group('hand', hand.GetIndices())
81-
157+
82158
if isinstance(hand, BarrettHand):
83159
hand_configs_path = FindCatkinResource('herbpy', 'config/barrett_preshapes.yaml')
84160
try:
@@ -88,7 +164,7 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
88164
hand_configs_path))
89165
else:
90166
logger.warn('Unrecognized hand class. Not loading named configurations.')
91-
167+
92168
# Initialize a default planning pipeline.
93169
from prpy.planning import (
94170
FirstSupported,
@@ -140,7 +216,7 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
140216
if not (self.trajopt_planner or self.chomp_planner):
141217
raise PrPyException('Unable to load both CHOMP and TrajOpt. At'
142218
' least one of these packages is required.')
143-
219+
144220
actual_planner = Sequence(
145221
# First, try the straight-line trajectory.
146222
self.snap_planner,
@@ -151,13 +227,13 @@ def __init__(self, left_arm_sim, right_arm_sim, right_ft_sim,
151227
self.trajopt_planner or self.chomp_planner
152228
)
153229
self.planner = FirstSupported(
154-
Sequence(actual_planner,
230+
Sequence(actual_planner,
155231
TSRPlanner(delegate_planner=actual_planner),
156232
self.cbirrt_planner),
157233
# Special purpose meta-planner.
158234
NamedPlanner(delegate_planner=actual_planner),
159235
)
160-
236+
161237
from prpy.planning.retimer import HauserParabolicSmoother
162238
self.smoother = HauserParabolicSmoother(do_blend=True, do_shortcut=True)
163239
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,
220296
# Initialize herbpy ROS Node
221297
import rospy
222298
if not rospy.core.is_initialized():
223-
rospy.init_node('herbpy', anonymous=True)
299+
rospy.init_node('herbpy')
224300
logger.debug('Started ROS node with name "%s".', rospy.get_name())
225301

226302
import talker.msg
227303
from actionlib import SimpleActionClient
228304
self._say_action_client = SimpleActionClient('say', talker.msg.SayAction)
229305

230-
231306
def CloneBindings(self, parent):
232307
from prpy import Cloned
233308
super(HERBRobot, self).CloneBindings(parent)
@@ -242,35 +317,179 @@ def CloneBindings(self, parent):
242317
self.planner = parent.planner
243318
self.base_planner = parent.base_planner
244319

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?')
247417

248-
active_manipulators = self.GetTrajectoryManipulators(traj)
418+
for controller in active_controllers:
419+
controller.SetPath(traj)
249420

250-
for manipulator in active_manipulators:
251-
manipulator.ClearTrajectoryStatus()
421+
if defer is True:
422+
import time
423+
import trollius
252424

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)
254428

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()
259459

260460
return value
261461

262462
# Inherit docstring from the parent class.
263463
ExecuteTrajectory.__doc__ = Robot.ExecuteTrajectory.__doc__
264464

265-
def SetStiffness(self, stiffness):
465+
def SetStiffness(self, stiffness, manip=None):
266466
"""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
270470
"""
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()
274493

275494
def Say(self, words, block=True):
276495
"""Speak 'words' using talker action service or espeak locally in simulation"""

0 commit comments

Comments
 (0)