Go to the documentation of this file.00001 #include "ros/ros.h"
00002 #include "geometry_msgs/Twist.h"
00003 #include "nav_msgs/Odometry.h"
00004 #include "ax2550/StampedEncoders.h"
00005 #include "tf/tf.h"
00006 #include <tf/transform_broadcaster.h>
00007
00008 #include <string>
00009 #include <cmath>
00010
00011 #include "ax2550/ax2550.h"
00012
00013 using namespace ax2550;
00014 using std::string;
00015
00016 AX2550 *mc;
00017 ros::Publisher odom_pub;
00018 ros::Publisher encoder_pub;
00019 tf::TransformBroadcaster *odom_broadcaster;
00020
00021 static double ENCODER_RESOLUTION = 250*4;
00022 double wheel_circumference = 0.0;
00023 double wheel_base_length = 0.0;
00024 double wheel_diameter = 0.0;
00025 double encoder_poll_rate;
00026 std::string odom_frame_id;
00027 size_t error_count;
00028 double target_speed = 0.0;
00029 double target_direction = 0.0;
00030
00031 double rot_cov = 0.0;
00032 double pos_cov = 0.0;
00033
00034 static double A_MAX = 20.0;
00035 static double B_MAX = 20.0;
00036
00037
00038 double prev_x = 0, prev_y = 0, prev_w = 0;
00039 ros::Time prev_time;
00040
00041 double wrapToPi(double angle) {
00042 angle += M_PI;
00043 bool is_neg = (angle < 0);
00044 angle = fmod(angle, (2.0*M_PI));
00045 if (is_neg) {
00046 angle += (2.0*M_PI);
00047 }
00048 angle -= M_PI;
00049 return angle;
00050 }
00051
00052 void cmd_velCallback(const geometry_msgs::Twist::ConstPtr& msg) {
00053 if(mc == NULL || !mc->isConnected())
00054 return;
00055
00056 double A = msg->linear.x;
00057 double B = msg->angular.z * (wheel_base_length/2.0);
00058
00059 double A_rpm = A * (60.0 / (M_PI*wheel_diameter));
00060 double B_rpm = B * (60.0 / (M_PI*wheel_diameter));
00061
00062
00063 double A_rel = (A_rpm * 250 * 64) / 58593.75;
00064 double B_rel = (B_rpm * 250 * 64) / 58593.75;
00065
00066
00067
00068
00069 if(A_rel > A_MAX)
00070 A_rel = A_MAX;
00071 if(A_rel < -1*A_MAX)
00072 A_rel = -1*A_MAX;
00073 if(B_rel > B_MAX)
00074 B_rel = B_MAX;
00075 if(B_rel < -1*B_MAX)
00076 B_rel = -1*B_MAX;
00077
00078
00079 target_speed = A_rel;
00080 target_direction = B_rel;
00081 }
00082
00083 void controlLoop() {
00084
00085 try {
00086 mc->move(target_speed, target_direction);
00087 } catch(const std::exception &e) {
00088 if (string(e.what()).find("did not receive") != string::npos
00089 || string(e.what()).find("failed to receive an echo") != string::npos) {
00090 ROS_WARN("Error commanding the motors: %s", e.what());
00091 } else {
00092 ROS_ERROR("Error commanding the motors: %s", e.what());
00093 mc->disconnect();
00094 }
00095 }
00096 }
00097
00098 void errorMsgCallback(const std::string &msg) {
00099 ROS_ERROR("%s", msg.c_str());
00100 }
00101
00102 void warnMsgCallback(const std::string &msg) {
00103 ROS_WARN("%s", msg.c_str());
00104 }
00105
00106 void infoMsgCallback(const std::string &msg) {
00107 ROS_INFO("%s", msg.c_str());
00108 }
00109
00110 void debugMsgCallback(const std::string &msg) {
00111 ROS_DEBUG("%s", msg.c_str());
00112 }
00113
00114 void queryEncoders() {
00115
00116 if(!ros::ok() || mc == NULL || !mc->isConnected())
00117 return;
00118
00119 long encoder1, encoder2;
00120 ros::Time now = ros::Time::now();
00121
00122 try {
00123 mc->queryEncoders(encoder1, encoder2, true);
00124 if (error_count > 0) {
00125 error_count -= 1;
00126 }
00127 } catch(std::exception &e) {
00128 if (string(e.what()).find("failed to receive ") != string::npos
00129 && error_count != 10) {
00130 error_count += 1;
00131 ROS_WARN("Error reading the Encoders: %s", e.what());
00132 } else {
00133 ROS_ERROR("Error reading the Encoders: %s", e.what());
00134 mc->disconnect();
00135 }
00136 return;
00137 }
00138
00139 double delta_time = (now - prev_time).toSec();
00140 prev_time = now;
00141
00142
00143 double left_v = encoder1 * 2*M_PI / ENCODER_RESOLUTION;
00144 left_v /= delta_time;
00145
00146 double right_v = -encoder2 * 2*M_PI / ENCODER_RESOLUTION;
00147 right_v /= delta_time;
00148
00149
00150 ax2550::StampedEncoders encoder_msg;
00151
00152 encoder_msg.header.stamp = now;
00153 encoder_msg.header.frame_id = "base_link";
00154 encoder_msg.encoders.time_delta = delta_time;
00155 encoder_msg.encoders.left_wheel = encoder1;
00156 encoder_msg.encoders.right_wheel = -encoder2;
00157
00158 encoder_pub.publish(encoder_msg);
00159
00160 double v = 0.0;
00161 double w = 0.0;
00162
00163 double r_L = wheel_diameter/2.0;
00164 double r_R = wheel_diameter/2.0;
00165
00166 v += r_L/2.0 * left_v;
00167 v += r_R/2.0 * right_v;
00168
00169 w += r_R/wheel_base_length * right_v;
00170 w -= r_L/wheel_base_length * left_v;
00171
00172
00173
00174 prev_x += delta_time * v
00175 * cos(prev_w + delta_time * (w/2.0));
00176
00177 prev_y += delta_time * v
00178 * sin(prev_w + delta_time * (w/2.0));
00179 prev_w += delta_time * w;
00180 prev_w = wrapToPi(prev_w);
00181
00182
00183
00184 geometry_msgs::Quaternion quat = tf::createQuaternionMsgFromYaw(prev_w);
00185
00186
00187 nav_msgs::Odometry odom_msg;
00188 odom_msg.header.stamp = now;
00189 odom_msg.header.frame_id = odom_frame_id;
00190 odom_msg.pose.pose.position.x = prev_x;
00191 odom_msg.pose.pose.position.y = prev_y;
00192 odom_msg.pose.pose.orientation = quat;
00193 odom_msg.pose.covariance[0] = pos_cov;
00194 odom_msg.pose.covariance[7] = pos_cov;
00195 odom_msg.pose.covariance[14] = 1e100;
00196 odom_msg.pose.covariance[21] = 1e100;
00197 odom_msg.pose.covariance[28] = 1e100;
00198 odom_msg.pose.covariance[35] = rot_cov;
00199
00200
00201 odom_msg.twist.twist.linear.x = v;
00202
00203 odom_msg.twist.twist.angular.z = w;
00204
00205 odom_pub.publish(odom_msg);
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219 }
00220
00221 int main(int argc, char **argv) {
00222
00223 ros::init(argc, argv, "ax2550_node");
00224 ros::NodeHandle n;
00225 prev_time = ros::Time::now();
00226
00227
00228 std::string port;
00229 n.param("serial_port", port, std::string("/dev/motor_controller"));
00230
00231
00232 n.param("wheel_diameter", wheel_diameter, 0.3048);
00233
00234 wheel_circumference = wheel_diameter * M_PI;
00235
00236
00237 n.param("wheel_base_length", wheel_base_length, 0.9144);
00238
00239
00240 n.param("odom_frame_id", odom_frame_id, std::string("odom"));
00241
00242
00243 n.param("rotation_covariance",rot_cov, 1.0);
00244 n.param("position_covariance",pos_cov, 1.0);
00245
00246
00247 n.param("encoder_poll_rate", encoder_poll_rate, 25.0);
00248 ros::Rate encoder_rate(encoder_poll_rate);
00249
00250
00251 odom_pub = n.advertise<nav_msgs::Odometry>("odom", 5);
00252
00253
00254 encoder_pub = n.advertise<ax2550::StampedEncoders>("encoders", 5);
00255
00256
00257 odom_broadcaster = new tf::TransformBroadcaster;
00258
00259
00260 ros::Subscriber sub = n.subscribe("cmd_vel", 1, cmd_velCallback);
00261
00262
00263 ros::AsyncSpinner spinner(1);
00264 spinner.start();
00265
00266 while(ros::ok()) {
00267 ROS_INFO("AX2550 connecting to port %s", port.c_str());
00268 try {
00269 mc = new AX2550();
00270 mc->warn = warnMsgCallback;
00271 mc->info = infoMsgCallback;
00272 mc->debug = debugMsgCallback;
00273 mc->connect(port);
00274 } catch(std::exception &e) {
00275 ROS_ERROR("Failed to connect to the AX2550: %s", e.what());
00276 if (mc != NULL) {
00277 mc->disconnect();
00278 }
00279 }
00280 int count = 0;
00281 while(mc != NULL && mc->isConnected() && ros::ok()) {
00282 queryEncoders();
00283 if (count == 1) {
00284 controlLoop();
00285 count = 0;
00286 } else {
00287 count += 1;
00288 }
00289 encoder_rate.sleep();
00290 }
00291 if (mc != NULL) {
00292 delete mc;
00293 }
00294 mc = NULL;
00295 if(!ros::ok())
00296 break;
00297 ROS_INFO("Will try to reconnect to the AX2550 in 5 seconds.");
00298 for (int i = 0; i < 100; ++i) {
00299 ros::Duration(5.0/100.0).sleep();
00300 if (!ros::ok())
00301 break;
00302 }
00303 target_speed = 0.0;
00304 target_direction = 0.0;
00305 }
00306
00307 spinner.stop();
00308
00309 return 0;
00310 }