corobot_phidget.cpp
Go to the documentation of this file.
00001 // Copyright 2008 Phidgets Inc.  All rights reserved.
00002 // This work is licensed under the Creative Commons Attribution 2.5 Canada License. 
00003 // view a copy of this license, visit http://creativecommons.org/licenses/by/2.5/ca/
00004 
00005 
00006 
00007 #include "ros/ros.h"
00008 #include <stdio.h>
00009 #include "corobot_srvs/SetOdom.h"
00010 #include "corobot_msgs/PosMsg.h"
00011 #include "corobot_msgs/PowerMsg.h"
00012 #include "corobot_msgs/IrMsg.h"
00013 #include "corobot_msgs/BumperMsg.h"
00014 #include "corobot_msgs/GripperMsg.h"
00015 #include "corobot_msgs/phidget_info.h"
00016 #include "corobot_msgs/spatial.h"
00017 #include "corobot_msgs/RangeSensor.h"
00018 #include "sensor_msgs/Imu.h"
00019 #include <phidget21.h>
00020 #include <tf/transform_datatypes.h>
00021 #include <diagnostic_updater/diagnostic_updater.h>
00022 #include <diagnostic_updater/publisher.h>
00023 
00024 #define GRAVITY 9.81
00025 
00026 CPhidgetEncoderHandle m_rightEncoder;
00027 CPhidgetEncoderHandle m_leftEncoder;
00028 CPhidgetInterfaceKitHandle ifKit = 0;
00029 CPhidgetSpatialHandle spatial = 0;
00030 
00031 int  analog_inputs_value [8] = {0,0,0,0,0,0,0,0};
00032 char digital_inputs_value = 0;
00033 float acc [3] = {0,0,0};
00034 float ang [3] = {0,0,0};
00035 float mag [3] = {0,0,0};
00036 
00037 bool m_encoder1Seen=false, m_encoder2Seen=false,m_encodersGood = false;//usefull to set up the phidget
00038 bool m_validAnalogs = false,m_validDigitals=false; //tells if we received any data from the phidget or not.
00039 int m_leftEncoderNumber,m_rightEncoderNumber;
00040 
00041 bool m_rearBumperPresent = false; // tells if the rear Bumper is present on the robot or not.
00042 bool sonarsPresent = false; //tells if some sonars are connected
00043 
00044 bool phidget888_connected, phidget_encoder_connected;
00045 
00046 bool spatial_good = false;
00047 
00048 ros::Publisher posdata_pub,powerdata_pub,irData_pub,bumper_pub,gripper_pub,spatial_pub, imu_pub, sonar_pub; //topics where we want to publish to
00049 
00050 int bwOutput = -1; //Output bw for the sonars. -1 if no sonars are present
00051 int strobeOutput = -1; //Output strobe for the sonars. -1 if no sonars are present
00052 int lastSonarInput  = -1; //last input index for the sonars. -1 if no sonars are present
00053 int firstSonarInput = -1; //first input index for the sonars. -1 if no sonars are present
00054 int batteryPort = 0;
00055 int irFrontPort = 1;
00056 int irBackPort = 2;
00057 int gripperPort = 3;
00058 
00059 bool motors_inverted; //specify if the wiring of the robot (motors + encoders) has been inverted
00060 bool encoders_inverted; //specify if the wiring of the encoders has been inverted
00061 
00062 int interfaceKitError = 0, spatialError = 0, encoderError = 0;
00063 
00064 int RightEncoderAttach(CPhidgetHandle phid, void *userPtr);
00065 int LeftEncoderAttach(CPhidgetHandle phid, void *userPtr);
00066 
00067 double dCMMatrix[3][3]; //Matrix essential for calculating the orientation with IMU data
00068 int timestampPreviousCall; //timestamp of the measurement got at the last call of the function
00069 double pitchOffset; //offset necessary go from angle in the intial device position coordinate system to the User coordinate system
00070 double rollOffset; //offset necessary go from angle in the intial device position coordinate system to the User coordinate system
00071 double gravity[3]; //We save the gravity to know if the gravity we got is different from before or not
00072 double proportionalVector[3];//Proportional correction
00073 double integratorVector[3];//Omega Integrator
00074 double gyroscopeVectorCorrected[3]; //Corrected GyroVector data
00075 double kpRollpitch = 0.015; //Used in a PID controller to calculate the roll and pitch angles
00076 double kiRollpitch = 0.000010; //Used in a PID controller to calculate the roll and pitch angles
00077 double gravityEpsilon = 0.07;
00078 int validGravityCounter = 0; //Counter the number of valid gravity vector(valid in the sense that the norm is near 1) that follow each other
00079 int validAccelerationVectorsNecessaryToDetectGravity = 10;
00080 double lastGravityVectorDetected[3] = {0,0,0}; //save the value of the last gravity vector detected with the algorithm values
00081 
00082 
00084 bool SetOdom(corobot_srvs::SetOdom::Request  &req,corobot_srvs::SetOdom::Response &res )
00085 {
00086         int err=0;
00087         err = CPhidgetEncoder_setPosition(m_leftEncoder, m_leftEncoderNumber,req.px);
00088         err += CPhidgetEncoder_setPosition(m_rightEncoder, m_rightEncoderNumber,req.py);
00089 
00090         if(err)
00091             res.err = 1;
00092         else
00093             res.err = 0;
00094 
00095         return true;
00096 
00097 }
00098 
00099 int AttachHandler(CPhidgetHandle IFK, void *userptr)
00100 {
00101         int serialNo;
00102         const char *name;
00103 
00104         CPhidget_getDeviceName(IFK, &name);
00105         CPhidget_getSerialNumber(IFK, &serialNo);
00106 
00107         printf("%s %10d attached!\n", name, serialNo);
00108 
00109         return 0;
00110 }
00111 
00112 int DetachHandler(CPhidgetHandle IFK, void *userptr)
00113 {
00114         int serialNo;
00115         const char *name;
00116 
00117         CPhidget_getDeviceName (IFK, &name);
00118         CPhidget_getSerialNumber(IFK, &serialNo);
00119 
00120         printf("%s %10d detached!\n", name, serialNo);
00121 
00122         return 0;
00123 }
00124 
00125 int ErrorHandler(CPhidgetHandle IFK, void *userptr, int ErrorCode, const char *unknown)
00126 {
00127         //printf("Error handled. %d - %s", ErrorCode, unknown);  //Do not print error info
00128         printf("Error handled. %d - %s \n", ErrorCode, unknown);
00129         return 0;
00130 }
00131 
00132 void encoderAttach(const int which)
00137 {
00138     if (which == 1)
00139     {
00140         m_encoder1Seen = true;
00141         // this could either be the left encoder board, or it's a single board that
00142         // supports both encoders. Check how many inputs it has
00143         int count;
00144         CPhidgetEncoder_getEncoderCount(m_leftEncoder, &count);
00145         if (count > 1)
00146         {
00147             // we have only one board that supports both motor encoders
00148             m_leftEncoderNumber = 0;
00149             m_rightEncoderNumber = 1;
00150                 
00151             if(motors_inverted || encoders_inverted){
00152                 m_leftEncoderNumber = 1;
00153                 m_rightEncoderNumber = 0;
00154             }
00155                 
00156             m_rightEncoder = m_leftEncoder;
00157             m_encoder2Seen = true;
00158             m_encodersGood = true;
00159         }
00160         else
00161         {
00162             m_leftEncoderNumber = 0;
00163             m_rightEncoderNumber = 0;
00164             // open the second encoder (assume it's the right one)
00165             CPhidgetEncoder_create(&m_rightEncoder);
00166             CPhidget_set_OnAttach_Handler((CPhidgetHandle) m_rightEncoder,
00167                                           RightEncoderAttach, NULL);
00168             CPhidget_open((CPhidgetHandle) m_rightEncoder, -1);
00169         }
00170     }
00171     else
00172     {
00173         m_encoder2Seen = true;
00174     }
00175     // have we seen both of the encoders?
00176     if (m_encoder1Seen && m_encoder2Seen)
00177     {
00178         // now check the assumption we have the encoders correct
00179         int leftSerial;
00180         int rightSerial;
00181 
00182         CPhidget_getSerialNumber((CPhidgetHandle) m_leftEncoder, &leftSerial);
00183         CPhidget_getSerialNumber((CPhidgetHandle) m_rightEncoder, &rightSerial);
00184 
00185         if (leftSerial > rightSerial)
00186         {
00187             // oops, we go them backwards so swap them
00188             CPhidgetEncoderHandle tempEncoder = m_leftEncoder;
00189             m_leftEncoder = m_rightEncoder;
00190             m_rightEncoder = tempEncoder;
00191 
00192             int tempNumber = m_leftEncoderNumber;
00193             m_leftEncoderNumber = m_rightEncoderNumber;
00194             m_rightEncoderNumber = tempNumber;
00195         }
00196         CPhidgetEncoder_setEnabled(m_leftEncoder, m_leftEncoderNumber, PTRUE);
00197         CPhidgetEncoder_setEnabled(m_rightEncoder, m_rightEncoderNumber, PTRUE);
00198         m_encodersGood = true;
00199 
00200     }
00201 }
00206 int RightEncoderAttach(CPhidgetHandle phid, void *userPtr)
00207 
00208 {
00209         encoderAttach(2);
00210         return 0;
00211 }
00215 int LeftEncoderAttach(CPhidgetHandle phid, void *userPtr)
00216 
00217 {
00218         encoderAttach(1);
00219         return 0;
00220 }
00221 
00227 static float irVoltageToDistance(float volts)
00228 {
00229   int sensorValue=int(volts*200.0+0.5);
00230   float distanceInCm;
00231   if (sensorValue>=80 && sensorValue <= 530) //Outside of those bonds, the value is incorrect as our sensor can detect froom 10cm to 80cm only
00232     {
00233       distanceInCm= 2076/(sensorValue-11);
00234     }
00235   else //out of bonds
00236     {
00237       distanceInCm = 2540; // 1000 inches in cm
00238     }
00239   return distanceInCm/100.0;
00240 }
00241 
00246 static double sonarVoltageToMeters(int value)
00247 {
00248         // The reported voltage is on the scale Vcc/512 per inch.
00249         // Because we are using the phidget board, Vcc is always 5
00250         const double vcc = 5.0;
00251         double voltage = value * vcc / 1000.0;
00252         double range_inches = voltage * 512 / vcc;
00253 
00254         return range_inches * 2.54 / 100;
00255 }
00256 
00260 int publish_data(){
00261 
00262     if(spatial_good)
00263     {
00264         corobot_msgs::spatial spatial;
00265         sensor_msgs::Imu imu;
00266 
00267         spatial.acc1 = acc[0];
00268         spatial.acc2 = acc[1];
00269         spatial.acc3 = acc[2];
00270         spatial.ang1 = ang[0];
00271         spatial.ang2 = ang[1];
00272         spatial.ang3 = ang[2];
00273         spatial.mag1 = mag[0];
00274         spatial.mag2 = mag[1];
00275         spatial.mag3 = mag[2];
00276 
00277         imu.header.frame_id = "base_link";
00278         imu.header.stamp = ros::Time::now();
00279         imu.orientation = tf::createQuaternionMsgFromRollPitchYaw((atan2(dCMMatrix[2][1],dCMMatrix[2][2]) + rollOffset),(-asin(dCMMatrix[2][0]) + pitchOffset), (atan2(dCMMatrix[1][0],dCMMatrix[0][0])));
00280         imu.angular_velocity.x = ang[0];
00281         imu.angular_velocity.y = ang[1];
00282         imu.angular_velocity.z = ang[2];
00283         imu.linear_acceleration.x = acc[0];
00284         imu.linear_acceleration.y = acc[1];
00285         imu.linear_acceleration.z = acc[2];
00286 
00287         spatial_pub.publish(spatial);
00288         imu_pub.publish(imu);
00289 
00290         spatialError = 0;
00291     }
00292 
00293     if(m_encodersGood) //the position is 4times the number of encoder counts.
00294     {
00295         corobot_msgs::PosMsg posdata;
00296 
00297         // prepare to read the encoder status
00298         int phidgetEncoderStatus = 0;
00299 
00300         CPhidget_getDeviceStatus((CPhidgetHandle) m_leftEncoder,
00301                                  &phidgetEncoderStatus);
00302         if (phidgetEncoderStatus != 0)
00303         {
00304             int value;
00305 
00306             CPhidgetEncoder_getPosition(m_leftEncoder, m_leftEncoderNumber, &value);
00307                     posdata.px = -value;
00308             printf("Left encoder value = %d", value);
00309 
00310         }
00311         else
00312         {
00313             encoderError = 2;
00314         }
00315         phidgetEncoderStatus = 0;
00316         CPhidget_getDeviceStatus((CPhidgetHandle) m_rightEncoder,
00317                                  &phidgetEncoderStatus);
00318         if (phidgetEncoderStatus != 0)
00319         {
00320             int value;
00321 
00322             CPhidgetEncoder_getPosition(m_rightEncoder, m_rightEncoderNumber, &value);
00323             // we negate this value since the right side motors turn the opposite direction
00324             // to the left side
00325                     posdata.py = value;
00326         }
00327         else
00328         {
00329             encoderError = 2;
00330         }
00331 
00332         posdata.header.stamp = ros::Time::now();
00333         posdata_pub.publish(posdata);
00334         encoderError = 0;
00335     }
00336     else
00337     {
00338         encoderError = 1;
00339     }
00340     if (m_validAnalogs){
00342         // Update power data
00343         if(batteryPort != -1)
00344         {
00345                 corobot_msgs::PowerMsg powerdata;
00346                 powerdata.volts = (float) (analog_inputs_value[batteryPort] - 500) * 0.0734;
00347                 //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
00348                 powerdata.min_volt = 10.0;
00349                 powerdata.max_volt = 14.2;
00350                 powerdata_pub.publish(powerdata);
00351         }
00352 
00354         // Update IR data
00355         if(irFrontPort != -1 || irBackPort != -1)
00356         {
00357                 corobot_msgs::IrMsg irData;
00358                 if(irFrontPort != -1)
00359                         irData.voltage1=(float) analog_inputs_value[irFrontPort]  / 200.0;
00360                 else
00361                         irData.voltage1=0;
00362                 if(irBackPort != -1)
00363                         irData.voltage2=(float) analog_inputs_value[irBackPort]  / 200.0;
00364                 else
00365                         irData.voltage2=0;
00366                 irData.range1=irVoltageToDistance(irData.voltage1);
00367                 irData.range2=irVoltageToDistance(irData.voltage2);
00368                 irData_pub.publish(irData);
00369         }
00370 
00371         if(gripperPort != -1)
00372         {
00373                 corobot_msgs::GripperMsg gripperData;  // Gripper connect to the 4th analog input port?
00374                 gripperData.state=analog_inputs_value[gripperPort];
00375                 gripper_pub.publish(gripperData);
00376         }
00377         interfaceKitError = 0;
00378     }
00379 
00380     if (m_validDigitals)
00381       {
00383         // Update the bumper data
00384         corobot_msgs::BumperMsg bumper_data;
00385 
00386         if (m_rearBumperPresent)
00387           {
00388             bumper_data.bumpers_count=4;
00389             bumper_data.value0=(digital_inputs_value) & (0x01<<0);
00390             bumper_data.value1=(digital_inputs_value) & (0x01<<1);
00391             bumper_data.value2=(digital_inputs_value) & (0x01<<2);
00392             bumper_data.value3=(digital_inputs_value) & (0x01<<3);
00393           } 
00394         else {
00395           bumper_data.bumpers_count=2;
00396           bumper_data.value0=(digital_inputs_value) & (0x01<<0);
00397           bumper_data.value1=(digital_inputs_value) & (0x01<<1);
00398         }
00399 
00400         bumper_pub.publish(bumper_data);
00401         interfaceKitError = 0;
00402 
00403       }
00404 
00405     return 0;
00406 }
00407 
00411 int sendSonarResult()
00412 {
00413         corobot_msgs::RangeSensor data;
00414         data.type = data.ULTRASOUND;
00415         data.numberSensors = lastSonarInput + 1 - firstSonarInput;      
00416 
00417         CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 1);
00418         ros::Duration(0.002).sleep(); // sleep for 2ms
00419         CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 0);
00420 
00421         //Acquire the data and transform them into values in meters
00422         for(int i = firstSonarInput; i<= lastSonarInput;i++)
00423         {
00424                 data.range.push_back(sonarVoltageToMeters(analog_inputs_value[i]));
00425         }
00426          sonar_pub.publish(data);
00427         return 0;
00428 }
00429 
00430 
00434 int InputChangeHandler(CPhidgetInterfaceKitHandle IFK, void *usrptr, int Index, int State)
00435 {
00436         printf("Digital Input: %d > State: %d\n", Index, State);
00437 
00438         if(State==PTRUE)
00439           digital_inputs_value = digital_inputs_value|(0x01<<Index);
00440         else if(State == PFALSE)
00441           digital_inputs_value = digital_inputs_value&(~(0x01<<Index));
00442          m_validDigitals = true;
00443   
00444         return 0;
00445 }
00446 
00447 //callback that will run if an output changes.
00448 //Index - Index of the output that generated the event, State - boolean (0 or 1) representing the output state (on or off)
00449 int OutputChangeHandler(CPhidgetInterfaceKitHandle IFK, void *usrptr, int Index, int State)
00450 {
00451         printf("Digital Output: %d > State: %d\n", Index, State);
00452         return 0;
00453 }
00454 
00455 //callback that will run if the sensor value changes by more than the OnSensorChange trigger.
00456 //Index - Index of the sensor that generated the event, Value - the sensor read value
00457 int SensorChangeHandler(CPhidgetInterfaceKitHandle IFK, void *usrptr, int Index, int Value)
00458 {
00459         printf("Sensor: %d > Value: %d\n", Index, Value);
00460 
00461         //sensorValue 0-1000 ==> 0-5V
00462 
00463         analog_inputs_value[Index]=Value;      //SEGMENTATION FAULT!!
00464 
00465         m_validAnalogs = true;
00466 
00467         return 0;
00468 }
00469 
00470 void vectorScale(double vectorOut[3],double vectorIn[3], double scale2)
00471 {
00472     for(int c=0; c<3; c++)
00473     {
00474         vectorOut[c]=vectorIn[c]*scale2; 
00475     }
00476 }
00477 
00478 
00479 
00480 void vectorCrossProduct(double vectorOut[3], double vectorIn1[3],double vectorIn2[3])
00481 {
00482     vectorOut[0]= (vectorIn1[1]*vectorIn2[2]) - (vectorIn1[2]*vectorIn2[1]);
00483     vectorOut[1]= (vectorIn1[2]*vectorIn2[0]) - (vectorIn1[0]*vectorIn2[2]);
00484     vectorOut[2]= (vectorIn1[0]*vectorIn2[1]) - (vectorIn1[1]*vectorIn2[0]);
00485 }
00486 
00487 void vectorAddition(double vectorOut[3],double vectorIn1[3], double vectorIn2[3])
00488 {
00489     for(int c=0; c<3; c++)
00490     {
00491         vectorOut[c]=vectorIn1[c]+vectorIn2[c];
00492     }
00493 }
00494 
00495 void matrixMultiply(double MatrixOut[3][3],double MatrixIn1[3][3], double MatrixIn2[3][3])
00496 {
00497     double op[3]; 
00498     for(int x=0; x<3; x++)
00499     {
00500         for(int y=0; y<3; y++)
00501         {
00502             for(int w=0; w<3; w++)
00503             {
00504                 op[w]=MatrixIn1[x][w]*MatrixIn2[w][y];
00505             } 
00506             MatrixOut[x][y]=0;
00507             MatrixOut[x][y]=op[0]+op[1]+op[2];
00508             
00509         }
00510     }
00511 }
00512 
00513 double ToRad(double value)
00514 {
00515     return value*0.01745329252;  // *pi/180
00516 }
00517 
00518 double vectorDotProduct(double vector1[3],double vector2[3])
00519 {
00520     double op=0;
00521     
00522     for(int c=0; c<3; c++)
00523     {
00524         op+=vector1[c]*vector2[c];
00525     }
00526     
00527     return op; 
00528 }
00529 
00530 double constrain(double value, double min, double max)
00531 {
00532     if((value>=min) && (value<= max))
00533         return value;
00534     else if(value<min)
00535         return min;
00536     else //a>max
00537         return max;
00538 }
00539 
00540 bool isEqual(double vector1[3], double vector2[3])
00541 {
00542     if(abs(vector1[0] - (vector2[0]/GRAVITY) + vector1[1] - (vector2[1]/GRAVITY) + vector1[2] - (vector2[2]/GRAVITY))>0.0001)
00543         return false;
00544     else
00545         return true;
00546 }
00547 
00548 // Update the dCM matrix to caculate the orientation
00549 void AnglesMatrixUpdate(float gyroscopeVector[3], int gyroscopeTimestamp)
00550 {
00551 
00552     
00553     double period;
00554         if(timestampPreviousCall==0) 
00555             period = 0;
00556         else
00557             period = (gyroscopeTimestamp - timestampPreviousCall); //time in seconds between the two last measures we got
00558     
00559     double temporaryVector[3]= {0,0,0};
00560     double updateMatrix[3][3]; //temporary matrix we use to update the dCMMatrix
00561     double temporaryMatrix[3][3];
00562     double gyroscopeVectorInvertedSystem[3]; //We call here Inverted System the Device coordinate system with Dx = Dy and Dy = Dx( the axis are exchanged)
00563     
00564     
00565     /****** We have to Exchange the x and y value of our vector to correspond with the system used by the algorithm *******/
00566     gyroscopeVectorInvertedSystem[0] =  gyroscopeVector[1];
00567     gyroscopeVectorInvertedSystem[1] =  gyroscopeVector[0];
00568     gyroscopeVectorInvertedSystem[2] =  gyroscopeVector[2];
00569     
00570     
00571     
00572     /******Calculate the GyroscopeVectorCorrected which is the gyroscope vector measure plus some vectors we get with the drift correction ( corresponding to the PID of the algorithm ********/
00573     vectorAddition(temporaryVector, gyroscopeVectorInvertedSystem, integratorVector);  //adding proportional term
00574     vectorAddition(gyroscopeVectorCorrected, temporaryVector, proportionalVector); //adding Integrator term
00575     
00576     
00577     /******We calculate the changement that the new measurement will imply*******/
00578     updateMatrix[0][0]=0;
00579     updateMatrix[0][1]=-period*gyroscopeVectorCorrected[2];
00580     updateMatrix[0][2]=period*gyroscopeVectorCorrected[1];
00581     updateMatrix[1][0]=period*gyroscopeVectorCorrected[2];
00582     updateMatrix[1][1]=0;
00583     updateMatrix[1][2]=-period*gyroscopeVectorCorrected[0];
00584     updateMatrix[2][0]=-period*gyroscopeVectorCorrected[1];
00585     updateMatrix[2][1]=period*gyroscopeVectorCorrected[0];
00586     updateMatrix[2][2]=0;
00587     
00588     
00589     
00590     /******We update the DCM Matrix *******/
00591     matrixMultiply(temporaryMatrix,dCMMatrix, updateMatrix); 
00592     
00593     for(int x=0; x<3; x++) //Matrix Addition (update)
00594     {
00595         for(int y=0; y<3; y++)
00596         {
00597             dCMMatrix[x][y]+=temporaryMatrix[x][y];
00598         } 
00599     }
00600     
00601     timestampPreviousCall = gyroscopeTimestamp;
00602     
00603 }
00604 
00605 //Normalize thed DCM matrix to caculate the orientation
00606 void AnglesNormalize()
00607 {
00608     double error=0;
00609     double temporaryMatrix[3][3];
00610     double vectorNorm=0;
00611     bool problem=false;
00612     
00613     /*******We want to make sure that the rows of our DCM Matrix are orthogonal. If not, we make them orthogonal.*******/
00614     
00615     error= -vectorDotProduct(dCMMatrix[0],dCMMatrix[1])*.5f; //eq.19
00616     
00617     vectorScale(temporaryMatrix[0], dCMMatrix[1], error); //eq.19
00618     vectorScale(temporaryMatrix[1], dCMMatrix[0], error); //eq.19
00619     
00620     vectorAddition(temporaryMatrix[0], temporaryMatrix[0], dCMMatrix[0]);//eq.19
00621     vectorAddition(temporaryMatrix[1], temporaryMatrix[1], dCMMatrix[1]);//eq.19
00622     
00623     vectorCrossProduct(temporaryMatrix[2],temporaryMatrix[0],temporaryMatrix[1]);  //eq.20
00624     
00625     
00626     /******We make sure that the norm of our vector is 1*******/
00627     
00628     vectorNorm= vectorDotProduct(temporaryMatrix[0],temporaryMatrix[0]); 
00629     if (vectorNorm < 1.5625f && vectorNorm > 0.64f) {
00630         vectorNorm= .5f * (3-vectorNorm);                                                 //eq.21
00631     } else if (vectorNorm < 100.0f && vectorNorm > 0.01f) {
00632         vectorNorm= 1. / sqrt(vectorNorm);
00633     } else {
00634         problem = true;
00635     }
00636     vectorScale(dCMMatrix[0], temporaryMatrix[0], vectorNorm);
00637     
00638     vectorNorm= vectorDotProduct(temporaryMatrix[1],temporaryMatrix[1]); 
00639     if (vectorNorm < 1.5625f && vectorNorm > 0.64f) {
00640         vectorNorm= .5f * (3-vectorNorm);                                                 //eq.21
00641     } else if (vectorNorm < 100.0f && vectorNorm > 0.01f) {
00642         vectorNorm= 1. / sqrt(vectorNorm);  
00643     } else {
00644         problem = true;
00645     }
00646     vectorScale(dCMMatrix[1], temporaryMatrix[1], vectorNorm);
00647     
00648     vectorNorm= vectorDotProduct(temporaryMatrix[2],temporaryMatrix[2]); 
00649     if (vectorNorm < 1.5625f && vectorNorm > 0.64f) {
00650         vectorNorm= .5f * (3-vectorNorm);                                                 //eq.21
00651     } else if (vectorNorm < 100.0f && vectorNorm > 0.01f) {
00652         vectorNorm= 1. / sqrt(vectorNorm);   
00653     } else {
00654         problem = true;  
00655     }
00656     vectorScale(dCMMatrix[2], temporaryMatrix[2], vectorNorm);
00657     
00658     
00659     /******If we can't renormalize ou matrix, then we reset it.*******/ 
00660     if (problem) {          // Our solution is blowing up and we will force back to initial condition.  Hope we are not upside down!
00661         dCMMatrix[0][0]= 1.0f;
00662         dCMMatrix[0][1]= 0.0f;
00663         dCMMatrix[0][2]= 0.0f;
00664         dCMMatrix[1][0]= 0.0f;
00665         dCMMatrix[1][1]= 1.0f;
00666         dCMMatrix[1][2]= 0.0f;
00667         dCMMatrix[2][0]= 0.0f;
00668         dCMMatrix[2][1]= 0.0f;
00669         dCMMatrix[2][2]= 1.0f;
00670         problem = false;  
00671     }
00672     
00673 }
00674 
00675 //Correct the drift or the gyroscope to get a more accurate result
00676 void AnglesDriftCorrection(float AccelerationVector[3])
00677 {
00678     
00679     double scaledIntegratorVector[3];
00680     double accelerationMagnitude;
00681     double accelerationWeight;
00682     double integratorMagnitude;
00683     double AccelerationVectorInvertedDystem[3];//We call here Inverted System the Device coordinate system with Dx = Dy and Dy = Dx( the axis are exchanged)
00684     double errorRollPitch[3];
00685     
00686     
00687     
00688     /******We calculate a vector  proportionalVector and integratorVector to add to the gyroscopeVector to cancel the drift. Those two vectors are calculated with the accelerometer vector. It doesn't cancel the drift for the yaw angle. *******/
00689     
00690     /****** Calculate the magnitude of the accelerometer vector***********/
00691     accelerationMagnitude = sqrt(AccelerationVector[0]*AccelerationVector[0] + AccelerationVector[1]*AccelerationVector[1] + AccelerationVector[2]*AccelerationVector[2]);
00692     accelerationMagnitude = accelerationMagnitude / GRAVITY; // We know have value of 1 = 1g
00693     
00694     // Dynamic weighting of accelerometer info (reliability filter)
00695     // Weight for accelerometer info (<0.5G = 0.0, 1G = 1.0- , >1.5G = 0.0)
00696     accelerationWeight = constrain(1 - 2*abs(1 - accelerationMagnitude),0,1);   
00697     
00698     
00699     /****We make sure that the acceleration vector has the same system as the one we use in the algorithm *******/
00700     AccelerationVectorInvertedDystem[0] =  AccelerationVector[1];
00701     AccelerationVectorInvertedDystem[1] =  AccelerationVector[0];
00702     AccelerationVectorInvertedDystem[2] =  AccelerationVector[2];
00703     
00704     
00705     /*****We calculate the weights using the fact that 1g = 101********/
00706     vectorScale(AccelerationVectorInvertedDystem,AccelerationVectorInvertedDystem,101/9.81);
00707     
00708     /******We calculate our two vectors proportionalVector and integratorVector********/
00709     vectorCrossProduct(errorRollPitch,AccelerationVectorInvertedDystem,dCMMatrix[2]); //adjust the ground of reference
00710     vectorScale(proportionalVector,errorRollPitch,kpRollpitch*accelerationWeight);
00711     
00712     vectorScale(scaledIntegratorVector,errorRollPitch,kiRollpitch*accelerationWeight);
00713     vectorAddition(integratorVector,integratorVector,scaledIntegratorVector);     
00714     
00715     
00716     
00717     //  Here we will place a limit on the integrator so that the integrator cannot ever exceed half the saturation limit of the gyros
00718     integratorMagnitude = sqrt(vectorDotProduct(integratorVector,integratorVector));
00719     if (integratorMagnitude > ToRad(300)) {
00720         vectorScale(integratorVector,integratorVector,0.5f*ToRad(300)/integratorMagnitude);
00721     }
00722     
00723     
00724 }
00725 
00726 void detectGravity(double gravity[3], float acceleration[3])
00727 {
00728     double norm = sqrt(acceleration[0] * acceleration[0] + acceleration[1] * acceleration[1] + acceleration[2] * acceleration[2]);
00729     
00730     /***** If the norm is near 1, then the vector is valid******/
00731     if(norm >= (GRAVITY*(1-gravityEpsilon)) && norm<= (GRAVITY*(1+gravityEpsilon)))
00732     {
00733         validGravityCounter++;
00734         
00735         /******After validAccelerationVectorsNecessaryToDetectGravity vectors, we say that the last valid vector is really the gravity and not the gravity + a certain acceleration*********/
00736         if(validGravityCounter == validAccelerationVectorsNecessaryToDetectGravity)
00737         {
00738             lastGravityVectorDetected[0] = acceleration[0];
00739             lastGravityVectorDetected[1] = acceleration[1];
00740             lastGravityVectorDetected[2] = acceleration[2];
00741             
00742             validGravityCounter = 0;
00743         }
00744     }
00745     else
00746         validGravityCounter =0;
00747     
00748     gravity[0] = lastGravityVectorDetected[0];
00749     gravity[1] = lastGravityVectorDetected[1];
00750     gravity[2] = lastGravityVectorDetected[2];
00751     
00752 }
00753 
00754 
00755 int SpatialDataHandler(CPhidgetSpatialHandle spatial, void *userptr, CPhidgetSpatial_SpatialEventDataHandle *data, int count)
00756 {
00757         int i;
00758         //printf("Number of Data Packets in this event: %d\n", count);
00759         /*for(i = 0; i < count; i++)
00760         {
00761                 printf("=== Data Set: %d ===\n", i);
00762                 printf("Acceleration> x: %6f  y: %6f  x: %6f\n", data[i]->acceleration[0], data[i]->acceleration[1], data[i]->acceleration[2]);
00763                 printf("Angular Rate> x: %6f  y: %6f  x: %6f\n", data[i]->angularRate[0], data[i]->angularRate[1], data[i]->angularRate[2]);
00764                 printf("Magnetic Field> x: %6f  y: %6f  x: %6f\n", data[i]->magneticField[0], data[i]->magneticField[1], data[i]->magneticField[2]);
00765                 printf("Timestamp> seconds: %d -- microseconds: %d\n", data[i]->timestamp.seconds, data[i]->timestamp.microseconds);
00766         }
00767 
00768         printf("---------------------------------------------\n");*/
00769         for(i = 0; i< count; i++)
00770         {
00771                 acc[0] = data[i]->acceleration[0];
00772                 acc[1] = data[i]->acceleration[1];
00773                 acc[2] = data[i]->acceleration[2];
00774                 ang[0] = data[i]->angularRate[0];
00775                 ang[1] = data[i]->angularRate[1];
00776                 ang[2] = data[i]->angularRate[2];
00777                 mag[0] = data[i]->magneticField[0];
00778                 mag[1] = data[i]->magneticField[1];
00779                 mag[2] = data[i]->magneticField[2];
00780 
00781                 if (ang[0] != 0 || ang[1] != 0 || ang[2] != 0)
00782                         AnglesMatrixUpdate(ang, data[i]->timestamp.seconds + ((float)data[i]->timestamp.microseconds)/1000000);
00783                 AnglesNormalize();
00784                 AnglesDriftCorrection(acc);
00785 
00786                 double gravity_[3];
00787                 detectGravity(gravity_, acc);
00788 
00789                 if(!isEqual(gravity, gravity_))
00790                 {
00791 
00792                 /*****We save the new gravity******/
00793 
00794                 gravity[0] = gravity_[0]/GRAVITY;
00795                 gravity[1] = gravity_[1]/GRAVITY;
00796                 gravity[2] = gravity_[2]/GRAVITY;
00797 
00798                 /****** This will give us the pitch and the yaw relative to the User axis and not the Device axis at t0
00799                  Calculations are made using the formulation of the rotation matrix*******/
00800                 pitchOffset = gravity[1];
00801                 if(pitchOffset>1.0f)
00802                     pitchOffset = 1.0f;
00803                 if(pitchOffset<-1.0f)
00804                     pitchOffset = -1.0f;
00805 
00806                 pitchOffset = asin(-pitchOffset);
00807 
00808                 rollOffset = gravity[2]/cos(pitchOffset);
00809                 if(rollOffset>1.0f)
00810                     rollOffset = 1.0f;
00811                 if(rollOffset<-1.0f)
00812                     rollOffset = -1.0f;
00813 
00814                 pitchOffset -= (-asin(dCMMatrix[2][0]));//We substract the actual pitch value from the algorithm
00815                 rollOffset = acos(rollOffset) - (atan2(dCMMatrix[2][1],dCMMatrix[2][2]));//We substract the actual roll value from the algorithm
00816                 }
00817 
00818         }
00819 
00820         ROS_INFO("publishing IMU data in ROS!");
00821         
00822         spatial_good = true;    
00823 
00824         return 0;
00825 }
00826 
00827 
00828 
00829 
00830 int interfacekit_simple()
00831 {
00832         int result, num_analog_inputs, num_digital_inputs;
00833         const char *err;
00834 
00835         //create the InterfaceKit object
00836         CPhidgetInterfaceKit_create(&ifKit);
00837 
00838         //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.
00839 
00840         CPhidget_set_OnDetach_Handler((CPhidgetHandle)ifKit, DetachHandler, NULL);
00841         CPhidget_set_OnError_Handler((CPhidgetHandle)ifKit, ErrorHandler, NULL);
00842            
00843         CPhidgetInterfaceKit_set_OnInputChange_Handler (ifKit, InputChangeHandler, NULL);
00844 
00845         CPhidgetInterfaceKit_set_OnSensorChange_Handler (ifKit, SensorChangeHandler, NULL);
00846 
00847         CPhidgetInterfaceKit_set_OnOutputChange_Handler (ifKit, OutputChangeHandler, NULL);
00848 
00849         //For phidget spatial
00850         //CPhidgetSpatialHandle spatial = 0;
00851         CPhidgetSpatial_create(&spatial);
00852         CPhidget_set_OnAttach_Handler((CPhidgetHandle)spatial, AttachHandler, NULL);
00853         CPhidget_set_OnDetach_Handler((CPhidgetHandle)spatial, DetachHandler, NULL);
00854         CPhidget_set_OnError_Handler((CPhidgetHandle)spatial, ErrorHandler, NULL);
00855         CPhidgetSpatial_set_OnSpatialData_Handler(spatial, SpatialDataHandler, NULL);
00856 
00857 
00858         //open the interfacekit for device connections
00859         CPhidget_open((CPhidgetHandle)ifKit, -1);
00860 
00861 
00862         CPhidget_open((CPhidgetHandle)spatial, -1);
00863 
00864 
00865         CPhidgetInterfaceKit_getInputCount(ifKit, &num_digital_inputs);
00866         CPhidgetInterfaceKit_getSensorCount(ifKit, &num_analog_inputs);
00867 
00868 
00869         printf("Waiting for spatial to be attached.... \n");
00870         if((result = CPhidget_waitForAttachment((CPhidgetHandle)spatial, 1000)))
00871         {
00872                 CPhidget_getErrorDescription(result, &err);
00873                 ROS_ERROR("Phidget Spatial: Problem waiting for attachment: %s\n", err);
00874                 spatialError = 1;
00875         }
00876 
00877         printf("Waiting for interface kit to be attached....");
00878         if((result = CPhidget_waitForAttachment((CPhidgetHandle)ifKit, 1000)))
00879         {
00880                 CPhidget_getErrorDescription(result, &err);
00881                 ROS_ERROR("Phidget IK: Problem waiting for attachment: %s\n", err);
00882                 phidget888_connected = false;
00883                 interfaceKitError = 1;
00884         }
00885 
00886         phidget888_connected = true;
00887         
00888         CPhidgetInterfaceKit_setRatiometric(ifKit, 0);//
00889 
00890         CPhidgetSpatial_setDataRate(spatial, 16);       
00891 
00892          CPhidgetEncoder_create(&m_leftEncoder);
00893          CPhidget_set_OnAttach_Handler((CPhidgetHandle) m_leftEncoder,LeftEncoderAttach, NULL);
00894          CPhidget_open((CPhidgetHandle) m_leftEncoder, -1);
00895         
00896         if (m_encoder1Seen && m_encoder2Seen)
00897             phidget_encoder_connected = true;
00898         else phidget_encoder_connected = false;
00899 
00900         //Initialize the sonars, if any are present
00901         if(sonarsPresent)
00902         {
00903                 CPhidgetInterfaceKit_setOutputState(ifKit, bwOutput, 1);
00904                 CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 0);
00905                 ros::Duration(0.250).sleep(); // sleep for 250ms
00906                 CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 1);
00907                 ros::Duration(0.002).sleep(); // sleep for 2ms
00908                 CPhidgetInterfaceKit_setOutputState(ifKit, strobeOutput, 0);
00909                 ros::Duration(0.150).sleep(); // sleep for 150ms
00910         }
00911         return 0;
00912 }
00913 
00914 void phidget_ik_diagnostic(diagnostic_updater::DiagnosticStatusWrapper &stat)
00918 {
00919         if (!interfaceKitError)  
00920                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::OK, "initialized");
00921         else
00922         {
00923                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::ERROR, "cannot be attached");
00924                 stat.addf("Recommendation", "Please unplug and replug the Phidget Interface Kit Board USB cable from the Motherboard.");
00925         }
00926 }
00927 
00928 void phidget_spatial_diagnostic(diagnostic_updater::DiagnosticStatusWrapper &stat)
00932 {
00933         if (!spatialError)  
00934                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::OK, "initialized");
00935         else
00936         {
00937                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::ERROR, "cannot be attached");
00938                 stat.addf("Recommendation", "Please verify that the robot has a Phidget Spatial board. If present, please unplug and replug the Phidget Spatial Board USB cable from the Motherboard.");
00939         }
00940 }
00941 
00942 void phidget_encoder_diagnostic(diagnostic_updater::DiagnosticStatusWrapper &stat)
00946 {
00947         if (!encoderError)  
00948                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::OK, "intialized");
00949         else if(encoderError == 1)
00950         {
00951                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::ERROR, "cannot be attached");
00952                 stat.addf("Recommendation", "Please verify that the robot has a Phidget Encoder board. If present, please unplug and replug the Phidget Spatial Board USB cable from the Motherboard.");
00953         }
00954         else if(encoderError == 2)
00955         {
00956                 stat.summaryf(diagnostic_msgs::DiagnosticStatus::ERROR, "cannot read");
00957                 stat.addf("Recommendation", "Please verify that the two encoders are connected to the Phidget Encoder Board.");
00958         }
00959 
00960 }
00961 
00962 int main(int argc, char* argv[])
00963 {
00964         ros::init(argc, argv, "phidget_component");
00965         ros::NodeHandle n;
00966         ros::NodeHandle nh("~");
00967         nh.param("rearBumper", m_rearBumperPresent, false);
00968         nh.param("bwOutput", bwOutput, -1);
00969         nh.param("strobeOutput", strobeOutput, -1);
00970         nh.param("lastSonarInput", lastSonarInput, -1);
00971         nh.param("firstSonarInput", firstSonarInput, -1);
00972         nh.param("battery", batteryPort, 0);
00973         nh.param("irFront", irFrontPort, 1);
00974         nh.param("irBack", irBackPort, 2);
00975         nh.param("gripper", gripperPort, 3);
00976         nh.param("motors_inverted", motors_inverted, false);
00977         nh.param("encoders_inverted", encoders_inverted, false);
00978 
00979         dCMMatrix[0][0]=1;
00980         dCMMatrix[0][1]=0;
00981         dCMMatrix[0][2]=0;
00982         dCMMatrix[1][0]=0;
00983         dCMMatrix[1][1]=1;
00984         dCMMatrix[1][2]=0;
00985         dCMMatrix[2][0]=0;
00986         dCMMatrix[2][1]=0;
00987         dCMMatrix[2][2]=1;
00988 
00989         timestampPreviousCall = 0;
00990         pitchOffset = 0;
00991         rollOffset = 0;
00992         proportionalVector[0] = 0;
00993         proportionalVector[1] = 0;
00994         proportionalVector[2] = 0;
00995 
00996         integratorVector[0] = 0;
00997         integratorVector[1] = 0;
00998         integratorVector[2] = 0;
00999         gyroscopeVectorCorrected[0] = 0;
01000         gyroscopeVectorCorrected[1] = 0;
01001         gyroscopeVectorCorrected[2] = 0;
01002 
01003         //create an updater that will send information on the diagnostics topics
01004         diagnostic_updater::Updater updater;
01005         updater.setHardwareIDf("Phidget");
01006         updater.add("Interface Kit", phidget_ik_diagnostic); //function that will be executed with updater.update()
01007         updater.add("Spatial", phidget_spatial_diagnostic); //function that will be executed with updater.update()
01008         updater.add("Encoders", phidget_encoder_diagnostic); //function that will be executed with updater.update()
01009         interfacekit_simple(); 
01010 
01011         ros::ServiceServer odom = n.advertiseService("phidgetnode_set_odom", SetOdom);
01012 
01013         posdata_pub = n.advertise<corobot_msgs::PosMsg>("position_data", 100);
01014         irData_pub = n.advertise<corobot_msgs::IrMsg>("infrared_data", 100);
01015         powerdata_pub = n.advertise<corobot_msgs::PowerMsg>("power_data", 100);
01016         bumper_pub = n.advertise<corobot_msgs::BumperMsg>("bumper_data", 100);
01017         gripper_pub = n.advertise<corobot_msgs::GripperMsg>("gripper_data",100);  //gripper as an analog input?
01018         sonar_pub = n.advertise<corobot_msgs::RangeSensor>("sonar_data", 100);
01019         spatial_pub = n.advertise<corobot_msgs::spatial>("spatial_data",100);
01020         imu_pub = n.advertise<sensor_msgs::Imu>("imu_data",100);
01021 
01022         ros::Publisher phidget_info_pub = n.advertise<corobot_msgs::phidget_info>("phidget_info", 1000);
01023 
01024         corobot_msgs::phidget_info info;
01025         if(phidget888_connected)
01026           info.phidget888_connected = 1;
01027         else info.phidget888_connected = 0;
01028         if(phidget_encoder_connected)
01029           info.phidget_encoder_connected = 1;
01030         else info.phidget_encoder_connected = 0;
01031         phidget_info_pub.publish(info);
01032 
01033         ros::Rate loop_rate(20);  //20 Hz
01034 
01035         while (ros::ok())
01036             {
01037                 ros::spinOnce();
01038                 if(sonarsPresent)
01039                         sendSonarResult();
01040                 publish_data();
01041                 loop_rate.sleep();
01042                 updater.update();
01043             }
01044         printf("Out of ros spin");
01045         
01046         CPhidget_close((CPhidgetHandle)ifKit);
01047         CPhidget_delete((CPhidgetHandle)ifKit);
01048         if (m_rightEncoder != m_leftEncoder)
01049         {
01050         CPhidget_close((CPhidgetHandle)m_leftEncoder);
01051         CPhidget_delete((CPhidgetHandle)m_leftEncoder);
01052         }
01053         CPhidget_close((CPhidgetHandle)m_rightEncoder);
01054         CPhidget_delete((CPhidgetHandle)m_rightEncoder);
01055         CPhidget_close((CPhidgetHandle)spatial);
01056         CPhidget_delete((CPhidgetHandle)spatial);
01057 
01058 
01059         return 0;
01060 }


corobot_phidgetIK
Author(s): CoroWare/mcormier@coroware.com
autogenerated on Tue Jan 7 2014 11:39:21