$search
00001 #!/usr/bin/env python 00002 00003 # SVN $HeadURL: http://alufr-ros-pkg.googlecode.com/svn/trunk/humanoid_stacks/nao_robot/nao_driver/scripts/nao_controller.py $ 00004 # SVN $Id: nao_controller.py 2737 2012-05-20 20:20:00Z hornunga@informatik.uni-freiburg.de $ 00005 00006 00007 # 00008 # ROS node to provide joint angle control to Nao by wrapping NaoQI 00009 # This code is currently compatible to NaoQI version 1.6 or newer (latest 00010 # tested: 1.10) 00011 # 00012 # Copyright 2011 Armin Hornung, University of Freiburg 00013 # http://www.ros.org/wiki/nao 00014 # 00015 # Redistribution and use in source and binary forms, with or without 00016 # modification, are permitted provided that the following conditions are met: 00017 # 00018 # # Redistributions of source code must retain the above copyright 00019 # notice, this list of conditions and the following disclaimer. 00020 # # Redistributions in binary form must reproduce the above copyright 00021 # notice, this list of conditions and the following disclaimer in the 00022 # documentation and/or other materials provided with the distribution. 00023 # # Neither the name of the University of Freiburg nor the names of its 00024 # contributors may be used to endorse or promote products derived from 00025 # this software without specific prior written permission. 00026 # 00027 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00028 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00029 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00030 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00031 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00032 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00033 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00034 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00035 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00036 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00037 # POSSIBILITY OF SUCH DAMAGE. 00038 # 00039 00040 import roslib 00041 00042 roslib.load_manifest('nao_driver') 00043 import rospy 00044 00045 import actionlib 00046 import nao_msgs.msg 00047 from nao_msgs.msg import JointTrajectoryGoal, JointTrajectoryResult, JointTrajectoryAction, JointAnglesWithSpeed, \ 00048 JointAnglesWithSpeedGoal, JointAnglesWithSpeedResult, JointAnglesWithSpeedAction 00049 00050 from nao_driver import * 00051 00052 import math 00053 from math import fabs 00054 00055 from std_msgs.msg import String 00056 from std_srvs.srv import Empty, EmptyResponse 00057 from sensor_msgs.msg import JointState 00058 00059 class NaoController(NaoNode): 00060 def __init__(self): 00061 NaoNode.__init__(self) 00062 00063 # ROS initialization: 00064 rospy.init_node('nao_controller') 00065 00066 self.connectNaoQi() 00067 00068 # store the number of joints in each motion chain and collection, used for sanity checks 00069 self.collectionSize = {} 00070 for collectionName in ['Head', 'LArm', 'LLeg', 'RLeg', 'RArm', 'Body', 'BodyJoints', 'BodyActuators']: 00071 self.collectionSize[collectionName] = len(self.motionProxy.getJointNames(collectionName)); 00072 00073 00074 # initial stiffness (defaults to 0 so it doesn't strain the robot when no teleoperation is running) 00075 # set to 1.0 if you want to control the robot immediately 00076 initStiffness = rospy.get_param('~init_stiffness', 0.0) 00077 00078 # TODO: parameterize 00079 if initStiffness > 0.0 and initStiffness <= 1.0: 00080 self.motionProxy.stiffnessInterpolation('Body', initStiffness, 0.5) 00081 00082 self.jointTrajectoryServer = actionlib.SimpleActionServer("joint_trajectory", JointTrajectoryAction, 00083 execute_cb=self.executeJointTrajectoryAction, 00084 auto_start=False) 00085 00086 self.jointStiffnessServer = actionlib.SimpleActionServer("joint_stiffness_trajectory", JointTrajectoryAction, 00087 execute_cb=self.executeJointStiffnessAction, 00088 auto_start=False) 00089 00090 self.jointAnglesServer = actionlib.SimpleActionServer("joint_angles_action", JointAnglesWithSpeedAction, 00091 execute_cb=self.executeJointAnglesWithSpeedAction, 00092 auto_start=False) 00093 00094 00095 # start services / actions: 00096 self.enableStiffnessSrv = rospy.Service("body_stiffness/enable", Empty, self.handleStiffnessSrv) 00097 self.disableStiffnessSrv = rospy.Service("body_stiffness/disable", Empty, self.handleStiffnessOffSrv) 00098 00099 self.jointTrajectoryServer.start() 00100 self.jointStiffnessServer.start() 00101 self.jointAnglesServer.start() 00102 00103 # subsribers last: 00104 rospy.Subscriber("joint_angles", JointAnglesWithSpeed, self.handleJointAngles, queue_size=10) 00105 rospy.Subscriber("joint_stiffness", JointState, self.handleJointStiffness, queue_size=10) 00106 00107 rospy.loginfo("nao_controller initialized") 00108 00109 def connectNaoQi(self): 00110 '''(re-) connect to NaoQI''' 00111 rospy.loginfo("Connecting to NaoQi at %s:%d", self.pip, self.pport) 00112 00113 self.motionProxy = self.getProxy("ALMotion") 00114 if self.motionProxy is None: 00115 exit(1) 00116 00117 def handleJointAngles(self, msg): 00118 rospy.logdebug("Received a joint angle target") 00119 try: 00120 # Note: changeAngles() and setAngles() are non-blocking functions. 00121 if (msg.relative==0): 00122 self.motionProxy.setAngles(list(msg.joint_names), list(msg.joint_angles), msg.speed) 00123 else: 00124 self.motionProxy.changeAngles(list(msg.joint_names), list(msg.joint_angles), msg.speed) 00125 except RuntimeError,e: 00126 rospy.logerr("Exception caught:\n%s", e) 00127 00128 def handleJointStiffness(self, msg): 00129 rospy.logdebug("Received a joint angle stiffness") 00130 try: 00131 self.motionProxy.setStiffnesses(list(msg.name), list(msg.effort)) 00132 except RuntimeError,e: 00133 rospy.logerr("Exception caught:\n%s", e) 00134 00135 def handleStiffnessSrv(self, req): 00136 try: 00137 self.motionProxy.stiffnessInterpolation("Body", 1.0, 0.5) 00138 rospy.loginfo("Body stiffness enabled") 00139 return EmptyResponse() 00140 except RuntimeError,e: 00141 rospy.logerr("Exception caught:\n%s", e) 00142 return None 00143 00144 def handleStiffnessOffSrv(self, req): 00145 try: 00146 self.motionProxy.stiffnessInterpolation("Body", 0.0, 0.5) 00147 rospy.loginfo("Body stiffness removed") 00148 return EmptyResponse() 00149 except RuntimeError,e: 00150 rospy.logerr("Exception caught:\n%s", e) 00151 return None 00152 00153 00154 def jointTrajectoryGoalMsgToAL(self, goal): 00155 """Helper, convert action goal msg to Aldebraran-style arrays for NaoQI""" 00156 names = list(goal.trajectory.joint_names) 00157 # if goal.trajectory.joint_names == ["Body"]: 00158 # names = self.motionProxy.getJointNames('Body') 00159 00160 if len(goal.trajectory.points) == 1 and len(goal.trajectory.points[0].positions) == 1: 00161 angles = goal.trajectory.points[0].positions[0] 00162 else: 00163 angles = list(list(p.positions[i] for p in goal.trajectory.points) for i in range(0,len(goal.trajectory.points[0].positions))) 00164 00165 #strip 6,7 and last 2 from angles if the pose was for H25 but we're running an H21 00166 if not isinstance(angles, float) and len(angles) > self.collectionSize["Body"]: 00167 rospy.loginfo("Stripping angles from %d down to %d", len(angles), self.collectionSize["Body"]) 00168 angles.pop(6) 00169 angles.pop(7) 00170 angles.pop() 00171 angles.pop() 00172 00173 if len(names) > self.collectionSize["Body"]: 00174 rospy.loginfo("Stripping names from %d down to %d", len(names), self.collectionSize["Body"]) 00175 names.pop(6) 00176 names.pop(7) 00177 names.pop() 00178 names.pop() 00179 00180 times = list(p.time_from_start.to_sec() for p in goal.trajectory.points) 00181 if len(times) == 1 and len(names) == 1: 00182 times = times[0] 00183 if (len(names) > 1): 00184 times = [times]*len(names) 00185 00186 return (names, angles, times) 00187 00188 00189 def executeJointTrajectoryAction(self, goal): 00190 rospy.loginfo("JointTrajectory action executing"); 00191 00192 names, angles, times = self.jointTrajectoryGoalMsgToAL(goal) 00193 00194 rospy.logdebug("Received trajectory for joints: %s times: %s", str(names), str(times)) 00195 rospy.logdebug("Trajectory angles: %s", str(angles)) 00196 00197 # exceptions will be thrown in simple_action_server's execute loop 00198 self.motionProxy.angleInterpolation(names, angles, times, (goal.relative==0)) 00199 00200 jointTrajectoryResult = JointTrajectoryResult() 00201 jointTrajectoryResult.goal_position.header.stamp = rospy.Time.now() 00202 jointTrajectoryResult.goal_position.position = self.motionProxy.getAngles(names, True) 00203 jointTrajectoryResult.goal_position.name = names 00204 00205 if not self.checkJointsLen(jointTrajectoryResult.goal_position): 00206 rospy.logerr("JointTrajectory action error in result: sizes mismatch") 00207 00208 self.jointTrajectoryServer.set_succeeded(jointTrajectoryResult) 00209 rospy.loginfo("JointTrajectory action done"); 00210 00211 def executeJointStiffnessAction(self, goal): 00212 rospy.loginfo("JointStiffness action executing"); 00213 00214 names, angles, times = self.jointTrajectoryGoalMsgToAL(goal) 00215 00216 rospy.logdebug("Received stiffness trajectory for joints: %s times: %s", str(names), str(times)) 00217 rospy.logdebug("stiffness values: %s", str(angles)) 00218 00219 # exceptions will be thrown in simple_action_server's execute loop 00220 self.motionProxy.stiffnessInterpolation(names, angles, times) 00221 00222 jointStiffnessResult = JointTrajectoryResult() 00223 jointStiffnessResult.goal_position.header.stamp = rospy.Time.now() 00224 jointStiffnessResult.goal_position.position = self.motionProxy.getStiffnesses(names) 00225 jointStiffnessResult.goal_position.name = names 00226 00227 if not self.checkJointsLen(jointStiffnessResult.goal_position): 00228 rospy.logerr("JointTrajectory action error in result: sizes mismatch") 00229 00230 self.jointStiffnessServer.set_succeeded(jointStiffnessResult) 00231 rospy.loginfo("JointStiffness action done"); 00232 00233 def executeJointAnglesWithSpeedAction(self, goal): 00234 00235 names = list(goal.joint_angles.joint_names) 00236 angles = list(goal.joint_angles.joint_angles) 00237 rospy.logdebug("Received JointAnglesWithSpeed for joints: %s angles: %s", str(names), str(angles)) 00238 00239 if goal.joint_angles.relative == 1: 00240 # TODO: this uses the current angles instead of the angles at the given timestamp 00241 currentAngles = self.motionProxy.getAngles(names, True) 00242 angles = list(map(lambda x,y: x+y, angles, currentAngles)) 00243 00244 # exceptions will be thrown in simple_action_server's execute loop 00245 self.motionProxy.angleInterpolationWithSpeed(names, angles, goal.joint_angles.speed) 00246 00247 jointAnglesWithSpeedResult = JointAnglesWithSpeedResult() 00248 jointAnglesWithSpeedResult.goal_position.header.stamp = rospy.Time.now() 00249 jointAnglesWithSpeedResult.goal_position.position = self.motionProxy.getAngles(names, True) 00250 jointAnglesWithSpeedResult.goal_position.name = names 00251 00252 if not self.checkJointsLen(jointAnglesWithSpeedResult.goal_position): 00253 rospy.logerr("JointAnglesWithSpeed action error in result: sizes mismatch") 00254 00255 self.jointAnglesServer.set_succeeded(jointAnglesWithSpeedResult) 00256 rospy.loginfo("JointAnglesWithSpeed action done"); 00257 00258 def checkJointsLen(self, goal_position): 00259 if len(goal_position.name) == 1 and self.collectionSize.has_key(goal_position.name[0]): 00260 return len(goal_position.position) == self.collectionSize[goal_position.name[0]] 00261 else: 00262 return len(goal_position.position) == len(goal_position.name) 00263 00264 00265 00266 if __name__ == '__main__': 00267 00268 controller = NaoController() 00269 rospy.loginfo("nao_controller running...") 00270 rospy.spin() 00271 00272 rospy.loginfo("nao_controller stopped.") 00273 exit(0)