corobot_phidget.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2009, CoroWare
00003  * All rights reserved.
00004  * 
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  * 
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Stanford U. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  * 
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 
00031 #include "ros/ros.h"
00032 #include "orientation.h"
00033 #include <stdio.h>
00034 #include "corobot_msgs/PosMsg.h"
00035 #include "corobot_msgs/PowerMsg.h"
00036 #include "corobot_msgs/SensorMsg.h"
00037 #include "sensor_msgs/MagneticField.h"
00038 #include "sensor_msgs/Imu.h"
00039 #include "std_msgs/Int16.h"
00040 #include <phidget21.h>
00041 #include <tf/transform_datatypes.h>
00042 #include <diagnostic_updater/diagnostic_updater.h>
00043 #include <diagnostic_updater/publisher.h>
00044 #include <corobot_diagnostics/diagnostics.h>
00045 #include <std_srvs/Empty.h>
00046 
00047 CPhidgetInterfaceKitHandle ifKit = 0;
00048 CPhidgetSpatialHandle spatial = 0;
00049 
00050 Orientation orientation_calculation;
00051 
00052 // tells if the rear Bumper is present on the robot or not.
00053 bool m_rearBumperPresent = false; 
00054 
00055 //tells if some sonars are connected
00056 bool sonarsPresent = false; 
00057 
00058 //tells if the imu are connected
00059 bool imu = true; 
00060 
00061 //topics where we want to publish to
00062 ros::Publisher powerdata_pub,irData_pub,bumper_pub, imu_pub, mag_pub, sonar_pub, other_pub; 
00063         
00064 // Service that when called calibrates the gyroscope
00065 // The service is an empty one, so no data is transmited
00066 ros::ServiceServer calibrate_gyroscope_service;
00067   
00068 //Output bw for the sonars. -1 if no sonars are present
00069 int bwOutput = -1; 
00070 
00071 //Output strobe for the sonars. -1 if no sonars are present
00072 int strobeOutput = -1; 
00073 
00074 //last input index for the sonars. -1 if no sonars are present
00075 int lastSonarInput  = -1;
00076 
00077 //first input index for the sonars. -1 if no sonars are present 
00078 int firstSonarInput = -1; 
00079 int batteryPort = 0;
00080 int irFrontPort = 1;
00081 int irBackPort = 2;
00082 
00083 //for diagnostics purpose
00084 int interfaceKitError = 0, spatialError = 0; 
00085 
00086 
00087 
00088 
00089 int ErrorHandler(CPhidgetHandle IFK, void *userptr, int ErrorCode, const char *unknown)
00090 {
00091         ROS_ERROR("Error handled. %d - %s \n", ErrorCode, unknown);
00092         return 0;
00093 }
00094 
00095 
00096 
00102 static float irVoltageToDistance(float volts)
00103 {
00104   int sensorValue=int(volts*200.0+0.5);
00105   float distanceInCm;
00106 //Outside of those bonds, the value is incorrect as our sensor can detect froom 10cm to 80cm only
00107   if (sensorValue>=80 && sensorValue <= 530) 
00108     {
00109       distanceInCm= 2076/(sensorValue-11);
00110     }
00111   //out of bonds
00112   else 
00113     {
00114       // 1000 inches in cm
00115       distanceInCm = 2540; 
00116     }
00117   return distanceInCm/100.0;
00118 }
00119 
00120 
00125 static double sonarVoltageToMeters(int value)
00126 {
00127         // The reported voltage is on the scale Vcc/512 per inch.
00128         // Because we are using the phidget board, Vcc is always 5
00129         const double vcc = 5.0;
00130         double voltage = value * vcc / 1000.0;
00131         double range_inches = voltage * 512 / vcc;
00132     
00133         return range_inches * 2.54 / 100;
00134 }
00135 
00138 int sendSonarResult()
00139 {
00140         
00141         CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 1);
00142         // sleep for 2ms
00143         ros::Duration(0.002).sleep(); 
00144         CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 0);
00145 }
00146 
00147 
00151 int DigitalInputHandler(CPhidgetInterfaceKitHandle IFK, void *usrptr, int Index, int State)
00152 {        
00153         corobot_msgs::SensorMsg msg;
00154         msg.value = ((State == PTRUE)?1:0);
00155         msg.index = Index;
00156         
00157         //Bumper sensor, front or rear
00158         if (Index <= 3 && m_rearBumperPresent == true && bumper_pub) 
00159         {
00160                 msg.type = msg.BUMPER;
00161                 bumper_pub.publish(msg);
00162         }
00163         //Bumper sensor, front
00164         else if (Index <= 1 && bumper_pub)  
00165         {
00166                 msg.type = msg.BUMPER;
00167                 bumper_pub.publish(msg);
00168         }
00169         // we don't know what it is but we publish
00170         else if(other_pub) 
00171         {
00172                 msg.type = msg.OTHER;
00173                 other_pub.publish(msg);
00174         }
00175         
00176 
00177         interfaceKitError = 0;
00178         return 0;
00179 }
00180 
00181 //callback that will run if the sensor value changes by more than the OnSensorChange trigger.
00182 //Index - Index of the sensor that generated the event, Value - the sensor read value
00183 int AnalogInputHandler(CPhidgetInterfaceKitHandle IFK, void *usrptr, int Index, int Value)
00184 {
00185         //sensorValue 0-1000 ==> 0-5V
00186 
00187 
00188     // Update power data    
00189     if(batteryPort == Index && powerdata_pub) 
00190     {
00191         corobot_msgs::PowerMsg powerdata;
00192         powerdata.volts = (float) (Value - 500) * 0.0734;
00193         //The Min and Max present here are for the Nimh battery as it is the only one type of battery sold with a Corobot at the moment
00194         powerdata.min_volt = 10.0;
00195         powerdata.max_volt = 14.2;
00196         powerdata_pub.publish(powerdata);
00197     }
00198 
00199     else if(irFrontPort == Index && irData_pub) // Update IR data
00200     {
00201         corobot_msgs::SensorMsg data;
00202         data.type = data.INFRARED_FRONT;
00203         data.index = Index;
00204 
00205         data.value = irVoltageToDistance((float) Value  / 200.0);       
00206         irData_pub.publish(data);
00207     }
00208         
00209     else if(irBackPort == Index && irData_pub) // Update IR data
00210     {
00211         corobot_msgs::SensorMsg data;
00212         data.type = data.INFRARED_REAR;
00213         data.index = Index;
00214         
00215         data.value = irVoltageToDistance((float) Value  / 200.0);
00216         irData_pub.publish(data);
00217     }
00218     //sonar
00219     else if(Index >= firstSonarInput && Index <= lastSonarInput && sonar_pub)
00220     {
00221             corobot_msgs::SensorMsg data;
00222         data.type = data.ULTRASOUND;
00223         data.index = Index;
00224         
00225         data.value = sonarVoltageToMeters((float) Value  / 200.0);
00226         sonar_pub.publish(data);
00227     }
00228     
00229     else if(other_pub) // We don't know what sensor it is, but we publish
00230     {
00231         corobot_msgs::SensorMsg data;
00232             data.type = data.OTHER;
00233         data.value = Value;
00234         data.index = Index;
00235         other_pub.publish(data);
00236     }
00237     
00238     
00239         interfaceKitError = 0;
00240         return 0;
00241 }
00242 
00243 
00247 int SpatialDataHandler(CPhidgetSpatialHandle spatial, void *userptr, CPhidgetSpatial_SpatialEventDataHandle *data, int count)
00248 {
00249         sensor_msgs::Imu imu;
00250         sensor_msgs::MagneticField mag;
00251 
00252         imu.header.frame_id = "base_link";
00253         imu.header.stamp = ros::Time::now();
00254 
00255         mag.header.frame_id = "base_link";
00256         mag.header.stamp = ros::Time::now();
00257 
00258         for(int i = 0; i< count; i++)
00259         {
00260                 if (data[i]->angularRate[0] != 0 || data[i]->angularRate[1] != 0 || data[i]->angularRate[2] != 0)
00261                         orientation_calculation.updateAngles((float*)&(data[i]->angularRate[0]), (float*)&(data[i]->acceleration), data[i]->timestamp.seconds + ((float)data[i]->timestamp.microseconds)/1000000);
00262 
00263         // Save info into the message and publish
00264                 imu.orientation = tf::createQuaternionMsgFromRollPitchYaw(orientation_calculation.get_roll(),orientation_calculation.get_pitch(), orientation_calculation.get_yaw());
00265                 
00266                 // Set up the angular velocity field. the data->angularRate is in deg/sec while we need the unit to be in rad/s
00267                 imu.angular_velocity.x = data[i]->angularRate[0] * (3.14 / 180);
00268                 imu.angular_velocity.y = data[i]->angularRate[1] * (3.14 / 180);
00269                 imu.angular_velocity.z = data[i]->angularRate[2] * (3.14 / 180);
00270                 // Set up the acceleration field. The data->acceleration is in g while we need the unit to be in m/s^2
00271                 imu.linear_acceleration.x = data[i]->acceleration[0] * 9.81;
00272                 imu.linear_acceleration.y = data[i]->acceleration[1] * 9.81;
00273                 imu.linear_acceleration.z = data[i]->acceleration[2] * 9.81;
00274     // Set up the magnetic_field field. The data->magneticField is in Gauss while we need the unit to be in Tesla.
00275                 mag.magnetic_field.x = data[i]->magneticField[0] * 0.0001;
00276                 mag.magnetic_field.y = data[i]->magneticField[1] * 0.0001;
00277                 mag.magnetic_field.z = data[i]->magneticField[2] * 0.0001;
00278 
00279                 if(imu_pub)
00280                         imu_pub.publish(imu);
00281                 if(mag_pub)
00282                         mag_pub.publish(mag);
00283         }
00284 
00285         spatialError = 0;
00286         return 0;
00287 }
00288 
00289 // Zero the gyroscope when the service is called
00290 // The procedure takes 1-2 seconds and the IMU should not move during the process
00291 bool calibrate_gyroscope(std_srvs::Empty::Request& request, std_srvs::Empty::Response& response)
00292 {
00293   CPhidgetSpatial_zeroGyro(spatial);
00294 }
00295 
00296 
00297 int interfacekit_simple()
00298 // initialize the phidget interface kit board and phidget spatial (imu)
00299 {
00300         int result, num_analog_inputs, num_digital_inputs;
00301         const char *err;
00302         ros::NodeHandle n;
00303 
00304         //create the InterfaceKit object
00305         CPhidgetInterfaceKit_create(&ifKit);
00306 
00307         //Set the handlers to be run when the device is plugged in or opened from software, unplugged or closed from software, or generates an error.
00308         CPhidget_set_OnError_Handler((CPhidgetHandle)ifKit, ErrorHandler, NULL);
00309         CPhidgetInterfaceKit_set_OnInputChange_Handler (ifKit, DigitalInputHandler, NULL);
00310         CPhidgetInterfaceKit_set_OnSensorChange_Handler (ifKit, AnalogInputHandler, NULL);
00311 
00312 
00313         //open the interfacekit and spatial for device connections
00314         CPhidget_open((CPhidgetHandle)ifKit, -1);
00315         
00316 
00317         CPhidgetInterfaceKit_getInputCount(ifKit, &num_digital_inputs);
00318         CPhidgetInterfaceKit_getSensorCount(ifKit, &num_analog_inputs);
00319 
00320 
00321         printf("Waiting for interface kit to be attached....");
00322         if((result = CPhidget_waitForAttachment((CPhidgetHandle)ifKit, 1000)))
00323         {
00324                 CPhidget_getErrorDescription(result, &err);
00325                 ROS_ERROR("Phidget IK: Problem waiting for attachment: %s\n", err);
00326                 interfaceKitError = 1;
00327         }
00328         else
00329         {
00330                 irData_pub = n.advertise<corobot_msgs::SensorMsg>("infrared_data", 100);
00331                 powerdata_pub = n.advertise<corobot_msgs::PowerMsg>("power_data", 100);
00332                 bumper_pub = n.advertise<corobot_msgs::SensorMsg>("bumper_data", 100);
00333                 // sensors connected to the phidget interface kit other than bumpers, voltage sensor, ir sensor and sonars. 
00334                 other_pub = n.advertise<corobot_msgs::SensorMsg>("sensor_data", 100); 
00335         }       
00336         
00337 
00338         //Initialize the phidget spatial board, if any
00339         if (imu)
00340         {
00341                 CPhidgetSpatial_create(&spatial);
00342                 CPhidget_set_OnError_Handler((CPhidgetHandle)spatial, ErrorHandler, NULL);
00343                 CPhidgetSpatial_set_OnSpatialData_Handler(spatial, SpatialDataHandler, NULL);
00344                 CPhidget_open((CPhidgetHandle)spatial, -1);
00345 
00346                 // attach the devices
00347                 printf("Waiting for spatial to be attached.... \n");
00348                 if((result = CPhidget_waitForAttachment((CPhidgetHandle)spatial, 1000)))
00349                 {
00350                         CPhidget_getErrorDescription(result, &err);
00351                         ROS_ERROR("Phidget Spatial: Problem waiting for attachment: %s\n", err);
00352                         spatialError = 1;
00353                 }
00354                 else
00355                 {
00356                         imu_pub = n.advertise<sensor_msgs::Imu>("imu_data",100);
00357                         mag_pub = n.advertise<sensor_msgs::MagneticField>("magnetic_data",100);
00358       calibrate_gyroscope_service = n.advertiseService("calibrate_gyroscope",calibrate_gyroscope);
00359                 }
00360 
00361                 CPhidgetSpatial_setDataRate(spatial, 4);
00362         }
00363 
00364         CPhidgetInterfaceKit_setRatiometric(ifKit, 0);
00365 
00366         //Initialize the sonars, if any are present
00367         if(sonarsPresent)
00368         {
00369                 CPhidgetInterfaceKit_setOutputState(ifKit, bwOutput, 1);
00370                 CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 0);
00371                 // sleep for 250ms
00372                 ros::Duration(0.250).sleep(); 
00373                 CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 1);
00374                 // sleep for 2ms
00375                 ros::Duration(0.002).sleep(); 
00376                 CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 0);
00377                 // sleep for 150ms
00378                 ros::Duration(0.150).sleep(); 
00379 
00380                 sonar_pub = n.advertise<corobot_msgs::SensorMsg>("sonar_data", 100);
00381         }
00382         return 0;
00383 }
00384 
00385 void phidget_ik_diagnostic(diagnostic_updater::DiagnosticStatusWrapper &stat)
00389 {
00390         if (!interfaceKitError)  
00391                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::OK, "initialized");
00392         else
00393         {
00394                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::ERROR, "Phidget 8/8/8 cannot be initialized");
00395                 stat.addf("Recommendation", PhidgetIK_INIT_ERROR);
00396         }
00397 }
00398 
00399 void phidget_spatial_diagnostic(diagnostic_updater::DiagnosticStatusWrapper &stat)
00403 {
00404         if (!spatialError)  
00405                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::OK, "initialized");
00406         else
00407         {
00408                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::ERROR, "IMU cannot be initialized");
00409                 stat.addf("Recommendation", PhidgetIMU_INIT_ERROR);
00410         }
00411 }
00412 
00413 int main(int argc, char* argv[])
00414 {
00415         ros::init(argc, argv, "phidget_component");
00416         ros::NodeHandle n;
00417         ros::NodeHandle nh("~");
00418         nh.param("rearBumper", m_rearBumperPresent, false);
00419         nh.param("bwOutput", bwOutput, -1);
00420         nh.param("strobeOutput", strobeOutput, -1);
00421         nh.param("lastSonarInput", lastSonarInput, -1);
00422         nh.param("firstSonarInput", firstSonarInput, -1);
00423         //index of the battery voltage sensor
00424         nh.param("battery", batteryPort, 0); 
00425         // index of the front ir sensor
00426         nh.param("irFront", irFrontPort, 1); 
00427         //index of the back ir sensor
00428         nh.param("irBack", irBackPort, 2); 
00429         nh.param("imu", imu, true);
00430 
00431         //create an updater that will send information on the diagnostics topics
00432         diagnostic_updater::Updater updater;
00433         updater.setHardwareIDf("Phidget");
00434         //function that will be executed with updater.update()
00435         updater.add("Interface Kit", phidget_ik_diagnostic); 
00436         //function that will be executed with updater.update()
00437         updater.add("Spatial", phidget_spatial_diagnostic);     
00438 
00439         interfacekit_simple();
00440     ros::Rate rate(70);
00441         while (ros::ok())
00442         {
00443                 // ROS loop
00444                 ros::spinOnce(); 
00445         
00446                 // acquire new sonar data if sonar sensors are present
00447                 if(sonarsPresent) 
00448                         sendSonarResult();
00449                 //update diagnostics
00450                 updater.update(); 
00451                 rate.sleep();
00452         }
00453 
00454         
00455         // close all the phidget devices
00456         CPhidget_close((CPhidgetHandle)ifKit);
00457         CPhidget_delete((CPhidgetHandle)ifKit);
00458         
00459         if (imu)
00460         {
00461                 CPhidget_close((CPhidgetHandle)spatial);
00462                 CPhidget_delete((CPhidgetHandle)spatial);
00463         }
00464 
00465         return 0;
00466 }


corobot_phidget_ik
Author(s):
autogenerated on Sun Oct 5 2014 23:18:06