Roboteq_Driver.cpp
Go to the documentation of this file.
00001 #include <string>
00002 #include <string.h>
00003 #include "RoboteqDevice.h"
00004 #include "ErrorCodes.h"
00005 #include "Constants.h"
00006 #include "ros/ros.h"
00007 #include "geometry_msgs/Twist.h"
00008 #include "komodo_rover/StampedEncoders.h"
00009 #include "nav_msgs/Odometry.h"
00010 #include "tf/tf.h"
00011 #include <tf/transform_broadcaster.h>
00012 #include <cmath>
00013 #include <iostream>
00014 #include <stdio.h>
00015 #include <std_srvs/Empty.h>
00016 #include "sensor_msgs/Imu.h"
00017 #include "tf/transform_datatypes.h"
00018 #include "std_msgs/Float32.h"
00019 
00020 using namespace std;
00021 
00022 ros::Publisher rover_bat_pub;
00023 
00024 RoboteqDevice device;
00025 
00026 #define MAX_ERRORS 5
00027 
00028 bool rp_from_imu = true;
00029 bool y_from_imu = true;
00030 int enc_loop_count = 0, enc_loop_div;
00031 int encoder_cpr, encoder_ppr;
00032 int status = -1;
00033 int prev_l_encoder = 0, prev_r_encoder = 0;
00034 int l_encoder_init = 0, r_encoder_init = 0;
00035 bool first_enc_read = true;
00036 double Left_Motor_command = 0;
00037 double Right_Motor_command = 0;
00038 ros::Publisher odom_pub;
00039 ros::Publisher encoder_pub;
00040 tf::TransformBroadcaster *odom_broadcaster;
00041 
00042 //ros::Subscriber imu_sub;
00043 //double imu_yaw=0;
00044 geometry_msgs::Quaternion q_imu = tf::createQuaternionMsgFromYaw(0);
00045 
00046 int l_encoder=0;
00047 int r_encoder=0;
00048 
00049 double wheel_circumference = 0.0;
00050 double wheel_base_length = 0.0;
00051 double wheel_diameter = 0.0;
00052 double motors_loop_rate;
00053 double encoders_loop_rate;
00054 double tf_loop_rate;
00055 std::string odom_frame_id;
00056 int enc_error_count = 0;
00057 int mot_error_count = 0;
00058 double target_speed = 0.0;
00059 double target_direction = 0.0;
00060 
00061 double rot_cov = 0.0;
00062 double pos_cov = 0.0;
00063 
00064 int rpm_limit;
00065 
00066 // Persistent variables
00067 double xx = 0, yy = 0, tt = 0;
00068 ros::Time prev_time, mot_loop_time, enc_loop_time, bat_loop_time, tf_loop_time;
00069 
00070 void updtae_state();
00071 
00072 double wrapToPi(double angle) {
00073         angle += M_PI;
00074         bool is_neg = (angle < 0);
00075         angle = fmod(angle, (2.0 * M_PI));
00076         if (is_neg) {
00077                 angle += (2.0 * M_PI);
00078         }
00079         angle -= M_PI;
00080         return angle;
00081 }
00082 
00083 void cmd_velCallback(const geometry_msgs::Twist::ConstPtr& msg) {
00084 
00085         //double Left_Motor_v = msg->linear.x - msg->angular.z * (wheel_base_length / 2.0);
00086         //double Right_Motor_v = msg->linear.x + msg->angular.z * (wheel_base_length / 2.0);
00087         double Right_Motor_v = msg->linear.x;
00088         double Left_Motor_v = msg->angular.z * (wheel_base_length / 2.0);
00089 
00090         double Left_Motor_rpm = Left_Motor_v * (60.0 / (M_PI * wheel_diameter));
00091         double Right_Motor_rpm = Right_Motor_v * (60.0 / (M_PI * wheel_diameter));
00092 
00093         if (Left_Motor_rpm > rpm_limit)
00094                 Left_Motor_rpm = rpm_limit;
00095         if (Left_Motor_rpm < -rpm_limit)
00096                 Left_Motor_rpm = -rpm_limit;
00097         if (Right_Motor_rpm > rpm_limit)
00098                 Right_Motor_rpm = rpm_limit;
00099         if (Right_Motor_rpm < -rpm_limit)
00100                 Right_Motor_rpm = -rpm_limit;
00101 
00102         //ROS_INFO("Left rpm: %d, Right rpm: %d", (int)Left_Motor_rpm, (int)Right_Motor_rpm);
00103         Left_Motor_command = Left_Motor_rpm*1000.0/(double)rpm_limit;
00104         Right_Motor_command = Right_Motor_rpm*1000.0/(double)rpm_limit;
00105 
00106         //ROS_INFO("Left command: %d, Right commandm: %d", (int)Left_Motor_command, (int)Right_Motor_command);
00107 
00108 }
00109 
00110 void controlLoop() {
00111         // ROS_INFO("Relative move commands: %f %f", target_speed, target_direction);
00112         int status1 = -1;
00113         int status2 = -1;
00114         //ROS_INFO("Left: %d, Right: %d", (int)Left_Motor_command, (int)Right_Motor_command);
00115         status1 = device.SetCommand(_GO, 1, (int) Right_Motor_command);
00116         status2 = device.SetCommand(_GO, 2, (int) Left_Motor_command);
00117         if ((status1 != RQ_SUCCESS) || (status2 != RQ_SUCCESS)) { 
00118                 ROS_ERROR("Error commanding motors. error codes: m1=%d m2=%d", status1,status2);
00119                 mot_error_count = mot_error_count + 1;
00120                 if (mot_error_count >= MAX_ERRORS) {
00121                         ROS_ERROR("Error commanding motors -> Resarting connection...");
00122                         device.Disconnect();
00123                 }
00124                 return;
00125         }
00126         mot_error_count = 0;
00127         Left_Motor_command = 0.0;
00128         Right_Motor_command = 0.0;
00129 
00130 }
00131 
00132 void queryBat() {
00133         
00134 
00135                 
00136         
00137         int status3 = -1;
00138         //
00139                 int bat;
00140                 status3 = device.GetValue(_VOLTS, 2, bat);
00141                 if (status3 == RQ_SUCCESS) {
00142                         //ROS_INFO("V=%d", bat);
00143                         std_msgs::Float32 v_bat;
00144                         v_bat.data=(float)bat/10;
00145                         rover_bat_pub.publish(v_bat);
00146                 } 
00147                 else {
00148                         ROS_ERROR("Error reading battary voltage");
00149                 }
00150 }
00151 
00152 void queryEncoders() {
00153 
00154         int status1 = -1, status2 = -1;
00155         
00156 
00157         if (!ros::ok() || !device.IsConnected())
00158                 return;
00159 
00160         ros::Time now = ros::Time::now();
00161 double delta_time = (now - prev_time).toSec();
00162         if (((status1 = device.GetValue(_C, 1, r_encoder)) != RQ_SUCCESS)
00163                         || ((status2 = device.GetValue(_C, 2, l_encoder)) != RQ_SUCCESS)) {
00164                 ROS_ERROR("Error reading encoders. error codes: e1=%d e2=%d", status1,
00165                                 status2);
00166                 enc_error_count = enc_error_count + 1;
00167 
00168                 if (enc_error_count >= MAX_ERRORS) {
00169                         ROS_ERROR("Error reading encoders -> Resarting connection...");
00170                         device.Disconnect();
00171                 }
00172                 return;
00173         }
00174         enc_error_count = 0;
00175 
00176         l_encoder = l_encoder - l_encoder_init;
00177         r_encoder = r_encoder - r_encoder_init;
00178 
00179         komodo_rover::StampedEncoders encoder_msg;
00180         encoder_msg.header.stamp = now;
00181         encoder_msg.header.frame_id = "base_link";
00182         encoder_msg.encoders.time_delta = delta_time;
00183         encoder_msg.encoders.left_wheel = l_encoder;
00184         encoder_msg.encoders.right_wheel = r_encoder;
00185 
00186         encoder_pub.publish(encoder_msg);
00187 }
00188 
00189 void updtae_state() {
00190 
00191         ros::Time now = ros::Time::now();
00192         double delta_time = (now - prev_time).toSec();
00193 
00194         prev_time = now;
00195 
00196         
00197         if (first_enc_read) {
00198         prev_l_encoder = l_encoder;
00199         prev_r_encoder = r_encoder;
00200 }
00201         int diff_l_encoder = l_encoder-prev_l_encoder ;
00202         int diff_r_encoder = r_encoder-prev_r_encoder ;
00203         prev_l_encoder = l_encoder;
00204         prev_r_encoder = r_encoder;
00205         // Convert to rad/s for each wheel from delta encoder ticks
00206         double l_w = diff_l_encoder * 2.0 * M_PI / (double) encoder_cpr
00207                         / delta_time;
00208 
00209         double r_w = diff_r_encoder * 2.0 * M_PI / (double) encoder_cpr
00210                         / delta_time;
00211 
00212         //cout << "l_e: " << l_encoder << "  r_e: " << r_encoder <<"     l_w: " << l_w << "  r_w: " << r_w << endl;
00213 
00214         double l_v = l_w * wheel_diameter / 2.0;
00215         double r_v = r_w * wheel_diameter / 2.0;
00216 
00217         double v = (l_v + r_v) / 2.0;
00218 
00219         double w = (r_v - l_v) / wheel_base_length;
00220 
00221         // Update the states based on model and input
00222         geometry_msgs::Quaternion quat;
00223         double roll = 0, pitch = 0, yaw = 0;
00224         tf::Quaternion q;
00225         tf::quaternionMsgToTF(q_imu, q);
00226         tf::Matrix3x3(q).getRPY(roll, pitch, yaw);
00227 
00228         if (first_enc_read) {
00229 
00230                 xx = 0;
00231                 yy = 0;
00232                 tt = 0;
00233                 first_enc_read = false;
00234         }
00235         else {
00236 
00237                 if (!rp_from_imu) {
00238                         roll = 0;
00239                         pitch = 0;
00240                 }
00241 
00242                 //      tt=wrapToPi(yaw);
00243                 //}
00244 
00245                 if (y_from_imu) {
00246                         tt = wrapToPi(yaw);
00247                 }
00248 
00249                 else {
00250                         tt = tt + w * delta_time;
00251                         tt = wrapToPi(tt);
00252 
00253                 }
00254         }
00255 
00256         quat = tf::createQuaternionMsgFromRollPitchYaw(wrapToPi(roll),
00257                         wrapToPi(pitch), tt);
00258         //ROS_INFO("RPY = (%lf, %lf, %lf)",  wrapToPi(roll), wrapToPi(pitch), wrapToPi(tt));
00259         //tt = wrapToPi(tt);
00260         xx = xx + (v * cos(tt))*delta_time;
00261         yy = yy + (v * sin(tt))*delta_time;
00262 
00263         //geometry_msgs::Quaternion quat = tf::createQuaternionMsgFromYaw(0);
00264         // Populate the msg
00265         nav_msgs::Odometry odom_msg;
00266 
00267         odom_msg.header.stamp = now;
00268         odom_msg.header.frame_id = odom_frame_id;
00269         odom_msg.pose.pose.position.x = xx;
00270         odom_msg.pose.pose.position.y = yy;
00271         odom_msg.pose.pose.position.z = 0;
00272         odom_msg.pose.pose.orientation = quat;
00273         odom_msg.child_frame_id = "base_link";
00274         odom_msg.twist.twist.linear.x = v;
00275         odom_msg.twist.twist.linear.y = 0;
00276         odom_msg.twist.twist.angular.z = w;
00277 
00278         odom_msg.pose.covariance[0] = pos_cov;
00279         odom_msg.pose.covariance[7] = pos_cov;
00280         odom_msg.pose.covariance[14] = 1e100;
00281         odom_msg.pose.covariance[21] = 1e100;
00282         odom_msg.pose.covariance[28] = 1e100;
00283         odom_msg.pose.covariance[35] = rot_cov;
00284         odom_msg.twist.covariance = odom_msg.pose.covariance;
00285 
00286         odom_pub.publish(odom_msg);
00287 
00288         //Add TF broadcaster
00289         geometry_msgs::TransformStamped odom_trans;
00290         odom_trans.header.stamp = now;
00291         odom_trans.header.frame_id = odom_frame_id;
00292         odom_trans.child_frame_id = "base_link";
00293         odom_trans.transform.translation.x = xx;
00294         odom_trans.transform.translation.y = yy;
00295         odom_trans.transform.translation.z = 0.0;
00296         odom_trans.transform.rotation = quat;
00297         odom_broadcaster->sendTransform(odom_trans);
00298 }
00299 
00300 bool zero_enc_callback(std_srvs::Empty::Request& request,
00301                 std_srvs::Empty::Response& response) {
00302         ROS_INFO("Zeroing odometry");
00303 
00304         first_enc_read = true;
00305         return true;
00306 }
00307 
00308 void imu_Callback(const sensor_msgs::Imu::ConstPtr& msg) {
00309         //ROS_INFO("imu yaw:%f",imu_yaw);
00310         q_imu = msg->orientation;
00311 //imu_yaw=tf::getYaw(msg->orientation);
00312         //ROS_INFO("imu yaw:%f",tf::getYaw(msg->orientation));
00313 }
00314 
00315 
00316 
00317 
00318 int main(int argc, char **argv) {
00319 
00320         // Node setup
00321 
00322         ros::init(argc, argv, "Roboteq_Driver");
00323         ros::NodeHandle n("~");
00324         prev_time = ros::Time::now();
00325         mot_loop_time = ros::Time::now();
00326         enc_loop_time = ros::Time::now();
00327         bat_loop_time = ros::Time::now();
00328         tf_loop_time = ros::Time::now();
00329         std::string port;
00330         n.param("serial_port", port, std::string("/dev/Roboteq"));
00331         n.param("wheel_diameter", wheel_diameter, 0.2);
00332         wheel_circumference = wheel_diameter * M_PI;
00333         n.param("wheel_base_length", wheel_base_length, 0.45);
00334         n.param("encoder_ppr", encoder_ppr, 512);
00335         encoder_cpr = encoder_ppr * 4;
00336         n.param("odom_frame_id", odom_frame_id, std::string("/odom"));
00337         n.param("rpm_limit", rpm_limit, 500);
00338         n.param("rotation_covariance", rot_cov, 1.0);
00339         n.param("position_covariance", pos_cov, 1.0);
00340         n.param("motors_loop_rate", motors_loop_rate, 20.0);
00341         //ros::Rate loop_rate1(loop_rate);
00342         n.param("encoders_loop_rate", encoders_loop_rate, 5.0);
00343         n.param("tf_loop_rate", tf_loop_rate, 10.0);
00344         n.param("roll_pitch_from_imu", rp_from_imu, true);
00345         n.param("yaw_from_imu", y_from_imu, true);
00346 
00347         ros::Subscriber imu_sub = n.subscribe("/imu/data", 10, imu_Callback);
00348 
00349         // Odometry Publisher
00350         odom_pub = n.advertise < nav_msgs::Odometry > ("/odom", 10);
00351         // Encoder Publisher
00352         encoder_pub = n.advertise < komodo_rover::StampedEncoders > ("/encoders", 10);
00353         // TF Broadcaster
00354         odom_broadcaster = new tf::TransformBroadcaster;
00355         // cmd_vel Subscriber
00356         ros::Subscriber sub = n.subscribe("/cmd_vel", 1, cmd_velCallback);
00357 
00358         ros::ServiceServer service = n.advertiseService("zero_odometry",
00359                         zero_enc_callback);
00360 
00361         
00362         rover_bat_pub = n.advertise<std_msgs::Float32>("/rover_bat", 10);
00363         
00364         // Spinner
00365         ros::AsyncSpinner spinner(1);
00366         spinner.start();
00367 
00368         while (ros::ok()) {
00369                 ROS_INFO("Connecting to port %s", port.c_str());
00370                 status = device.Connect(port);
00371                 if (status != RQ_SUCCESS) {
00372                         ROS_ERROR(
00373                                         "Error connecting to device. Error code: %d. Trying again in 5 seconds...",
00374                                         status);
00375                         for (int i = 0; i < 100; ++i) {
00376                                 updtae_state();
00377                                 ros::Duration(5.0 / 100.0).sleep();
00378                                 if (!ros::ok())
00379                                         break;
00380                                 
00381                                 ros::Time now_time0 = ros::Time::now();
00382                                 if ((now_time0 - tf_loop_time).toSec()
00383                                         >= 1.0 / tf_loop_rate) {
00384                                 
00385                                 updtae_state ();
00386                                 tf_loop_time = now_time0;
00387                         }
00388 
00389                         }
00390                         continue;
00391                 }
00392                 
00393                 //int s1 = -1;
00394                 //      if ((s1 = device.SetCommand(_HOME, 1)) != RQ_SUCCESS)
00395                 //      ROS_ERROR("HOME 1 failed!");
00396                 //if ((s1 = device.SetCommand(_HOME, 2)) != RQ_SUCCESS)
00397                 //      ROS_ERROR("HOME 2 failed!");
00398 
00399                 while ((device.IsConnected()) && (ros::ok())) {
00400 
00401                         ros::Time now_time = ros::Time::now();
00402 
00403                         if ((now_time - mot_loop_time).toSec() >= 1.0 / motors_loop_rate) {
00404                                 controlLoop();
00405                                 //      cout<<"loop"<<endl;
00406                                 mot_loop_time = now_time;
00407                         }
00408                         if ((now_time - enc_loop_time).toSec()
00409                                         >= 1.0 / encoders_loop_rate) {
00410                                 queryEncoders();
00411                                 //cout<<"enc"<<endl;;
00412                                 enc_loop_time = now_time;
00413                         }
00414                         if ((now_time - tf_loop_time).toSec()
00415                                         >= 1.0 / tf_loop_rate) {
00416                                 
00417                                 updtae_state ();
00418                                 tf_loop_time = now_time;
00419                         }
00420 
00421                         if ((now_time - bat_loop_time).toSec() >= 1.0) { //1Hz read battery
00422                                 queryBat();
00423                                 //cout<<"enc"<<endl;
00424                                 bat_loop_time = now_time;
00425                         }
00426 
00427                         //loop_rate1.sleep();
00428 
00429                 }
00430                 if ((ros::ok()) && (!device.IsConnected())) {
00431                         ROS_ERROR("Connection failed!");
00432                         continue;
00433                 }
00434 
00435         }
00436 
00437         spinner.stop();
00438         device.Disconnect();
00439         ROS_INFO("Disconnected");
00440         return 0;
00441 }
00442 


komodo_rover
Author(s): RoboTiCan
autogenerated on Tue Jan 7 2014 11:20:12