00001
00002
00003 """
00004 simple_arm_server.py - this is a simple way to move an arm
00005 Copyright (c) 2011 Michael Ferguson. All right reserved.
00006
00007 Redistribution and use in source and binary forms, with or without
00008 modification, are permitted provided that the following conditions are met:
00009
00010 * Redistributions of source code must retain the above copyright
00011 notice, this list of conditions and the following disclaimer.
00012 * Redistributions in binary form must reproduce the above copyright
00013 notice, this list of conditions and the following disclaimer in the
00014 documentation and/or other materials provided with the distribution.
00015 * Neither the name of the copyright holders nor the names of its
00016 contributors may be used to endorse or promote products derived
00017 from this software without specific prior written permission.
00018
00019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022 DISCLAIMED. IN NO EVENT SHALL VANADIUM LABS BE LIABLE FOR ANY DIRECT, INDIRECT,
00023 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00024 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
00025 OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00026 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
00027 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00028 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 """
00030
00031 import roslib; roslib.load_manifest('simple_arm_server')
00032 import rospy, actionlib
00033
00034 from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
00035 from control_msgs.msg import *
00036
00037 import tf
00038 from tf.transformations import euler_from_quaternion, quaternion_from_euler
00039
00040 from actionlib_msgs.msg import GoalStatus
00041 from kinematics_msgs.msg import KinematicSolverInfo, PositionIKRequest, PositionIKRequest
00042 from kinematics_msgs.srv import GetKinematicSolverInfo, GetPositionIK, GetPositionIKRequest, GetConstraintAwarePositionIK, GetConstraintAwarePositionIKRequest
00043 from geometry_msgs.msg import PoseStamped
00044 from std_msgs.msg import Float64
00045 from sensor_msgs.msg import JointState
00046 from simple_arm_server.msg import *
00047 from simple_arm_server.srv import *
00048
00049 from arm_navigation_msgs.srv import *
00050
00051 from math import *
00052
00053 class SimpleArmServer:
00054 """
00055 A class to move a 4/5/6DOF arm to a designated location (x,y,z),
00056 respecting wrist rotation (roll) and gripper rotation (pitch).
00057 """
00058
00059 def __init__(self):
00060 rospy.init_node("simple_arm_server")
00061
00062
00063 self.dof = rospy.get_param("~arm_dof", 5)
00064 self.root = rospy.get_param("~root_name","base_link")
00065 self.tip = rospy.get_param("~tip_name","gripper_link")
00066 self.timeout = rospy.get_param("~timeout",5.0)
00067
00068
00069 rospy.wait_for_service('arm_kinematics/get_constraint_aware_ik')
00070 rospy.wait_for_service('arm_kinematics/get_ik_solver_info')
00071 self._get_ik_proxy = rospy.ServiceProxy('arm_kinematics/get_constraint_aware_ik', GetConstraintAwarePositionIK, persistent=True)
00072 self._get_ik_solver_info_proxy = rospy.ServiceProxy('arm_kinematics/get_ik_solver_info', GetKinematicSolverInfo)
00073
00074
00075 rospy.wait_for_service('/environment_server/set_planning_scene_diff')
00076 self._setPlanningDiff = rospy.ServiceProxy('/environment_server/set_planning_scene_diff', SetPlanningSceneDiff)
00077
00078
00079 self._listener = tf.TransformListener()
00080
00081
00082 self._client = actionlib.SimpleActionClient('follow_joint_trajectory', FollowJointTrajectoryAction)
00083 self._client.wait_for_server()
00084 self._gripper = rospy.Publisher('gripper_controller/command', Float64)
00085
00086
00087 self.servos = dict()
00088 rospy.Subscriber('joint_states', JointState, self.stateCb)
00089
00090
00091 self.server = actionlib.SimpleActionServer("move_arm", MoveArmAction, execute_cb=self.actionCb, auto_start=False)
00092 self.server.start()
00093
00094
00095 rospy.Service('~get_trajectory_validity', GetTrajectoryValidity, self.validityCb)
00096
00097 rospy.loginfo('simple_arm_server node started')
00098 rospy.spin()
00099
00100
00101 def validityCb(self, req):
00102 """ Check a trajectory for validity. """
00103 for action in req.motions:
00104 if action.type == ArmAction.MOVE_ARM:
00105 if self.motionToTrajectory(action, req.header.frame_id) == None:
00106 return GetTrajectoryValidityResponse(False)
00107 return GetTrajectoryValidityResponse(True)
00108
00109
00110 def stateCb(self, msg):
00111 """ Update our known location of joints, for interpolation. """
00112 for i in range(len(msg.name)):
00113 joint = msg.name[i]
00114 self.servos[joint] = msg.position[i]
00115
00116
00117 def actionCb(self, req):
00118 """ Given pose to move gripper to, do it. """
00119 self.arm_solver_info = self._get_ik_solver_info_proxy()
00120 self._setPlanningDiff(SetPlanningSceneDiffRequest())
00121 computed_actions = list()
00122
00123
00124 for action in req.motions:
00125 if self.server.is_preempt_requested():
00126 self.server.set_preempted( MoveArmResult(False) )
00127 return
00128 if action.type == ArmAction.MOVE_ARM:
00129 msg = self.motionToTrajectory(action, req.header.frame_id)
00130 if msg != None:
00131 computed_actions.append( [ 'move', msg, msg.points[0].time_from_start ] )
00132 else:
00133 self.server.set_aborted( MoveArmResult(False) )
00134 return
00135 else:
00136 computed_actions.append( [ 'grip', Float64(action.command), action.move_time ] )
00137
00138
00139 traj = None
00140 for action in computed_actions:
00141 msg = action[1]
00142 time = action[2]
00143 if action[0] == 'move':
00144
00145 if traj == None:
00146 traj = msg
00147 else:
00148 msg.points[0].time_from_start += traj.points[-1].time_from_start
00149 traj.points.append(msg.points[0])
00150 else:
00151
00152 if traj != None:
00153 if not self.executeTrajectory(traj):
00154 return
00155 traj = None
00156
00157 rospy.loginfo("Move gripper to " + str(msg.data))
00158 self._gripper.publish( msg )
00159 rospy.sleep( time )
00160 if traj != None:
00161 print traj
00162 if not self.executeTrajectory(traj):
00163 return
00164
00165 self.server.set_succeeded( MoveArmResult(True) )
00166 return
00167
00168
00169 def executeTrajectory(self, traj):
00170 """ Execute a trajectory. """
00171 goal = FollowJointTrajectoryGoal()
00172 goal.trajectory = traj
00173 rospy.loginfo("Sending action with " + str(len(traj.points)) + " points.")
00174 self._client.send_goal(goal)
00175 while self._client.get_state() != GoalStatus.SUCCEEDED:
00176 if self.server.is_preempt_requested():
00177 self._client.cancel_goal()
00178 self.server.set_preempted( MoveArmResult(False) )
00179 return False
00180 rospy.loginfo("Action succeeded: " + str(self._client.wait_for_result()))
00181 return True
00182
00183
00184 def motionToTrajectory(self, action, frame_id):
00185 """ Convert an arm movement action into a trajectory. """
00186 ps = PoseStamped()
00187 ps.header.frame_id = frame_id
00188 ps.pose = action.goal
00189 pose = self._listener.transformPose(self.root, ps)
00190 rospy.loginfo("Parsing move to:\n" + str(pose))
00191
00192
00193 request = GetConstraintAwarePositionIKRequest()
00194 request.timeout = rospy.Duration(self.timeout)
00195
00196 request.ik_request.pose_stamped.header.frame_id = self.root;
00197 request.ik_request.ik_link_name = self.tip;
00198 request.ik_request.pose_stamped.pose.position.x = pose.pose.position.x
00199 request.ik_request.pose_stamped.pose.position.y = pose.pose.position.y
00200 request.ik_request.pose_stamped.pose.position.z = pose.pose.position.z
00201
00202 e = euler_from_quaternion([pose.pose.orientation.x,pose.pose.orientation.y,pose.pose.orientation.z,pose.pose.orientation.w])
00203 e = [i for i in e]
00204 if self.dof < 6:
00205
00206 e[2] = atan2(pose.pose.position.y, pose.pose.position.x)
00207 if self.dof < 5:
00208
00209 e[0] = 0
00210 q = quaternion_from_euler(e[0], e[1], e[2])
00211
00212 request.ik_request.pose_stamped.pose.orientation.x = q[0]
00213 request.ik_request.pose_stamped.pose.orientation.y = q[1]
00214 request.ik_request.pose_stamped.pose.orientation.z = q[2]
00215 request.ik_request.pose_stamped.pose.orientation.w = q[3]
00216
00217 request.ik_request.ik_seed_state.joint_state.name = self.arm_solver_info.kinematic_solver_info.joint_names
00218 request.ik_request.ik_seed_state.joint_state.position = [self.servos[joint] for joint in request.ik_request.ik_seed_state.joint_state.name]
00219
00220
00221 tries = 0
00222 pitch = e[1]
00223 while tries < 80:
00224 try:
00225 response = self._get_ik_proxy(request)
00226 if response.error_code.val == response.error_code.SUCCESS:
00227 break
00228 else:
00229 tries += 1
00230
00231 pitch = e[1] + ((-1)**tries)*((tries+1)/2)*0.025
00232
00233 q = quaternion_from_euler(e[0], pitch, e[2])
00234 request.ik_request.pose_stamped.pose.orientation.x = q[0]
00235 request.ik_request.pose_stamped.pose.orientation.y = q[1]
00236 request.ik_request.pose_stamped.pose.orientation.z = q[2]
00237 request.ik_request.pose_stamped.pose.orientation.w = q[3]
00238 except rospy.ServiceException, e:
00239 rospy.logerr("Service did not process request: %s"%str(e))
00240
00241 if response.error_code.val == response.error_code.SUCCESS:
00242 arm_solver_info = self._get_ik_solver_info_proxy()
00243 msg = JointTrajectory()
00244 msg.joint_names = arm_solver_info.kinematic_solver_info.joint_names
00245 msg.points = list()
00246 point = JointTrajectoryPoint()
00247 point.positions = [ 0.0 for servo in msg.joint_names ]
00248 point.velocities = [ 0.0 for servo in msg.joint_names ]
00249 for joint in request.ik_request.ik_seed_state.joint_state.name:
00250 i = msg.joint_names.index(joint)
00251 point.positions[i] = response.solution.joint_state.position[response.solution.joint_state.name.index(joint)]
00252 if action.move_time > rospy.Duration(0.0):
00253 point.time_from_start = action.move_time
00254 else:
00255 point.time_from_start = rospy.Duration(5.0)
00256 msg.points.append(point)
00257 msg.header.stamp = rospy.Time.now() + rospy.Duration(0.01)
00258
00259 return msg
00260 else:
00261 return None
00262
00263
00264 if __name__ == '__main__':
00265 try:
00266 SimpleArmServer()
00267 except rospy.ROSInterruptException:
00268 rospy.loginfo("And that's all folks...")
00269