/***********************************************************************/ /** @name spline.c Contains functions calculate a spline to a certain posint with a certain heading and have a robot drive to it. @author Birgit Graf, UWA, 1998 */ /***********************************************************************/ /*@{*/ #include "global.h" #include "general.h" #define DEBUG 0 /** factors to change linear/rotational speed */ #define lin_fact 1.5 #define rot_fact 3.0 /** time intervall after which spline function is called again (period in 1/100 seconds) */ #define loop_time 15 /** accuracy parameter for robot movement (in m), angle < epsilon -> don't move */ #define epsilon 0.01 /** threshold for robot movement (in m), stop if dist < thres_stop */ #define thresh_stop 0.05 /** record to store robot's start and end position when driving on a spline */ PositionType startpos, endpos; /** desired slope at start/end point (x and y value) */ float startpos_d_x, endpos_d_x, startpos_d_y, endpos_d_y; /** u in spline formula */ float u; /** factor to increase fraction of spline in each step */ float fract_step; /** flag set to true when driving spline */ int spline_flag = FALSE; /** spline timer */ TimerHandle splinetimer; meterPerSec lin_old; /***********************************************************************/ /** Get angle. Get angle between robot orientation phi and line to point (diffx, diffy). Called by drive_spline(). @see drive_spline() @param diffx, diffxy difference between coordinates of point to current robot position @param phi robot heading @return diffphi angle between robot heading and point (x, y) */ /***********************************************************************/ float get_angle(float diffx, float diffy, float phi) { float diffphi; /* angle to desired position */ if (fabs(diffx) < epsilon && fabs(diffy) < epsilon) diffphi = phi; /* point reached -> no angle difference */ else diffphi = atan2(diffy, diffx); /* difference to current heading */ diffphi -= phi; /* angle always between 0 and 2*PI */ if (diffphi >= 2.0 * PI) diffphi -= 2.0 * PI; if (diffphi <= 0) diffphi += 2.0 * PI; /* angle always between -PI and PI */ if (diffphi >= PI) diffphi -= 2.0 * PI; /* if (fabs(diffphi) > PI/2) */ /* AUTone(1200,200); */ return diffphi; } /* compute hermite spline */ void herm_spline(float u, PositionType *spline) { float u2, u3; float blend0, blend1, blend2, blend3; /* blending fcts for point on spline */ if (u > 1.0) u = 1.0; u2 = u * u; u3 = u2 * u; blend0 = 2.0 * u3 - 3.0 * u2 + 1.0; blend1 = -2.0 * u3 + 3.0 * u2; blend2 = u3 - 2.0 * u2 + u; blend3 = u3 - u2; /* desired position on spline */ spline->x = startpos.x * blend0 + endpos.x * blend1 + startpos_d_x * blend2 + endpos_d_x * blend3; spline->y = startpos.y * blend0 + endpos.y * blend1 + startpos_d_y * blend2 + endpos_d_y * blend3; } /***********************************************************************/ /** Drive Spline IRQ. Drive curve from startpos to endpos, considering orientation Called by start_spline(). Executed every loop_time *10 mseconds. @see start_spline() */ /***********************************************************************/ void drive_spline_IRQ() { radPerSec rot_new; /* new rotatonal speed */ meterPerSec lin_new; /* new linear speed */ PositionType pos; PositionType diff; /* difference between desired and current position of robot */ float diff_dist, min_dist; /* distance between current and desired position */ float diff_ball; /* angle between current and ball position */ PositionType spline; /* position on spline */ if (spline_flag) { if (u == 1.0) { spline_flag = FALSE; /* VWSetSpeed(vw, 0.0, 0.0); */ } else { VWGetPosition(vw, &pos); diff_dist = 1.0; /* 1 meter - should never be reached */ do { min_dist = diff_dist; u += fract_step; herm_spline(u, &spline); /* difference between current and spline position */ diff.x = spline.x - pos.x; diff.y = spline.y - pos.y; diff_dist = fabs(diff.x) + fabs(diff.y); } while ((diff_dist < min_dist) && (u < 1.0)); if (u > 1.0) u = 1.0; /* last step */ if (DEBUG) { LCDSetPos(1,0); LCDPrintf("u: % 1.2f\n",u); LCDSetPos(2,0); LCDPrintf(" x y\n"); LCDSetPos(3,0); LCDPrintf("cur % 1.2f % 1.2f\n",pos.x,pos.y); LCDSetPos(4,0); LCDPrintf("des % 1.2f % 1.2f\n",spline.x,spline.y); LCDSetPos(5,0); LCDPrintf("dif % 1.2f % 1.2f\n",diff.x,diff.y); } /* angle between current and desired position */ diff.phi = get_angle(diff.x, diff.y, pos.phi); /* angle in range [-PI/2 .. +PI/2] */ rot_new = rot_fact * diff.phi; /* distance between current and desired position */ diff_dist = fabs(diff.x) + fabs(diff.y); lin_new = (u + 0.5) * lin_fact * diff_dist; /* rot_new = 0.1 * fsign(diff.phi) * diff_dist * rot_fact; */ VWSetSpeed(vw, lin_new, rot_new); } } } /***********************************************************************/ /** Start Spline. Start timer function which calls drive_spline every loop_time mseconds. Called by drive_to_ball(). @see drive_to_ball() */ /***********************************************************************/ void start_spline(PositionType start, PositionType end) { float fract_distx, fract_disty, fract_dist, fract_angle; startpos = start; endpos = end; /* factor to increase fraction of spline in each step */ fract_distx = endpos.x - startpos.x; fract_disty = endpos.y - startpos.y; fract_dist = fabs(fract_distx) + fabs(fract_disty); fract_angle = fabs(get_angle(fract_distx, fract_disty, startpos.phi)); fract_step = 0.005 * LIN_SPEED * (float)loop_time; /* desired slope at start/end point (x and y value) */ startpos_d_x = cos(startpos.phi); endpos_d_x = cos(endpos.phi); startpos_d_y = sin(startpos.phi); endpos_d_y = sin(endpos.phi); u = 0.0; /* start with next point */ lin_old = 0.0; spline_flag = TRUE; } /***********************************************************************/ /** Stop Spline. /***********************************************************************/ void stop_spline() { spline_flag = FALSE; /* VWSetSpeed(vw, 0.0, 0.0); */ /* OSDetachTimer(splinetimer); */ } /***********************************************************************/ /** Spline Done. /***********************************************************************/ int spline_done() { return (u >= 1.0); } /***********************************************************************/ /** Attach drive_spline_IRQ to timer. /***********************************************************************/ void start_spline_IRQ() { spline_flag = FALSE; splinetimer = OSAttachTimer(loop_time, drive_spline_IRQ); } /***********************************************************************/ /** Detach drive_spline_IRQ to timer. /***********************************************************************/ void stop_spline_IRQ() { OSDetachTimer(splinetimer); } /*@}*/