ax2550_node.cc
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 // Persistent variables
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     // Convert mps to rpm
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     // Convert rpm to relative
00063     double A_rel = (A_rpm * 250 * 64) / 58593.75;
00064     double B_rel = (B_rpm * 250 * 64) / 58593.75;
00065     
00066     // ROS_INFO("Arpm: %f, Arel: %f, Brpm: %f, Brel: %f", A_rpm, A_rel, B_rpm, B_rel);
00067     
00068     // Bounds check
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     // Set the targets
00079     target_speed = A_rel;
00080     target_direction = B_rel;
00081 }
00082 
00083 void controlLoop() {
00084     // ROS_INFO("Relative move commands: %f %f", target_speed, target_direction);
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     // Make sure we are connected
00116     if(!ros::ok() || mc == NULL || !mc->isConnected())
00117         return;
00118     
00119     long encoder1, encoder2;
00120     ros::Time now = ros::Time::now();
00121     // Retreive the data
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     // Convert to mps for each wheel from delta encoder ticks
00143     double left_v = encoder1 * 2*M_PI / ENCODER_RESOLUTION;
00144     left_v /= delta_time;
00145     // left_v *= encoder_poll_rate;
00146     double right_v = -encoder2 * 2*M_PI / ENCODER_RESOLUTION;
00147     right_v /= delta_time;
00148     // right_v *= encoder_poll_rate;
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     // Update the states based on model and input
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     // ROS_INFO("%f", prev_w);
00183     
00184     geometry_msgs::Quaternion quat = tf::createQuaternionMsgFromYaw(prev_w);
00185     
00186     // Populate the msg
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     // odom_msg.twist.twist.linear.x = v/delta_time;
00201     odom_msg.twist.twist.linear.x = v;
00202     // odom_msg.twist.twist.angular.z = w/delta_time;
00203     odom_msg.twist.twist.angular.z = w;
00204     
00205     odom_pub.publish(odom_msg);
00206     
00207     // TODO: Add TF broadcaster
00208     // geometry_msgs::TransformStamped odom_trans;
00209     //     odom_trans.header.stamp = now;
00210     //     odom_trans.header.frame_id = "odom";
00211     //     odom_trans.child_frame_id = "base_footprint";
00212     // 
00213     //     odom_trans.transform.translation.x = prev_x;
00214     //     odom_trans.transform.translation.y = prev_y;
00215     //     odom_trans.transform.translation.z = 0.0;
00216     //     odom_trans.transform.rotation = quat;
00217     //     
00218     //     odom_broadcaster->sendTransform(odom_trans);
00219 }
00220 
00221 int main(int argc, char **argv) {
00222     // Node setup
00223     ros::init(argc, argv, "ax2550_node");
00224     ros::NodeHandle n;
00225     prev_time = ros::Time::now();
00226     
00227     // Serial port parameter
00228     std::string port;
00229     n.param("serial_port", port, std::string("/dev/motor_controller"));
00230     
00231     // Wheel diameter parameter
00232     n.param("wheel_diameter", wheel_diameter, 0.3048);
00233     
00234     wheel_circumference = wheel_diameter * M_PI;
00235     
00236     // Wheel base length
00237     n.param("wheel_base_length", wheel_base_length, 0.9144);
00238     
00239     // Odom Frame id parameter
00240     n.param("odom_frame_id", odom_frame_id, std::string("odom"));
00241 
00242     // Load up some covariances from parameters
00243     n.param("rotation_covariance",rot_cov, 1.0);
00244     n.param("position_covariance",pos_cov, 1.0);
00245     
00246     // Setup Encoder polling
00247     n.param("encoder_poll_rate", encoder_poll_rate, 25.0);
00248     ros::Rate encoder_rate(encoder_poll_rate);
00249     
00250     // Odometry Publisher
00251     odom_pub = n.advertise<nav_msgs::Odometry>("odom", 5);
00252     
00253     // Encoder Publisher
00254     encoder_pub = n.advertise<ax2550::StampedEncoders>("encoders", 5);
00255     
00256     // TF Broadcaster
00257     odom_broadcaster = new tf::TransformBroadcaster;
00258     
00259     // cmd_vel Subscriber
00260     ros::Subscriber sub = n.subscribe("cmd_vel", 1, cmd_velCallback);
00261     
00262     // Spinner
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 }


ax2550
Author(s): William Woodall , Michael Carroll
autogenerated on Sat Jun 8 2019 18:26:02