base controller of ackermann autonomous car
Dependencies: QEI PID Motor BNO055 LamborSteering ros_lib_kinetic
main.cpp
- Committer:
- worasuchad
- Date:
- 2019-02-19
- Revision:
- 0:74030c9b6949
File content as of revision 0:74030c9b6949:
/***************************< File comment >***************************/ /* project: Lamborghini's base control */ /* project description : Mobile Robot */ /*--------------------------------------------------------------------*/ /* version : 1.0 */ /* board : NUCLEO-F746ZG */ /* detail : DC motor position control with potentiometer */ /*--------------------------------------------------------------------*/ /* create : 14/12/2018 */ /* programmer : Worasuchad Haomachai */ /**********************************************************************/ /*--------------------------------------------------------------------*/ /* Include file */ /*--------------------------------------------------------------------*/ #include "mbed.h" #include "LamborSteer.h" #include "Motor.h" #include "QEI.h" #include "PID.h" #include "BNO055.h" #include <ros.h> #include <std_msgs/Float64.h> #include <std_msgs/Int32.h> #include <std_msgs/Empty.h> #include <geometry_msgs/Twist.h> Timer timer1; Thread thread1; //Steering thread Thread thread2; //Drive thread Serial pc(USBTX, USBRX); //Serial Port Serial mc(PD_5, PD_6); //Tx Rx Serial Port BNO055 imu(I2C_SDA,I2C_SCL); ros::NodeHandle nh; /* Steering */ LamborSteer lamborSteer(PE_15, PE_14, PE_12, A4, 0); //InA, InB, PWM, Potentiometer, Offset InterruptIn mybutton1(D13); InterruptIn mybutton2(D12); servo_status current_status; /* Drive */ Motor leftMotor(D6, D7, D8); //pwm, inA, inB Motor rightMotor(D9, D10, D11); //pwm, inB, inA QEI leftQei(D2, D3, NC, 200); //chanA, chanB, index, ppr (19600 = 200*98) QEI rightQei(D4, D5, NC, 200); //chanB, chanA, index, ppr PID leftPid(0.4312, 0.01, 0.0, 0.01); //Kc, Ti, Td PID rightPid(0.4312, 0.01, 0.0, 0.01); //Kc, Ti, Td /* Sonic */ AnalogIn pinL(A2); AnalogIn pinR(A1); /* LED */ DigitalOut led1(LED1); DigitalOut led2(LED2); DigitalOut led3(LED3); /*--------------------------------------------------------------------*/ /* Global variable */ /*--------------------------------------------------------------------*/ int angle; int forward = 1; bool cmDrive = true; char cmSteer; /* Drive */ int leftPulses = 0; //How far the left wheel has travelled. int leftPrevPulses = 0; //The previous reading of how far the left wheel has travelled. float leftVelocity = 0.0; //The velocity of the left wheel in pulses per second. int rightPulses = 0; //How far the right wheel has travelled. int rightPrevPulses = 0; //The previous reading of how far the right wheelhas travelled. float rightVelocity = 0.0; //The velocity of the right wheel in pulses persecond. int numTick = 0; int norTick = 0; /* ros */ float g_req_linear_vel_x = 0; float g_req_linear_vel_y = 0; float g_req_angular_vel_z = 0; float servo_steering_angle; float delta_st; /* Timer variable */ uint32_t watchTimer; uint32_t pubTimer; // ros uint32_t g_prev_command_time; uint32_t prev_control_time; #define MAX_STEERING_ANGLE 1.6 // max steering angle. This only applies to Ackermann steering #define PI 3.14 // ultrasonic float voltL, voltR, inches, mm, cmL, cmR, cmM; // IMU float yaw_angle = 0.0; float init_yaw = 0.0; bool yawFirst = true; float change_yaw = 0.0; float cYaw = 0.0; /*--------------------------------------------------------------------*/ /* prototype fun */ /*--------------------------------------------------------------------*/ void pressed(); void watchSteer(); void Drive(); void Ultrasonic(); void IMU(); //ros void commandCallback(const geometry_msgs::Twist& cmd_msg); void moveBase(); float steer(float steering_angle); float mapFloat(float x, float in_min, float in_max, float out_min, float out_max); ros::Subscriber<geometry_msgs::Twist> cmd_sub("cmd_vel", commandCallback); std_msgs::Float64 Tick; ros::Publisher Encoder("encTick", &Tick); std_msgs::Float64 ackermann; ros::Publisher Steer("steerPub", &ackermann); std_msgs::Float64 yaw; ros::Publisher pub_yaw("eulerYaw", &yaw); /*--------------------------------------------------------------------*/ /* main */ /*--------------------------------------------------------------------*/ int main() { pc.baud(115200); mc.baud(115200); nh.initNode(); nh.subscribe(cmd_sub); nh.advertise(Encoder); nh.advertise(Steer); nh.advertise(pub_yaw); /* IMU reset */ imu.reset(); /* Steering */ mybutton1.fall(&pressed); mybutton2.fall(&pressed); current_status = lamborSteer.read(); angle = current_status.current_angle; lamborSteer.write(angle); /* Drive */ leftMotor.period(0.00005); //Set motor PWM periods to 20KHz. rightMotor.period(0.00005); leftPid.setInputLimits(0, 50000); leftPid.setOutputLimits(0.0, 1.0); leftPid.setMode(AUTO_MODE); rightPid.setInputLimits(0, 50000); rightPid.setOutputLimits(0.0, 1.0); rightPid.setMode(AUTO_MODE); //Velocity to mantain in pulses per second. leftPid.setSetPoint(19600); rightPid.setSetPoint(19600); timer1.start(); // start timer counting thread1.start(watchSteer); // Steering thread start thread2.start(Drive); // Drive thread start /* IMU Check */ if (!imu.check()) { while (true) { led3 = !led3; wait(0.1); } } prev_control_time = timer1.read_ms(); while(1) { IMU(); // IMU func if ((timer1.read_ms() - prev_control_time) >= 1) // read time in 1 ms { moveBase(); //mc.printf("%.3f\t\t", g_req_linear_vel_x); //mc.printf("%.3f\t\t", g_req_angular_vel_z); //mc.printf("%.3f\n\r", servo_steering_angle*2); if(change_yaw < -5 && change_yaw > -300) { cYaw = 80; } else if(change_yaw < -300) { cYaw = -80; } else { cYaw = 0; } lamborSteer.write(servo_steering_angle*2 + 80 + cYaw); forward = g_req_linear_vel_x/2; prev_control_time = timer1.read_ms(); } //IMU(); // IMU func Ultrasonic(); // Ultrasonic func nh.spinOnce(); wait_ms(1); } } /*--------------------------------------------------------------------*/ /* IMU */ /*--------------------------------------------------------------------*/ void IMU() { imu.setmode(OPERATION_MODE_NDOF); imu.get_calib(); imu.get_angles(); imu.get_temp(); imu.get_quat(); if(yawFirst) { init_yaw = imu.euler.yaw; yawFirst = false; } yaw_angle = imu.euler.yaw; change_yaw = init_yaw - yaw_angle; yaw.data = yaw_angle; pub_yaw.publish(&yaw); //pc.printf("Temperature\t%d\n", imu.temperature); //pc.printf("Quaternion: w:%f\tx:%f\ty:%f\tz:%f\n", imu.quat.w, imu.quat.x, imu.quat.y, imu.quat.z); //pc.printf("%u\t roll:%5.1f\t pitch:%5.1f\t yaw:%5.1f\r\n",imu.calib,imu.euler.roll,imu.euler.pitch,imu.euler.yaw); } /*--------------------------------------------------------------------*/ /* Ultrasonic */ /*--------------------------------------------------------------------*/ void Ultrasonic() { mc.printf("sonic!"); voltL = pinL; voltR = pinR; cmL = voltL * 2000; //Takes bit count and converts it to mm cmR = voltR * 2000; //mc.printf("Distance L = %.3f cm\t\t", cmL); //mc.printf("Distance R = %.3f cm\n\r", cmR); if (cmL < 80 || cmR < 80 ) { led1 = 1 ; // LED is ON led2 = 1 ; //led3 = 1 ; forward = 0; //leftMotor.brake(); //rightMotor.brake(); } else { led1 = 0 ; // LED is OFF led2 = 0 ; //led3 = 0 ; } //wait(0.1); } /*--------------------------------------------------------------------*/ /* checkST */ /*--------------------------------------------------------------------*/ void watchSteer() { watchTimer = timer1.read_ms(); pubTimer = timer1.read_ms(); while(1) { //lamborSteer.timer_int_routine(); if ((timer1.read_ms() - watchTimer) >= 10) // read time in 10 ms { current_status = lamborSteer.read(); //mc.printf("%d\t\t", current_status.current_angle); //mc.printf("%d\t\t", current_status.wanted_angle); //mc.printf("%d\n\r", current_status.speed); lamborSteer.timer_int_routine(); /* if(current_status.current_angle < 200) { delta_st = -( (-PI * current_status.current_angle / 400) + (PI / 2) ); } else if(current_status.current_angle >= 200) { delta_st = current_status.current_angle - 200; delta_st = PI*delta_st / 400; } ackermann.data = delta_st; Steer.publish( &ackermann ); */ watchTimer = timer1.read_ms(); } /***********************/ if((timer1.read_ms() - pubTimer) >= 100) // read time in 100 ms { if(current_status.current_angle < 200) { delta_st = -( (-PI * current_status.current_angle / 400) + (PI / 2) ); } else if(current_status.current_angle >= 200) { delta_st = current_status.current_angle - 200; delta_st = PI*delta_st / 400; } ackermann.data = delta_st; Steer.publish( &ackermann ); pubTimer = timer1.read_ms(); } /***********************/ } } /*--------------------------------------------------------------------*/ /* Drive */ /*--------------------------------------------------------------------*/ void Drive() { while(1) { if(cmDrive) // Drive if cmDrive = true { leftPulses = leftQei.getPulses(); leftVelocity = (leftPulses - leftPrevPulses) / 0.01; leftPrevPulses = leftPulses; leftPid.setProcessValue(fabs(leftVelocity)); leftMotor.speed(forward*leftPid.compute()); rightPulses = rightQei.getPulses(); rightVelocity = (rightPulses - rightPrevPulses) / 0.01; norTick = rightPulses - rightPrevPulses; // normarlize tick rightPrevPulses = rightPulses; rightPid.setProcessValue(fabs(rightVelocity)); rightMotor.speed(forward*rightPid.compute()); if( norTick >= 196) // 100 tick-per-r { numTick = numTick + 1; Tick.data = numTick; Encoder.publish( &Tick ); norTick = 0; } wait(0.01); } else { leftMotor.brake(); rightMotor.brake(); } } } /*--------------------------------------------------------------------*/ /* pressed */ /*--------------------------------------------------------------------*/ void pressed() { lamborSteer.stop(); } /*--------------------------------------------------------------------*/ /* ros */ /*--------------------------------------------------------------------*/ void commandCallback(const geometry_msgs::Twist& cmd_msg) { //callback function every time linear and angular speed is received from 'cmd_vel' topic //this callback function receives cmd_msg object where linear and angular speed are stored g_req_linear_vel_x = cmd_msg.linear.x; g_req_linear_vel_y = cmd_msg.linear.y; g_req_angular_vel_z = cmd_msg.angular.z; //pc.printf("%.3f\t\t", g_req_linear_vel_x); //pc.printf("%.3f\t\t", g_req_linear_vel_y); //pc.printf("%.3f\n\r", g_req_angular_vel_z); g_prev_command_time = timer1.read_ms();; } void moveBase() { float current_steering_angle; current_steering_angle = steer(g_req_angular_vel_z); //pc.printf("%.3f\t\t", g_req_linear_vel_x); //pc.printf("%.3f\t\t", g_req_linear_vel_y); //pc.printf("%.3f\t\t", g_req_angular_vel_z); //pc.printf("%.3f\n\r", current_steering_angle); } float steer(float steering_angle) { //steering function for ACKERMANN base //float servo_steering_angle; //steering_angle = constrain(steering_angle, -MAX_STEERING_ANGLE, MAX_STEERING_ANGLE); servo_steering_angle = mapFloat(steering_angle, -MAX_STEERING_ANGLE, MAX_STEERING_ANGLE, 0, PI) * (180 / PI); if(servo_steering_angle < 0) { servo_steering_angle = 0; } else if(servo_steering_angle > 200) { servo_steering_angle = 200; } //servo_steering_angle = mapFloat(steering_angle, -MAX_STEERING_ANGLE, MAX_STEERING_ANGLE, PI, 0) * (180 / PI); //steering_servo.write(servo_steering_angle); //return steering_angle; return servo_steering_angle; } float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } /*--------------------------------------------------------------------*/