00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "ias_mechanism_controllers/rosie_odometry.h"
00039 #include "pluginlib/class_list_macros.h"
00040
00041 PLUGINLIB_DECLARE_CLASS(ias_mechanism_controllers, RosieOdometry, controller::RosieOdometry, pr2_controller_interface::Controller)
00042
00043 namespace controller {
00044
00045 RosieOdometry::RosieOdometry()
00046 {
00047 }
00048
00049 RosieOdometry::~RosieOdometry()
00050 {
00051 }
00052
00053 bool RosieOdometry::init(pr2_mechanism_model::RobotState *robot_state, ros::NodeHandle &node)
00054 {
00055 node_ = node;
00056 node.param("odometer/distance", odometer_distance_, 0.0);
00057 node.param("odometer/angle", odometer_angle_, 0.0);
00058 node.param("odom/x", odom_.x, 0.0);
00059 node.param("odom/y", odom_.y, 0.0);
00060 node.param("odom/z", odom_.z, 0.0);
00061
00062 node.param<std::string> ("ils_weight_type", ils_weight_type_, "Gaussian");
00063 node.param<int> ("ils_max_iterations", ils_max_iterations_, 3);
00064 node.param<std::string> ("odom_frame", odom_frame_, "/odom");
00065 node.param<std::string> ("base_link_frame", base_link_frame_, "/base_link");
00066
00067 node.param<double> ("sigma_x", sigma_x_, 0.002);
00068 node.param<double> ("sigma_y", sigma_y_, 0.002);
00069 node.param<double> ("sigma_theta", sigma_theta_, 0.017);
00070
00071 node.param<double> ("cov_x_y", cov_x_y_, 0.0);
00072 node.param<double> ("cov_x_theta", cov_x_theta_, 0.0);
00073 node.param<double> ("cov_y_theta", cov_y_theta_, 0.0);
00074
00075
00076 node.param("expected_publish_time", expected_publish_time_, 0.03);
00077
00078 if(!base_kin_.init(robot_state, node_))
00079 return false;
00080
00081 cbv_lhs_ = Eigen::MatrixXf::Zero(16, 3);
00082 cbv_rhs_ = Eigen::MatrixXf::Zero(16, 1);
00083 cbv_soln_ = Eigen::MatrixXf::Zero(3, 1);
00084
00085 fit_lhs_ = Eigen::MatrixXf::Zero(16, 3);
00086 fit_rhs_ = Eigen::MatrixXf::Zero(16, 1);
00087 fit_soln_ = Eigen::MatrixXf::Zero(3, 1);
00088
00089 fit_residual_ = Eigen::MatrixXf::Zero(16, 1);
00090 odometry_residual_ = Eigen::MatrixXf::Zero(16, 1);
00091
00092 weight_matrix_ = Eigen::MatrixXf::Identity(16, 16);
00093
00094 odometry_publisher_.reset(new realtime_tools::RealtimePublisher<nav_msgs::Odometry>(node_,odom_frame_, 1));
00095 transform_publisher_.reset(new realtime_tools::RealtimePublisher<tf::tfMessage>(node_,"/tf", 1));
00096 transform_publisher_->msg_.transforms.resize(1);
00097
00098 return true;
00099 }
00100
00101 bool RosieOdometry::initXml(pr2_mechanism_model::RobotState *robot_state, TiXmlElement *config)
00102 {
00103 ros::NodeHandle n(config->Attribute("name"));
00104 return init(robot_state,n);
00105 }
00106
00107 void RosieOdometry::starting()
00108 {
00109 current_time_ = base_kin_.robot_state_->getTime();
00110 last_time_ = base_kin_.robot_state_->getTime();
00111 last_publish_time_ = base_kin_.robot_state_->getTime();
00112 }
00113
00114 void RosieOdometry::update()
00115 {
00116 current_time_ = base_kin_.robot_state_->getTime();
00117 updateOdometry();
00118 publish();
00119 last_time_ = current_time_;
00120 }
00121
00122 void RosieOdometry::updateOdometry()
00123 {
00124 double dt = (current_time_ - last_time_).toSec();
00125 double theta = odom_.z;
00126 double costh = cos(theta);
00127 double sinth = sin(theta);
00128
00129 computeBaseVelocity();
00130
00131 double odom_delta_x = (odom_vel_.linear.x * costh - odom_vel_.linear.y * sinth) * dt;
00132 double odom_delta_y = (odom_vel_.linear.x * sinth + odom_vel_.linear.y * costh) * dt;
00133 double odom_delta_th = odom_vel_.angular.z * dt;
00134
00135 odom_.x += odom_delta_x;
00136 odom_.y += odom_delta_y;
00137 odom_.z += odom_delta_th;
00138
00139 odometer_distance_ += sqrt(odom_delta_x * odom_delta_x + odom_delta_y * odom_delta_y);
00140 odometer_angle_ += fabs(odom_delta_th);
00141 }
00142
00143 void RosieOdometry::getOdometry(geometry_msgs::Point &odom, geometry_msgs::Twist &odom_vel)
00144 {
00145 odom = odom_;
00146 odom_vel = odom_vel_;
00147 return;
00148 }
00149
00150 void RosieOdometry::getOdometryMessage(nav_msgs::Odometry &msg)
00151 {
00152 msg.header.frame_id = odom_frame_;
00153 msg.header.stamp = current_time_;
00154 msg.pose.pose.position.x = odom_.x;
00155 msg.pose.pose.position.y = odom_.y;
00156 msg.pose.pose.position.z = 0.0;
00157
00158 tf::Quaternion quat_trans = tf::Quaternion(odom_.z, 0.0, 0.0);
00159 msg.pose.pose.orientation.x = quat_trans.x();
00160 msg.pose.pose.orientation.y = quat_trans.y();
00161 msg.pose.pose.orientation.z = quat_trans.z();
00162 msg.pose.pose.orientation.w = quat_trans.w();
00163
00164 msg.twist.twist = odom_vel_;
00165
00166
00167
00168 populateCovariance(odometry_residual_max_,msg);
00169 }
00170
00171 void RosieOdometry::populateCovariance(double residual, nav_msgs::Odometry &msg)
00172 {
00173
00174
00175 double odom_multiplier;
00176 if (residual < 0.05)
00177 {
00178 odom_multiplier = ((residual-0.00001)*(0.99999/0.04999))+0.00001;
00179 }
00180 else
00181 {
00182 odom_multiplier = ((residual-0.05)*(19.0/0.15))+1.0;
00183 }
00184 odom_multiplier = fmax(0.00001, fmin(100.0, odom_multiplier));
00185 odom_multiplier *= 2.0;
00186
00187
00188 msg.pose.covariance[0] = odom_multiplier*pow(sigma_x_,2);
00189 msg.pose.covariance[7] = odom_multiplier*pow(sigma_y_,2);
00190 msg.pose.covariance[35] = odom_multiplier*pow(sigma_theta_,2);
00191
00192 msg.pose.covariance[1] = odom_multiplier*cov_x_y_;
00193 msg.pose.covariance[6] = odom_multiplier*cov_x_y_;
00194
00195 msg.pose.covariance[31] = odom_multiplier*cov_y_theta_;
00196 msg.pose.covariance[11] = odom_multiplier*cov_y_theta_;
00197
00198 msg.pose.covariance[30] = odom_multiplier*cov_x_theta_;
00199 msg.pose.covariance[5] = odom_multiplier*cov_x_theta_;
00200
00201 msg.pose.covariance[14] = DBL_MAX;
00202 msg.pose.covariance[21] = DBL_MAX;
00203 msg.pose.covariance[28] = DBL_MAX;
00204
00205 msg.twist.covariance = msg.pose.covariance;
00206 }
00207
00208
00209 void RosieOdometry::getOdometry(double &x, double &y, double &yaw, double &vx, double &vy, double &vw)
00210 {
00211 x = odom_.x;
00212 y = odom_.y;
00213 yaw = odom_.z;
00214 vx = odom_vel_.linear.x;
00215 vy = odom_vel_.linear.y;
00216 vw = odom_vel_.angular.z;
00217 }
00218
00219 void RosieOdometry::computeBaseVelocity()
00220 {
00221 double steer_angle, wheel_speed, costh, sinth;
00222 geometry_msgs::Twist caster_local_velocity;
00223 geometry_msgs::Twist wheel_local_velocity;
00224 geometry_msgs::Point wheel_position;
00225 for(int i = 0; i < base_kin_.num_wheels_; i++)
00226 {
00227 base_kin_.wheel_[i].updatePosition();
00228 steer_angle = base_kin_.wheel_[i].parent_->joint_->position_;
00229 wheel_position = base_kin_.wheel_[i].position_;
00230 costh = cos(steer_angle);
00231 sinth = sin(steer_angle);
00232 wheel_speed = getCorrectedWheelSpeed(i);
00233 cbv_rhs_(i * 2, 0) = base_kin_.wheel_[i].wheel_radius_ * wheel_speed;
00234 cbv_rhs_(i * 2 + 1, 0) = 0;
00235
00236 cbv_lhs_(i * 2, 0) = costh;
00237 cbv_lhs_(i * 2, 1) = sinth;
00238 cbv_lhs_(i * 2, 2) = -costh * wheel_position.y + sinth * wheel_position.x;
00239 cbv_lhs_(i * 2 + 1, 0) = -sinth;
00240 cbv_lhs_(i * 2 + 1, 1) = costh;
00241 cbv_lhs_(i * 2 + 1, 2) = sinth * wheel_position.y + costh * wheel_position.x;
00242 }
00243 cbv_soln_ = iterativeLeastSquares(cbv_lhs_, cbv_rhs_, ils_weight_type_, ils_max_iterations_);
00244
00245 odometry_residual_ = cbv_lhs_ * cbv_soln_ - cbv_rhs_;
00246 odometry_residual_max_ = odometry_residual_.cwise().abs().maxCoeff();
00247
00248 odom_vel_.linear.x = cbv_soln_(0, 0);
00249 odom_vel_.linear.y = cbv_soln_(1, 0);
00250 odom_vel_.angular.z = cbv_soln_(2, 0);
00251 }
00252
00253 double RosieOdometry::getCorrectedWheelSpeed(int index)
00254 {
00255 double wheel_speed;
00256 geometry_msgs::Twist caster_local_vel;
00257 geometry_msgs::Twist wheel_local_vel;
00258 caster_local_vel.angular.z = base_kin_.wheel_[index].parent_->joint_->velocity_;
00259 wheel_local_vel = base_kin_.pointVel2D(base_kin_.wheel_[index].offset_, caster_local_vel);
00260 wheel_speed = base_kin_.wheel_[index].joint_->velocity_ - wheel_local_vel.linear.x / (base_kin_.wheel_[index].wheel_radius_);
00261 return wheel_speed;
00262 }
00263
00264 Eigen::MatrixXf RosieOdometry::iterativeLeastSquares(Eigen::MatrixXf lhs, Eigen::MatrixXf rhs, std::string weight_type, int max_iter)
00265 {
00266 weight_matrix_ = Eigen::MatrixXf::Identity(16, 16);
00267 for(int i = 0; i < max_iter; i++)
00268 {
00269 fit_lhs_ = weight_matrix_ * lhs;
00270 fit_rhs_ = weight_matrix_ * rhs;
00271
00272 fit_lhs_.svd().solve(fit_rhs_, &fit_soln_);
00273 fit_residual_ = rhs - lhs * fit_soln_;
00274
00275 for(int j = 0; j < base_kin_.num_wheels_; j++)
00276 {
00277 int fw = 2 * j;
00278 int sw = fw + 1;
00279 if(fabs(fit_residual_(fw, 0)) > fabs(fit_residual_(sw, 0)))
00280 {
00281 fit_residual_(fw, 0) = fabs(fit_residual_(fw, 0));
00282 fit_residual_(sw, 0) = fit_residual_(fw, 0);
00283 }
00284 else
00285 {
00286 fit_residual_(fw, 0) = fabs(fit_residual_(sw, 0));
00287 fit_residual_(sw, 0) = fit_residual_(fw, 0);
00288 }
00289 }
00290 weight_matrix_ = findWeightMatrix(fit_residual_, weight_type);
00291 }
00292 return fit_soln_;
00293 }
00294
00295 Eigen::MatrixXf RosieOdometry::findWeightMatrix(Eigen::MatrixXf residual, std::string weight_type)
00296 {
00297 Eigen::MatrixXf w_fit = Eigen::MatrixXf::Identity(16, 16);
00298 double epsilon = 0;
00299 double g_sigma = 0.1;
00300
00301 if(weight_type == std::string("BubeLagan"))
00302 {
00303 for(int i = 0; i < 2 * base_kin_.num_wheels_; i++)
00304 {
00305 if(fabs(residual(i, 0) > epsilon))
00306 epsilon = fabs(residual(i, 0));
00307 }
00308 epsilon = epsilon / 100.0;
00309 }
00310 for(int i = 0; i < 2 * base_kin_.num_wheels_; i++)
00311 {
00312 if(weight_type == std::string("L1norm"))
00313 {
00314 w_fit(i, i) = 1.0 / (1 + sqrt(fabs(residual(i, 0))));
00315 }
00316 else if(weight_type == std::string("fair"))
00317 {
00318 w_fit(i, i) = 1.0 / (1 + fabs(residual(i, 0)));
00319 }
00320 else if(weight_type == std::string("Cauchy"))
00321 {
00322 w_fit(i, i) = 1.0 / (1 + residual(i, 0) * residual(i, 0));
00323 }
00324 else if(weight_type == std::string("BubeLangan"))
00325 {
00326 w_fit(i, i) = 1.0 / pow((1 + pow((residual(i, 0) / epsilon), 2)), 0.25);
00327 }
00328 else if(weight_type == std::string("Gaussian"))
00329 {
00330 w_fit(i, i) = sqrt(exp(-pow(residual(i, 0), 2) / (2 * g_sigma * g_sigma)));
00331 }
00332 else
00333 {
00334 w_fit(i, i) = 1.0 / (0.1 + sqrt(fabs(residual(i, 0))));
00335 }
00336 }
00337 return w_fit;
00338 }
00339
00340 void RosieOdometry::publish()
00341 {
00342 if(fabs((last_publish_time_ - current_time_).toSec()) < expected_publish_time_)
00343 return;
00344
00345 if(odometry_publisher_->trylock())
00346 {
00347 getOdometryMessage(odometry_publisher_->msg_);
00348 odometry_publisher_->unlockAndPublish();
00349 }
00350
00351 if(transform_publisher_->trylock())
00352 {
00353
00354 double x(0.), y(0.0), yaw(0.0), vx(0.0), vy(0.0), vyaw(0.0);
00355 this->getOdometry(x, y, yaw, vx, vy, vyaw);
00356
00357 geometry_msgs::TransformStamped &out = transform_publisher_->msg_.transforms[0];
00358 out.header.stamp = current_time_;
00359 out.header.frame_id = odom_frame_;
00360 out.child_frame_id = base_link_frame_;
00361 out.transform.translation.x = x;
00362 out.transform.translation.y = y;
00363 out.transform.translation.z = 0;
00364 tf::Quaternion quat_trans = tf::Quaternion(yaw, 0.0, 0.0);
00365
00366 out.transform.rotation.x = quat_trans.x();
00367 out.transform.rotation.y = quat_trans.y();
00368 out.transform.rotation.z = quat_trans.z();
00369 out.transform.rotation.w = quat_trans.w();
00370
00371 transform_publisher_->unlockAndPublish();
00372 }
00373
00374 last_publish_time_ = current_time_;
00375
00376 }
00377 }