pose_controller.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 #
00004 # ROS node to provide joint angle control to Nao by wrapping NaoQI
00005 # This code is currently compatible to NaoQI version 1.6 or newer (latest
00006 # tested: 1.12)
00007 #
00008 # Copyright 2011 Armin Hornung, University of Freiburg
00009 # http://www.ros.org/wiki/nao
00010 #
00011 # Redistribution and use in source and binary forms, with or without
00012 # modification, are permitted provided that the following conditions are met:
00013 #
00014 #    # Redistributions of source code must retain the above copyright
00015 #       notice, this list of conditions and the following disclaimer.
00016 #    # Redistributions in binary form must reproduce the above copyright
00017 #       notice, this list of conditions and the following disclaimer in the
00018 #       documentation and/or other materials provided with the distribution.
00019 #    # Neither the name of the University of Freiburg nor the names of its
00020 #       contributors may be used to endorse or promote products derived from
00021 #       this software without specific prior written permission.
00022 #
00023 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00027 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00029 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00030 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00031 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00032 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 # POSSIBILITY OF SUCH DAMAGE.
00034 #
00035 
00036 
00037 import rospy
00038 import actionlib
00039 from naoqi_bridge_msgs.msg import(
00040     JointTrajectoryResult,
00041     JointTrajectoryAction,
00042     JointAnglesWithSpeed,
00043     JointAnglesWithSpeedResult,
00044     JointAnglesWithSpeedAction,
00045     BodyPoseWithSpeedAction,
00046     BodyPoseWithSpeedGoal,
00047     BodyPoseWithSpeedResult)
00048 
00049 from naoqi_driver.naoqi_node import NaoqiNode
00050 
00051 from std_msgs.msg import String
00052 from std_srvs.srv import Empty, EmptyResponse, Trigger, TriggerResponse
00053 from sensor_msgs.msg import JointState
00054 
00055 class PoseController(NaoqiNode):
00056     def __init__(self):
00057         NaoqiNode.__init__(self, 'pose_controller')
00058 
00059         self.connectNaoQi()
00060 
00061         self.rate = rospy.Rate(10) 
00062 
00063         # store the number of joints in each motion chain and collection, used for sanity checks
00064         self.collectionSize = {}
00065         for collectionName in ['Head', 'LArm', 'LLeg', 'RLeg', 'RArm', 'Body', 'BodyJoints', 'BodyActuators']:
00066             try:
00067                 self.collectionSize[collectionName] = len(self.motionProxy.getJointNames(collectionName));
00068             except RuntimeError:
00069                 # the following is useful for old NAOs with no legs/arms
00070                 rospy.logwarn('Collection %s not found on your robot.' % collectionName)
00071 
00072         # Get poll rate for actionlib (ie. how often to check whether currently running task has been preempted)
00073         # Defaults to 200ms
00074         self.poll_rate = int(rospy.get_param("~poll_rate", 0.2)*1000)
00075 
00076         # initial stiffness (defaults to 0 so it doesn't strain the robot when no teleoperation is running)
00077         # set to 1.0 if you want to control the robot immediately
00078         initStiffness = rospy.get_param('~init_stiffness', 0.0)
00079 
00080         # TODO: parameterize
00081         if initStiffness > 0.0 and initStiffness <= 1.0:
00082             self.motionProxy.stiffnessInterpolation('Body', initStiffness, 0.5)
00083 
00084         # advertise topics:
00085         self.getLifeStatePub = rospy.Publisher("get_life_state", String, queue_size=10)
00086 
00087         # start services / actions:
00088         self.enableStiffnessSrv = rospy.Service("body_stiffness/enable", Empty, self.handleStiffnessSrv)
00089         self.disableStiffnessSrv = rospy.Service("body_stiffness/disable", Empty, self.handleStiffnessOffSrv)
00090         self.wakeUpSrv = rospy.Service("wakeup", Empty, self.handleWakeUpSrv)
00091         self.restSrv = rospy.Service("rest", Empty, self.handleRestSrv)
00092         self.enableLifeSrv = rospy.Service("life/enable", Empty, self.handleLifeSrv)
00093         self.disableLifeSrv = rospy.Service("life/disable", Empty, self.handleLifeOffSrv)
00094         self.getLifeSrv = rospy.Service("life/get_state", Trigger, self.handleGetLifeSrv)
00095 
00096 
00097         #Start simple action servers
00098         self.jointTrajectoryServer = actionlib.SimpleActionServer("joint_trajectory", JointTrajectoryAction,
00099                                                                   execute_cb=self.executeJointTrajectoryAction,
00100                                                                   auto_start=False)
00101 
00102         self.jointStiffnessServer = actionlib.SimpleActionServer("joint_stiffness_trajectory", JointTrajectoryAction,
00103                                                                   execute_cb=self.executeJointStiffnessAction,
00104                                                                   auto_start=False)
00105 
00106         self.jointAnglesServer = actionlib.SimpleActionServer("joint_angles_action", JointAnglesWithSpeedAction,
00107                                                                   execute_cb=self.executeJointAnglesWithSpeedAction,
00108                                                                   auto_start=False)
00109 
00110         #Start action servers
00111         self.jointTrajectoryServer.start()
00112         self.jointStiffnessServer.start()
00113         self.jointAnglesServer.start()
00114 
00115         # only start when ALRobotPosture proxy is available
00116         if not (self.robotPostureProxy is None):
00117             self.bodyPoseWithSpeedServer = actionlib.SimpleActionServer("body_pose_naoqi", BodyPoseWithSpeedAction,
00118                                               execute_cb=self.executeBodyPoseWithSpeed,
00119                                               auto_start=False)
00120             self.bodyPoseWithSpeedServer.start()
00121         else:
00122             rospy.logwarn("Proxy to ALRobotPosture not available, requests to body_pose_naoqi will be ignored.")
00123 
00124         # subscribers last:
00125         rospy.Subscriber("joint_angles", JointAnglesWithSpeed, self.handleJointAngles, queue_size=10)
00126         rospy.Subscriber("joint_stiffness", JointState, self.handleJointStiffness, queue_size=10)
00127 
00128         rospy.loginfo("nao_controller initialized")
00129 
00130     def connectNaoQi(self):
00131         '''(re-) connect to NaoQI'''
00132         rospy.loginfo("Connecting to NaoQi at %s:%d", self.pip, self.pport)
00133 
00134         self.motionProxy = self.get_proxy("ALMotion")
00135         if self.motionProxy is None:
00136             exit(1)
00137 
00138         # optional, newly introduced in 1.14
00139         self.robotPostureProxy = self.get_proxy("ALRobotPosture")
00140 
00141         # get a proxy to autonomous life
00142         self.lifeProxy = self.get_proxy("ALAutonomousLife")
00143 
00144 
00145     def handleJointAngles(self, msg):
00146         rospy.logdebug("Received a joint angle target")
00147         try:
00148             # Note: changeAngles() and setAngles() are non-blocking functions.
00149             if (msg.relative==0):
00150                 self.motionProxy.setAngles(list(msg.joint_names), list(msg.joint_angles), msg.speed)
00151             else:
00152                 self.motionProxy.changeAngles(list(msg.joint_names), list(msg.joint_angles), msg.speed)
00153         except RuntimeError,e:
00154             rospy.logerr("Exception caught:\n%s", e)
00155 
00156     def handleJointStiffness(self, msg):
00157         rospy.logdebug("Received a joint angle stiffness")
00158         try:
00159             self.motionProxy.setStiffnesses(list(msg.name), list(msg.effort))
00160         except RuntimeError,e:
00161             rospy.logerr("Exception caught:\n%s", e)
00162 
00163     def handleStiffnessSrv(self, req):
00164         try:
00165             self.motionProxy.stiffnessInterpolation("Body", 1.0, 0.5)
00166             rospy.loginfo("Body stiffness enabled")
00167             return EmptyResponse()
00168         except RuntimeError,e:
00169             rospy.logerr("Exception caught:\n%s", e)
00170             return None
00171 
00172     def handleStiffnessOffSrv(self, req):
00173         try:
00174             self.motionProxy.stiffnessInterpolation("Body", 0.0, 0.5)
00175             rospy.loginfo("Body stiffness removed")
00176             return EmptyResponse()
00177         except RuntimeError,e:
00178             rospy.logerr("Exception caught:\n%s", e)
00179             return None
00180 
00181     def handleWakeUpSrv(self, req):
00182         try:
00183             self.motionProxy.wakeUp()
00184             rospy.loginfo("Wake Up")
00185             return EmptyResponse()
00186         except RuntimeError,e:
00187             rospy.logerr("Exception caught:\n%s", e)
00188             return None
00189 
00190     def handleRestSrv(self, req):
00191         try:
00192             self.motionProxy.rest()
00193             rospy.loginfo("Rest")
00194             return EmptyResponse()
00195         except RuntimeError,e:
00196             rospy.logerr("Exception caught:\n%s", e)
00197             return None
00198 
00199     def handleLifeSrv(self, req):
00200         try:
00201             self.lifeProxy.setState("solitary")
00202             rospy.loginfo("set life state to solitary")
00203             return EmptyResponse()
00204         except RuntimeError, e:
00205             rospy.logerr("Exception while setting life state:\n%s", e)
00206             return None
00207 
00208     def handleLifeOffSrv(self, req):
00209         try:
00210             self.lifeProxy.setState("disabled")
00211             rospy.loginfo("set life state to disabled")
00212             return EmptyResponse()
00213         except RuntimeError, e:
00214             rospy.logerr("Exception while disabling life state:\n%s", e)
00215             return None
00216 
00217     def handleGetLifeSrv(self, req):
00218         try:
00219             res = TriggerResponse()
00220             res.success = True
00221             res.message = self.lifeProxy.getState()
00222             rospy.loginfo("current life state is " + str(res.message))
00223             return res
00224         except RuntimeError, e:
00225             rospy.logerr("Exception while getting life state:\n%s", e)
00226             return None
00227 
00228     def jointTrajectoryGoalMsgToAL(self, goal):
00229         """Helper, convert action goal msg to Aldebraran-style arrays for NaoQI"""
00230         names = list(goal.trajectory.joint_names)
00231 #        if goal.trajectory.joint_names == ["Body"]:
00232 #            names = self.motionProxy.getJointNames('Body')
00233 
00234         if len(goal.trajectory.points) == 1 and len(goal.trajectory.points[0].positions) == 1:
00235             angles = goal.trajectory.points[0].positions[0]
00236         else:
00237             angles = list(list(p.positions[i] for p in goal.trajectory.points) for i in range(0,len(goal.trajectory.points[0].positions)))
00238 
00239         #strip 6,7 and last 2 from angles if the pose was for H25 but we're running an H21
00240         if not isinstance(angles, float) and len(angles) > self.collectionSize["Body"]:
00241             rospy.loginfo("Stripping angles from %d down to %d", len(angles), self.collectionSize["Body"])
00242             angles.pop(6)
00243             angles.pop(7)
00244             angles.pop()
00245             angles.pop()
00246 
00247         if len(names) > self.collectionSize["Body"]:
00248             rospy.loginfo("Stripping names from %d down to %d", len(names), self.collectionSize["Body"])
00249             names.pop(6)
00250             names.pop(7)
00251             names.pop()
00252             names.pop()
00253 
00254         times = list(p.time_from_start.to_sec() for p in goal.trajectory.points)
00255         if len(times) == 1 and len(names) == 1:
00256             times = times[0]
00257         if (len(names) > 1):
00258             times = [times]*len(names)
00259 
00260         return (names, angles, times)
00261 
00262 
00263     def executeJointTrajectoryAction(self, goal):
00264         rospy.loginfo("JointTrajectory action executing");
00265 
00266         names, angles, times = self.jointTrajectoryGoalMsgToAL(goal)
00267 
00268         rospy.logdebug("Received trajectory for joints: %s times: %s", str(names), str(times))
00269         rospy.logdebug("Trajectory angles: %s", str(angles))
00270 
00271         task_id = None
00272         running = True
00273         #Wait for task to complete
00274         while running and not self.jointTrajectoryServer.is_preempt_requested() and not rospy.is_shutdown():
00275             #If we haven't started the task already...
00276             if task_id is None:
00277                 # ...Start it in another thread (thanks to motionProxy.post)
00278                 task_id = self.motionProxy.post.angleInterpolation(names, angles, times, (goal.relative==0))
00279 
00280             #Wait for a bit to complete, otherwise check we can keep running
00281             running = self.motionProxy.wait(task_id, self.poll_rate)
00282 
00283         # If still running at this point, stop the task
00284         if running and task_id:
00285             self.motionProxy.stop( task_id )
00286 
00287         jointTrajectoryResult = JointTrajectoryResult()
00288         jointTrajectoryResult.goal_position.header.stamp = rospy.Time.now()
00289         jointTrajectoryResult.goal_position.position = self.motionProxy.getAngles(names, True)
00290         jointTrajectoryResult.goal_position.name = names
00291 
00292         if not self.checkJointsLen(jointTrajectoryResult.goal_position):
00293             self.jointTrajectoryServer.set_aborted(jointTrajectoryResult)
00294             rospy.logerr("JointTrajectory action error in result: sizes mismatch")
00295 
00296         elif running:
00297             self.jointTrajectoryServer.set_preempted(jointTrajectoryResult)
00298             rospy.logdebug("JointTrajectory preempted")
00299 
00300         else:
00301             self.jointTrajectoryServer.set_succeeded(jointTrajectoryResult)
00302             rospy.loginfo("JointTrajectory action done")
00303 
00304 
00305     def executeJointStiffnessAction(self, goal):
00306         rospy.loginfo("JointStiffness action executing");
00307 
00308         names, angles, times = self.jointTrajectoryGoalMsgToAL(goal)
00309 
00310         rospy.logdebug("Received stiffness trajectory for joints: %s times: %s", str(names), str(times))
00311         rospy.logdebug("stiffness values: %s", str(angles))
00312 
00313         task_id = None
00314         running = True
00315         #Wait for task to complete
00316         while running and not self.jointStiffnessServer.is_preempt_requested() and not rospy.is_shutdown():
00317             #If we haven't started the task already...
00318             if task_id is None:
00319                 # ...Start it in another thread (thanks to motionProxy.post)
00320                 task_id = self.motionProxy.post.stiffnessInterpolation(names, angles, times)
00321 
00322             #Wait for a bit to complete, otherwise check we can keep running
00323             running = self.motionProxy.wait(task_id, self.poll_rate)
00324 
00325         # If still running at this point, stop the task
00326         if running and task_id:
00327             self.motionProxy.stop( task_id )
00328 
00329         jointStiffnessResult = JointTrajectoryResult()
00330         jointStiffnessResult.goal_position.header.stamp = rospy.Time.now()
00331         jointStiffnessResult.goal_position.position = self.motionProxy.getStiffnesses(names)
00332         jointStiffnessResult.goal_position.name = names
00333 
00334         if not self.checkJointsLen(jointStiffnessResult.goal_position):
00335             self.jointStiffnessServer.set_aborted(jointStiffnessResult)
00336             rospy.logerr("JointStiffness action error in result: sizes mismatch")
00337 
00338         elif running:
00339             self.jointStiffnessServer.set_preempted(jointStiffnessResult)
00340             rospy.logdebug("JointStiffness preempted")
00341 
00342         else:
00343             self.jointStiffnessServer.set_succeeded(jointStiffnessResult)
00344             rospy.loginfo("JointStiffness action done")
00345 
00346 
00347     def executeJointAnglesWithSpeedAction(self, goal):
00348 
00349         names = list(goal.joint_angles.joint_names)
00350         angles = list(goal.joint_angles.joint_angles)
00351         rospy.logdebug("Received JointAnglesWithSpeed for joints: %s angles: %s", str(names), str(angles))
00352 
00353         if goal.joint_angles.relative == 1:
00354             # TODO: this uses the current angles instead of the angles at the given timestamp
00355             currentAngles = self.motionProxy.getAngles(names, True)
00356             angles = list(map(lambda x,y: x+y, angles, currentAngles))
00357 
00358         task_id = None
00359         running = True
00360         #Wait for task to complete
00361         while running and not self.jointAnglesServer.is_preempt_requested() and not rospy.is_shutdown():
00362             #If we haven't started the task already...
00363             if task_id is None:
00364                 # ...Start it in another thread (thanks to motionProxy.post)
00365                 task_id = self.motionProxy.post.angleInterpolationWithSpeed(names, angles, goal.joint_angles.speed)
00366 
00367             #Wait for a bit to complete, otherwise check we can keep running
00368             running = self.motionProxy.wait(task_id, self.poll_rate)
00369 
00370         # If still running at this point, stop the task
00371         if running and task_id:
00372             self.motionProxy.stop( task_id )
00373 
00374         jointAnglesWithSpeedResult = JointAnglesWithSpeedResult()
00375         jointAnglesWithSpeedResult.goal_position.header.stamp = rospy.Time.now()
00376         jointAnglesWithSpeedResult.goal_position.position = self.motionProxy.getAngles(names, True)
00377         jointAnglesWithSpeedResult.goal_position.name = names
00378 
00379         if not self.checkJointsLen(jointAnglesWithSpeedResult.goal_position):
00380             self.jointAnglesServer.set_aborted(jointAnglesWithSpeedResult)
00381             rospy.logerr("JointAnglesWithSpeed action error in result: sizes mismatch")
00382 
00383         elif running:
00384             self.jointAnglesServer.set_preempted(jointAnglesWithSpeedResult)
00385             rospy.logdebug("JointAnglesWithSpeed preempted")
00386 
00387         else:
00388             self.jointAnglesServer.set_succeeded(jointAnglesWithSpeedResult)
00389             rospy.loginfo("JointAnglesWithSpeed action done")
00390 
00391     def checkJointsLen(self, goal_position):
00392         if len(goal_position.name) == 1 and self.collectionSize.has_key(goal_position.name[0]):
00393             return len(goal_position.position) == self.collectionSize[goal_position.name[0]]
00394         else:
00395             return len(goal_position.position) ==  len(goal_position.name)
00396 
00397     def executeBodyPoseWithSpeed(self, goal):
00398 
00399         #~ Sanity checks
00400         if (goal.speed < 0.0) or (goal.speed > 1.0):
00401             bodyPoseWithSpeedResult = BodyPoseWithSpeedResult()
00402             self.bodyPoseWithSpeedServer.set_aborted(bodyPoseWithSpeedResult)
00403             rospy.logerr("Body pose setter: Not a valid speed value.")
00404             return
00405 
00406         valid_postures = self.robotPostureProxy.getPostureList()
00407 
00408         if goal.posture_name not in valid_postures:
00409             bodyPoseWithSpeedResult = BodyPoseWithSpeedResult()
00410             self.bodyPoseWithSpeedServer.set_aborted(bodyPoseWithSpeedResult)
00411             rospy.logerr("Body pose setter: Not a valid posture.")
00412             return
00413 
00414         #~ Must set stiffness on
00415         try:
00416             self.motionProxy.stiffnessInterpolation("Body", 1.0, 0.5)
00417             rospy.loginfo("Body stiffness enabled")
00418         except RuntimeError,e:
00419             rospy.logerr("Exception caught:\n%s", e)
00420             return
00421 
00422         #~ Go to posture. This is blocking
00423         self.robotPostureProxy.goToPosture(goal.posture_name, goal.speed)
00424         #~ Return success
00425         self.bodyPoseWithSpeedServer.set_succeeded()
00426 
00427     def run(self):
00428         while self.is_looping():
00429             try:
00430                 if self.getLifeStatePub.get_num_connections() > 0:
00431                     get_life_state_msg = String()
00432                     get_life_state_msg.data = self.lifeProxy.getState()
00433                     self.getLifeStatePub.publish(get_life_state_msg)
00434 
00435             except RuntimeError, e:
00436                 print "Error accessing ALMotion, ALRobotPosture, ALAutonomousLife, exiting...\n"
00437                 print e
00438                 rospy.signal_shutdown("No NaoQI available anymore")
00439 
00440             self.rate.sleep()
00441 
00442 if __name__ == '__main__':
00443 
00444     controller = PoseController()
00445     controller.start()
00446     rospy.loginfo("nao pose_controller running...")
00447     rospy.spin()
00448 
00449     rospy.loginfo("nao pose_controller stopped.")
00450     exit(0)


naoqi_pose
Author(s): Armin Hornung, Séverin Lemaignan
autogenerated on Sat Jun 8 2019 20:30:15