with class
Dependencies: ISR_Mini-explorer mbed
Fork of VirtualForces by
MiniExplorerCoimbra.cpp
- Committer:
- Ludwigfr
- Date:
- 2017-06-09
- Revision:
- 34:c208497dd079
- Parent:
- 33:814bcd7d3cfe
- Child:
- 35:68f9edbb3cff
File content as of revision 34:c208497dd079:
#include "MiniExplorerCoimbra.hpp" #include "robot.h" #define PI 3.14159 MiniExplorerCoimbra::MiniExplorerCoimbra(float defaultX, float defaultY, float defaultTheta):map(200,200,20,20),sonarLeft(10*PI/36,-4,4),sonarFront(0,0,5),sonarRight(-10*PI/36,4,4){ i2c1.frequency(100000); initRobot(); //Initializing the robot pc.baud(9600); // baud for the pc communication measure_always_on();//TODO check if needed this->setXYTheta(defaultX,defaultY,defaultTheta); this->radiusWheels=3.25; this->distanceWheels=7.2; this->khro=12; this->ka=30; this->kb=-13; this->kv=200; this->kh=200; this->rangeForce=30; this->attractionConstantForce=10000; this->repulsionConstantForce=1; } void MiniExplorerCoimbra::setXYTheta(float x, float y, float theta){ X=x; Y=y; theta=theta; } //generate a position randomly and makes the robot go there while updating the map void MiniExplorerCoimbra::randomize_and_map() { //TODO check that it's aurelien's work float movementOnX=rand()%(int)(this->map.widthRealMap/2); float movementOnY=rand()%(int)(this->map.heightRealMap/2); float signOfX=rand()%2; if(signOfX < 1) signOfX=-1; float signOfY=rand()%2; if(signOfY < 1) signOfY=-1; float targetX = movementOnX*signOfX; float targetY = movementOnY*signOfY; float targetAngle = 2*((float)(rand()%31416)-15708)/10000.0; this->go_to_point_with_angle(targetX, targetY, targetAngle); } //go the the given position while updating the map void MiniExplorerCoimbra::go_to_point_with_angle(float targetX, float targetY, float targetAngle) { bool keep_going=true; float leftMm; float frontMm; float rightMm; float dt; Timer t; float d2; do { //Timer stuff dt = t.read(); t.reset(); t.start(); //Updating X,Y and theta with the odometry values Odometria(); leftMm = get_distance_left_sensor(); frontMm = get_distance_front_sensor(); rightMm = get_distance_right_sensor(); //if in dangerzone if((frontMm < 150 && frontMm > 0)|| (leftMm <150 && leftMm > 0) || (rightMm <150 && rightMm > 0) ){ leftMotor(1,0); rightMotor(1,0); this->update_sonar_values(leftMm, frontMm, rightMm); //TODO Giorgos maybe you can also test the do_half_flip() function Odometria(); //do a flip TODO keep_going=false; this->do_half_flip(); }else{ //if not in danger zone continue as usual this->update_sonar_values(leftMm, frontMm, rightMm); //Updating motor velocities d2=this->update_angular_speed_wheels_go_to_point_with_angle(targetX,targetY,targetAngle,dt); wait(0.2); //Timer stuff t.stop(); pc.printf("\n\r dist to target= %f",d2); } } while(d2>1 && (abs(targetAngle-theta)>0.01) && keep_going); //Stop at the end leftMotor(1,0); rightMotor(1,0); } void MiniExplorerCoimbra::update_sonar_values(float leftMm,float frontMm,float rightMm){ float xWorldCell; float yWorldCell; float xRobotWorld=this->get_converted_robot_X_into_world(); float yRobotWorld=this->get_converted_robot_Y_into_world(); for(int i=0;i<this->map.nbCellWidth;i++){ for(int j=0;j<this->map.nbCellHeight;j++){ xWorldCell=this->map.cell_width_coordinate_to_world(i); yWorldCell=this->map.cell_height_coordinate_to_world(j); //compute_probability_t(float distanceObstacleDetected, float x, float y, float[2] robotCoordinatesInWorld) this->map.update_cell_value(i,j,this->sonarRight.compute_probability_t(rightMm,xWorldCell,yWorldCell,xRobotWorld,yRobotWorld,theta)); this->map.update_cell_value(i,j,this->sonarLeft.compute_probability_t(leftMm,xWorldCell,yWorldCell,xRobotWorld,yWorldCell,theta)); this->map.update_cell_value(i,j,this->sonarFront.compute_probability_t(frontMm,xWorldCell,yWorldCell,xRobotWorld,yWorldCell,theta)); } } } float MiniExplorerCoimbra::get_converted_robot_X_into_world(){ //x world coordinate return this->map.nbCellWidth*this->map.sizeCellWidth-Y; } float MiniExplorerCoimbra::get_converted_robot_Y_into_world(){ //y worldcoordinate return X; } void MiniExplorerCoimbra::do_half_flip(){ Odometria(); float theta_plus_h_pi=theta+PI/2;//theta is between -PI and PI if(theta_plus_h_pi > PI) theta_plus_h_pi=-(2*PI-theta_plus_h_pi); leftMotor(0,100); rightMotor(1,100); while(abs(theta_plus_h_pi-theta)>0.05){ Odometria(); // pc.printf("\n\r diff=%f", abs(theta_plus_pi-theta)); } leftMotor(1,0); rightMotor(1,0); } //Distance computation function float MiniExplorerCoimbra::dist(float robotX, float robotY, float targetX, float targetY){ //pc.printf("%f, %f, %f, %f",robotX,robotY,targetX,targetY); return sqrt(pow(targetY-robotY,2) + pow(targetX-robotX,2)); } float MiniExplorerCoimbra::update_angular_speed_wheels_go_to_point_with_angle(float targetX, float targetY, float targetAngle, float dt){ //compute_angles_and_distance float alpha = atan2((targetY-Y),(targetX-X))-theta; alpha = atan(sin(alpha)/cos(alpha)); float rho = this->dist(X, Y, targetX, targetY); float d2 = rho; float beta = -alpha-theta+targetAngle; //Computing angle error and distance towards the target value rho += dt*(-this->khro*cos(alpha)*rho); float temp = alpha; alpha += dt*(this->khro*sin(alpha)-this->ka*alpha-this->kb*beta); beta += dt*(-this->khro*sin(temp)); //Computing linear and angular velocities float linear; float angular; if(alpha>=-1.5708 && alpha<=1.5708){ linear=this->khro*rho; angular=this->ka*alpha+this->kb*beta; } else{ linear=-this->khro*rho; angular=-this->ka*alpha-this->kb*beta; } float angular_left=(linear-0.5*this->distanceWheels*angular)/this->radiusWheels; float angular_right=(linear+0.5*this->distanceWheels*angular)/this->radiusWheels; //Normalize speed for motors if(angular_left>angular_right) { angular_right=this->speed*angular_right/angular_left; angular_left=this->speed; } else { angular_left=this->speed*angular_left/angular_right; angular_right=this->speed; } //compute_linear_angular_velocities leftMotor(1,angular_left); rightMotor(1,angular_right); return d2; } void MiniExplorerCoimbra::try_to_reach_target(float targetXWorld,float targetYWorld){ //atan2 gives the angle beetween PI and -PI float angleToTarget=atan2(targetXWorld,targetYWorld); turn_to_target(angleToTarget); bool reached=false; int print=0; while (!reached) { vff(&reached,targetXWorld,targetYWorld); //test_got_to_line(&reached); if(print==10){ leftMotor(1,0); rightMotor(1,0); /* pc.printf("\r\n theta=%f", theta); pc.printf("\r\n IN ORTHO:"); pc.printf("\r\n X Robot=%f", this->get_converted_robot_X_into_world()); pc.printf("\r\n Y Robot=%f", this->get_converted_robot_Y_into_world()); pc.printf("\r\n X Target=%f", targetXWorld); pc.printf("\r\n Y Target=%f", targetYWorld); */ this->print_final_map_with_robot_position_and_target(X,Y,targetXWorld,targetYWorld); print=0; }else print+=1; } //Stop at the end leftMotor(1,0); rightMotor(1,0); pc.printf("\r\n target reached"); wait(3);// } void MiniExplorerCoimbra::vff(bool* reached, float targetXWorld, float targetYWorld){ float line_a; float line_b; float line_c; //Updating X,Y and theta with the odometry values float forceX=0; float forceY=0; //we update the odometrie Odometria(); //we check the sensors float leftMm = get_distance_left_sensor(); float frontMm = get_distance_front_sensor(); float rightMm = get_distance_right_sensor(); //update the probabilities values this->update_sonar_values(leftMm, frontMm, rightMm); //we compute the force on X and Y this->compute_forceX_and_forceY(&forceX, &forceY,targetXWorld,targetYWorld); //we compute a new ine this->calculate_line(forceX, forceY, &line_a,&line_b,&line_c); //Updating motor velocities this->go_to_line(line_a,line_b,line_c,targetXWorld,targetYWorld); //wait(0.1); Odometria(); if(dist(this->get_converted_robot_X_into_world(),this->get_converted_robot_Y_into_world(),targetXWorld,targetYWorld)<10) *reached=true; } //compute the force on X and Y void MiniExplorerCoimbra::compute_forceX_and_forceY(float* forceX, float* forceY, float targetXWorld, float targetYWorld){ float xRobotWorld=this->get_converted_robot_X_into_world(); float yRobotWorld=this->get_converted_robot_Y_into_world(); float forceRepulsionComputedX=0; float forceRepulsionComputedY=0; //for each cell of the map we compute a force of repulsion for(int i=0;i<this->map.nbCellWidth;i++){ for(int j=0;j<this->map.nbCellHeight;j++){ this->update_force(i,j,&forceRepulsionComputedX,&forceRepulsionComputedY,xRobotWorld,yRobotWorld); } } //update with attraction force *forceX=+forceRepulsionComputedX; *forceY=+forceRepulsionComputedY; float distanceTargetRobot=sqrt(pow(targetXWorld-xRobotWorld,2)+pow(targetYWorld-yRobotWorld,2)); if(distanceTargetRobot != 0){ *forceX-=this->attractionConstantForce*(targetXWorld-xRobotWorld)/distanceTargetRobot; *forceY-=this->attractionConstantForce*(targetYWorld-yRobotWorld)/distanceTargetRobot; } float amplitude=sqrt(pow(*forceX,2)+pow(*forceY,2)); if(amplitude!=0){//avoid division by 0 if forceX and forceY == 0 *forceX=*forceX/amplitude; *forceY=*forceY/amplitude; } } void MiniExplorerCoimbra::update_force(int widthIndice, int heightIndice, float* forceX, float* forceY, float xRobotWorld, float yRobotWorld ){ //get the coordonate of the map and the robot in the ortonormal space float xWorldCell=this->map.cell_width_coordinate_to_world(widthIndice); float yWorldCell=this->map.cell_height_coordinate_to_world(heightIndice); //compute the distance beetween the cell and the robot float distanceCellToRobot=sqrt(pow(xWorldCell-xRobotWorld,2)+pow(yWorldCell-yRobotWorld,2)); //check if the cell is in range if(distanceCellToRobot <= this->rangeForce) { float probaCell=this->map.get_proba_cell(widthIndice,heightIndice); float xForceComputed=this->repulsionConstantForce*probaCell*(xWorldCell-xRobotWorld)/pow(distanceCellToRobot,3); float yForceComputed=this->repulsionConstantForce*probaCell*(yWorldCell-yRobotWorld)/pow(distanceCellToRobot,3); *forceX+=xForceComputed; *forceY+=yForceComputed; } } //robotX and robotY are from Odometria, calculate line_a, line_b and line_c void MiniExplorerCoimbra::calculate_line(float forceX, float forceY, float *line_a, float *line_b, float *line_c){ /* in the teachers maths it is *line_a=forceY; *line_b=-forceX; because a*x+b*y+c=0 a impact the vertical and b the horizontal and he has to put them like this because Robot space: orthonormal space: ^ ^ |x |y <- R O -> y x but since our forceX, forceY are already computed in the orthonormal space I m not sure we need to */ *line_a=forceX; *line_b=forceY; //because the line computed always pass by the robot center we dont need lince_c //*line_c=forceX*yRobotWorld+forceY*xRobotWorld; *line_c=0; } //currently line_c is not used void MiniExplorerCoimbra::go_to_line(float line_a, float line_b, float line_c,float targetXWorld, float targetYWorld){ float lineAngle; float angleError; float linear; float angular; bool direction=true; //take as parameter how much the robot is supposed to move on x and y (world) float angleToTarget=atan2(targetXWorld-this->get_converted_robot_X_into_world(),targetYWorld-this->get_converted_robot_Y_into_world()); bool inFront=true; if(angleToTarget < 0)//the target is in front inFront=false; if(theta > 0){ //the robot is oriented to the left if(theta > PI/2){ //the robot is oriented lower left if(inFront) direction=false;//robot and target not oriented the same way }else{ //the robot is oriented upper left if(!inFront) direction=false;//robot and target not oriented the same way } }else{ //the robot is oriented to the right if(theta < -PI/2){ //the robot is oriented lower right if(inFront) direction=false;//robot and target not oriented the same way }else{ //the robot is oriented upper right if(!inFront) direction=false;//robot and target not oriented the same way } } //pc.printf("\r\n Target is in front"); if(line_b!=0){ if(!direction) lineAngle=atan(-line_a/line_b); else lineAngle=atan(line_a/-line_b); } else{ lineAngle=1.5708; } //Computing angle error angleError = lineAngle-theta; angleError = atan(sin(angleError)/cos(angleError)); //Calculating velocities linear=this->kv*(3.1416); angular=this->kh*angleError; float angularLeft=(linear-0.5*this->distanceWheels*angular)/this->radiusWheels; float angularRight=(linear+0.5*this->distanceWheels*angular)/this->radiusWheels; //Normalize speed for motors if(abs(angularLeft)>abs(angularRight)) { angularRight=this->speed*abs(angularRight/angularLeft)*this->sign1(angularRight); angularLeft=this->speed*this->sign1(angularLeft); } else { angularLeft=this->speed*abs(angularLeft/angularRight)*this->sign1(angularLeft); angularRight=this->speed*this->sign1(angularRight); } leftMotor(this->sign2(angularLeft),abs(angularLeft)); rightMotor(this->sign2(angularRight),abs(angularRight)); } //return 1 if positiv, -1 if negativ float MiniExplorerCoimbra::sign1(float value){ if(value>=0) return 1; else return -1; } //return 1 if positiv, 0 if negativ int MiniExplorerCoimbra::sign2(float value){ if(value>=0) return 1; else return 0; } /*angleToTarget is obtained through atan2 so it s: < 0 if the angle is bettween PI and 2pi on a trigo circle > 0 if it is between 0 and PI */ void MiniExplorerCoimbra::turn_to_target(float angleToTarget){ Odometria(); float theta_plus_h_pi=theta+PI/2;//theta is between -PI and PI if(theta_plus_h_pi > PI) theta_plus_h_pi=-(2*PI-theta_plus_h_pi); if(angleToTarget>0){ leftMotor(0,1); rightMotor(1,1); }else{ leftMotor(1,1); rightMotor(0,1); } while(abs(angleToTarget-theta_plus_h_pi)>0.05){ Odometria(); theta_plus_h_pi=theta+PI/2;//theta is between -PI and PI if(theta_plus_h_pi > PI) theta_plus_h_pi=-(2*PI-theta_plus_h_pi); //pc.printf("\n\r diff=%f", abs(angleToTarget-theta_plus_h_pi)); } leftMotor(1,0); rightMotor(1,0); } /* //x and y passed are TargetNotMap float get_error_angles(float x, float y,float theta){ float angleBeetweenRobotAndTarget=atan2(y,x); if(y > 0){ if(angleBeetweenRobotAndTarget < PI/2)//up right angleBeetweenRobotAndTarget=-PI/2+angleBeetweenRobotAndTarget; else//up right angleBeetweenRobotAndTarget=angleBeetweenRobotAndTarget-PI/2; }else{ if(angleBeetweenRobotAndTarget > -PI/2)//lower right angleBeetweenRobotAndTarget=angleBeetweenRobotAndTarget-PI/2; else//lower left angleBeetweenRobotAndTarget=2*PI+angleBeetweenRobotAndTarget-PI/2; } return angleBeetweenRobotAndTarget-theta; } */ void MiniExplorerCoimbra::print_final_map_with_robot_position(float robot_x,float robot_y) { float currProba; float Xrobot=this->map.robot_x_coordinate_in_world(robot_x,robot_y); float Yrobot=this->map.robot_y_coordinate_in_world(robot_x,robot_y); float heightIndiceInOrthonormal; float widthIndiceInOrthonormal; float widthMalus=-(3*this->map.sizeCellWidth/2); float widthBonus=this->map.sizeCellWidth/2; float heightMalus=-(3*this->map.sizeCellHeight/2); float heightBonus=this->map.sizeCellHeight/2; pc.printf("\n\r"); for (int y = this->map.nbCellHeight -1; y>-1; y--) { for (int x= 0; x<this->map.nbCellWidth; x++) { heightIndiceInOrthonormal=this->map.cell_height_coordinate_to_world(y); widthIndiceInOrthonormal=this->map.cell_width_coordinate_to_world(x); if(Yrobot >= (heightIndiceInOrthonormal+heightMalus) && Yrobot <= (heightIndiceInOrthonormal+heightBonus) && Xrobot >= (widthIndiceInOrthonormal+widthMalus) && Xrobot <= (widthIndiceInOrthonormal+widthBonus)) pc.printf(" R "); else{ currProba=this->map.log_to_proba(this->map.cellsLogValues[x][y]); if ( currProba < 0.5) pc.printf(" "); else{ if(currProba==0.5) pc.printf(" . "); else pc.printf(" X "); } } } pc.printf("\n\r"); } } void MiniExplorerCoimbra::print_final_map_with_robot_position_and_target(float robot_x,float robot_y,float targetXWorld, float targetYWorld) { float currProba; float Xrobot=this->map.robot_x_coordinate_in_world(robot_x,robot_y); float Yrobot=this->map.robot_y_coordinate_in_world(robot_x,robot_y); float heightIndiceInOrthonormal; float widthIndiceInOrthonormal; float widthMalus=-(3*this->map.sizeCellWidth/2); float widthBonus=this->map.sizeCellWidth/2; float heightMalus=-(3*this->map.sizeCellHeight/2); float heightBonus=this->map.sizeCellHeight/2; pc.printf("\n\r"); for (int y = this->map.nbCellHeight -1; y>-1; y--) { for (int x= 0; x<this->map.nbCellWidth; x++) { heightIndiceInOrthonormal=this->map.cell_height_coordinate_to_world(y); widthIndiceInOrthonormal=this->map.cell_width_coordinate_to_world(x); if(Yrobot >= (heightIndiceInOrthonormal+heightMalus) && Yrobot <= (heightIndiceInOrthonormal+heightBonus) && Xrobot >= (widthIndiceInOrthonormal+widthMalus) && Xrobot <= (widthIndiceInOrthonormal+widthBonus)) pc.printf(" R "); else{ if(targetYWorld >= (heightIndiceInOrthonormal+heightMalus) && targetYWorld <= (heightIndiceInOrthonormal+heightBonus) && targetXWorld >= (widthIndiceInOrthonormal+widthMalus) && targetXWorld <= (widthIndiceInOrthonormal+widthBonus)) pc.printf(" T "); else{ currProba=this->map.log_to_proba(this->map.cellsLogValues[x][y]); if ( currProba < 0.5) pc.printf(" "); else{ if(currProba==0.5) pc.printf(" . "); else pc.printf(" X "); } } } } pc.printf("\n\r"); } } void MiniExplorerCoimbra::print_final_map() { float currProba; pc.printf("\n\r"); for (int y = this->map.nbCellHeight -1; y>-1; y--) { for (int x= 0; x<this->map.nbCellWidth; x++) { currProba=this->map.log_to_proba(this->map.cellsLogValues[x][y]); if ( currProba < 0.5) { pc.printf(" "); } else { if(currProba==0.5) pc.printf(" . "); else pc.printf(" X "); } } pc.printf("\n\r"); } }