with class

Dependencies:   ISR_Mini-explorer mbed

Fork of VirtualForces by Georgios Tsamis

Committer:
Ludwigfr
Date:
Thu Jun 15 23:17:55 2017 +0000
Revision:
38:5ed7c79fb724
Parent:
37:b4c45e43ad29
Child:
39:890439b495e3
with some part of lab 4

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Ludwigfr 33:814bcd7d3cfe 1 #include "MiniExplorerCoimbra.hpp"
Ludwigfr 34:c208497dd079 2 #include "robot.h"
Ludwigfr 33:814bcd7d3cfe 3
Ludwigfr 33:814bcd7d3cfe 4 #define PI 3.14159
Ludwigfr 33:814bcd7d3cfe 5
Ludwigfr 38:5ed7c79fb724 6 MiniExplorerCoimbra::MiniExplorerCoimbra(float defaultXWorld, float defaultYWorld, float defaultThetaWorld):map(200,200,20,20),sonarLeft(10*PI/36,-4,4),sonarFront(0,0,5),sonarRight(-10*PI/36,4,4){
Ludwigfr 34:c208497dd079 7 i2c1.frequency(100000);
Ludwigfr 33:814bcd7d3cfe 8 initRobot(); //Initializing the robot
Ludwigfr 33:814bcd7d3cfe 9 pc.baud(9600); // baud for the pc communication
Ludwigfr 33:814bcd7d3cfe 10
Ludwigfr 33:814bcd7d3cfe 11 measure_always_on();//TODO check if needed
Ludwigfr 33:814bcd7d3cfe 12
Ludwigfr 35:68f9edbb3cff 13 this->setXYThetaAndXYThetaWorld(defaultXWorld,defaultYWorld,defaultThetaWorld);
Ludwigfr 34:c208497dd079 14 this->radiusWheels=3.25;
Ludwigfr 34:c208497dd079 15 this->distanceWheels=7.2;
Ludwigfr 34:c208497dd079 16
Ludwigfr 34:c208497dd079 17 this->khro=12;
Ludwigfr 34:c208497dd079 18 this->ka=30;
Ludwigfr 34:c208497dd079 19 this->kb=-13;
Ludwigfr 34:c208497dd079 20 this->kv=200;
Ludwigfr 34:c208497dd079 21 this->kh=200;
Ludwigfr 33:814bcd7d3cfe 22
Ludwigfr 34:c208497dd079 23 this->rangeForce=30;
Ludwigfr 34:c208497dd079 24 this->attractionConstantForce=10000;
Ludwigfr 34:c208497dd079 25 this->repulsionConstantForce=1;
Ludwigfr 33:814bcd7d3cfe 26 }
Ludwigfr 33:814bcd7d3cfe 27
Ludwigfr 35:68f9edbb3cff 28 void MiniExplorerCoimbra::setXYThetaAndXYThetaWorld(float defaultXWorld, float defaultYWorld, float defaultThetaWorld){
Ludwigfr 35:68f9edbb3cff 29 this->xWorld=defaultXWorld;
Ludwigfr 35:68f9edbb3cff 30 this->yWorld=defaultYWorld;
Ludwigfr 35:68f9edbb3cff 31 this->thetaWorld=defaultThetaWorld;
Ludwigfr 35:68f9edbb3cff 32 X=defaultYWorld;
Ludwigfr 35:68f9edbb3cff 33 Y=-defaultXWorld;
Ludwigfr 35:68f9edbb3cff 34 if(defaultThetaWorld < -PI/2)
Ludwigfr 35:68f9edbb3cff 35 theta=PI/2+PI-defaultThetaWorld;
Ludwigfr 35:68f9edbb3cff 36 else
Ludwigfr 35:68f9edbb3cff 37 theta=defaultThetaWorld-PI/2;
Ludwigfr 35:68f9edbb3cff 38 }
Ludwigfr 35:68f9edbb3cff 39
Ludwigfr 35:68f9edbb3cff 40 void MiniExplorerCoimbra::myOdometria(){
Ludwigfr 35:68f9edbb3cff 41 Odometria();
Ludwigfr 35:68f9edbb3cff 42 this->xWorld=-Y;
Ludwigfr 35:68f9edbb3cff 43 this->yWorld=X;
Ludwigfr 35:68f9edbb3cff 44 if(theta >PI/2)
Ludwigfr 35:68f9edbb3cff 45 this->thetaWorld=-PI+(theta-PI/2);
Ludwigfr 35:68f9edbb3cff 46 else
Ludwigfr 35:68f9edbb3cff 47 this->thetaWorld=theta+PI/2;
Ludwigfr 33:814bcd7d3cfe 48 }
Ludwigfr 33:814bcd7d3cfe 49
Ludwigfr 33:814bcd7d3cfe 50 //generate a position randomly and makes the robot go there while updating the map
Ludwigfr 33:814bcd7d3cfe 51 void MiniExplorerCoimbra::randomize_and_map() {
Ludwigfr 33:814bcd7d3cfe 52 //TODO check that it's aurelien's work
Ludwigfr 33:814bcd7d3cfe 53 float movementOnX=rand()%(int)(this->map.widthRealMap/2);
Ludwigfr 33:814bcd7d3cfe 54 float movementOnY=rand()%(int)(this->map.heightRealMap/2);
Ludwigfr 33:814bcd7d3cfe 55
Ludwigfr 33:814bcd7d3cfe 56 float signOfX=rand()%2;
Ludwigfr 33:814bcd7d3cfe 57 if(signOfX < 1)
Ludwigfr 33:814bcd7d3cfe 58 signOfX=-1;
Ludwigfr 33:814bcd7d3cfe 59 float signOfY=rand()%2;
Ludwigfr 33:814bcd7d3cfe 60 if(signOfY < 1)
Ludwigfr 33:814bcd7d3cfe 61 signOfY=-1;
Ludwigfr 33:814bcd7d3cfe 62
Ludwigfr 35:68f9edbb3cff 63 float targetXWorld = movementOnX*signOfX;
Ludwigfr 35:68f9edbb3cff 64 float targetYWorld = movementOnY*signOfY;
Ludwigfr 35:68f9edbb3cff 65 float targetAngleWorld = 2*((float)(rand()%31416)-15708)/10000.0;
Ludwigfr 35:68f9edbb3cff 66 this->go_to_point_with_angle(targetXWorld, targetYWorld, targetAngleWorld);
Ludwigfr 33:814bcd7d3cfe 67 }
Ludwigfr 33:814bcd7d3cfe 68
Ludwigfr 35:68f9edbb3cff 69 //move of targetXWorld and targetYWorld ending in a targetAngleWorld
Ludwigfr 35:68f9edbb3cff 70 void MiniExplorerCoimbra::go_to_point_with_angle(float targetXWorld, float targetYWorld, float targetAngleWorld) {
Ludwigfr 35:68f9edbb3cff 71 bool keepGoing=true;
Ludwigfr 33:814bcd7d3cfe 72 float leftMm;
Ludwigfr 33:814bcd7d3cfe 73 float frontMm;
Ludwigfr 33:814bcd7d3cfe 74 float rightMm;
Ludwigfr 33:814bcd7d3cfe 75 float dt;
Ludwigfr 33:814bcd7d3cfe 76 Timer t;
Ludwigfr 35:68f9edbb3cff 77 float distanceToTarget;
Ludwigfr 33:814bcd7d3cfe 78 do {
Ludwigfr 33:814bcd7d3cfe 79 //Timer stuff
Ludwigfr 33:814bcd7d3cfe 80 dt = t.read();
Ludwigfr 33:814bcd7d3cfe 81 t.reset();
Ludwigfr 33:814bcd7d3cfe 82 t.start();
Ludwigfr 33:814bcd7d3cfe 83
Ludwigfr 33:814bcd7d3cfe 84 //Updating X,Y and theta with the odometry values
Ludwigfr 35:68f9edbb3cff 85 this->myOdometria();
Ludwigfr 33:814bcd7d3cfe 86 leftMm = get_distance_left_sensor();
Ludwigfr 33:814bcd7d3cfe 87 frontMm = get_distance_front_sensor();
Ludwigfr 33:814bcd7d3cfe 88 rightMm = get_distance_right_sensor();
Ludwigfr 33:814bcd7d3cfe 89
Ludwigfr 33:814bcd7d3cfe 90 //if in dangerzone
Ludwigfr 33:814bcd7d3cfe 91 if((frontMm < 150 && frontMm > 0)|| (leftMm <150 && leftMm > 0) || (rightMm <150 && rightMm > 0) ){
Ludwigfr 33:814bcd7d3cfe 92 leftMotor(1,0);
Ludwigfr 33:814bcd7d3cfe 93 rightMotor(1,0);
Ludwigfr 33:814bcd7d3cfe 94 this->update_sonar_values(leftMm, frontMm, rightMm);
Ludwigfr 33:814bcd7d3cfe 95 //TODO Giorgos maybe you can also test the do_half_flip() function
Ludwigfr 35:68f9edbb3cff 96 this->myOdometria();
Ludwigfr 33:814bcd7d3cfe 97 //do a flip TODO
Ludwigfr 35:68f9edbb3cff 98 keepGoing=false;
Ludwigfr 33:814bcd7d3cfe 99 this->do_half_flip();
Ludwigfr 33:814bcd7d3cfe 100 }else{
Ludwigfr 33:814bcd7d3cfe 101 //if not in danger zone continue as usual
Ludwigfr 33:814bcd7d3cfe 102 this->update_sonar_values(leftMm, frontMm, rightMm);
Ludwigfr 33:814bcd7d3cfe 103
Ludwigfr 33:814bcd7d3cfe 104 //Updating motor velocities
Ludwigfr 35:68f9edbb3cff 105 distanceToTarget=this->update_angular_speed_wheels_go_to_point_with_angle(targetXWorld,targetYWorld,targetAngleWorld,dt);
Ludwigfr 33:814bcd7d3cfe 106
Ludwigfr 33:814bcd7d3cfe 107 wait(0.2);
Ludwigfr 33:814bcd7d3cfe 108 //Timer stuff
Ludwigfr 33:814bcd7d3cfe 109 t.stop();
Ludwigfr 35:68f9edbb3cff 110 pc.printf("\n\r dist to target= %f",distanceToTarget);
Ludwigfr 33:814bcd7d3cfe 111 }
Ludwigfr 35:68f9edbb3cff 112 } while(distanceToTarget>1 && (abs(targetAngleWorld-this->thetaWorld)>0.01) && keepGoing);
Ludwigfr 33:814bcd7d3cfe 113
Ludwigfr 33:814bcd7d3cfe 114 //Stop at the end
Ludwigfr 33:814bcd7d3cfe 115 leftMotor(1,0);
Ludwigfr 33:814bcd7d3cfe 116 rightMotor(1,0);
Ludwigfr 33:814bcd7d3cfe 117 }
Ludwigfr 33:814bcd7d3cfe 118
Ludwigfr 35:68f9edbb3cff 119 float MiniExplorerCoimbra::update_angular_speed_wheels_go_to_point_with_angle(float targetXWorld, float targetYWorld, float targetAngleWorld, float dt){
Ludwigfr 33:814bcd7d3cfe 120 //compute_angles_and_distance
Ludwigfr 35:68f9edbb3cff 121 //atan2 take the deplacement on x and the deplacement on y as parameters
Ludwigfr 35:68f9edbb3cff 122 float angleToPoint = atan2((targetYWorld-this->yWorld),(targetXWorld-this->xWorld))-this->thetaWorld;
Ludwigfr 35:68f9edbb3cff 123 angleToPoint = atan(sin(angleToPoint)/cos(angleToPoint));
Ludwigfr 35:68f9edbb3cff 124 //rho is the distance to the point of arrival
Ludwigfr 35:68f9edbb3cff 125 float rho = dist(targetXWorld,targetYWorld,this->xWorld,this->yWorld);
Ludwigfr 35:68f9edbb3cff 126 float distanceToTarget = rho;
Ludwigfr 35:68f9edbb3cff 127 //TODO check that
Ludwigfr 35:68f9edbb3cff 128 float beta = targetAngleWorld-angleToPoint-this->thetaWorld;
Ludwigfr 33:814bcd7d3cfe 129
Ludwigfr 33:814bcd7d3cfe 130 //Computing angle error and distance towards the target value
Ludwigfr 35:68f9edbb3cff 131 rho += dt*(-this->khro*cos(angleToPoint)*rho);
Ludwigfr 35:68f9edbb3cff 132 float temp = angleToPoint;
Ludwigfr 35:68f9edbb3cff 133 angleToPoint += dt*(this->khro*sin(angleToPoint)-this->ka*angleToPoint-this->kb*beta);
Ludwigfr 33:814bcd7d3cfe 134 beta += dt*(-this->khro*sin(temp));
Ludwigfr 33:814bcd7d3cfe 135
Ludwigfr 33:814bcd7d3cfe 136 //Computing linear and angular velocities
Ludwigfr 33:814bcd7d3cfe 137 float linear;
Ludwigfr 33:814bcd7d3cfe 138 float angular;
Ludwigfr 35:68f9edbb3cff 139 if(angleToPoint>=-1.5708 && angleToPoint<=1.5708){
Ludwigfr 33:814bcd7d3cfe 140 linear=this->khro*rho;
Ludwigfr 35:68f9edbb3cff 141 angular=this->ka*angleToPoint+this->kb*beta;
Ludwigfr 33:814bcd7d3cfe 142 }
Ludwigfr 33:814bcd7d3cfe 143 else{
Ludwigfr 33:814bcd7d3cfe 144 linear=-this->khro*rho;
Ludwigfr 35:68f9edbb3cff 145 angular=-this->ka*angleToPoint-this->kb*beta;
Ludwigfr 33:814bcd7d3cfe 146 }
Ludwigfr 33:814bcd7d3cfe 147 float angular_left=(linear-0.5*this->distanceWheels*angular)/this->radiusWheels;
Ludwigfr 33:814bcd7d3cfe 148 float angular_right=(linear+0.5*this->distanceWheels*angular)/this->radiusWheels;
Ludwigfr 33:814bcd7d3cfe 149
Ludwigfr 33:814bcd7d3cfe 150 //Normalize speed for motors
Ludwigfr 33:814bcd7d3cfe 151 if(angular_left>angular_right) {
Ludwigfr 33:814bcd7d3cfe 152 angular_right=this->speed*angular_right/angular_left;
Ludwigfr 33:814bcd7d3cfe 153 angular_left=this->speed;
Ludwigfr 33:814bcd7d3cfe 154 } else {
Ludwigfr 33:814bcd7d3cfe 155 angular_left=this->speed*angular_left/angular_right;
Ludwigfr 33:814bcd7d3cfe 156 angular_right=this->speed;
Ludwigfr 33:814bcd7d3cfe 157 }
Ludwigfr 33:814bcd7d3cfe 158
Ludwigfr 33:814bcd7d3cfe 159 //compute_linear_angular_velocities
Ludwigfr 33:814bcd7d3cfe 160 leftMotor(1,angular_left);
Ludwigfr 33:814bcd7d3cfe 161 rightMotor(1,angular_right);
Ludwigfr 33:814bcd7d3cfe 162
Ludwigfr 35:68f9edbb3cff 163 return distanceToTarget;
Ludwigfr 33:814bcd7d3cfe 164 }
Ludwigfr 33:814bcd7d3cfe 165
Ludwigfr 35:68f9edbb3cff 166 void MiniExplorerCoimbra::update_sonar_values(float leftMm,float frontMm,float rightMm){
Ludwigfr 35:68f9edbb3cff 167 float xWorldCell;
Ludwigfr 35:68f9edbb3cff 168 float yWorldCell;
Ludwigfr 35:68f9edbb3cff 169 for(int i=0;i<this->map.nbCellWidth;i++){
Ludwigfr 35:68f9edbb3cff 170 for(int j=0;j<this->map.nbCellHeight;j++){
Ludwigfr 35:68f9edbb3cff 171 xWorldCell=this->map.cell_width_coordinate_to_world(i);
Ludwigfr 35:68f9edbb3cff 172 yWorldCell=this->map.cell_height_coordinate_to_world(j);
Ludwigfr 38:5ed7c79fb724 173 this->map.update_cell_value(i,j,this->sonarRight.compute_probability_t(rightMm/10,xWorldCell,yWorldCell,this->xWorld,this->yWorld,this->thetaWorld));
Ludwigfr 38:5ed7c79fb724 174 this->map.update_cell_value(i,j,this->sonarLeft.compute_probability_t(leftMm/10,xWorldCell,yWorldCell,this->xWorld,this->yWorld,this->thetaWorld));
Ludwigfr 38:5ed7c79fb724 175 this->map.update_cell_value(i,j,this->sonarFront.compute_probability_t(frontMm/10,xWorldCell,yWorldCell,this->xWorld,this->yWorld,this->thetaWorld));
Ludwigfr 35:68f9edbb3cff 176
Ludwigfr 35:68f9edbb3cff 177 }
Ludwigfr 35:68f9edbb3cff 178 }
Ludwigfr 35:68f9edbb3cff 179 }
Ludwigfr 35:68f9edbb3cff 180
Ludwigfr 35:68f9edbb3cff 181 //Distance computation function
Ludwigfr 35:68f9edbb3cff 182 float MiniExplorerCoimbra::dist(float x1, float y1, float x2, float y2){
Ludwigfr 35:68f9edbb3cff 183 return sqrt(pow(y2-y1,2) + pow(x2-x1,2));
Ludwigfr 35:68f9edbb3cff 184 }
Ludwigfr 35:68f9edbb3cff 185
Ludwigfr 35:68f9edbb3cff 186 //use virtual force field
Ludwigfr 33:814bcd7d3cfe 187 void MiniExplorerCoimbra::try_to_reach_target(float targetXWorld,float targetYWorld){
Ludwigfr 33:814bcd7d3cfe 188 //atan2 gives the angle beetween PI and -PI
Ludwigfr 35:68f9edbb3cff 189 this->myOdometria();
Ludwigfr 35:68f9edbb3cff 190 /*
Ludwigfr 35:68f9edbb3cff 191 float deplacementOnXWorld=targetXWorld-this->xWorld;
Ludwigfr 35:68f9edbb3cff 192 float deplacementOnYWorld=targetYWorld-this->yWorld;
Ludwigfr 35:68f9edbb3cff 193 */
Ludwigfr 37:b4c45e43ad29 194 float angleToTarget=atan2(targetYWorld-this->yWorld,targetXWorld-this->xWorld);
Ludwigfr 33:814bcd7d3cfe 195 turn_to_target(angleToTarget);
Ludwigfr 33:814bcd7d3cfe 196 bool reached=false;
Ludwigfr 33:814bcd7d3cfe 197 int print=0;
Ludwigfr 33:814bcd7d3cfe 198 while (!reached) {
Ludwigfr 33:814bcd7d3cfe 199 vff(&reached,targetXWorld,targetYWorld);
Ludwigfr 33:814bcd7d3cfe 200 //test_got_to_line(&reached);
Ludwigfr 33:814bcd7d3cfe 201 if(print==10){
Ludwigfr 33:814bcd7d3cfe 202 leftMotor(1,0);
Ludwigfr 33:814bcd7d3cfe 203 rightMotor(1,0);
Ludwigfr 37:b4c45e43ad29 204 this->print_map_with_robot_position_and_target(targetXWorld,targetYWorld);
Ludwigfr 33:814bcd7d3cfe 205 print=0;
Ludwigfr 33:814bcd7d3cfe 206 }else
Ludwigfr 33:814bcd7d3cfe 207 print+=1;
Ludwigfr 33:814bcd7d3cfe 208 }
Ludwigfr 33:814bcd7d3cfe 209 //Stop at the end
Ludwigfr 33:814bcd7d3cfe 210 leftMotor(1,0);
Ludwigfr 33:814bcd7d3cfe 211 rightMotor(1,0);
Ludwigfr 33:814bcd7d3cfe 212 pc.printf("\r\n target reached");
Ludwigfr 33:814bcd7d3cfe 213 wait(3);//
Ludwigfr 33:814bcd7d3cfe 214 }
Ludwigfr 33:814bcd7d3cfe 215
Ludwigfr 33:814bcd7d3cfe 216 void MiniExplorerCoimbra::vff(bool* reached, float targetXWorld, float targetYWorld){
Ludwigfr 33:814bcd7d3cfe 217 float line_a;
Ludwigfr 33:814bcd7d3cfe 218 float line_b;
Ludwigfr 33:814bcd7d3cfe 219 float line_c;
Ludwigfr 33:814bcd7d3cfe 220 //Updating X,Y and theta with the odometry values
Ludwigfr 35:68f9edbb3cff 221 float forceXWorld=0;
Ludwigfr 35:68f9edbb3cff 222 float forceYWorld=0;
Ludwigfr 33:814bcd7d3cfe 223 //we update the odometrie
Ludwigfr 35:68f9edbb3cff 224 this->myOdometria();
Ludwigfr 33:814bcd7d3cfe 225 //we check the sensors
Ludwigfr 33:814bcd7d3cfe 226 float leftMm = get_distance_left_sensor();
Ludwigfr 33:814bcd7d3cfe 227 float frontMm = get_distance_front_sensor();
Ludwigfr 33:814bcd7d3cfe 228 float rightMm = get_distance_right_sensor();
Ludwigfr 33:814bcd7d3cfe 229 //update the probabilities values
Ludwigfr 33:814bcd7d3cfe 230 this->update_sonar_values(leftMm, frontMm, rightMm);
Ludwigfr 33:814bcd7d3cfe 231 //we compute the force on X and Y
Ludwigfr 35:68f9edbb3cff 232 this->compute_forceX_and_forceY(&forceXWorld, &forceYWorld,targetXWorld,targetYWorld);
Ludwigfr 33:814bcd7d3cfe 233 //we compute a new ine
Ludwigfr 35:68f9edbb3cff 234 this->calculate_line(forceXWorld, forceYWorld, &line_a,&line_b,&line_c);
Ludwigfr 33:814bcd7d3cfe 235 //Updating motor velocities
Ludwigfr 33:814bcd7d3cfe 236 this->go_to_line(line_a,line_b,line_c,targetXWorld,targetYWorld);
Ludwigfr 33:814bcd7d3cfe 237
Ludwigfr 33:814bcd7d3cfe 238 //wait(0.1);
Ludwigfr 35:68f9edbb3cff 239 this->myOdometria();
Ludwigfr 35:68f9edbb3cff 240 if(dist(this->xWorld,this->yWorld,targetXWorld,targetYWorld)<10)
Ludwigfr 33:814bcd7d3cfe 241 *reached=true;
Ludwigfr 33:814bcd7d3cfe 242 }
Ludwigfr 33:814bcd7d3cfe 243
Ludwigfr 33:814bcd7d3cfe 244 //compute the force on X and Y
Ludwigfr 35:68f9edbb3cff 245 void MiniExplorerCoimbra::compute_forceX_and_forceY(float* forceXWorld, float* forceYWorld, float targetXWorld, float targetYWorld){
Ludwigfr 33:814bcd7d3cfe 246 float forceRepulsionComputedX=0;
Ludwigfr 33:814bcd7d3cfe 247 float forceRepulsionComputedY=0;
Ludwigfr 33:814bcd7d3cfe 248 //for each cell of the map we compute a force of repulsion
Ludwigfr 33:814bcd7d3cfe 249 for(int i=0;i<this->map.nbCellWidth;i++){
Ludwigfr 33:814bcd7d3cfe 250 for(int j=0;j<this->map.nbCellHeight;j++){
Ludwigfr 35:68f9edbb3cff 251 this->update_force(i,j,&forceRepulsionComputedX,&forceRepulsionComputedY);
Ludwigfr 33:814bcd7d3cfe 252 }
Ludwigfr 33:814bcd7d3cfe 253 }
Ludwigfr 33:814bcd7d3cfe 254 //update with attraction force
Ludwigfr 35:68f9edbb3cff 255 *forceXWorld=+forceRepulsionComputedX;
Ludwigfr 35:68f9edbb3cff 256 *forceYWorld=+forceRepulsionComputedY;
Ludwigfr 35:68f9edbb3cff 257 float distanceTargetRobot=sqrt(pow(targetXWorld-this->xWorld,2)+pow(targetYWorld-this->yWorld,2));
Ludwigfr 33:814bcd7d3cfe 258 if(distanceTargetRobot != 0){
Ludwigfr 35:68f9edbb3cff 259 *forceXWorld-=this->attractionConstantForce*(targetXWorld-this->xWorld)/distanceTargetRobot;
Ludwigfr 35:68f9edbb3cff 260 *forceYWorld-=this->attractionConstantForce*(targetYWorld-this->yWorld)/distanceTargetRobot;
Ludwigfr 33:814bcd7d3cfe 261 }
Ludwigfr 35:68f9edbb3cff 262 float amplitude=sqrt(pow(*forceXWorld,2)+pow(*forceYWorld,2));
Ludwigfr 33:814bcd7d3cfe 263 if(amplitude!=0){//avoid division by 0 if forceX and forceY == 0
Ludwigfr 35:68f9edbb3cff 264 *forceXWorld=*forceXWorld/amplitude;
Ludwigfr 35:68f9edbb3cff 265 *forceYWorld=*forceYWorld/amplitude;
Ludwigfr 33:814bcd7d3cfe 266 }
Ludwigfr 33:814bcd7d3cfe 267 }
Ludwigfr 33:814bcd7d3cfe 268
Ludwigfr 35:68f9edbb3cff 269 void MiniExplorerCoimbra::update_force(int widthIndice, int heightIndice, float* forceRepulsionComputedX, float* forceRepulsionComputedY ){
Ludwigfr 33:814bcd7d3cfe 270 //get the coordonate of the map and the robot in the ortonormal space
Ludwigfr 33:814bcd7d3cfe 271 float xWorldCell=this->map.cell_width_coordinate_to_world(widthIndice);
Ludwigfr 33:814bcd7d3cfe 272 float yWorldCell=this->map.cell_height_coordinate_to_world(heightIndice);
Ludwigfr 33:814bcd7d3cfe 273 //compute the distance beetween the cell and the robot
Ludwigfr 35:68f9edbb3cff 274 float distanceCellToRobot=sqrt(pow(xWorldCell-this->xWorld,2)+pow(yWorldCell-this->yWorld,2));
Ludwigfr 33:814bcd7d3cfe 275 //check if the cell is in range
Ludwigfr 33:814bcd7d3cfe 276 if(distanceCellToRobot <= this->rangeForce) {
Ludwigfr 33:814bcd7d3cfe 277 float probaCell=this->map.get_proba_cell(widthIndice,heightIndice);
Ludwigfr 35:68f9edbb3cff 278 *forceRepulsionComputedX+=this->repulsionConstantForce*probaCell*(xWorldCell-this->xWorld)/pow(distanceCellToRobot,3);
Ludwigfr 35:68f9edbb3cff 279 *forceRepulsionComputedY+=this->repulsionConstantForce*probaCell*(yWorldCell-this->yWorld)/pow(distanceCellToRobot,3);
Ludwigfr 33:814bcd7d3cfe 280 }
Ludwigfr 33:814bcd7d3cfe 281 }
Ludwigfr 33:814bcd7d3cfe 282
Ludwigfr 35:68f9edbb3cff 283 //robotX and robotY are from this->myOdometria(), calculate line_a, line_b and line_c
Ludwigfr 33:814bcd7d3cfe 284 void MiniExplorerCoimbra::calculate_line(float forceX, float forceY, float *line_a, float *line_b, float *line_c){
Ludwigfr 33:814bcd7d3cfe 285 /*
Ludwigfr 33:814bcd7d3cfe 286 in the teachers maths it is
Ludwigfr 33:814bcd7d3cfe 287
Ludwigfr 33:814bcd7d3cfe 288 *line_a=forceY;
Ludwigfr 33:814bcd7d3cfe 289 *line_b=-forceX;
Ludwigfr 33:814bcd7d3cfe 290
Ludwigfr 33:814bcd7d3cfe 291 because a*x+b*y+c=0
Ludwigfr 33:814bcd7d3cfe 292 a impact the vertical and b the horizontal
Ludwigfr 33:814bcd7d3cfe 293 and he has to put them like this because
Ludwigfr 35:68f9edbb3cff 294 Robot space: World space:
Ludwigfr 33:814bcd7d3cfe 295 ^ ^
Ludwigfr 33:814bcd7d3cfe 296 |x |y
Ludwigfr 33:814bcd7d3cfe 297 <- R O ->
Ludwigfr 33:814bcd7d3cfe 298 y x
Ludwigfr 33:814bcd7d3cfe 299 but since our forceX, forceY are already computed in the orthonormal space I m not sure we need to
Ludwigfr 33:814bcd7d3cfe 300 */
Ludwigfr 33:814bcd7d3cfe 301 *line_a=forceX;
Ludwigfr 33:814bcd7d3cfe 302 *line_b=forceY;
Ludwigfr 33:814bcd7d3cfe 303 //because the line computed always pass by the robot center we dont need lince_c
Ludwigfr 35:68f9edbb3cff 304 //*line_c=forceX*this->yWorld+forceY*this->xWorld;
Ludwigfr 33:814bcd7d3cfe 305 *line_c=0;
Ludwigfr 33:814bcd7d3cfe 306 }
Ludwigfr 33:814bcd7d3cfe 307
Ludwigfr 33:814bcd7d3cfe 308 //currently line_c is not used
Ludwigfr 33:814bcd7d3cfe 309 void MiniExplorerCoimbra::go_to_line(float line_a, float line_b, float line_c,float targetXWorld, float targetYWorld){
Ludwigfr 33:814bcd7d3cfe 310 float lineAngle;
Ludwigfr 33:814bcd7d3cfe 311 float angleError;
Ludwigfr 33:814bcd7d3cfe 312 float linear;
Ludwigfr 33:814bcd7d3cfe 313 float angular;
Ludwigfr 33:814bcd7d3cfe 314
Ludwigfr 35:68f9edbb3cff 315 //atan2 use the deplacement on X and the deplacement on Y
Ludwigfr 37:b4c45e43ad29 316 float angleToTarget=atan2(targetYWorld-this->yWorld,targetXWorld-this->xWorld);
Ludwigfr 35:68f9edbb3cff 317 bool aligned=false;
Ludwigfr 35:68f9edbb3cff 318
Ludwigfr 35:68f9edbb3cff 319 //this condition is passed if the target is in the same direction as the robot orientation
Ludwigfr 35:68f9edbb3cff 320 if((angleToTarget>0 && this->thetaWorld > 0) || (angleToTarget<0 && this->thetaWorld <0))
Ludwigfr 35:68f9edbb3cff 321 aligned=true;
Ludwigfr 33:814bcd7d3cfe 322
Ludwigfr 35:68f9edbb3cff 323 //line angle is beetween pi/2 and -pi/2
Ludwigfr 35:68f9edbb3cff 324 /*
Ludwigfr 35:68f9edbb3cff 325 ax+by+c=0 (here c==0)
Ludwigfr 35:68f9edbb3cff 326 y=x*-a/b
Ludwigfr 35:68f9edbb3cff 327 if a*b > 0, the robot is going down
Ludwigfr 35:68f9edbb3cff 328 if a*b <0, the robot is going up
Ludwigfr 35:68f9edbb3cff 329 */
Ludwigfr 33:814bcd7d3cfe 330 if(line_b!=0){
Ludwigfr 35:68f9edbb3cff 331 if(!aligned)
Ludwigfr 33:814bcd7d3cfe 332 lineAngle=atan(-line_a/line_b);
Ludwigfr 33:814bcd7d3cfe 333 else
Ludwigfr 33:814bcd7d3cfe 334 lineAngle=atan(line_a/-line_b);
Ludwigfr 33:814bcd7d3cfe 335 }
Ludwigfr 33:814bcd7d3cfe 336 else{
Ludwigfr 33:814bcd7d3cfe 337 lineAngle=1.5708;
Ludwigfr 33:814bcd7d3cfe 338 }
Ludwigfr 33:814bcd7d3cfe 339
Ludwigfr 33:814bcd7d3cfe 340 //Computing angle error
Ludwigfr 35:68f9edbb3cff 341 angleError = lineAngle-this->thetaWorld;//TODO that I m not sure
Ludwigfr 33:814bcd7d3cfe 342 angleError = atan(sin(angleError)/cos(angleError));
Ludwigfr 33:814bcd7d3cfe 343
Ludwigfr 33:814bcd7d3cfe 344 //Calculating velocities
Ludwigfr 33:814bcd7d3cfe 345 linear=this->kv*(3.1416);
Ludwigfr 33:814bcd7d3cfe 346 angular=this->kh*angleError;
Ludwigfr 33:814bcd7d3cfe 347
Ludwigfr 33:814bcd7d3cfe 348 float angularLeft=(linear-0.5*this->distanceWheels*angular)/this->radiusWheels;
Ludwigfr 33:814bcd7d3cfe 349 float angularRight=(linear+0.5*this->distanceWheels*angular)/this->radiusWheels;
Ludwigfr 33:814bcd7d3cfe 350
Ludwigfr 33:814bcd7d3cfe 351 //Normalize speed for motors
Ludwigfr 33:814bcd7d3cfe 352 if(abs(angularLeft)>abs(angularRight)) {
Ludwigfr 33:814bcd7d3cfe 353 angularRight=this->speed*abs(angularRight/angularLeft)*this->sign1(angularRight);
Ludwigfr 33:814bcd7d3cfe 354 angularLeft=this->speed*this->sign1(angularLeft);
Ludwigfr 33:814bcd7d3cfe 355 }
Ludwigfr 33:814bcd7d3cfe 356 else {
Ludwigfr 33:814bcd7d3cfe 357 angularLeft=this->speed*abs(angularLeft/angularRight)*this->sign1(angularLeft);
Ludwigfr 33:814bcd7d3cfe 358 angularRight=this->speed*this->sign1(angularRight);
Ludwigfr 33:814bcd7d3cfe 359 }
Ludwigfr 33:814bcd7d3cfe 360 leftMotor(this->sign2(angularLeft),abs(angularLeft));
Ludwigfr 33:814bcd7d3cfe 361 rightMotor(this->sign2(angularRight),abs(angularRight));
Ludwigfr 33:814bcd7d3cfe 362 }
Ludwigfr 33:814bcd7d3cfe 363
Ludwigfr 33:814bcd7d3cfe 364 /*angleToTarget is obtained through atan2 so it s:
Ludwigfr 33:814bcd7d3cfe 365 < 0 if the angle is bettween PI and 2pi on a trigo circle
Ludwigfr 33:814bcd7d3cfe 366 > 0 if it is between 0 and PI
Ludwigfr 33:814bcd7d3cfe 367 */
Ludwigfr 33:814bcd7d3cfe 368 void MiniExplorerCoimbra::turn_to_target(float angleToTarget){
Ludwigfr 35:68f9edbb3cff 369 this->myOdometria();
Ludwigfr 33:814bcd7d3cfe 370 float theta_plus_h_pi=theta+PI/2;//theta is between -PI and PI
Ludwigfr 33:814bcd7d3cfe 371 if(theta_plus_h_pi > PI)
Ludwigfr 33:814bcd7d3cfe 372 theta_plus_h_pi=-(2*PI-theta_plus_h_pi);
Ludwigfr 33:814bcd7d3cfe 373 if(angleToTarget>0){
Ludwigfr 33:814bcd7d3cfe 374 leftMotor(0,1);
Ludwigfr 33:814bcd7d3cfe 375 rightMotor(1,1);
Ludwigfr 33:814bcd7d3cfe 376 }else{
Ludwigfr 33:814bcd7d3cfe 377 leftMotor(1,1);
Ludwigfr 33:814bcd7d3cfe 378 rightMotor(0,1);
Ludwigfr 33:814bcd7d3cfe 379 }
Ludwigfr 33:814bcd7d3cfe 380 while(abs(angleToTarget-theta_plus_h_pi)>0.05){
Ludwigfr 35:68f9edbb3cff 381 this->myOdometria();
Ludwigfr 33:814bcd7d3cfe 382 theta_plus_h_pi=theta+PI/2;//theta is between -PI and PI
Ludwigfr 33:814bcd7d3cfe 383 if(theta_plus_h_pi > PI)
Ludwigfr 33:814bcd7d3cfe 384 theta_plus_h_pi=-(2*PI-theta_plus_h_pi);
Ludwigfr 33:814bcd7d3cfe 385 //pc.printf("\n\r diff=%f", abs(angleToTarget-theta_plus_h_pi));
Ludwigfr 33:814bcd7d3cfe 386 }
Ludwigfr 33:814bcd7d3cfe 387 leftMotor(1,0);
Ludwigfr 33:814bcd7d3cfe 388 rightMotor(1,0);
Ludwigfr 33:814bcd7d3cfe 389 }
Ludwigfr 33:814bcd7d3cfe 390
Ludwigfr 35:68f9edbb3cff 391 void MiniExplorerCoimbra::do_half_flip(){
Ludwigfr 35:68f9edbb3cff 392 this->myOdometria();
Ludwigfr 35:68f9edbb3cff 393 float theta_plus_h_pi=theta+PI/2;//theta is between -PI and PI
Ludwigfr 35:68f9edbb3cff 394 if(theta_plus_h_pi > PI)
Ludwigfr 35:68f9edbb3cff 395 theta_plus_h_pi=-(2*PI-theta_plus_h_pi);
Ludwigfr 35:68f9edbb3cff 396 leftMotor(0,100);
Ludwigfr 35:68f9edbb3cff 397 rightMotor(1,100);
Ludwigfr 35:68f9edbb3cff 398 while(abs(theta_plus_h_pi-theta)>0.05){
Ludwigfr 35:68f9edbb3cff 399 this->myOdometria();
Ludwigfr 35:68f9edbb3cff 400 // pc.printf("\n\r diff=%f", abs(theta_plus_pi-theta));
Ludwigfr 35:68f9edbb3cff 401 }
Ludwigfr 35:68f9edbb3cff 402 leftMotor(1,0);
Ludwigfr 35:68f9edbb3cff 403 rightMotor(1,0);
Ludwigfr 35:68f9edbb3cff 404 }
Ludwigfr 33:814bcd7d3cfe 405
Ludwigfr 37:b4c45e43ad29 406 void MiniExplorerCoimbra::print_map_with_robot_position() {
Ludwigfr 34:c208497dd079 407 float currProba;
Ludwigfr 34:c208497dd079 408
Ludwigfr 34:c208497dd079 409 float heightIndiceInOrthonormal;
Ludwigfr 34:c208497dd079 410 float widthIndiceInOrthonormal;
Ludwigfr 34:c208497dd079 411
Ludwigfr 34:c208497dd079 412 float widthMalus=-(3*this->map.sizeCellWidth/2);
Ludwigfr 34:c208497dd079 413 float widthBonus=this->map.sizeCellWidth/2;
Ludwigfr 34:c208497dd079 414
Ludwigfr 34:c208497dd079 415 float heightMalus=-(3*this->map.sizeCellHeight/2);
Ludwigfr 34:c208497dd079 416 float heightBonus=this->map.sizeCellHeight/2;
Ludwigfr 34:c208497dd079 417
Ludwigfr 34:c208497dd079 418 pc.printf("\n\r");
Ludwigfr 34:c208497dd079 419 for (int y = this->map.nbCellHeight -1; y>-1; y--) {
Ludwigfr 34:c208497dd079 420 for (int x= 0; x<this->map.nbCellWidth; x++) {
Ludwigfr 34:c208497dd079 421 heightIndiceInOrthonormal=this->map.cell_height_coordinate_to_world(y);
Ludwigfr 34:c208497dd079 422 widthIndiceInOrthonormal=this->map.cell_width_coordinate_to_world(x);
Ludwigfr 35:68f9edbb3cff 423 if(this->yWorld >= (heightIndiceInOrthonormal+heightMalus) && this->yWorld <= (heightIndiceInOrthonormal+heightBonus) && this->xWorld >= (widthIndiceInOrthonormal+widthMalus) && this->xWorld <= (widthIndiceInOrthonormal+widthBonus))
Ludwigfr 34:c208497dd079 424 pc.printf(" R ");
Ludwigfr 34:c208497dd079 425 else{
Ludwigfr 34:c208497dd079 426 currProba=this->map.log_to_proba(this->map.cellsLogValues[x][y]);
Ludwigfr 34:c208497dd079 427 if ( currProba < 0.5)
Ludwigfr 34:c208497dd079 428 pc.printf(" ");
Ludwigfr 34:c208497dd079 429 else{
Ludwigfr 34:c208497dd079 430 if(currProba==0.5)
Ludwigfr 34:c208497dd079 431 pc.printf(" . ");
Ludwigfr 34:c208497dd079 432 else
Ludwigfr 34:c208497dd079 433 pc.printf(" X ");
Ludwigfr 34:c208497dd079 434 }
Ludwigfr 34:c208497dd079 435 }
Ludwigfr 34:c208497dd079 436 }
Ludwigfr 34:c208497dd079 437 pc.printf("\n\r");
Ludwigfr 34:c208497dd079 438 }
Ludwigfr 34:c208497dd079 439 }
Ludwigfr 34:c208497dd079 440
Ludwigfr 37:b4c45e43ad29 441 void MiniExplorerCoimbra::print_map_with_robot_position_and_target(float targetXWorld, float targetYWorld) {
Ludwigfr 34:c208497dd079 442 float currProba;
Ludwigfr 34:c208497dd079 443
Ludwigfr 34:c208497dd079 444 float heightIndiceInOrthonormal;
Ludwigfr 34:c208497dd079 445 float widthIndiceInOrthonormal;
Ludwigfr 34:c208497dd079 446
Ludwigfr 34:c208497dd079 447 float widthMalus=-(3*this->map.sizeCellWidth/2);
Ludwigfr 34:c208497dd079 448 float widthBonus=this->map.sizeCellWidth/2;
Ludwigfr 34:c208497dd079 449
Ludwigfr 34:c208497dd079 450 float heightMalus=-(3*this->map.sizeCellHeight/2);
Ludwigfr 34:c208497dd079 451 float heightBonus=this->map.sizeCellHeight/2;
Ludwigfr 34:c208497dd079 452
Ludwigfr 34:c208497dd079 453 pc.printf("\n\r");
Ludwigfr 34:c208497dd079 454 for (int y = this->map.nbCellHeight -1; y>-1; y--) {
Ludwigfr 34:c208497dd079 455 for (int x= 0; x<this->map.nbCellWidth; x++) {
Ludwigfr 34:c208497dd079 456 heightIndiceInOrthonormal=this->map.cell_height_coordinate_to_world(y);
Ludwigfr 34:c208497dd079 457 widthIndiceInOrthonormal=this->map.cell_width_coordinate_to_world(x);
Ludwigfr 35:68f9edbb3cff 458 if(this->yWorld >= (heightIndiceInOrthonormal+heightMalus) && this->yWorld <= (heightIndiceInOrthonormal+heightBonus) && this->xWorld >= (widthIndiceInOrthonormal+widthMalus) && this->xWorld <= (widthIndiceInOrthonormal+widthBonus))
Ludwigfr 34:c208497dd079 459 pc.printf(" R ");
Ludwigfr 34:c208497dd079 460 else{
Ludwigfr 34:c208497dd079 461 if(targetYWorld >= (heightIndiceInOrthonormal+heightMalus) && targetYWorld <= (heightIndiceInOrthonormal+heightBonus) && targetXWorld >= (widthIndiceInOrthonormal+widthMalus) && targetXWorld <= (widthIndiceInOrthonormal+widthBonus))
Ludwigfr 34:c208497dd079 462 pc.printf(" T ");
Ludwigfr 34:c208497dd079 463 else{
Ludwigfr 34:c208497dd079 464 currProba=this->map.log_to_proba(this->map.cellsLogValues[x][y]);
Ludwigfr 34:c208497dd079 465 if ( currProba < 0.5)
Ludwigfr 34:c208497dd079 466 pc.printf(" ");
Ludwigfr 34:c208497dd079 467 else{
Ludwigfr 34:c208497dd079 468 if(currProba==0.5)
Ludwigfr 34:c208497dd079 469 pc.printf(" . ");
Ludwigfr 34:c208497dd079 470 else
Ludwigfr 34:c208497dd079 471 pc.printf(" X ");
Ludwigfr 34:c208497dd079 472 }
Ludwigfr 34:c208497dd079 473 }
Ludwigfr 34:c208497dd079 474 }
Ludwigfr 34:c208497dd079 475 }
Ludwigfr 34:c208497dd079 476 pc.printf("\n\r");
Ludwigfr 34:c208497dd079 477 }
Ludwigfr 34:c208497dd079 478 }
Ludwigfr 34:c208497dd079 479
Ludwigfr 37:b4c45e43ad29 480 void MiniExplorerCoimbra::print_map() {
Ludwigfr 34:c208497dd079 481 float currProba;
Ludwigfr 34:c208497dd079 482 pc.printf("\n\r");
Ludwigfr 34:c208497dd079 483 for (int y = this->map.nbCellHeight -1; y>-1; y--) {
Ludwigfr 34:c208497dd079 484 for (int x= 0; x<this->map.nbCellWidth; x++) {
Ludwigfr 34:c208497dd079 485 currProba=this->map.log_to_proba(this->map.cellsLogValues[x][y]);
Ludwigfr 34:c208497dd079 486 if ( currProba < 0.5) {
Ludwigfr 34:c208497dd079 487 pc.printf(" ");
Ludwigfr 34:c208497dd079 488 } else {
Ludwigfr 34:c208497dd079 489 if(currProba==0.5)
Ludwigfr 34:c208497dd079 490 pc.printf(" . ");
Ludwigfr 34:c208497dd079 491 else
Ludwigfr 34:c208497dd079 492 pc.printf(" X ");
Ludwigfr 34:c208497dd079 493 }
Ludwigfr 34:c208497dd079 494 }
Ludwigfr 34:c208497dd079 495 pc.printf("\n\r");
Ludwigfr 34:c208497dd079 496 }
Ludwigfr 34:c208497dd079 497 }
Ludwigfr 35:68f9edbb3cff 498
Ludwigfr 35:68f9edbb3cff 499 //return 1 if positiv, -1 if negativ
Ludwigfr 35:68f9edbb3cff 500 float MiniExplorerCoimbra::sign1(float value){
Ludwigfr 35:68f9edbb3cff 501 if(value>=0)
Ludwigfr 35:68f9edbb3cff 502 return 1;
Ludwigfr 35:68f9edbb3cff 503 else
Ludwigfr 35:68f9edbb3cff 504 return -1;
Ludwigfr 35:68f9edbb3cff 505 }
Ludwigfr 35:68f9edbb3cff 506
Ludwigfr 35:68f9edbb3cff 507 //return 1 if positiv, 0 if negativ
Ludwigfr 35:68f9edbb3cff 508 int MiniExplorerCoimbra::sign2(float value){
Ludwigfr 35:68f9edbb3cff 509 if(value>=0)
Ludwigfr 35:68f9edbb3cff 510 return 1;
Ludwigfr 35:68f9edbb3cff 511 else
Ludwigfr 35:68f9edbb3cff 512 return 0;
Ludwigfr 35:68f9edbb3cff 513 }
Ludwigfr 35:68f9edbb3cff 514
Ludwigfr 35:68f9edbb3cff 515 /*UNUSED
Ludwigfr 35:68f9edbb3cff 516 float MiniExplorerCoimbra::get_converted_robot_X_into_world(){
Ludwigfr 35:68f9edbb3cff 517 //x world coordinate
Ludwigfr 35:68f9edbb3cff 518 return this->map.nbCellWidth*this->map.sizeCellWidth-Y;
Ludwigfr 35:68f9edbb3cff 519 }
Ludwigfr 35:68f9edbb3cff 520
Ludwigfr 35:68f9edbb3cff 521 float MiniExplorerCoimbra::get_converted_robot_Y_into_world(){
Ludwigfr 35:68f9edbb3cff 522 //y worldcoordinate
Ludwigfr 35:68f9edbb3cff 523 return X;
Ludwigfr 35:68f9edbb3cff 524 }
Ludwigfr 35:68f9edbb3cff 525
Ludwigfr 35:68f9edbb3cff 526
Ludwigfr 35:68f9edbb3cff 527 //x and y passed are TargetNotMap
Ludwigfr 35:68f9edbb3cff 528 float get_error_angles(float x, float y,float theta){
Ludwigfr 35:68f9edbb3cff 529 float angleBeetweenRobotAndTarget=atan2(y,x);
Ludwigfr 35:68f9edbb3cff 530 if(y > 0){
Ludwigfr 35:68f9edbb3cff 531 if(angleBeetweenRobotAndTarget < PI/2)//up right
Ludwigfr 35:68f9edbb3cff 532 angleBeetweenRobotAndTarget=-PI/2+angleBeetweenRobotAndTarget;
Ludwigfr 35:68f9edbb3cff 533 else//up right
Ludwigfr 35:68f9edbb3cff 534 angleBeetweenRobotAndTarget=angleBeetweenRobotAndTarget-PI/2;
Ludwigfr 35:68f9edbb3cff 535 }else{
Ludwigfr 35:68f9edbb3cff 536 if(angleBeetweenRobotAndTarget > -PI/2)//lower right
Ludwigfr 35:68f9edbb3cff 537 angleBeetweenRobotAndTarget=angleBeetweenRobotAndTarget-PI/2;
Ludwigfr 35:68f9edbb3cff 538 else//lower left
Ludwigfr 35:68f9edbb3cff 539 angleBeetweenRobotAndTarget=2*PI+angleBeetweenRobotAndTarget-PI/2;
Ludwigfr 35:68f9edbb3cff 540 }
Ludwigfr 35:68f9edbb3cff 541 return angleBeetweenRobotAndTarget-theta;
Ludwigfr 35:68f9edbb3cff 542 }
Ludwigfr 35:68f9edbb3cff 543 */