00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <cmath>
00021 #include <fstream>
00022 #include <boost/thread/recursive_mutex.hpp>
00023
00024 #include <ros/ros.h>
00025 #include <geometry_msgs/Twist.h>
00026 #include <sensor_msgs/Imu.h>
00027 #include <sensor_msgs/LaserScan.h>
00028 #include <nav_msgs/Odometry.h>
00029
00030 #define PI 3.14159265359
00031
00032 class BaseCalibration
00033 {
00034 public:
00035 BaseCalibration(ros::NodeHandle& n) : ready_(false)
00036 {
00037
00038 last_odom_stamp_ = last_imu_stamp_ = last_scan_stamp_ = ros::Time::now();
00039
00040
00041 ros::NodeHandle nh("~");
00042
00043
00044 nh.param<double>("min_angle", min_angle_, -0.5);
00045 nh.param<double>("max_angle", max_angle_, 0.5);
00046
00047
00048 nh.param<double>("accel_limit", accel_limit_, 2.0);
00049
00050
00051 cmd_pub_ = n.advertise<geometry_msgs::Twist>("cmd_vel", 10);
00052
00053
00054 odom_subscriber_ = n.subscribe("odom", 5, &BaseCalibration::odometryCallback, this);
00055 imu_subscriber_ = n.subscribe("imu", 5, &BaseCalibration::imuCallback, this);
00056 scan_subscriber_ = n.subscribe("base_scan", 1, &BaseCalibration::laserCallback, this);
00057
00058 resetInternal();
00059 }
00060
00061 void reset()
00062 {
00063 scan_.clear();
00064 odom_.clear();
00065 imu_.clear();
00066 }
00067
00068 std::string print()
00069 {
00070 std::stringstream ss;
00071 ss << scan_r2_ << " " << imu_angle_ << " " << odom_angle_ << " " << scan_angle_;
00072 return ss.str();
00073 }
00074
00075 std::string printCalibrationData()
00076 {
00077 double odom, imu;
00078 ros::NodeHandle nh;
00079 nh.param<double>("base_controller/track_width", odom, 0.37476);
00080 nh.param<double>("imu/gyro/scale", imu, 0.001221729);
00081
00082
00083 double odom_scale = 0.0;
00084 double imu_scale = 0.0;
00085
00086
00087 for (size_t i = 0; i < scan_.size(); ++i)
00088 {
00089 odom_scale += (scan_[i] - odom_[i]) / odom_[i];
00090 imu_scale += (scan_[i] - imu_[i]) / imu_[i];
00091 }
00092
00093 odom_scale /= scan_.size();
00094 imu_scale /= scan_.size();
00095
00096 std::stringstream ss;
00097 ss << "odom: " << odom * (1.0 + odom_scale) << std::endl;
00098 ss << "imu: " << imu * (1.0 + imu_scale) << std::endl;
00099 return ss.str();
00100 }
00101
00103 bool align(bool verbose = false)
00104 {
00105 while (!ready_)
00106 ros::Duration(0.1).sleep();
00107
00108 std::cout << "aligning..." << std::endl;
00109
00110 double velocity = 0.2;
00111 if (scan_angle_ < 0)
00112 velocity = -0.2;
00113
00114 while (fabs(scan_angle_) > 0.2 || (scan_r2_ < 0.1))
00115 {
00116 if (verbose)
00117 std::cout << scan_r2_ << " " << scan_angle_ << std::endl;
00118 sendVelocityCommand(velocity);
00119 ros::Duration(0.02).sleep();
00120 }
00121 sendVelocityCommand(0.0);
00122 std::cout << "...done" << std::endl;
00123 ros::Duration(0.25).sleep();
00124
00125 return true;
00126 }
00127
00129 bool spin(double velocity, int rotations, bool verbose = false)
00130 {
00131 double scan_start = scan_angle_;
00132
00133 align();
00134 resetInternal();
00135 std::cout << "spin..." << std::endl;
00136
00137
00138 double angle = rotations * 2 * PI - (0.5 * velocity * velocity / accel_limit_);
00139
00140 while (fabs(odom_angle_) < angle)
00141 {
00142 if (verbose)
00143 std::cout << scan_angle_ << " " << odom_angle_ << " " << imu_angle_ << std::endl;
00144 sendVelocityCommand(velocity);
00145 ros::Duration(0.02).sleep();
00146 }
00147 sendVelocityCommand(0.0);
00148 std::cout << "...done" << std::endl;
00149
00150
00151 ros::Duration(0.5 + fabs(velocity) / accel_limit_).sleep();
00152
00153
00154 imu_.push_back(imu_angle_);
00155 odom_.push_back(odom_angle_);
00156 if (velocity > 0)
00157 scan_.push_back(scan_start + 2 * rotations * PI - scan_angle_);
00158 else
00159 scan_.push_back(scan_start - 2 * rotations * PI - scan_angle_);
00160
00161 return true;
00162 }
00163
00164 private:
00165 void odometryCallback(const nav_msgs::Odometry::Ptr& odom)
00166 {
00167 boost::recursive_mutex::scoped_lock lock(data_mutex_);
00168
00169 double dt = (odom->header.stamp - last_odom_stamp_).toSec();
00170 odom_angle_ += odom->twist.twist.angular.z * dt;
00171
00172 last_odom_stamp_ = odom->header.stamp;
00173 }
00174
00175 void imuCallback(const sensor_msgs::Imu::Ptr& imu)
00176 {
00177 boost::recursive_mutex::scoped_lock lock(data_mutex_);
00178
00179 double dt = (imu->header.stamp - last_imu_stamp_).toSec();
00180 imu_angle_ += imu->angular_velocity.z * dt;
00181
00182 last_imu_stamp_ = imu->header.stamp;
00183 }
00184
00185 void laserCallback(const sensor_msgs::LaserScan::Ptr& scan)
00186 {
00187 boost::recursive_mutex::scoped_lock lock(data_mutex_);
00188
00189 double angle = scan->angle_min;
00190 double mean_x, mean_y, n;
00191 mean_x = mean_y = n = 0;
00192 int start = -1;
00193 for (size_t i = 0; i < scan->ranges.size(); ++i, angle += scan->angle_increment)
00194 {
00195 if (angle < min_angle_ || angle > max_angle_)
00196 continue;
00197 if (start < 0)
00198 start = i;
00199
00200
00201 double px = sin(angle) * scan->ranges[i];
00202 double py = cos(angle) * scan->ranges[i];
00203
00204 mean_x += px;
00205 mean_y += py;
00206 ++n;
00207 }
00208
00209 if (n == 0)
00210 return;
00211
00212 mean_x /= n;
00213 mean_y /= n;
00214
00215 angle = scan->angle_min + start * scan->angle_increment;
00216 double x, y, xx, xy, yy;
00217 x = y = xx = xy = yy = n = 0;
00218 for (size_t i = start; i < scan->ranges.size(); ++i, angle += scan->angle_increment)
00219 {
00220 if (angle > max_angle_)
00221 break;
00222
00223
00224 double px = sin(angle) * scan->ranges[i] - mean_x;
00225 double py = cos(angle) * scan->ranges[i] - mean_y;
00226
00227
00228 xx += px * px;
00229 xy += px * py;
00230 x += px;
00231 y += py;
00232 yy += py * py;
00233 ++n;
00234 }
00235
00236 scan_dist_ = mean_y;
00237 scan_angle_ = atan2((n*xy-x*y)/(n*xx-x*x), 1.0);
00238 scan_r2_ = fabs(xy)/(xx * yy);
00239 last_scan_stamp_ = scan->header.stamp;
00240 ready_ = true;
00241 }
00242
00243
00244 void sendVelocityCommand(double vel)
00245 {
00246 geometry_msgs::Twist twist;
00247 twist.angular.z = vel;
00248 cmd_pub_.publish(twist);
00249 }
00250
00251
00252 void resetInternal()
00253 {
00254 boost::recursive_mutex::scoped_lock lock(data_mutex_);
00255 odom_angle_ = imu_angle_ = scan_angle_ = scan_r2_ = 0.0;
00256 }
00257
00258 ros::Publisher cmd_pub_;
00259
00260 ros::Subscriber odom_subscriber_;
00261 ros::Subscriber imu_subscriber_;
00262 ros::Subscriber scan_subscriber_;
00263
00264 ros::Time last_odom_stamp_;
00265 double odom_angle_;
00266
00267 ros::Time last_imu_stamp_;
00268 double imu_angle_;
00269
00270 ros::Time last_scan_stamp_;
00271 double scan_angle_, scan_r2_, scan_dist_;
00272
00273 double min_angle_, max_angle_;
00274 double accel_limit_;
00275
00276 std::vector<double> scan_;
00277 std::vector<double> imu_;
00278 std::vector<double> odom_;
00279
00280 boost::recursive_mutex data_mutex_;
00281 bool ready_;
00282 };
00283
00284 int main(int argc, char** argv)
00285 {
00286 ros::init(argc, argv,"base_calibration_node");
00287 ros::NodeHandle nh;
00288
00289
00290 ros::AsyncSpinner spinner(1);
00291 spinner.start();
00292
00293 BaseCalibration b(nh);
00294 b.reset();
00295
00296
00297 b.spin(0.5, 1);
00298 b.spin(1.5, 1);
00299 b.spin(3.0, 2);
00300 b.spin(-0.5, 1);
00301 b.spin(-1.5, 1);
00302 b.spin(-3.0, 2);
00303
00304
00305
00306
00307 {
00308
00309 char datecode[80];
00310 {
00311 std::time_t t = std::time(NULL);
00312 std::strftime(datecode, 80, "%Y_%m_%d_%H_%M_%S", std::localtime(&t));
00313 }
00314 std::stringstream yaml_name;
00315 yaml_name << "/tmp/base_calibration_" << datecode << ".yaml";
00316 std::ofstream file;
00317 file.open(yaml_name.str().c_str());
00318 std::string cal = b.printCalibrationData();
00319 file << cal;
00320 file.close();
00321 std::cout << cal;
00322 }
00323
00324 spinner.stop();
00325 return 0;
00326 }