teleop_nao_joy.cpp
Go to the documentation of this file.
00001 /*
00002  * Nao Joystick / Gamepad teleoperation
00003  *
00004  * Copyright 2009-2012 Armin Hornung, University of Freiburg
00005  * http://www.ros.org/wiki/nao
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 University of Freiburg nor the names of its
00016  *       contributors may be used to endorse or promote products derived from
00017  *       this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00020  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00023  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00024  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00025  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00026  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00027  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00028  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00029  * POSSIBILITY OF SUCH DAMAGE.
00030  */
00031 
00032 #include <nao_teleop/teleop_nao_joy.h>
00033 #include <naoqi_bridge_msgs/BodyPoseActionGoal.h>
00034 #include <naoqi_bridge_msgs/CmdVelService.h>
00035 #include <naoqi_bridge_msgs/JointTrajectoryAction.h>
00036 
00037 using sensor_msgs::Joy;
00038 
00039 namespace nao_teleop{
00040 TeleopNaoJoy::TeleopNaoJoy()
00041 : privateNh("~"), m_enabled(false),
00042   m_xAxis(3), m_yAxis(2), m_turnAxis(0), m_headYawAxis(4),      m_headPitchAxis(5),
00043   m_crouchBtn(8), m_initPoseBtn(0), m_enableBtn(9), m_modifyHeadBtn(5),
00044   m_maxVx(1.0), m_maxVy(1.0), m_maxVw(0.5),
00045   m_maxHeadYaw(2.0943), m_maxHeadPitch(0.7853),
00046   m_bodyPoseTimeOut(5.0),
00047   m_inhibitCounter(0), m_previousJoystick_initialized(false),
00048   m_bodyPoseClient("body_pose", true),
00049   m_prevMotionZero(true), m_prevHeadZero(true)
00050 {
00051   privateNh.param("axis_x", m_xAxis, m_xAxis);
00052   privateNh.param("axis_y", m_yAxis, m_yAxis);
00053   privateNh.param("axis_turn", m_turnAxis, m_turnAxis);
00054   privateNh.param("axis_head_yaw", m_headYawAxis, m_headYawAxis);
00055   privateNh.param("axis_head_pitch", m_headPitchAxis, m_headPitchAxis);
00056   privateNh.param("btn_crouch", m_crouchBtn, m_crouchBtn);
00057   privateNh.param("btn_init_pose", m_initPoseBtn, m_initPoseBtn);
00058   privateNh.param("btn_enable_control", m_enableBtn, m_enableBtn);
00059   privateNh.param("btn_head_mod", m_modifyHeadBtn, m_modifyHeadBtn);
00060   privateNh.param("max_vx", m_maxVx, m_maxVx);
00061   privateNh.param("max_vy", m_maxVy, m_maxVy);
00062   privateNh.param("max_vw", m_maxVw, m_maxVw);
00063 
00064   privateNh.param("max_head_yaw", m_maxHeadYaw, m_maxHeadYaw);
00065   privateNh.param("max_head_pitch", m_maxHeadPitch, m_maxHeadPitch);
00066 
00067   m_motion.linear.x = m_motion.linear.y = m_motion.angular.z = 0.0;
00068   m_headAngles.joint_names.push_back("HeadYaw");
00069   m_headAngles.joint_names.push_back("HeadPitch");
00070   m_headAngles.joint_angles.resize(2, 0.0f);
00071   m_headAngles.relative = 0;
00072   m_headAngles.speed = 0.2; // TODO: param
00073 
00074   m_movePub = nh.advertise<geometry_msgs::Twist>("cmd_vel", 10);
00075   m_headPub = nh.advertise<naoqi_bridge_msgs::JointAnglesWithSpeed>("joint_angles", 1);
00076   m_speechPub = nh.advertise<std_msgs::String>("speech", 1);
00077   m_inhibitWalkSrv = nh.advertiseService("inhibit_walk", &TeleopNaoJoy::inhibitWalk, this);
00078   m_uninhibitWalkSrv = nh.advertiseService("uninhibit_walk", &TeleopNaoJoy::uninhibitWalk, this);
00079   m_cmdVelClient = nh.serviceClient<naoqi_bridge_msgs::CmdVelService>("cmd_vel_srv");
00080   m_stiffnessDisableClient = nh.serviceClient<std_srvs::Empty>("body_stiffness/disable");
00081   m_stiffnessEnableClient = nh.serviceClient<std_srvs::Empty>("body_stiffness/enable");
00082 
00083 
00084   if (!m_bodyPoseClient.waitForServer(ros::Duration(3.0))){
00085     ROS_WARN_STREAM("Could not connect to \"body_pose\" action server, "
00086         << "there will be no body poses available on button presses.\n"
00087         << "Is the pose_manager node running?");
00088   }
00089 
00090   std::cout << "starting button is set to " << m_enableBtn << std::endl;
00091 }
00092 
00093 
00094 ros::Subscriber TeleopNaoJoy::subscribeToJoystick() {
00095   return subscribeToJoystick(&TeleopNaoJoy::joyCallback, this);
00096 }
00097 
00098 bool TeleopNaoJoy::callBodyPoseClient(const std::string& poseName){
00099   if (!m_bodyPoseClient.isServerConnected()){
00100     return false;
00101   }
00102 
00103   naoqi_bridge_msgs::BodyPoseGoal goal;
00104   goal.pose_name = poseName;
00105   m_bodyPoseClient.sendGoalAndWait(goal, m_bodyPoseTimeOut);
00106   actionlib::SimpleClientGoalState state = m_bodyPoseClient.getState();
00107   if (state != actionlib::SimpleClientGoalState::SUCCEEDED){
00108     ROS_ERROR("Pose action \"%s\" did not succeed (%s): %s", goal.pose_name.c_str(), state.toString().c_str(), state.text_.c_str());
00109     return false;
00110   } else{
00111     ROS_INFO("Pose action \"%s\" succeeded", goal.pose_name.c_str());
00112     return true;
00113   }
00114 
00115 }
00116 
00117 
00118 void TeleopNaoJoy::initializePreviousJoystick(const Joy::ConstPtr& joy) {
00119   if(!m_previousJoystick_initialized) {
00120     // if no previous joystick message has been received
00121     // assume all buttons and axes have been zero
00122     //
00123     Joy::Ptr pJoy(new Joy());
00124     pJoy->buttons.resize( joy->buttons.size(), 0);
00125     pJoy->axes.resize( joy->axes.size(), 0.0);
00126     m_previousJoystick = pJoy;
00127     m_previousJoystick_initialized = true;
00128   }
00129 
00130 }
00131 
00132 
00138 void TeleopNaoJoy::joyCallback(const Joy::ConstPtr& joy){
00139   initializePreviousJoystick(joy);
00140 
00141   // Buttons:
00142   // TODO: make buttons generally configurable by mapping btn_id => pose_string
00143 
00144   if (m_enabled && buttonTriggered(m_crouchBtn, joy) && m_bodyPoseClient.isServerConnected()){
00145     if (callBodyPoseClient("crouch")){
00146       std_srvs::Empty e;
00147       m_stiffnessDisableClient.call(e);
00148     }
00149   }
00150 
00151   if (m_enabled && buttonTriggered(m_initPoseBtn, joy) && m_bodyPoseClient.isServerConnected()){
00152     callBodyPoseClient("init");
00153   }
00154 
00155   if (buttonTriggered(m_enableBtn, joy)){
00156     std_msgs::String string;
00157     if (m_enabled){
00158       m_enabled = false;
00159       string.data = "Gamepad control disabled";
00160 
00161     } else{
00162       m_enabled = true;
00163       string.data = "Gamepad control enabled";
00164       std_srvs::Empty e;
00165       m_stiffnessEnableClient.call(e);
00166     }
00167     m_speechPub.publish(string);
00168     ROS_INFO("%s", (string.data).c_str());
00169 
00170   }
00171 
00172 
00173   // directional commands
00174   // walking velocities and head movements
00175   if (!axisValid(m_xAxis, joy) ||  !axisValid(m_yAxis, joy) || !axisValid(m_turnAxis, joy)){
00176     m_motion.linear.x = m_motion.linear.y = m_motion.angular.z = 0.0f;
00177     m_headAngles.joint_angles[0] = m_headAngles.joint_angles[1] = 0.0f;
00178     ROS_WARN("Joystick message too short for Move or Turn axis!\n");
00179   } else{
00180     if (buttonPressed(m_modifyHeadBtn, joy)){
00181       // move head
00182       m_headAngles.header.stamp = ros::Time::now();
00183       m_headAngles.relative = 1;
00184       m_headAngles.joint_angles[0] = joy->axes[m_turnAxis];
00185       m_headAngles.joint_angles[1] = joy->axes[m_xAxis];
00186 
00187     } else {
00188       // stop head:
00189       m_headAngles.joint_angles[0] = m_headAngles.joint_angles[1] = 0.0f;
00190       // move base:
00191       m_motion.linear.x = m_maxVx * std::max(std::min(joy->axes[m_xAxis], 1.0f), -1.0f);
00192       m_motion.linear.y = m_maxVy * std::max(std::min(joy->axes[m_yAxis], 1.0f), -1.0f);
00193       m_motion.angular.z = m_maxVw * std::max(std::min(joy->axes[m_turnAxis], 1.0f), -1.0f);
00194 
00195     }
00196   }
00197 
00198   /*
00199       // Head pos:
00200       if (!axisValid(m_headPitchAxis, joy) || !axisValid(m_headYawAxis, joy)){
00201       m_headAngles.absolute = 0;
00202       m_headAngles.yaw = 0.0;
00203       m_headAngles.pitch = 0.0;
00204       } else {
00205       m_headAngles.absolute = 0;
00206       m_headAngles.yaw = joy->axes[m_headYawAxis];
00207       m_headAngles.pitch = joy->axes[m_headPitchAxis];
00208       }
00209    */
00210 
00211   setPreviousJoystick(joy);
00212 
00213 }
00214 
00222 bool TeleopNaoJoy::buttonPressed(int button, const Joy::ConstPtr& joy) const{
00223   return (button >= 0 && unsigned(button) < joy->buttons.size() && joy->buttons[button] == 1);
00224 }
00225 
00233 bool TeleopNaoJoy::buttonTriggered(int button, const Joy::ConstPtr& joy) const{
00234   return (buttonPressed(button, joy) && buttonChanged(button, joy, m_previousJoystick));
00235 }
00236 
00237 
00238 
00248 bool TeleopNaoJoy::buttonChanged(int button, const Joy::ConstPtr& joy, const Joy::ConstPtr& prevJoy) const{
00249   return (unsigned(button) < joy->buttons.size() && joy->buttons[button] != prevJoy->buttons[button] );
00250 }
00251 
00259 bool TeleopNaoJoy::axisValid(int axis, const Joy::ConstPtr& joy) const{
00260   return (axis >= 0 && unsigned(axis) < joy->buttons.size());
00261 }
00262 
00266 void TeleopNaoJoy::pubMsg(){
00267   if (m_enabled && m_inhibitCounter == 0)
00268   {
00269     const bool headZero = m_headAngles.joint_angles[0] == 0.0f && m_headAngles.joint_angles[1] == 0.0f;
00270     // Send head angle only if it is non-zero or the previous angle was non-zero.
00271     // This avoids sending zero angles repeatedly, which would interfere with other
00272     // modules sending motion commands (e.g., planners) when the joystick is not in use.
00273     if (!headZero || !m_prevHeadZero)
00274     {
00275       m_headPub.publish(m_headAngles);
00276       std::cout << "going to publish head angles" << std::endl;
00277     }
00278     m_prevHeadZero = headZero;
00279 
00280     const bool motionZero = m_motion.linear.x == 0.0f && m_motion.linear.y == 0.0f && m_motion.angular.z == 0.0f;
00281     if (!motionZero || !m_prevMotionZero)
00282     {
00283       m_movePub.publish(m_motion);
00284       std::cout << "going to publish motion commands" << std::endl;
00285     }
00286     m_prevMotionZero = motionZero;
00287 
00288   }
00289 }
00290 
00303 bool TeleopNaoJoy::inhibitWalk(std_srvs::EmptyRequest& /*req*/, std_srvs::EmptyResponse& res) {
00304   if(m_inhibitCounter == 0) {
00305     // not yet inhibited: publish zero walk command before inhibiting joystick
00306     m_motion.linear.x = 0.0;
00307     m_motion.linear.y = 0.0;
00308     m_motion.linear.z = 0.0;
00309     m_motion.angular.x = 0.0;
00310     m_motion.angular.y = 0.0;
00311     m_motion.angular.z = 0.0;
00312     naoqi_bridge_msgs::CmdVelService service_call;
00313     service_call.request.twist = m_motion;
00314     m_cmdVelClient.call(service_call);
00315   }
00316   m_inhibitCounter++;
00317   ROS_DEBUG("Inhibit counter: %d", m_inhibitCounter);
00318   return true;
00319 }
00320 
00331 bool TeleopNaoJoy::uninhibitWalk(std_srvs::EmptyRequest &req, std_srvs::EmptyResponse& res) {
00332   if(m_inhibitCounter > 0) {
00333     m_inhibitCounter--;
00334     ROS_DEBUG("Inhibit counter: %d", m_inhibitCounter);
00335   } else {
00336     m_inhibitCounter = 0;
00337     ROS_WARN("/uninhibit_walk called more times than /inhibit_walk - ignoring");
00338   }
00339   return true;
00340 }
00341 }
00342 


nao_teleop
Author(s): Armin Hornung
autogenerated on Thu Feb 18 2016 11:13:05