teleop_nao_ni.cpp
Go to the documentation of this file.
00001   
00002 /*
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions are met:
00006  *
00007  *     * Redistributions of source code must retain the above copyright
00008  *       notice, this list of conditions and the following disclaimer.
00009  *     * Redistributions in binary form must reproduce the above copyright
00010  *       notice, this list of conditions and the following disclaimer in the
00011  *       documentation and/or other materials provided with the distribution.
00012  *     * Neither the name of the <ORGANIZATION> nor the names of its
00013  *       contributors may be used to endorse or promote products derived from
00014  *       this software without specific prior written permission.
00015  *
00016  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00017  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00018  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00019  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00020  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00021  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00022  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00023  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00024  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00025  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00026  * POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00029 /*
00030  * Nao Natural Interface Teleoperation in ROS
00031  *
00032  * Copyright December 2010, H. Bener SUAY 
00033  * 
00034  * Worcester Polytechnic Institute
00035  *
00036  * wpi.edu/~benersuay
00037  * benersuay@wpi.edu
00038  */
00039 
00040 /* Release 0.0.1
00041  * This code is the fusion of Nao Remote of Albert-Ludwig University
00042  * and OpenNI_SampleUserTracker of The University of Tokyo, with, of course, modifications.
00043  */
00044 
00045 /* It doesn't show the video as SampleUserTracker does, so you might want to run the UserTracker as well to see your self.
00046  * Also, it takes more time to actually start tracking the user because of the low publishing rate.
00047  * IMPORTANT: Wait in Psi Pose until this code prints out "Calibration complete, start tracking user", NOTE THAT IT IS SLOWER THAN Sample-NiUserTracker!!!
00048  */
00049 
00050 /*
00051  * Main purpose is to gets data from Microsoft Kinect, and to remap 
00052  * the data to teleoperation commands that nao_ctrl understands.
00053  */
00054 
00055 /* A lot To Do:
00056  * - More comments (it's never enough)
00057  * - Speed up user calibration, make it independent of Nao's pubRate (see main).
00058  * - Fix wrong user enumeration
00059  * - Right now arm angles are published as HeadAngles, a new ArmAngles message type should be defined in nao_ctrl package
00060  * - Some messages are still published as Button messages, new State messages should be defined in nao_ctrl package
00061  * - Find a better way for Body Control / Gaze Direction Control switch
00062  * - What is the smart way to add voice control to this (i.e. when someone hacks the audio driver)?
00063  * - Fix false detections
00064  * - Fix 2 seconds waitings with a nice solution
00065  * - Build a gesture library
00066  * - Use error codes 
00067  * - Find more things to do.
00068  */
00069 
00070 /* Known issues:
00071  * - Sometimes OpenNI recognizes the skeleton with high confidence but still in weird positions, which confuses
00072  *   this code and causes false control mode switches.
00073  * - Calibration takes way too long.
00074  */   
00075 
00076 // General Includes
00077 #include <math.h>
00078 
00079 // ROS Includes
00080 #include <ros/ros.h>
00081 #include <ros/package.h>
00082 #include <tf/transform_broadcaster.h>
00083 #include <kdl/frames.hpp>
00084 
00085 
00086 // Nao Includes
00087 #include <std_msgs/String.h> // For speech messages to Nao
00088 #include <geometry_msgs/Twist.h> // For lin. and ang. motion messages to Nao
00089 #include <nao_ctrl/MotionCommandBtn.h> // For messages such as stiffness on/of, control enable/disable
00090 #include <nao_ctrl/HeadAngles.h> // Originally this was created to change head pitch / yaw angle, but here it's
00091                                  // used for both head AND arm angles.
00092 
00093 // Prime Sense Includes
00094 #include <XnOpenNI.h>
00095 #include <XnCodecIDs.h>
00096 #include <XnCppWrapper.h>
00097 
00098 using std::string;
00099 
00100 // Prime Sense Variables and objects begin
00101 xn::Context        g_Context;
00102 xn::DepthGenerator g_DepthGenerator;
00103 xn::UserGenerator  g_UserGenerator;
00104 
00105 XnBool g_bNeedPose   = FALSE;
00106 XnChar g_strPose[20] = "";
00107 // Prime Sense Variables and objects end
00108 
00109 std_msgs::String naosay;
00110 XnPoint3D init_usr_loc; // Previous body location of the user
00111 
00112 /*           left_safe_line
00113  *           :     
00114  *           :    right_safe_line
00115  *           :    :
00116  *        -----------    .....anterior_control_line
00117  *       |             |
00118  *       |    -----    | .....anterior_safe_line
00119  *       |   |     |cd |
00120  *       |   |  O  |-->|
00121  *       |   |_____|   | .....posterior_safe_line
00122  *       |      |cd    |
00123  *       |__ ___v_____ | .....posterior_control_line
00124  *       :             :
00125  *       :             right_control_line
00126  *       :
00127  *       left_control_line
00128  *
00129  * cd: control distance, see define section.
00130  *
00131  * This is basically how the control pad works:
00132  * - Point O is where you turn on the motors of Nao
00133  * - If you step somewhere between anterior_safe_line and anterior_control_line, the robot walks forward
00134  * - If you rotate your shoulders between safety_rot and control_rot the robot rotates its body.
00135  *
00136  */
00137 
00138 // Initially all lines are zero. They will be set when the user turns the motors ON.
00139 float anterior_safe_line = 0.0;
00140 float posterior_safe_line = 0.0;
00141 float right_safe_line = 0.0;
00142 float left_safe_line = 0.0;
00143 float safe_angle = 0.0; 
00144 
00145 float anterior_control_line = 0.0;
00146 float posterior_control_line = 0.0;
00147 float right_control_line = 0.0;
00148 float left_control_line = 0.0;
00149 float control_angle = 0.0;
00150 
00151 XnPoint3D init_usr_RS; // Initial location of the user's Right Shoulder (for rotation of the body)
00152 XnPoint3D init_usr_LS; // Initial location of the user's Left Shoulder (for rotation of the body)
00153 
00154 // OpenNI Check Fault
00155 #define CHECK_RC(nRetVal, what)                                         \
00156   if (nRetVal != XN_STATUS_OK)                                          \
00157     {                                                                   \
00158       printf("%s failed: %s\n", what, xnGetStatusString(nRetVal));      \
00159       return nRetVal;                                                   \
00160     }
00161 
00162 #define my_pi 3.141592653589793 // Define the PI number for vector calculations
00163 #define walking_speed 0.3 // Walking speed of the robot [0.0 - 1.0]
00164 #define rotating_speed 0.3 // Angular speed of the robot
00165 
00166 #define control_dist 300.0 // Distance to step in order to control the robot
00167 #define safety_dist 200.0 // Distance where user can reset the speed of the robot
00168 
00169 #define control_rot 1.57 // Angle to turn, in order to rotate the torso of the robot
00170 #define safety_rot 0.3 // Angle up to which the user can reset the rotational speed of the robot
00171 
00172 #define rot_constant 0.04 // Tune this!
00173 
00174 class TeleopNaoNi
00175 {
00176 public:
00177   TeleopNaoNi();
00178   void pubMsg();
00179   ros::NodeHandle nh; // Node that will publish messages 
00180   ros::NodeHandle privateNh; // Initialized but not used in this code. Inherited from Albert-Ludwig University's implementation
00181 
00182   bool m_enabled; // Motors enabled
00183   bool m_teleopEnabled; // Teleoperation enabled - Body Control (mutually exclusive with attentionEnabled)
00184   bool m_attentionEnabled; // Attention enabled - Gaze Direction Control (mutually exclusive with bodyControl)
00185   bool m_commandEnabled; // Apply command (i.e. apply the defined command, in this case walk until receiving STOP command)
00186   int m_xAxis; // Not Used
00187   int m_yAxis; // Not Used
00188   int m_turnAxis; // Not Used
00189   int m_headYawAxis; // Not Used
00190   int m_headPitchAxis; // Not Used
00191   int m_crouchBtn; // Button number which makes Nao crouch
00192   int m_initPoseBtn; // Button number wich makes Nao to stand up and take the initial pose
00193   int m_enableBtn;  // Button number which makes Nao to enable it's motors
00194   int m_modifyHeadBtn; // Not Used
00195   int m_startScanBtn; // Not Used
00196   int m_stopScanBtn; // Not Used
00197   double m_maxVx; // Max. Linear Velocity in X
00198   double m_maxVy; // Max. Linear Velocity in Y
00199   double m_maxVw; // Max. Angular Velocity
00200   double m_maxHeadYaw; // Max. Head Yaw Angle
00201   double m_maxHeadPitch; // Max. Head Pitch Angle
00202   
00203   ros::Publisher m_movePub; // Publishes the lin. and ang. motions
00204   ros::Publisher m_moveBtnPub; // Publishes button numbers
00205   ros::Publisher m_headPub; // Publishes head angles
00206   ros::Publisher m_speechPub; // Publishes speech string
00207   ros::Publisher m_lShoulderPub; // Publishes left shoulder angles
00208   ros::Publisher m_rShoulderPub; //           right shoulder angles
00209   ros::Publisher m_lElbowPub; // Publishes left elbow angles
00210   ros::Publisher m_rElbowPub; //           right elbow angles
00211   geometry_msgs::Twist m_motion; // Linear velocities for Nao's navigation
00212   nao_ctrl::HeadAngles m_headAngles; // Head angles of Nao
00213   nao_ctrl::HeadAngles m_lShoulderAngles; // Left shoulder angles
00214   nao_ctrl::HeadAngles m_rShoulderAngles; // Right shoulder angles
00215   nao_ctrl::HeadAngles m_lElbowAngles; // Left elbow angles
00216   nao_ctrl::HeadAngles m_rElbowAngles; // Right elbow angles
00217 };
00218 
00219 // Initialization list of object TeleopNaoNi
00220 TeleopNaoNi::TeleopNaoNi()
00221   :     privateNh("~"), m_enabled(false), m_teleopEnabled(false), m_attentionEnabled(false), m_commandEnabled(false),
00222    m_xAxis(3), m_yAxis(2), m_turnAxis(0), m_headYawAxis(4),     m_headPitchAxis(5),
00223    m_crouchBtn(9), m_initPoseBtn(0), m_enableBtn(8), m_modifyHeadBtn(5),
00224    m_startScanBtn(2), m_stopScanBtn(3),
00225    m_maxVx(1.0), m_maxVy(1.0), m_maxVw(0.5),
00226    m_maxHeadYaw(2.0943), m_maxHeadPitch(0.7853)
00227 {
00228         privateNh.param("axis_x", m_xAxis, m_xAxis);
00229         privateNh.param("axis_y", m_yAxis, m_yAxis);
00230         privateNh.param("axis_turn", m_turnAxis, m_turnAxis);
00231         privateNh.param("axis_head_yaw", m_headYawAxis, m_headYawAxis);
00232         privateNh.param("axis_head_pitch", m_headPitchAxis, m_headPitchAxis);
00233         privateNh.param("btn_crouch", m_crouchBtn, m_crouchBtn);
00234         privateNh.param("btn_enable_control", m_enableBtn, m_enableBtn);
00235         privateNh.param("btn_head_mod", m_modifyHeadBtn, m_modifyHeadBtn);
00236         privateNh.param("btn_start_scan", m_startScanBtn, m_startScanBtn);
00237         privateNh.param("btn_stop_scan", m_stopScanBtn, m_stopScanBtn);
00238         privateNh.param("max_vx", m_maxVx, m_maxVx);
00239         privateNh.param("max_vy", m_maxVy, m_maxVy);
00240         privateNh.param("max_vw", m_maxVw, m_maxVw);
00241 
00242         privateNh.param("max_head_yaw", m_maxHeadYaw, m_maxHeadYaw);
00243         privateNh.param("max_head_pitch", m_maxHeadPitch, m_maxHeadPitch);
00244 
00245         // Initialize all velocities and angles at zero.
00246         m_motion.linear.x = m_motion.linear.y = m_motion.angular.z = 0;
00247         m_headAngles.yaw = 0.0;
00248         m_headAngles.pitch = 0.0;
00249         m_headAngles.absolute = 1.0;
00250 
00251         m_lShoulderAngles.yaw=0.0;
00252         m_lShoulderAngles.pitch=0.0;
00253         m_lShoulderAngles.absolute=1.0;
00254 
00255         m_rShoulderAngles.yaw=0.0;
00256         m_rShoulderAngles.pitch=0.0;
00257         m_rShoulderAngles.absolute=1.0;
00258 
00259         m_lElbowAngles.yaw=0.0;
00260         m_lElbowAngles.pitch=0.0;
00261         m_lElbowAngles.absolute=1.0;
00262 
00263         m_rElbowAngles.yaw=0.0;
00264         m_rElbowAngles.pitch=0.0;
00265         m_rElbowAngles.absolute=1.0;
00266 
00267         // Messages to publish (to be subscribed by nao_ctrl)
00268         m_movePub = nh.advertise<geometry_msgs::Twist>("cmd_vel", 10); 
00269         m_moveBtnPub = nh.advertise<nao_ctrl::MotionCommandBtn>("motion_command_btn", 1);
00270         m_headPub = nh.advertise<nao_ctrl::HeadAngles>("head_angles", 1);
00271         m_speechPub = nh.advertise<std_msgs::String>("speech", 1);
00272         m_lShoulderPub = nh.advertise<nao_ctrl::HeadAngles>("lshoulder_angles",1);
00273         m_rShoulderPub = nh.advertise<nao_ctrl::HeadAngles>("rshoulder_angles",1);
00274         m_lElbowPub = nh.advertise<nao_ctrl::HeadAngles>("lelbow_angles",1);
00275         m_rElbowPub = nh.advertise<nao_ctrl::HeadAngles>("relbow_angles",1);    
00276 }
00277 
00278 
00279 
00280 //Publish motion message to Nao
00281 void TeleopNaoNi::pubMsg(){
00282   // m_enabled = false in the beginning by default. See the initialization list of TeleopNaoNi.
00283   if (m_enabled){
00284     // Publish appropriate messages (to be subscribed by nao_ctrl)
00285     m_movePub.publish(m_motion); 
00286     m_headPub.publish(m_headAngles); 
00287     m_lShoulderPub.publish(m_lShoulderAngles);
00288     m_rShoulderPub.publish(m_rShoulderAngles);
00289     m_lElbowPub.publish(m_lElbowAngles);
00290     m_rElbowPub.publish(m_rElbowAngles);
00291   }
00292 }
00293 
00294 void stopWalking(TeleopNaoNi &robot){
00295         robot.m_motion.linear.x = robot.m_motion.linear.y = robot.m_motion.angular.z = 0;
00296 }
00297 
00298 
00299 void XN_CALLBACK_TYPE User_NewUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie) {
00300         printf("New User %d\n", nId);
00301         //naosay.data="New user";
00302         if (g_bNeedPose)
00303                 g_UserGenerator.GetPoseDetectionCap().StartPoseDetection(g_strPose, nId);
00304         else
00305                 g_UserGenerator.GetSkeletonCap().RequestCalibration(nId, TRUE);
00306 }
00307 
00308 void XN_CALLBACK_TYPE User_LostUser(xn::UserGenerator& generator, XnUserID nId, void* pCookie) {
00309   //naosay.data="Lost user";
00310   printf("Lost user %d\n", nId);
00311 }
00312 
00313 void XN_CALLBACK_TYPE UserCalibration_CalibrationStart(xn::SkeletonCapability& capability, XnUserID nId, void* pCookie) {
00314   //naosay.data="Calibration started for user";
00315   printf("Calibration started for user %d\n", nId);
00316 }
00317 
00318 void XN_CALLBACK_TYPE UserCalibration_CalibrationEnd(xn::SkeletonCapability& capability, XnUserID nId, XnBool bSuccess, void* pCookie) {
00319   if (bSuccess) {
00320     printf("Calibration complete, start tracking user %d\n", nId);
00321     //naosay.data="Calibration complete. Start tracking user";
00322     g_UserGenerator.GetSkeletonCap().StartTracking(nId);
00323   }
00324   else {
00325     printf("Calibration failed for user %d\n", nId);
00326     //naosay.data="Calibration failed for user";
00327     if (g_bNeedPose)
00328       g_UserGenerator.GetPoseDetectionCap().StartPoseDetection(g_strPose, nId);
00329     else
00330       g_UserGenerator.GetSkeletonCap().RequestCalibration(nId, TRUE);
00331   }
00332 }
00333 
00334 void XN_CALLBACK_TYPE UserPose_PoseDetected(xn::PoseDetectionCapability& capability, XnChar const* strPose, XnUserID nId, void* pCookie) {
00335     printf("Pose %s detected for user %d\n", strPose, nId);
00336     //naosay.data="Pose is detected for user";
00337     g_UserGenerator.GetPoseDetectionCap().StopPoseDetection(nId);
00338     g_UserGenerator.GetSkeletonCap().RequestCalibration(nId, TRUE);
00339 }
00340 
00341 // Get and return the location of the user (the origin point, for the control of Nao)
00342 XnPoint3D getBodyLoc(XnUserID const& user, XnSkeletonJoint const& eJoint1){
00343   XnPoint3D pt;
00344 
00345   // Fault Check
00346   if (!g_UserGenerator.GetSkeletonCap().IsTracking(user))
00347     {
00348       printf("not tracked!\n");
00349       pt.X=0.0; pt.Y=0.0; pt.Z=0.0;
00350       return pt;
00351     }
00352   
00353   // Get joint position (this is supposed to be "the Torso point" of the skeleton)
00354   XnSkeletonJointPosition joint1;
00355   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint1, joint1);
00356 
00357   // Make sure that all joints are found with enough confidence...
00358   if (joint1.fConfidence < 0.7)
00359     {
00360       pt.X=0.0; pt.Y=0.0; pt.Z=0.0;
00361       return pt;
00362     }
00363   
00364   pt = joint1.position; // Return the position
00365   return pt;
00366 }
00367 
00368 // Get and return the angle between two NOT JOINT limbs of the skeleton, e.g. angle between (hip to hip vector) ^ ( upper arm )
00369 float getAngleBetweenLimbs(XnUserID const& user, XnSkeletonJoint const& eJoint1, XnSkeletonJoint const& eJoint2, XnSkeletonJoint const& eJoint3, XnSkeletonJoint const& eJoint4){
00370 
00371   // This function is different than getLimbAngle, in the sense that, it finds the angle of two disconnected
00372   // limbs (each limb considered as a vector).
00373 
00374   // In order to find the shoulder roll angles of Nao, we need to find the angle between
00375   // [Right Hip --> Left Hip] vector and [Right Shoulder --> Right Elbow] vector
00376  
00377   if (!g_UserGenerator.GetSkeletonCap().IsTracking(user))
00378     {
00379       printf("not tracked!\n");
00380       return -998.0; // Error code (though, it's not used yet)
00381     }
00382   
00383   // Get joint positions
00384   XnSkeletonJointPosition joint1, joint2, joint3, joint4;
00385   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint1, joint1);
00386   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint2, joint2);
00387   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint3, joint3);
00388   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint4, joint4);
00389   
00390 
00391   // Make sure that all joints are found with enough confidence...
00392   if (joint1.fConfidence < 0.6 && joint2.fConfidence < 0.6 && joint3.fConfidence < 0.6 && joint4.fConfidence < 0.6)
00393     {
00394       return -999.0; // Error code (though, it's not used yet)
00395     }
00396   
00397   XnPoint3D pt[4]; // Create four points
00398   pt[0] = joint1.position; // Vector 0, Point 0
00399   pt[1] = joint2.position; // Vector 0, Point 1
00400   pt[2] = joint3.position; // Vector 1, Point 0
00401   pt[3] = joint4.position; // Vector 1, Point 1
00402 
00403   XnVector3D v[2]; // Create two vectors
00404 
00405   // First vector
00406   // V_{RHIP,LHIP} = V_{LHIP} - V_{RHIP}
00407   v[0].X=pt[2].X-pt[3].X;
00408   v[0].Y=pt[2].Y-pt[3].Y;
00409   v[0].Z=pt[2].Z-pt[3].Z;
00410 
00411   // Second vector
00412   // V_{S,E} = V_{E} - V_{S}
00413   v[1].X=pt[0].X-pt[1].X;
00414   v[1].Y=pt[0].Y-pt[1].Y; 
00415   v[1].Z=pt[0].Z-pt[1].Z;
00416 
00417   // Calculate the magnitude of the vectors (limbs)
00418   float v0_magnitude = sqrt(v[0].X*v[0].X + v[0].Y*v[0].Y + v[0].Z*v[0].Z);
00419   float v1_magnitude = sqrt(v[1].X*v[1].X + v[1].Y*v[1].Y + v[1].Z*v[1].Z);
00420 
00421   //printf("V0 MAG: %f, V1 MAG: %f\n",v0_magnitude, v1_magnitude);
00422 
00423   // If neither of vectors != 0.0 then find the angle between them
00424   if(v0_magnitude != 0.0 && v1_magnitude != 0.0){
00425     v[0].X = v[0].X * (1.0/v0_magnitude); 
00426     v[0].Y = v[0].Y * (1.0/v0_magnitude);
00427     v[0].Z = v[0].Z * (1.0/v0_magnitude);
00428     
00429     v[1].X = v[1].X * (1.0/v1_magnitude); 
00430     v[1].Y = v[1].Y * (1.0/v1_magnitude); 
00431     v[1].Z = v[1].Z * (1.0/v1_magnitude); 
00432   
00433 
00434     // Find and convert the angle between upper and lower arms
00435     float theta = acos(v[0].X*v[1].X + v[0].Y*v[1].Y + v[0].Z*v[1].Z);
00436     float angle_in_degrees = theta * 180 / my_pi;
00437 
00438     return angle_in_degrees;
00439   }
00440   else return -997.0; // Error code (though, it's not used yet)
00441 }
00442 
00443 // Get and return the angle between JOINT limbs (e.g. upper arm ^ lower arm; upper leg ^ lower leg; hip_to_hip ^ upper_leg etc.)
00444 float getLimbAngle(XnUserID const& user, XnSkeletonJoint const& eJoint1, XnSkeletonJoint const& eJoint2, XnSkeletonJoint const& eJoint3){
00445   if (!g_UserGenerator.GetSkeletonCap().IsTracking(user))
00446     {
00447       printf("not tracked!\n");
00448       return -998.0; // Error code (though, it's not used yet) 
00449     }
00450   
00451   // Get joint positions
00452   XnSkeletonJointPosition joint1, joint2, joint3;
00453   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint1, joint1);
00454   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint2, joint2);
00455   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint3, joint3);
00456 
00457   // Make sure that all joints are found with enough confidence...
00458   if (joint1.fConfidence < 0.6 && joint2.fConfidence < 0.6 && joint3.fConfidence < 0.6 )
00459     {
00460       return -999.0; // Error code (though, it's not used yet) 
00461     }
00462   
00463   XnPoint3D pt[3]; // Create 3 points
00464   pt[0] = joint1.position; // E.g. Shoulder
00465   pt[1] = joint2.position; // E.g. Elbow
00466   pt[2] = joint3.position; // E.g. Hand
00467   
00468   // or another e.g. 
00469   // pt_0: Hip
00470   // pt_1: Knee
00471   // pt_2: Foot
00472 
00473   // The first vector is, Shoulder --> Elbow or Hip --> Knee
00474   // The second vector is,  Elbow --> Hand or Knee --> Foot 
00475 
00476   XnVector3D v[2]; // Create two vectors
00477 
00478   // S: Shoulder, E: Elbow, H: Hand
00479   // S  *
00480   //    |
00481   // E  *---*
00482   //        H
00483 
00484   // Calculate the first vector
00485   // V_{H,E} = V_{E} - V_{H}
00486   v[0].X=pt[1].X-pt[2].X;
00487   v[0].Y=pt[1].Y-pt[2].Y;
00488   v[0].Z=pt[1].Z-pt[2].Z;
00489 
00490   // Calculate the second vector
00491   // V_{E,S} = V_{S} - V_{E}
00492   v[1].X=pt[0].X-pt[1].X;
00493   v[1].Y=pt[0].Y-pt[1].Y; 
00494   v[1].Z=pt[0].Z-pt[1].Z;
00495 
00496   // Calculate the magnitudes of the vectors
00497   float v0_magnitude = sqrt(v[0].X*v[0].X + v[0].Y*v[0].Y + v[0].Z*v[0].Z);
00498   float v1_magnitude = sqrt(v[1].X*v[1].X + v[1].Y*v[1].Y + v[1].Z*v[1].Z);
00499 
00500   //printf("V0 MAG: %f, V1 MAG: %f\n",v0_magnitude, v1_magnitude);
00501 
00502   if(v0_magnitude != 0.0 && v1_magnitude != 0.0){
00503     v[0].X = v[0].X * (1.0/v0_magnitude); 
00504     v[0].Y = v[0].Y * (1.0/v0_magnitude);
00505     v[0].Z = v[0].Z * (1.0/v0_magnitude);
00506     
00507     v[1].X = v[1].X * (1.0/v1_magnitude); 
00508     v[1].Y = v[1].Y * (1.0/v1_magnitude); 
00509     v[1].Z = v[1].Z * (1.0/v1_magnitude); 
00510   
00511 
00512     // Find and convert the angle between JOINT limbs
00513     float theta = acos(v[0].X*v[1].X + v[0].Y*v[1].Y + v[0].Z*v[1].Z);
00514     float angle_in_degrees = theta * 180 / my_pi;
00515 
00516     return angle_in_degrees;
00517   }
00518   else return -997.0; // Error code (though, it's not used yet) 
00519 }
00520 
00521 // Gives the angle between:
00522 // the initial AND current position of the user's shoulders, to understand how much he/she rotated his/her shoulders.
00523 float getTorsoRotation(XnUserID const& user, XnSkeletonJoint const& eJoint1, XnSkeletonJoint const& eJoint2){
00524 
00525   if (!g_UserGenerator.GetSkeletonCap().IsTracking(user))
00526     {
00527       printf("not tracked!\n");
00528       return -998.0; // Error code (though, it's not used yet)
00529     }
00530   
00531   // Get joint positions
00532   XnSkeletonJointPosition joint1, joint2;
00533   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint1, joint1);
00534   g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, eJoint2, joint2);
00535 
00536   // Make sure that all joints are found with enough confidence...
00537   if (joint1.fConfidence < 0.6 && joint2.fConfidence < 0.6)
00538     {
00539       return -999.0; // Error code (though, it's not used yet)
00540     }
00541   
00542   XnPoint3D pt[4]; // Create four points
00543   pt[0] = init_usr_LS; // Vector 0, Point 0 - Initial location of the left shoulder
00544   pt[1] = init_usr_RS; // Vector 0, Point 1 - Initial location of the right shoulder
00545   pt[2] = joint1.position; // Vector 1, Point 0 - Current location of the left shoulder
00546   pt[3] = joint2.position; // Vector 1, Point 1 - Current location of the right shoulder
00547 
00548   XnVector3D v[2]; // Create two vectors
00549 
00550   // Assign the vectors
00551   v[0].X=pt[2].X-pt[3].X;
00552   v[0].Y=pt[2].Y-pt[3].Y;
00553   v[0].Z=pt[2].Z-pt[3].Z;
00554 
00555   v[1].X=pt[0].X-pt[1].X;
00556   v[1].Y=pt[0].Y-pt[1].Y; 
00557   v[1].Z=pt[0].Z-pt[1].Z;
00558 
00559   // Calculate the magnitude of the vectors
00560   float v0_magnitude = sqrt(v[0].X*v[0].X + v[0].Y*v[0].Y + v[0].Z*v[0].Z);
00561   float v1_magnitude = sqrt(v[1].X*v[1].X + v[1].Y*v[1].Y + v[1].Z*v[1].Z);
00562 
00563   //printf("V0 MAG: %f, V1 MAG: %f\n",v0_magnitude, v1_magnitude);
00564 
00565   // If neither of the vectors are != 0.0 then calculate the angle between them
00566   if(v0_magnitude != 0.0 && v1_magnitude != 0.0){
00567     v[0].X = v[0].X * (1.0/v0_magnitude); 
00568     v[0].Y = v[0].Y * (1.0/v0_magnitude);
00569     v[0].Z = v[0].Z * (1.0/v0_magnitude);
00570     
00571     v[1].X = v[1].X * (1.0/v1_magnitude); 
00572     v[1].Y = v[1].Y * (1.0/v1_magnitude); 
00573     v[1].Z = v[1].Z * (1.0/v1_magnitude); 
00574   
00575 
00576     // Calculate and convert the angle between the initial and current locations of the shoulders
00577     float theta = acos(v[0].X*v[1].X + v[0].Y*v[1].Y + v[0].Z*v[1].Z); 
00578     float angle_in_degrees = theta * 180 / my_pi;
00579 
00580     return angle_in_degrees;
00581   }
00582   else return -997.0; // Error code (though, it's not used yet)
00583 }
00584 
00585 void checkPose(TeleopNaoNi &obj) {
00586     XnUserID users[2];
00587     XnUInt16 users_count = 2;
00588     g_UserGenerator.GetUsers(users, users_count);
00589 
00590     for (int i = 0; i < users_count; ++i) {
00591       XnUserID user = users[i];
00592       if (!g_UserGenerator.GetSkeletonCap().IsTracking(user))
00593         continue;
00594 
00595       /*  If right and left shoulder - elbow - hand make 90 (+/-5) degrees 
00596        *  with a certain confidence tell Nao to switch to teleoperation mode
00597        */
00598       
00599       // Angle between Right leg vectors (Right_Hip --> Knee --> Foot)
00600       float right_limb_angle = getLimbAngle(user, XN_SKEL_RIGHT_HIP, XN_SKEL_RIGHT_KNEE, XN_SKEL_RIGHT_FOOT);
00601       //printf("Right limb angle is %f\n", right_limb_angle);
00602 
00603 
00604       // Angle between Left leg vectors (Left_Hip --> Knee --> Foot)
00605       float left_limb_angle = getLimbAngle(user, XN_SKEL_LEFT_HIP, XN_SKEL_LEFT_KNEE, XN_SKEL_LEFT_FOOT);
00606       //printf("Left limb angle is %f\n", left_limb_angle);
00607 
00608       // Angle between Left arm vectors (Left_Shoulder --> Elbow --> Hand)
00609       float left_elbow_angle = getLimbAngle(user, XN_SKEL_LEFT_SHOULDER, XN_SKEL_LEFT_ELBOW, XN_SKEL_LEFT_HAND);
00610       
00611       // Get the current body (torso) location
00612       XnPoint3D current_bl = getBodyLoc(user, XN_SKEL_TORSO);
00613 
00614       // If the motors are enabled and in BODY CONTROL  mode, then move the robot
00615       if(obj.m_enabled && obj.m_teleopEnabled){
00616         if( (left_control_line <=current_bl.X) && (current_bl.X<=left_safe_line )){
00617           // Step left
00618           obj.m_motion.linear.y = walking_speed;
00619         }
00620         else if( (right_control_line>=current_bl.X) && (current_bl.X>=right_safe_line) ){
00621           // Step right
00622           obj.m_motion.linear.y = -walking_speed;
00623         }
00624         else if( (left_safe_line<=current_bl.X) && (current_bl.X<=right_safe_line)){
00625           // Stop lateral motion
00626           obj.m_motion.linear.y = 0.0;
00627         }
00628         
00629         if( (anterior_control_line<=current_bl.Z) && (current_bl.Z<=anterior_safe_line)){
00630           // Step fwd
00631           obj.m_motion.linear.x = walking_speed;
00632         }
00633         else if( (posterior_safe_line<=current_bl.Z) && (current_bl.Z<=posterior_control_line)){
00634           // Step bwd
00635           obj.m_motion.linear.x = -walking_speed;
00636         }
00637         else if( (anterior_safe_line<=current_bl.Z) && (current_bl.Z<=posterior_safe_line)){
00638           // Stop fwd/bwd motion
00639           obj.m_motion.linear.x = 0.0;
00640         }
00641           
00642         
00643         // Get the current left and right shoulder locations
00644         XnPoint3D current_ls = getBodyLoc(user, XN_SKEL_LEFT_SHOULDER);
00645         XnPoint3D current_rs = getBodyLoc(user, XN_SKEL_RIGHT_SHOULDER);
00646 
00647         // if positive rotate right, else rotate left       
00648         float current_angle = getTorsoRotation(user, XN_SKEL_LEFT_SHOULDER, XN_SKEL_RIGHT_SHOULDER)*my_pi/180.0; 
00649 
00650         // If the user's body is rotated enough to control the robot then rotate the robot
00651         if( (control_rot>=current_angle) && (current_angle>=safety_rot)){
00652           if((current_rs.Z+100.0) < current_ls.Z){
00653             obj.m_motion.angular.z = rotating_speed;
00654             //printf("Rot Left\n");
00655           }
00656           else if(current_rs.Z > (current_ls.Z+100.0)){
00657             obj.m_motion.angular.z = -1.0*rotating_speed;
00658             //printf("Rot Right\n");
00659           }
00660         }
00661         else if(safety_rot>=current_angle && current_angle>=0.0 ){
00662           obj.m_motion.angular.z = 0.0;
00663         }
00664 
00665         // Get shoulder pitch angles in Radians and publish
00666         float lsap = getLimbAngle(user, XN_SKEL_LEFT_HIP, XN_SKEL_LEFT_SHOULDER, XN_SKEL_LEFT_ELBOW)*my_pi/180.0;
00667         lsap = lsap - 1.6;
00668         if(lsap>=-2.0 && lsap<=2.0){
00669           obj.m_lShoulderAngles.pitch = lsap;
00670         }
00671         //printf("Left Shoulder Pitch: %f\n", lsap);
00672 
00673         // Left elbow angle roll
00674         float lear = getLimbAngle(user, XN_SKEL_LEFT_SHOULDER, XN_SKEL_LEFT_ELBOW, XN_SKEL_LEFT_HAND)*my_pi/180.0;
00675         lear = -1*lear;
00676         if(lear>=-1.55 && lear<=-0.01){
00677           obj.m_lElbowAngles.pitch = lear;
00678         }
00679 
00680         // Left elbow angle yaw
00681         float leay = getAngleBetweenLimbs(user, XN_SKEL_LEFT_SHOULDER, XN_SKEL_LEFT_HIP, XN_SKEL_LEFT_ELBOW, XN_SKEL_LEFT_HAND)*my_pi/180.0;
00682         // Hand down: leay = 0; Nao: 119.5 (~2.0 rad.)
00683         //            leay = 90; Nao: 0
00684         //            leay = 180; Nao: -119.5
00685         leay = 1.57 - leay;
00686         if(2.0>=leay && leay>=-2.0){
00687           obj.m_lElbowAngles.yaw = leay;
00688         }
00689 
00690         // Right shoulder angles pitch
00691         float rsap = getLimbAngle(user, XN_SKEL_RIGHT_HIP, XN_SKEL_RIGHT_SHOULDER, XN_SKEL_RIGHT_ELBOW)*my_pi/180.0;
00692         rsap = rsap - 1.6;
00693         if(rsap>=-2.0 && rsap<=2.0){
00694           obj.m_rShoulderAngles.pitch = rsap;
00695         }
00696         //printf("Right Shoulder Pitch: %f\n", rsap);
00697 
00698         // Right elbow angle roll
00699         float rear = getLimbAngle(user, XN_SKEL_RIGHT_SHOULDER, XN_SKEL_RIGHT_ELBOW, XN_SKEL_RIGHT_HAND)*my_pi/180.0;
00700         if(1.55>=rear && rear>=0.01){
00701           obj.m_rElbowAngles.pitch = rear;
00702         }
00703 
00704         // Right elbow angle yaw
00705         float reay = getAngleBetweenLimbs(user, XN_SKEL_RIGHT_SHOULDER, XN_SKEL_RIGHT_HIP, XN_SKEL_RIGHT_ELBOW, XN_SKEL_RIGHT_HAND)*my_pi/180.0;
00706         reay = reay - 1.57;
00707         if(2.0>=reay && reay>=-2.0){
00708           obj.m_rElbowAngles.yaw = reay;
00709         }
00710 
00711         // Right shoulder angle roll
00712         float rsar = getAngleBetweenLimbs(user, XN_SKEL_RIGHT_HIP, XN_SKEL_LEFT_HIP, XN_SKEL_RIGHT_SHOULDER, XN_SKEL_RIGHT_ELBOW)*my_pi/180.0;
00713         // In front: 90 (1.57) --> -0.5
00714         // Wide open: 180 (3.14) --> -94.5
00715         
00716         rsar = 1.63 - rsar;
00717         if(-0.01>=rsar && rsar>=-1.63){
00718           obj.m_rShoulderAngles.yaw = rsar;
00719         }
00720 
00721         // Left shoulder angle roll
00722         float lsar = getAngleBetweenLimbs(user, XN_SKEL_LEFT_HIP, XN_SKEL_RIGHT_HIP, XN_SKEL_LEFT_SHOULDER, XN_SKEL_LEFT_ELBOW)*my_pi/180.0;
00723         // In front: 90 (1.57) --> 0.5
00724         // Wide open: 180 (3.14) --> 94.5
00725         lsar = lsar - 1.57;
00726         if(1.63>=lsar && lsar>=0.01){
00727           obj.m_lShoulderAngles.yaw = lsar;
00728         }
00729       }
00730 
00731       // If motors are enabled and GAZE DIRECTION CONTROL mode is true
00732       if(obj.m_enabled && obj.m_attentionEnabled){
00733 
00734         // Left elbow angle yaw
00735         float leay = getAngleBetweenLimbs(user, XN_SKEL_LEFT_SHOULDER, XN_SKEL_LEFT_HIP, XN_SKEL_LEFT_ELBOW, XN_SKEL_LEFT_HAND);
00736 
00737         // If the user made APPLY COMMAND gesture in GAZE DIRECTION CONTROL mode
00738         if(left_elbow_angle>=87.0 && left_elbow_angle<=93.0 && leay >= 135.0){
00739 
00740           // If the command was being already executed then stop
00741           if(obj.m_commandEnabled){
00742             obj.m_commandEnabled = false;
00743             stopWalking(obj); // Stop Walking
00744             printf("Command Mode OFF, Back to attention mode\n");
00745             naosay.data="mission accomplished"; // let the user know what you're up to
00746           }
00747           // If the command was not being executed then execute the command
00748           else{
00749             obj.m_commandEnabled = true;
00750             printf("Command Mode ON, Walking towards target\n");
00751             naosay.data="as you wish"; // let the user know that you finished executing
00752           }
00753           ros::Duration(2.0).sleep(); // Sleep for a while, skip switching until noise cancelling.
00754         } 
00755       }
00756 
00757       // If the motors are enabled and the command is being executed, then
00758       if(obj.m_enabled && obj.m_commandEnabled){
00759         
00760         // Rotate until your head is looking at 0.0 degrees
00761         // Then start walking in that direction.
00762         float counter = floor(obj.m_headAngles.yaw / rot_constant);
00763         if (counter>0.0){
00764           obj.m_motion.angular.z = rotating_speed;
00765           obj.m_headAngles.yaw -= rot_constant;
00766         }
00767         else if (counter<0.0){
00768           obj.m_motion.angular.z = -rotating_speed;
00769           obj.m_headAngles.yaw += rot_constant;
00770         }
00771         else if (counter == 0.0){
00772           obj.m_motion.angular.z = 0.0;
00773           obj.m_motion.linear.x = walking_speed;
00774         } 
00775       }
00776 
00777       // If the motors are enabled, GAZE DIRECTION MODE is on, and the command is not being executed,
00778       // then change the Gaze Direction (head angles) of Nao, with respect to the right arm of the user.
00779       if(obj.m_enabled && obj.m_attentionEnabled && !obj.m_commandEnabled){
00780 
00781         // Head Angle Yaw
00782         float hay = getAngleBetweenLimbs(user, XN_SKEL_RIGHT_HIP, XN_SKEL_LEFT_HIP, XN_SKEL_RIGHT_ELBOW, XN_SKEL_RIGHT_HAND)*my_pi/180.0;
00783         hay = hay - 1.57;
00784         if(2.0>=hay && hay>=-2.0){
00785           obj.m_headAngles.yaw = hay;
00786           }
00787 
00788         // Head Angle Pitch
00789         float hap = getAngleBetweenLimbs(user, XN_SKEL_RIGHT_SHOULDER, XN_SKEL_RIGHT_HIP, XN_SKEL_RIGHT_ELBOW, XN_SKEL_RIGHT_HAND)*my_pi/180.0;
00790         hap = 0.5 - hap;
00791         if(0.5>=hay && hay>=-0.66){
00792           obj.m_headAngles.pitch = hap;
00793           }
00794         obj.m_headAngles.absolute = 1.0;        
00795       } 
00796       
00797 
00798       // If the angle of the right Knee is in the desired interval
00799       // Then switch between Gaze Direction Mode (attention)  and Body Control Mode (teleoperation)
00800       if ((right_limb_angle>=87.0) && (right_limb_angle<=93) ){
00801         if(obj.m_teleopEnabled){
00802           obj.m_teleopEnabled = false;
00803           obj.m_attentionEnabled = true;
00804           naosay.data = "Attention mode";
00805           printf("Teleop OFF, Attention ON\n");
00806         }
00807         else{
00808           obj.m_teleopEnabled = true;
00809           obj.m_attentionEnabled = false;
00810           naosay.data = "Tele operation mode";
00811           printf("Teleop ON, Attention OFF\n");
00812         }
00813         ros::Duration(2.0).sleep(); // Sleep for a while, skip switching until noise cancelling.
00814         
00815       }
00816 
00817 
00818       // If the angle of the left Knee is in the desired interval
00819       // Then turn the motors ON / OFF
00820       if((left_limb_angle>=87.0) && (left_limb_angle<=93)){
00821         if(obj.m_enabled){
00822           obj.m_enabled = false;
00823           naosay.data = "Motors off";
00824           printf("Motors OFF\n");
00825 
00826           nao_ctrl::MotionCommandBtn motionCmd;
00827           motionCmd.button = nao_ctrl::MotionCommandBtn::crouchNoStiff; // Stop walking and remove stiffness
00828           obj.m_moveBtnPub.publish(motionCmd);
00829         }
00830         else{
00831           obj.m_enabled = true;
00832           
00833           naosay.data = "Motors on";
00834           printf("Motors ON\n");
00835         
00836           ros::Duration(0.2).sleep();
00837 
00838           nao_ctrl::MotionCommandBtn motionCmd;
00839           motionCmd.button = nao_ctrl::MotionCommandBtn::stiffnessOn; // Power up the motors
00840           obj.m_moveBtnPub.publish(motionCmd);
00841 
00842           ros::Duration(0.2).sleep();
00843 
00844           motionCmd.button = nao_ctrl::MotionCommandBtn::initPose; // Stand up and take initial position
00845           obj.m_moveBtnPub.publish(motionCmd);
00846           
00847           init_usr_loc = getBodyLoc(user, XN_SKEL_TORSO); // Initial location of the user right after enabling the motors
00848 
00849           // Draw safe zone's lines and angle (for rotation of the torso)
00850           anterior_safe_line = init_usr_loc.Z - safety_dist;
00851           posterior_safe_line = init_usr_loc.Z + safety_dist;
00852           
00853           right_safe_line = init_usr_loc.X + safety_dist;
00854           left_safe_line = init_usr_loc.X - safety_dist;
00855 
00856           // Draw control zone's lines and angle (for rotation of the torso)
00857           anterior_control_line = anterior_safe_line - control_dist;
00858           posterior_control_line = posterior_safe_line + control_dist;
00859 
00860           right_control_line = right_safe_line + control_dist;
00861           left_control_line = left_safe_line - control_dist;
00862 
00863           // Initial location of the user's Right / Left shoulders
00864           init_usr_RS = getBodyLoc(user,XN_SKEL_RIGHT_SHOULDER);
00865           init_usr_LS = getBodyLoc(user,XN_SKEL_LEFT_SHOULDER);
00866 
00867         }
00868         ros::Duration(2.0).sleep(); // Sleep for half a second, skip switching until noise cancelling.
00869       }
00870      
00871       if (naosay.data != ""){
00872         obj.m_speechPub.publish(naosay);
00873         naosay.data=""; // Reset the speech string of Nao
00874       }
00875 
00876     }
00877  
00878 }
00879 
00880 int main(int argc, char** argv)
00881 {
00882   
00883   ros::init(argc, argv, "teleop_nao_ni");// Initialize ROS
00884 
00885   TeleopNaoNi teleopNao;// Declare and initialize the variables and object(s)
00886   naosay.data=""; // Initialize the string variable (This string defines what Nao will say)
00887   string configFilename = ros::package::getPath("nao_openni") + "/openni_tracker.xml";
00888 
00889  
00890   double publishRate = 5.0;  // rate of publishing motion commands (too high stresses the Nao's CPU)
00891   teleopNao.privateNh.param("motion_publish_rate", publishRate, publishRate);
00892   ros::Rate pubRate(publishRate); // Set the publishing rate
00893 
00894   unsigned char state=0; // State variable for initializing (resetting) the control.
00895 
00896   // OpenNI functions begin -------------------------------------------------
00897   // Refer to OpenNI Manual and Discussion Group to get help about these functions and variables
00898   XnStatus nRetVal = g_Context.InitFromXmlFile(configFilename.c_str());
00899   CHECK_RC(nRetVal, "InitFromXml");
00900   
00901   nRetVal = g_Context.FindExistingNode(XN_NODE_TYPE_DEPTH, g_DepthGenerator);
00902   CHECK_RC(nRetVal, "Find depth generator");
00903   
00904   nRetVal = g_Context.FindExistingNode(XN_NODE_TYPE_USER, g_UserGenerator);
00905   if (nRetVal != XN_STATUS_OK) {
00906     nRetVal = g_UserGenerator.Create(g_Context);
00907     CHECK_RC(nRetVal, "Find user generator");
00908   }
00909   
00910   if (!g_UserGenerator.IsCapabilitySupported(XN_CAPABILITY_SKELETON)) {
00911     printf("Supplied user generator doesn't support skeleton\n");
00912     return 1;
00913   }
00914   
00915   XnCallbackHandle hUserCallbacks;
00916   g_UserGenerator.RegisterUserCallbacks(User_NewUser, User_LostUser, NULL, hUserCallbacks);
00917   
00918   XnCallbackHandle hCalibrationCallbacks;
00919   g_UserGenerator.GetSkeletonCap().RegisterCalibrationCallbacks(UserCalibration_CalibrationStart, UserCalibration_CalibrationEnd, NULL, hCalibrationCallbacks);
00920   
00921   if (g_UserGenerator.GetSkeletonCap().NeedPoseForCalibration()) {
00922     g_bNeedPose = TRUE;
00923     if (!g_UserGenerator.IsCapabilitySupported(XN_CAPABILITY_POSE_DETECTION)) {
00924       printf("Pose required, but not supported\n");
00925       return 1;
00926     }
00927     
00928     XnCallbackHandle hPoseCallbacks;
00929     g_UserGenerator.GetPoseDetectionCap().RegisterToPoseCallbacks(UserPose_PoseDetected, NULL, NULL, hPoseCallbacks);
00930     
00931     g_UserGenerator.GetSkeletonCap().GetCalibrationPose(g_strPose);
00932   }
00933   
00934   g_UserGenerator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_ALL);
00935   
00936   nRetVal = g_Context.StartGeneratingAll();
00937   CHECK_RC(nRetVal, "StartGenerating");
00938   // OpenNI functions end-------------------------------------------------
00939 
00940   // Endless loop to publish messages
00941   while(teleopNao.nh.ok()){
00942 
00943     // We don't need to use ros::spin() or ros::spinOnce() because we don't subscribe to any topic.
00944 
00945     // For the very first cycle test if everything works ok, and reset the control.
00946     // 0. Enable the remote control
00947     // 1. Then in the second pass remove the stiffness
00948     // 2. Then finally disable the remote control again until teleoperation.
00949 
00950     if (state == 0){
00951 
00952       teleopNao.m_enabled = true;
00953       naosay.data = "Control enabled";
00954       nao_ctrl::MotionCommandBtn motionCmd;
00955       motionCmd.button = nao_ctrl::MotionCommandBtn::stiffnessOn;
00956       teleopNao.m_moveBtnPub.publish(motionCmd);
00957       state++;
00958     }
00959     else if (state == 1){
00960       
00961       naosay.data = "Stiffness removed";
00962       nao_ctrl::MotionCommandBtn motionCmd;
00963       motionCmd.button = nao_ctrl::MotionCommandBtn::stiffnessOff;
00964       teleopNao.m_moveBtnPub.publish(motionCmd);
00965       state++;
00966 
00967     }
00968     else if (state == 2){
00969       
00970       teleopNao.m_enabled = false;
00971       naosay.data = "Control disabled";
00972       state++;
00973     }
00974 
00975     if (naosay.data != ""){
00976       teleopNao.m_speechPub.publish(naosay);
00977       naosay.data=""; // Reset the string to nothing.
00978     }  
00979 
00980     g_Context.WaitAndUpdateAll(); // Update skeleton recognition (OpenNI's callbacks)
00981     
00982     checkPose(teleopNao); // Check user's pose and assign according values to the messages that will be published
00983 
00984     teleopNao.pubMsg(); // Publish messages to Nao
00985 
00986     pubRate.sleep(); // Sleep to get to the rate of publishing
00987   }
00988 
00989   g_Context.Shutdown(); // Close OpenNI
00990   return 0;
00991 
00992 }


nao_openni
Author(s): Bener SUAY
autogenerated on Mon Jan 6 2014 11:27:51