evarobot_minimu9.cpp
Go to the documentation of this file.
00001 #include "evarobot_minimu9/evarobot_minimu9.h"
00002 
00003 #include <dynamic_reconfigure/server.h>
00004 #include <evarobot_minimu9/ParamsConfig.h>
00005 
00006 int i_error_code = 0;
00007 
00008 std::ostream & operator << (std::ostream & os, const vector & vector)
00009 {
00010     return os << FLOAT_FORMAT << vector(0) << ' '
00011               << FLOAT_FORMAT << vector(1) << ' '
00012               << FLOAT_FORMAT << vector(2);
00013 }
00014 
00015 std::ostream & operator << (std::ostream & os, const matrix & matrix)
00016 {
00017     return os << (vector)matrix.row(0) << ' '
00018               << (vector)matrix.row(1) << ' '
00019               << (vector)matrix.row(2);
00020 }
00021 
00022 std::ostream & operator << (std::ostream & os, const quaternion & quat)
00023 {
00024     return os << FLOAT_FORMAT << quat.w() << ' '
00025               << FLOAT_FORMAT << quat.x() << ' '
00026               << FLOAT_FORMAT << quat.y() << ' '
00027               << FLOAT_FORMAT << quat.z();
00028 }
00029 
00030 typedef void rotation_output_function(quaternion& rotation);
00031 
00032 void output_quaternion(quaternion & rotation)
00033 {
00034     ROS_DEBUG_STREAM("EvarobotIMU: " << rotation);
00035 }
00036 
00037 void output_matrix(quaternion & rotation)
00038 {
00039     ROS_DEBUG_STREAM("EvarobotIMU: " << rotation.toRotationMatrix());
00040 }
00041 
00042 void output_euler(quaternion & rotation)
00043 {
00044     ROS_DEBUG_STREAM("EvarobotIMU: " << (vector)(rotation.toRotationMatrix().eulerAngles(2, 1, 0) * (180 / M_PI)));
00045 }
00046 
00047 int millis()
00048 {
00049     struct timeval tv;
00050     gettimeofday(&tv, NULL);
00051     return (tv.tv_sec) * 1000 + (tv.tv_usec)/1000;
00052 }
00053 
00054 void streamRawValues(IMU& imu, int count)
00055 {
00056     imu.enable();
00057     for(int i = 0; i < count; i++)
00058     {
00059         imu.read();
00060         printf("%7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
00061                imu.raw_m[0], imu.raw_m[1], imu.raw_m[2],
00062                imu.raw_a[0], imu.raw_a[1], imu.raw_a[2],
00063                imu.raw_g[0], imu.raw_g[1], imu.raw_g[2]
00064         );
00065         usleep(20*1000);
00066     }
00067 }
00068 
00070 // to get a noisy estimate of the current rotation matrix.
00071 // This function is where we define the coordinate system we are using
00072 // for the ground coords:  North, East, Down.
00073 matrix rotationFromCompass(const vector& acceleration, const vector& magnetic_field)
00074 {
00075     vector down = -acceleration;     // usually true
00076     vector east = down.cross(magnetic_field); // actually it's magnetic east
00077     vector north = east.cross(down);
00078 
00079     east.normalize();
00080     north.normalize();
00081     down.normalize();
00082 
00083     matrix r;
00084     r.row(0) = north;
00085     r.row(1) = east;
00086     r.row(2) = down;
00087     return r;
00088 }
00089 
00090 typedef void fuse_function(quaternion& rotation, float dt, const vector& angular_velocity,
00091                   const vector& acceleration, const vector& magnetic_field);
00092 
00093 void fuse_compass_only(quaternion& rotation, float dt, const vector& angular_velocity,
00094   const vector& acceleration, const vector& magnetic_field)
00095 {
00096     // Implicit conversion of rotation matrix to quaternion.
00097     rotation = rotationFromCompass(acceleration, magnetic_field);
00098 }
00099 
00100 // Uses the given angular velocity and time interval to calculate
00101 // a rotation and applies that rotation to the given quaternion.
00102 // w is angular velocity in radians per second.
00103 // dt is the time.
00104 void rotate(quaternion& rotation, const vector& w, float dt)
00105 {
00106     // Multiply by first order approximation of the
00107     // quaternion representing this rotation.
00108     rotation *= quaternion(1, w(0)*dt/2, w(1)*dt/2, w(2)*dt/2);
00109     rotation.normalize();
00110 }
00111 
00112 void fuse_gyro_only(quaternion& rotation, float dt, const vector& angular_velocity,
00113   const vector& acceleration, const vector& magnetic_field)
00114 {
00115     rotate(rotation, angular_velocity, dt);
00116 }
00117 
00118 void fuse_default(quaternion& rotation, float dt, const vector& angular_velocity,
00119   const vector& acceleration, const vector& magnetic_field)
00120 {
00121     vector correction = vector(0, 0, 0);
00122 
00123     if (abs(acceleration.norm() - 1) <= 0.3)
00124     {
00125         // The magnetidude of acceleration is close to 1 g, so
00126         // it might be pointing up and we can do drift correction.
00127 
00128         const float correction_strength = 1;
00129 
00130         matrix rotationCompass = rotationFromCompass(acceleration, magnetic_field);
00131         matrix rotationMatrix = rotation.toRotationMatrix();
00132 
00133         correction = (
00134             rotationCompass.row(0).cross(rotationMatrix.row(0)) +
00135             rotationCompass.row(1).cross(rotationMatrix.row(1)) +
00136             rotationCompass.row(2).cross(rotationMatrix.row(2))
00137           ) * correction_strength;
00138 
00139     }
00140 
00141     rotate(rotation, angular_velocity + correction, dt);
00142 }
00143 
00144 void ahrs(IMU & imu, fuse_function * fuse, rotation_output_function * output)
00145 {
00146     imu.loadCalibration();
00147     imu.enable();
00148     imu.measureOffsets();
00149     
00150     // The quaternion that can convert a vector in body coordinates
00151     // to ground coordinates when it its changed to a matrix.
00152     quaternion rotation = quaternion::Identity();
00153 
00154     int start = millis(); // truncate 64-bit return value
00155     while(1)
00156     {
00157         int last_start = start;
00158         start = millis();
00159         float dt = (start-last_start)/1000.0;
00160         if (dt < 0){ 
00161                         ROS_INFO(GetErrorDescription(-107).c_str());
00162                         i_error_code = -107;
00163                         throw std::runtime_error("Time went backwards."); 
00164                 }
00165 
00166         vector angular_velocity = imu.readGyro();
00167         vector acceleration = imu.readAcc();
00168         vector magnetic_field = imu.readMag();
00169 
00170         fuse(rotation, dt, angular_velocity, acceleration, magnetic_field);
00171 
00172         output(rotation);
00173   //      std::cout << "  " << acceleration << "  " << magnetic_field << std::endl << std::flush;
00174 
00175         // Ensure that each iteration of the loop takes at least 20 ms.
00176         while(millis() - start < 20)
00177         {
00178             usleep(1000);
00179         }
00180     }
00181 }
00182 
00183 bool b_is_received_params = false;
00184 
00185 void CallbackReconfigure(evarobot_minimu9::ParamsConfig &config, uint32_t level)
00186 {
00187    b_is_received_params = true;        
00188 }
00189 
00190 void ProduceDiagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat)
00191 {
00192     if(i_error_code<0)
00193     {
00194         stat.summaryf(diagnostic_msgs::DiagnosticStatus::ERROR, "%s", GetErrorDescription(i_error_code).c_str());
00195         i_error_code = 0;
00196     }
00197     else
00198     {
00199                 stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "EvarobotIMU: No problem.");
00200     }
00201 }
00202 
00203 
00204 int main(int argc, char *argv[])
00205 {
00206         // Semaphore
00207         key_t key;
00208         sem_t *mutex;
00209         FILE * fd;
00210         
00211   std::stringstream ss;
00212         
00213         // ROS PARAMS
00214         double d_frequency;
00215         double d_min_freq, d_max_freq;
00216         // ROS PARAMS END
00217         
00218         //name the shared memory segment
00219         key = 1005;
00220 //      printf("create & initialize semaphore\n");
00221         
00222         
00223         //create & initialize semaphore
00224         mutex = sem_open(SEM_NAME,O_CREAT,0644,1);
00225         if(mutex == SEM_FAILED)
00226         {
00227                 ROS_INFO(GetErrorDescription(-105).c_str());
00228         i_error_code = -105;
00229                 sem_unlink(SEM_NAME);
00230                 
00231                 return(-1);
00232         }
00233         
00234         // Define what all the command-line parameters are.
00235     std::string mode, output_mode, i2cDevice;
00236   
00237         ros::init(argc, argv, "evarobot_minimu9");
00238         ros::NodeHandle n;
00239         
00240         /*if(!n.getParam("evarobot_sonar/frequency", d_frequency))
00241         {
00242                 ROS_INFO("Failed to get param 'frequency'");
00243                 d_frequency = 10.0;
00244         }
00245         
00246         ros::Rate loop_rate(d_frequency);*/
00247                 
00248         n.param<std::string>("evarobot_minimu9/i2cDevice", i2cDevice, "/dev/i2c-1");
00249         n.param("evarobot_odometry/minFreq", d_min_freq, 0.2);
00250         n.param("evarobot_odometry/maxFreq", d_max_freq, 10.0);
00251         n.param("evarobot_minimu9/frequency", d_frequency, 10.0);
00252 /*      {
00253             ROS_INFO(GetErrorDescription(-106).c_str());
00254         i_error_code = -106;
00255         }       
00256 */      
00257         realtime_tools::RealtimePublisher<sensor_msgs::Imu> * imu_pub = new realtime_tools::RealtimePublisher<sensor_msgs::Imu>(n, "imu", 10);
00258         
00259         // Dynamic Reconfigure
00260         dynamic_reconfigure::Server<evarobot_minimu9::ParamsConfig> srv;
00261         dynamic_reconfigure::Server<evarobot_minimu9::ParamsConfig>::CallbackType f;
00262         f = boost::bind(&CallbackReconfigure, _1, _2);
00263         srv.setCallback(f);
00265         
00266         // Diagnostics
00267         diagnostic_updater::Updater updater;
00268         updater.setHardwareID("minimu9");
00269         updater.add("imu", &ProduceDiagnostics);
00270         diagnostic_updater::HeaderlessTopicDiagnostic pub_freq("imu", updater,
00271                                                                                         diagnostic_updater::FrequencyStatusParam(&d_min_freq, &d_max_freq, 0.1, 10));
00272         
00273         output_mode = "matrix";
00274         mode = "normal";
00275 
00276         MinIMU9 imu(i2cDevice.c_str());
00277         
00278         if (argc == 2 && atoi(argv[1]) > 0)
00279         {
00280             ROS_DEBUG("raw mode");
00281             streamRawValues(imu, atoi(argv[1]));
00282             return 0;
00283         }
00284 
00285 
00286         try
00287         {
00288                 ROS_DEBUG("imu mode");
00289 //              MinIMU9 imu(i2cDevice.c_str());
00290                 
00291                 // void ahrs(IMU & imu, fuse_function * fuse, rotation_output_function * output)
00292                 imu.loadCalibration();
00293                 imu.enable();
00294                 imu.measureOffsets();
00295 
00296                 // The quaternion that can convert a vector in body coordinates
00297                 // to ground coordinates when it its changed to a matrix.
00298                 quaternion rotation = quaternion::Identity();
00299 
00300                 int start = millis(); // truncate 64-bit return value
00301                 while(ros::ok())
00302                 {
00303                         
00304                         if(b_is_received_params)
00305                         {
00306                                 ROS_DEBUG("EvarobotIMU: Updating IMU Params...");
00307                         
00308                                 b_is_received_params = false;
00309                         }       
00310                         
00311                         int last_start = start;
00312                         start = millis();
00313                         float dt = (start-last_start)/1000.0;
00314                         if (dt < 0){ 
00315                                 ROS_INFO(GetErrorDescription(-107).c_str());
00316                                 i_error_code = -107;
00317                                 throw std::runtime_error("Time went backwards."); 
00318                         }
00319 
00320                         vector angular_velocity = imu.readGyro();
00321                         vector acceleration = imu.readAcc();
00322                         vector magnetic_field = imu.readMag();
00323 
00324                         sem_wait(mutex);
00325                         fuse_default(rotation, dt, angular_velocity, acceleration, magnetic_field);
00326                         sem_post(mutex);
00327 
00328                         double roll, pitch, yaw;
00329                         double qx, qy, qz, qw;
00330                         vector euler_angles;
00331                         
00332                         euler_angles = (vector)(rotation.toRotationMatrix().eulerAngles(2, 1, 0));
00333                         
00334                         roll = euler_angles(2);
00335                         pitch = -euler_angles(1);
00336                         yaw = -euler_angles(0);
00337                         
00338                         qx = sin(roll*0.5) * cos(pitch*0.5) * cos(yaw*0.5) - cos(roll*0.5) * sin(pitch*0.5) * sin(yaw*0.5);
00339                         qy = cos(roll*0.5) * sin(pitch*0.5) * cos(yaw*0.5) + sin(roll*0.5) * cos(pitch*0.5) * sin(yaw*0.5);
00340                         qz = cos(roll*0.5) * cos(pitch*0.5) * sin(yaw*0.5) - sin(roll*0.5) * sin(pitch*0.5) * cos(yaw*0.5);
00341                         qw = cos(roll*0.5) * cos(pitch*0.5) * cos(yaw*0.5) + sin(roll*0.5) * sin(pitch*0.5) * sin(yaw*0.5);
00342                 
00343                         
00344 //                      std::cout << "Roll: " << -roll * (180 / M_PI);
00345 //                      std::cout << "  Pitch: " << pitch * (180 / M_PI) << std::endl;
00346 
00347 
00348 //                      output_euler(rotation);
00349 //                      std::cout << std::endl;
00350                         
00351                         ss.str("");
00352                         ss << n.resolveName(n.getNamespace(), true) << "/imu_link";
00353                         imu_pub->msg_.header.frame_id = ss.str();
00354                         imu_pub->msg_.header.stamp = ros::Time::now();
00355                                                 
00356                         imu_pub->msg_.orientation.x = qx; //rotation.x();
00357                         imu_pub->msg_.orientation.y = qy; //rotation.y();
00358                         imu_pub->msg_.orientation.z = qz; //rotation.z();
00359                         imu_pub->msg_.orientation.w = qw; //rotation.w();
00360 
00361                         if (imu_pub->trylock())
00362                         {
00363                                 imu_pub->unlockAndPublish();
00364                         }                       
00365                         pub_freq.tick();
00366                         
00367                         updater.update();
00368                         ros::spinOnce();
00369 
00370 
00371                         // Ensure that each iteration of the loop takes at least 20 ms.
00372                         while(millis() - start < 20)
00373                         {
00374                                 usleep(1000);
00375                         }
00376                 }
00377         
00378         //      sem_close(mutex);
00379                 sem_unlink(SEM_NAME);
00380         
00381         return 0;
00382     }
00383     catch(const std::system_error & error)
00384     {
00385         std::string what = error.what();
00386         const std::error_code & code = error.code();
00387         ROS_INFO_STREAM("[ERROR] EvarobotIMU: " << what << "  " << code.message() << " (" << code << ")");
00388         ROS_ERROR_STREAM("[ERROR] EvarobotIMU: " << what << "  " << code.message() << " (" << code << ")");
00389         return 2;
00390     }
00391     catch(const opts::multiple_occurrences & error)
00392     {
00393         ROS_INFO_STREAM("[ERROR] EvarobotIMU: " << error.what() << " of " << error.get_option_name() << " option.");
00394         ROS_ERROR_STREAM("[ERROR] EvarobotIMU: " << error.what() << " of " << error.get_option_name() << " option.");
00395         return 1;
00396     }
00397     catch(const std::exception & error)    
00398     {
00399         ROS_INFO_STREAM("[ERROR] EvarobotIMU: " << error.what());
00400         ROS_ERROR_STREAM("[ERROR] EvarobotIMU: " << error.what());
00401         return 9;
00402     }
00403 }
00404 


evarobot_minimu9
Author(s): Mehmet Akcakoca
autogenerated on Fri Feb 12 2016 01:15:25