teleop_flyer.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2010, UC Regents
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the University of California nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 #include <ros/ros.h>
00036 #include <nodelet/nodelet.h>
00037 #include <pluginlib/class_list_macros.h>
00038 #include <string>
00039 #include <diagnostic_updater/diagnostic_updater.h>
00040 #include "flyer_controller/controller_status.h"
00041 #include "flyer_controller/controller_cmd.h"
00042 #include "std_msgs/Bool.h"
00043 #include "starmac_msgs/OperatorCommands.h"
00044 
00045 #include <sensor_msgs/Joy.h>
00046 
00047 namespace flyer_controller
00048 {
00049 // Button definitions
00050 // TRIGGER
00051 const int BTN_TRIGGER = 0;
00052 const int BTN_PROCEED = 8;
00053 const int BTN_CANCEL = 6;
00054 const int BTN_ESTOP = 11;
00055 // Standby Mode
00056 const int BTN_STANDBY_GO_OPERATIONAL = 2;
00057 // Operational Mode
00058 const int BTN_OPERATIONAL_MODE_ATTITUDE = 2;
00059 const int BTN_OPERATIONAL_MODE_HOVER = 3;
00060 
00061 namespace TeleopTypes
00062 {
00063 enum TeleopStates
00064 {
00065   ERROR = 0, OFF = 1, INITIALIZE = 2, STANDBY = 3, OPERATIONAL = 4
00066 };
00067 typedef TeleopStates TeleopState;
00068 
00069 }
00070 
00071 using namespace TeleopTypes;
00072 using namespace std;
00073 
00074 class TeleopFlyer : public nodelet::Nodelet
00075 {
00076 private:
00077   ros::NodeHandle nh;
00078   ros::NodeHandle nh_priv;
00079   // Publishers
00080   ros::Publisher oper_cmd_pub;
00081   ros::Publisher controller_cmd_pub;
00082   ros::Publisher motor_enable_pub;
00083   ros::Publisher estop_pub;
00084   // Subscribers
00085   ros::Subscriber joy_sub;
00086   ros::Subscriber controller_status_sub;
00087   // Timer
00088   ros::Timer republish_timer;
00089   // Members
00090   bool got_first_joy;
00091   sensor_msgs::Joy prev_joy;
00092   sensor_msgs::Joy latest_joy;
00093   ros::Time latest_joy_time;
00094 
00095   // Diagnostic Updater
00096   diagnostic_updater::Updater diag_updater;
00097 
00098   // Parameters
00099   double joy_republish_rate; // [Hz] rate to republish Joy messages at
00100   double max_interval; // [s] max time between received Joy messages
00101   bool use_udp; // whether to ask for unreliable transport (UDP) in Joy subscriber
00102   double pitch_deadband; // joystick units, 0.1 = 10% of half-range
00103   double roll_deadband; // joystick units, 0.1 = 10% of half-range
00104   double yaw_deadband; // joystick units, 0.1 = 10% of half-range
00105 
00106   // Members
00107   TeleopState state;
00108   bool command_pending; // is there a command pending?
00109   string pending_command; // the pending command
00110   bool lost_joystick; // Latches true if no joystic message seen for too long
00111   double last_trigger_duration;
00112   controller_status latest_controller_status;
00113   double max_joystick_dt; // maximum amount of time seen between joystick messages
00114   bool motors_on;
00115 
00116 public:
00117   TeleopFlyer() :
00118     got_first_joy(false),
00119         diag_updater(), //
00120         joy_republish_rate(20), max_interval(2.0),
00121         use_udp(true), //
00122         pitch_deadband(0.05), roll_deadband(0.05),
00123         yaw_deadband(0.1), //
00124         state(OFF), command_pending(false), pending_command(""), lost_joystick(false), last_trigger_duration(0),
00125         max_joystick_dt(0), motors_on(false)
00126   {
00127   }
00128 
00129   void onInit()
00130   {
00131     nh = getMTNodeHandle();
00132     nh_priv = getMTPrivateNodeHandle();
00133 
00134     // Diagnostics
00135     diag_updater.add("TeleopFlyer Status", this, &TeleopFlyer::diagnostics);
00136     diag_updater.setHardwareID("none");
00137     diag_updater.force_update();
00138     // Parameters
00139     nh_priv.param("joy_republish_rate", joy_republish_rate, joy_republish_rate);
00140     nh_priv.param("max_interval", max_interval, max_interval);
00141     nh_priv.param("use_udp", use_udp, use_udp);
00142     nh_priv.param("pitch_deadband", pitch_deadband, pitch_deadband);
00143     nh_priv.param("roll_deadband", roll_deadband, roll_deadband);
00144     nh_priv.param("yaw_deadband", yaw_deadband, yaw_deadband);
00145 
00146     // Publishers
00147     oper_cmd_pub = nh_priv.advertise<starmac_msgs::OperatorCommands> ("operator_cmds", 1);
00148     //controller_cmd_pub = nh_priv.advertise<controller_cmd> ("controller_cmd", 1, true);
00149     motor_enable_pub = nh_priv.advertise<std_msgs::Bool> ("motor_enable", 1);
00150     estop_pub = nh_priv.advertise<std_msgs::Bool> ("estop", 1);
00151     // Subscribers
00152     ros::TransportHints thints;
00153     if (use_udp)
00154     {
00155       thints.udp();
00156     }
00157     else
00158     {
00159       thints.tcp().tcpNoDelay();
00160     }
00161     joy_sub = nh_priv.subscribe("joy", 10, &TeleopFlyer::joyCallback, this, thints);
00162     controller_status_sub = nh.subscribe("controller/status", 10, &TeleopFlyer::controllerStatusCallback, this); //,
00163     //                                         ros::TransportHints().unreliable().tcpNoDelay());
00164     // Timers
00165     republish_timer = nh.createTimer(ros::Duration(1 / joy_republish_rate), &TeleopFlyer::republishCallback, this);
00166   }
00167 
00168   std::string stateToString(TeleopState t)
00169   {
00170     switch (t)
00171     {
00172       case OFF:
00173         return "OFF";
00174       case ERROR:
00175         return "ERROR";
00176       case INITIALIZE:
00177         return "INITIALIZE";
00178       case STANDBY:
00179         return "STANDBY";
00180       case OPERATIONAL:
00181         return "OPERATIONAL";
00182     }
00183     return "Unknown";
00184   }
00185 
00186 private:
00187 
00188   void diagnostics(diagnostic_updater::DiagnosticStatusWrapper& stat)
00189   {
00190     int status;
00191     status = ((state != ERROR && !lost_joystick) ? int(diagnostic_msgs::DiagnosticStatus::OK)
00192         : int(diagnostic_msgs::DiagnosticStatus::ERROR));
00193     stat.summary(status, stateToString(state));
00194     stat.add("Command Pending", command_pending);
00195     stat.add("Pending Command", pending_command);
00196     stat.add("Lost Joystick", lost_joystick);
00197     stat.add("Trigger Duration", last_trigger_duration);
00198     stat.add("Max time between joystick messages", max_joystick_dt);
00199   }
00200 
00201   void controllerStatusCallback(const controller_statusConstPtr& msg)
00202   {
00203     state = TeleopState(msg->state); // make teleop's state match that of Controller
00204     latest_controller_status = *msg;
00205   }
00206 
00207   void estop()
00208   {
00209     static bool issued_warning = false;
00210     std_msgs::BoolPtr estop_msg(new std_msgs::Bool);
00211     estop_msg->data = true;
00212     estop_pub.publish(estop_msg);
00213     if (not issued_warning)
00214     {
00215       ROS_WARN("E-STOP triggered");
00216       issued_warning = true;
00217     }
00218   }
00219 
00220   void joyCallback(const sensor_msgs::JoyConstPtr msg)
00221   {
00222     //ROS_DEBUG("Joy callback called");
00223     ros::Time rightnow = ros::Time::now();
00224     //latest_joy = msg.get();
00225     if (msg->axes.size() == 0)
00226     {
00227       // didn't get a useful message, so ignore it
00228       NODELET_WARN_STREAM_THROTTLE(0.5, "Received joystick message with zero-size axes array, ignoring...");
00229       return;
00230     }
00231     prev_joy = latest_joy;
00232     latest_joy = *msg;
00233     if (not got_first_joy)
00234     {
00235       got_first_joy = true;
00236       ROS_INFO("Got first joystick message");
00237     }
00238     else
00239     {
00240       double dT = (rightnow - latest_joy_time).toSec();
00241       max_joystick_dt = max(max_joystick_dt, dT);
00242       if (dT > max_interval and (state == OPERATIONAL))
00243       {
00244         ROS_ERROR_STREAM("Time (" << dT << " s) between Joy messages exceeded allowable (" << max_interval << ")");
00245         lost_joystick = true;
00246       }
00247 
00248       if (state == OPERATIONAL)
00249       {
00250         // Look for motor start/stop
00251         // TODO
00252         if (not motors_on and //
00253             (latest_joy.axes[2] <= -0.95) and (latest_joy.axes[3] <= -0.95) // full right yaw and full min altitude
00254         )
00255         {
00256           motors_on = true;
00257           NODELET_INFO("Motors ON");
00258         }
00259 
00260         if (motors_on and //
00261             (latest_joy.axes[2] >= 0.95) and (latest_joy.axes[3] <= -0.95) // full left yaw and full min altitude
00262         )
00263         {
00264           motors_on = false;
00265           NODELET_INFO("Motors OFF");
00266         }
00267       }
00268       else
00269       {
00270         motors_on = false;
00271       }
00272 
00273       latest_joy_time = rightnow;
00274     }
00275 
00276   }
00277 
00278   void republishCallback(const ros::TimerEvent& e)
00279   {
00280     ROS_DEBUG_STREAM("Timer callback triggered " << e.current_real.toSec());
00281     if (got_first_joy)
00282     {
00283       starmac_msgs::OperatorCommandsPtr opercmd_msg_ptr(new starmac_msgs::OperatorCommands);
00284 
00285       double roll_in = double(-latest_joy.axes[0]);
00286       double pitch_in = double(-latest_joy.axes[1]);
00287       double yaw_in = double(-latest_joy.axes[2]);
00288       double roll_sign = (roll_in > 0) ? 1.0 : -1.0;
00289       double pitch_sign = (pitch_in > 0) ? 1.0 : -1.0;
00290       double yaw_sign = (yaw_in > 0) ? 1.0 : -1.0;
00291       opercmd_msg_ptr->roll_cmd = roll_sign * roll_in > roll_deadband ? (max(-1.0, min(1.0, roll_in)) - roll_sign
00292           * roll_deadband) / (1.0 - roll_deadband) : 0.0;
00293       opercmd_msg_ptr->pitch_cmd = pitch_sign * pitch_in > pitch_deadband ? (max(-1.0, min(1.0, pitch_in)) - pitch_sign
00294           * pitch_deadband) / (1.0 - pitch_deadband) : 0.0;
00295       opercmd_msg_ptr->alt_cmd = (min(1.0, max(-1.0, double(latest_joy.axes[3]))) + 1.0) / 2.0; // clip, offset, scale to 0..1 range
00296 
00297       if (opercmd_msg_ptr->alt_cmd > 0.05)
00298       {
00299         opercmd_msg_ptr->yaw_cmd = yaw_sign * yaw_in > yaw_deadband ? (max(-1.0, min(1.0, yaw_in)) - yaw_sign
00300             * yaw_deadband) / (1.0 - yaw_deadband) : 0.0;
00301       }
00302       else
00303       {
00304         opercmd_msg_ptr->yaw_cmd = 0; // don't issue yaw command if alt lever is bottomed out, user probably wants to turn on/off motors
00305       }
00306       opercmd_msg_ptr->motors_on = motors_on;
00307       oper_cmd_pub.publish(opercmd_msg_ptr);
00308     }
00309     else
00310     {
00311       NODELET_WARN_STREAM_THROTTLE(10, "No joystick messages received yet - if this message recurs, check launchfile configuration");
00312       // nothing to republish yet
00313       return;
00314     }
00315     diag_updater.update();
00316     ros::Time now = ros::Time::now();
00317     double joy_interval = (now - latest_joy_time).toSec();
00318     bool joystick_is_recent = (not got_first_joy) or (joy_interval < max_interval);
00319     std_msgs::BoolPtr mtr_enable_msg(new std_msgs::Bool);
00320     if (lost_joystick)
00321     {
00322       estop();
00323       mtr_enable_msg->data = false;
00324       motor_enable_pub.publish(mtr_enable_msg);
00325     }
00326     else
00327     {
00328       if (joystick_is_recent)
00329       {
00330         if (state == OPERATIONAL)
00331         {
00332           mtr_enable_msg->data = motors_on;
00333           motor_enable_pub.publish(mtr_enable_msg);
00334         }
00335         else
00336         {
00337           mtr_enable_msg->data = false;
00338           motor_enable_pub.publish(mtr_enable_msg);
00339         }
00340       }
00341       else
00342       {
00343         if (got_first_joy and (state == OPERATIONAL)) // can't lose it until we've first had it
00344 
00345         {
00346           lost_joystick = true;
00347           ROS_ERROR_STREAM("Too large (" << joy_interval << " s) an interval between joystick messages!");
00348           mtr_enable_msg->data = false;
00349           motor_enable_pub.publish(mtr_enable_msg);
00350         }
00351       }
00352     }
00353   }
00354 
00355 };
00356 PLUGINLIB_DECLARE_CLASS(flyer_controller, TeleopFlyer, flyer_controller::TeleopFlyer, nodelet::Nodelet)
00357 ;
00358 
00359 }
00360 // namespace
00361 
00362 //int main(int argc, char** argv)
00363 //{
00364 //  ros::init(argc, argv, "teleop_flyer");
00365 //  flyer_controller::TeleopFlyer teleop_flyer;
00366 //
00367 //  ros::spin();
00368 //
00369 //  return 0;
00370 //}


flyer_controller
Author(s): Patrick Bouffard
autogenerated on Sun Jan 5 2014 11:37:54