$search
00001 #include <string> 00002 00003 #include <ros/ros.h> 00004 #include <ros/console.h> 00005 00006 #include <geometry_msgs/Twist.h> 00007 #include <nav_msgs/Odometry.h> 00008 #include <tf/transform_broadcaster.h> 00009 #include <tf/transform_listener.h> 00010 #include <sensor_msgs/JointState.h> 00011 #include <sensor_msgs/Imu.h> 00012 #include <sensor_msgs/Range.h> 00013 00014 #include "kurt.h" 00015 #include "comm.h" 00016 00017 class ROSComm : public Comm 00018 { 00019 public: 00020 ROSComm( 00021 const ros::NodeHandle &n, 00022 double sigma_x, 00023 double sigma_theta, 00024 double cov_x_y, 00025 double cov_x_theta, 00026 double cov_y_theta, 00027 int ticks_per_turn_of_wheel) : 00028 n_(n), 00029 sigma_x_(sigma_x), 00030 sigma_theta_(sigma_theta), 00031 cov_x_y_(cov_x_y), 00032 cov_x_theta_(cov_x_theta), 00033 cov_y_theta_(cov_y_theta), 00034 ticks_per_turn_of_wheel_(ticks_per_turn_of_wheel), 00035 publish_tf_(false), 00036 odom_pub_(n_.advertise<nav_msgs::Odometry> ("odom", 10)), 00037 range_pub_(n_.advertise<sensor_msgs::Range> ("range", 10)), 00038 imu_pub_(n_.advertise<sensor_msgs::Imu> ("imu", 10)), 00039 joint_pub_(n_.advertise<sensor_msgs::JointState> ("joint_states", 1)) { } 00040 virtual void send_odometry(double z, double x, double theta, double 00041 v_encoder, double v_encoder_angular, int wheel_a, int wheel_b, double 00042 v_encoder_left, double v_encoder_right); virtual void 00043 send_sonar_leftBack(int ir_left_back); 00044 virtual void send_sonar_front_usound_leftFront_left(int ir_right_front, int 00045 usound, int ir_left_front, int ir_left); 00046 virtual void send_sonar_back_rightBack_rightFront(int ir_back, int 00047 ir_right_back, int ir_right); 00048 virtual void send_pitch_roll(double pitch, double roll); 00049 virtual void send_gyro(double theta, double sigma); 00050 virtual void send_rotunit(double rot); 00051 00052 void setTFPrefix(const std::string &tf_prefix); 00053 00054 private: 00055 void populateCovariance(nav_msgs::Odometry &msg, double v_encoder, double 00056 v_encoder_angular); 00057 00058 ros::NodeHandle n_; 00059 double sigma_x_, sigma_theta_, cov_x_y_, cov_x_theta_, cov_y_theta_; 00060 int ticks_per_turn_of_wheel_; 00061 bool publish_tf_; 00062 std::string tf_prefix_; 00063 00064 tf::TransformBroadcaster odom_broadcaster_; 00065 ros::Publisher odom_pub_; 00066 ros::Publisher range_pub_; 00067 ros::Publisher imu_pub_; 00068 ros::Publisher joint_pub_; 00069 }; 00070 00071 void ROSComm::setTFPrefix(const std::string &tf_prefix) 00072 { 00073 tf_prefix_ = tf_prefix; 00074 } 00075 00076 void ROSComm::populateCovariance(nav_msgs::Odometry &msg, double v_encoder, double v_encoder_angular) 00077 { 00078 double odom_multiplier = 1.0; 00079 00080 if (fabs(v_encoder) <= 1e-8 && fabs(v_encoder_angular) <= 1e-8) 00081 { 00082 //nav_msgs::Odometry has a 6x6 covariance matrix 00083 msg.twist.covariance[0] = 1e-12; 00084 msg.twist.covariance[35] = 1e-12; 00085 00086 msg.twist.covariance[30] = 1e-12; 00087 msg.twist.covariance[5] = 1e-12; 00088 } 00089 else 00090 { 00091 //nav_msgs::Odometry has a 6x6 covariance matrix 00092 msg.twist.covariance[0] = odom_multiplier * pow(sigma_x_, 2); 00093 msg.twist.covariance[35] = odom_multiplier * pow(sigma_theta_, 2); 00094 00095 msg.twist.covariance[30] = odom_multiplier * cov_x_theta_; 00096 msg.twist.covariance[5] = odom_multiplier * cov_x_theta_; 00097 } 00098 00099 msg.twist.covariance[7] = DBL_MAX; 00100 msg.twist.covariance[14] = DBL_MAX; 00101 msg.twist.covariance[21] = DBL_MAX; 00102 msg.twist.covariance[28] = DBL_MAX; 00103 00104 msg.pose.covariance = msg.twist.covariance; 00105 00106 if (fabs(v_encoder) <= 1e-8 && fabs(v_encoder_angular) <= 1e-8) 00107 { 00108 msg.pose.covariance[7] = 1e-12; 00109 00110 msg.pose.covariance[1] = 1e-12; 00111 msg.pose.covariance[6] = 1e-12; 00112 00113 msg.pose.covariance[31] = 1e-12; 00114 msg.pose.covariance[11] = 1e-12; 00115 } 00116 else 00117 { 00118 msg.pose.covariance[7] = odom_multiplier * pow(sigma_x_, 2) * pow(sigma_theta_, 2); 00119 00120 msg.pose.covariance[1] = odom_multiplier * cov_x_y_; 00121 msg.pose.covariance[6] = odom_multiplier * cov_x_y_; 00122 00123 msg.pose.covariance[31] = odom_multiplier * cov_y_theta_; 00124 msg.pose.covariance[11] = odom_multiplier * cov_y_theta_; 00125 } 00126 } 00127 00128 void ROSComm::send_odometry(double z, double x, double theta, double v_encoder, double v_encoder_angular, int wheel_a, int wheel_b, double v_encoder_left, double v_encoder_right) 00129 { 00130 nav_msgs::Odometry odom; 00131 odom.header.frame_id = tf::resolve(tf_prefix_, "odom_combined"); 00132 odom.child_frame_id = tf::resolve(tf_prefix_, "base_footprint"); 00133 00134 odom.header.stamp = ros::Time::now(); 00135 odom.pose.pose.position.x = z; 00136 odom.pose.pose.position.y = -x; 00137 odom.pose.pose.position.z = 0.0; 00138 odom.pose.pose.orientation = tf::createQuaternionMsgFromYaw(-theta); 00139 00140 odom.twist.twist.linear.x = v_encoder; 00141 odom.twist.twist.linear.y = 0.0; 00142 odom.twist.twist.angular.z = v_encoder_angular; 00143 populateCovariance(odom, v_encoder, v_encoder_angular); 00144 00145 odom_pub_.publish(odom); 00146 00147 if (publish_tf_) 00148 { 00149 geometry_msgs::TransformStamped odom_trans; 00150 odom_trans.header.frame_id = tf::resolve(tf_prefix_, "odom_combined"); 00151 odom_trans.child_frame_id = tf::resolve(tf_prefix_, "base_footprint"); 00152 00153 odom_trans.header.stamp = ros::Time::now(); 00154 odom_trans.transform.translation.x = z; 00155 odom_trans.transform.translation.y = -x; 00156 odom_trans.transform.translation.z = 0.0; 00157 odom_trans.transform.rotation = tf::createQuaternionMsgFromYaw(-theta); 00158 00159 odom_broadcaster_.sendTransform(odom_trans); 00160 } 00161 00162 sensor_msgs::JointState joint_state; 00163 joint_state.header.stamp = ros::Time::now(); 00164 joint_state.name.resize(6); 00165 joint_state.position.resize(6); 00166 joint_state.name[0] = "left_front_wheel_joint"; 00167 joint_state.name[1] = "left_middle_wheel_joint"; 00168 joint_state.name[2] = "left_rear_wheel_joint"; 00169 joint_state.name[3] = "right_front_wheel_joint"; 00170 joint_state.name[4] = "right_middle_wheel_joint"; 00171 joint_state.name[5] = "right_rear_wheel_joint"; 00172 00173 static double wheelpos_l = 0; 00174 wheelpos_l += 2.0 * M_PI * wheel_a / ticks_per_turn_of_wheel_; 00175 if (wheelpos_l > M_PI) 00176 wheelpos_l -= 2.0 * M_PI; 00177 if (wheelpos_l < -M_PI) 00178 wheelpos_l += 2.0 * M_PI; 00179 00180 static double wheelpos_r = 0; 00181 wheelpos_r += 2 * M_PI * wheel_b / ticks_per_turn_of_wheel_; 00182 if (wheelpos_r > M_PI) 00183 wheelpos_r -= 2.0 * M_PI; 00184 if (wheelpos_r < -M_PI) 00185 wheelpos_r += 2.0 * M_PI; 00186 00187 joint_state.position[0] = joint_state.position[1] = joint_state.position[2] = wheelpos_l; 00188 joint_state.position[3] = joint_state.position[4] = joint_state.position[5] = wheelpos_r; 00189 00190 // note: we reuse joint_state here, i.e., we modify joint_state after publishing. 00191 // this is only safe as long as nothing in the same process subscribes to the 00192 // joint_states topic. same for imu above. 00193 joint_pub_.publish(joint_state); 00194 } 00195 00196 void ROSComm::send_sonar_leftBack(int ir_left_back) 00197 { 00198 sensor_msgs::Range range; 00199 range.header.stamp = ros::Time::now(); 00200 00201 range.header.frame_id = tf::resolve(tf_prefix_, "ir_left_back"); 00202 range.radiation_type = sensor_msgs::Range::INFRARED; 00203 range.field_of_view = IR_FOV; 00204 range.min_range = IR_MIN; 00205 range.max_range = IR_MAX; 00206 range.range = ir_left_back / 100.0; 00207 range_pub_.publish(range); 00208 } 00209 00210 void ROSComm::send_sonar_front_usound_leftFront_left(int ir_right_front, int usound, int ir_left_front, int ir_left) 00211 { 00212 sensor_msgs::Range range; 00213 range.header.stamp = ros::Time::now(); 00214 00215 range.header.frame_id = tf::resolve(tf_prefix_, "ir_right_front"); 00216 range.radiation_type = sensor_msgs::Range::INFRARED; 00217 range.field_of_view = IR_FOV; 00218 range.min_range = IR_MIN; 00219 range.max_range = IR_MAX; 00220 range.range = ir_right_front / 100.0; 00221 range_pub_.publish(range); 00222 00223 range.header.frame_id = tf::resolve(tf_prefix_, "ultrasound_front"); 00224 range.radiation_type = sensor_msgs::Range::ULTRASOUND; 00225 range.field_of_view = SONAR_FOV; 00226 range.min_range = SONAR_MIN; 00227 range.max_range = SONAR_MAX; 00228 range.range = usound / 100.0; 00229 range_pub_.publish(range); 00230 00231 range.header.frame_id = tf::resolve(tf_prefix_, "ir_left_front"); 00232 range.radiation_type = sensor_msgs::Range::INFRARED; 00233 range.field_of_view = IR_FOV; 00234 range.min_range = IR_MIN; 00235 range.max_range = IR_MAX; 00236 range.range = ir_left_front / 100.0; 00237 range_pub_.publish(range); 00238 00239 range.header.frame_id = tf::resolve(tf_prefix_, "ir_left"); 00240 range.radiation_type = sensor_msgs::Range::INFRARED; 00241 range.field_of_view = IR_FOV; 00242 range.min_range = IR_MIN; 00243 range.max_range = IR_MAX; 00244 range.range = ir_left / 100.0; 00245 range_pub_.publish(range); 00246 } 00247 00248 void ROSComm::send_sonar_back_rightBack_rightFront(int ir_back, int ir_right_back, int ir_right) 00249 { 00250 sensor_msgs::Range range; 00251 range.header.stamp = ros::Time::now(); 00252 00253 range.header.frame_id = tf::resolve(tf_prefix_, "ir_back"); 00254 range.radiation_type = sensor_msgs::Range::INFRARED; 00255 range.field_of_view = IR_FOV; 00256 range.min_range = IR_MIN; 00257 range.max_range = IR_MAX; 00258 range.range = ir_back / 100.0; 00259 range_pub_.publish(range); 00260 00261 range.header.frame_id = tf::resolve(tf_prefix_, "ir_right_back"); 00262 range.radiation_type = sensor_msgs::Range::INFRARED; 00263 range.field_of_view = IR_FOV; 00264 range.min_range = IR_MIN; 00265 range.max_range = IR_MAX; 00266 range.range = ir_right_back / 100.0; 00267 range_pub_.publish(range); 00268 00269 range.header.frame_id = tf::resolve(tf_prefix_, "ir_right"); 00270 range.radiation_type = sensor_msgs::Range::INFRARED; 00271 range.field_of_view = IR_FOV; 00272 range.min_range = IR_MIN; 00273 range.max_range = IR_MAX; 00274 range.range = ir_right / 100.0; 00275 range_pub_.publish(range); 00276 } 00277 00278 void ROSComm::send_pitch_roll(double pitch, double roll) 00279 { 00280 //TODO 00281 } 00282 00283 void ROSComm::send_gyro(double theta, double sigma) 00284 { 00285 sensor_msgs::Imu imu; 00286 00287 // this is intentionally base_link (the location of the imu) and not base_footprint, 00288 // but because they are connected by a fixed link, it doesn't matter 00289 imu.header.frame_id = tf::resolve(tf_prefix_, "base_link"); 00290 imu.header.stamp = ros::Time::now(); 00291 00292 imu.angular_velocity_covariance[0] = -1; // no data avilable, see Imu.msg 00293 imu.linear_acceleration_covariance[0] = -1; 00294 00295 imu.orientation = tf::createQuaternionMsgFromYaw(theta); 00296 imu.orientation_covariance[0] = sigma; 00297 imu.orientation_covariance[4] = sigma; 00298 imu.orientation_covariance[8] = sigma; 00299 imu_pub_.publish(imu); 00300 } 00301 00302 void ROSComm::send_rotunit(double rot) 00303 { 00304 sensor_msgs::JointState joint_state; 00305 joint_state.header.stamp = ros::Time::now(); 00306 joint_state.name.resize(1); 00307 joint_state.position.resize(1); 00308 joint_state.name[0] = "laser_rot_joint"; 00309 joint_state.position[0] = rot; 00310 00311 joint_pub_.publish(joint_state); 00312 } 00313 00314 class ROSCall 00315 { 00316 public: 00317 ROSCall(Kurt &kurt, double axis_length) : 00318 kurt_(kurt), 00319 axis_length_(axis_length), 00320 v_l_soll_(0.0), 00321 v_r_soll_(0.0), 00322 AntiWindup_(1.0), 00323 last_cmd_vel_time_(0.0) { } 00324 void velCallback(const geometry_msgs::Twist::ConstPtr& msg); 00325 void pidCallback(const ros::TimerEvent& event); 00326 void rotunitCallback(const geometry_msgs::Twist::ConstPtr& msg); 00327 00328 private: 00329 Kurt &kurt_; 00330 double axis_length_; 00331 double v_l_soll_; 00332 double v_r_soll_; 00333 double AntiWindup_; 00334 ros::Time last_cmd_vel_time_; 00335 }; 00336 00337 void ROSCall::velCallback(const geometry_msgs::Twist::ConstPtr& msg) 00338 { 00339 AntiWindup_ = 1.0; 00340 last_cmd_vel_time_ = ros::Time::now(); 00341 v_l_soll_ = msg->linear.x - axis_length_ * msg->angular.z /*/ wheelRadius*/; 00342 v_r_soll_ = msg->linear.x + axis_length_ * msg->angular.z/*/wheelRadius*/; 00343 00344 if (msg->linear.x == 0 && msg->angular.z == 0) 00345 { 00346 AntiWindup_ = 0.0; 00347 } 00348 } 00349 00350 void ROSCall::pidCallback(const ros::TimerEvent& event) 00351 { 00352 double v_l_soll = 0.0; 00353 double v_r_soll = 0.0; 00354 double AntiWindup = 1.0; 00355 00356 if (ros::Time::now() - last_cmd_vel_time_ < ros::Duration(0.6)) 00357 { 00358 v_l_soll = v_l_soll_; 00359 v_r_soll = v_r_soll_; 00360 AntiWindup = AntiWindup_; 00361 } 00362 00363 kurt_.set_wheel_speed(v_l_soll, v_r_soll, AntiWindup); 00364 } 00365 00366 void ROSCall::rotunitCallback(const geometry_msgs::Twist::ConstPtr& msg) 00367 { 00368 kurt_.can_rotunit_send(msg->angular.z); 00369 } 00370 00371 int main(int argc, char** argv) 00372 { 00373 ros::init(argc, argv, "kurt_base"); 00374 ros::NodeHandle n; 00375 ros::NodeHandle nh_ns("~"); 00376 00377 //Odometry parameter (defaults for kurt2 indoor) 00378 double wheel_perimeter; 00379 nh_ns.param("wheel_perimeter", wheel_perimeter, 0.379); 00380 double axis_length; 00381 nh_ns.param("axis_length", axis_length, 0.28); 00382 00383 double turning_adaptation; 00384 nh_ns.param("turning_adaptation", turning_adaptation, 0.69); 00385 int ticks_per_turn_of_wheel; 00386 nh_ns.param("ticks_per_turn_of_wheel", ticks_per_turn_of_wheel, 21950); 00387 00388 double sigma_x, sigma_theta, cov_x_y, cov_x_theta, cov_y_theta; 00389 nh_ns.param("x_stddev", sigma_x, 0.002); 00390 nh_ns.param("rotation_stddev", sigma_theta, 0.017); 00391 nh_ns.param("cov_xy", cov_x_y, 0.0); 00392 nh_ns.param("cov_xrotation", cov_x_theta, 0.0); 00393 nh_ns.param("cov_yrotation", cov_y_theta, 0.0); 00394 00395 ROSComm roscomm(n, sigma_x, sigma_theta, cov_x_y, cov_x_theta, cov_y_theta, ticks_per_turn_of_wheel); 00396 00397 Kurt kurt(roscomm, wheel_perimeter, axis_length, turning_adaptation, ticks_per_turn_of_wheel); 00398 00399 //PID parameter (disables micro controller) 00400 std::string speedPwmLeerlaufTable; 00401 if (nh_ns.getParam("speedtable", speedPwmLeerlaufTable)) 00402 { 00403 double feedforward_turn; 00404 nh_ns.param("feedforward_turn", feedforward_turn, 0.35); 00405 double ki, kp; 00406 nh_ns.param("ki", ki, 3.4); 00407 nh_ns.param("kp", kp, 0.4); 00408 if (!kurt.setPWMData(speedPwmLeerlaufTable, feedforward_turn, ki, kp)) 00409 return 1; 00410 } 00411 00412 bool use_rotunit; 00413 nh_ns.param("use_rotunit", use_rotunit, false); 00414 if (use_rotunit) { 00415 double rotunit_speed; 00416 nh_ns.param("rotunit_speed", rotunit_speed, M_PI/6.0); 00417 kurt.can_rotunit_send(rotunit_speed); 00418 } 00419 00420 bool publish_tf; 00421 nh_ns.param("publish_tf", publish_tf, false); 00422 std::string tf_prefix; 00423 tf_prefix = tf::getPrefixParam(nh_ns); 00424 roscomm.setTFPrefix(tf_prefix); 00425 00426 ROSCall roscall(kurt, axis_length); 00427 00428 ros::Timer pid_timer = n.createTimer(ros::Duration(0.01), &ROSCall::pidCallback, &roscall); 00429 ros::Subscriber cmd_vel_sub = n.subscribe("cmd_vel", 10, &ROSCall::velCallback, &roscall); 00430 ros::Subscriber rot_vel_sub; 00431 if (use_rotunit) 00432 rot_vel_sub = n.subscribe("rot_vel", 10, &ROSCall::rotunitCallback, &roscall); 00433 00434 while (ros::ok()) 00435 { 00436 kurt.can_read_fifo(); 00437 ros::spinOnce(); 00438 } 00439 00440 return 0; 00441 }