/* File: field.c Author: Andrew W. Moore Created: Fri Oct 16th 1992 Description: The old car-on-field problem Copyright (C) 1992, Andrew W. Moore */ #include #include #include "ambs.h" /* Very basic operations */ #include "amma.h" /* Fast, non-fragmenting, memory management */ #include "amar.h" /* Obvious operations on 1-d arrays */ #include "amgr.h" /* Simple ag_ graphics */ #include "maxdim.h" /* The MAX_DIM declaration */ #include "gpro.h" /* Graphics projections kd->2d space */ #include "hype.h" /* Hyper-rectangles from ../kdtr */ #include "region.h" /* K-d region Data Structure */ #include "wrld.h" /* Spec. of the World Control problem */ #include "whis.h" /* History of worsts */ /* equation of motion in a potential field with N point charges. The object in motion has 1 unit of positive charge. The other charges have location c_i and charge q_i (+ or -) respectively. an external uniform force can be applied to the charge to control it. ______ \ x'' = F + \ q_i / ------------- / ( x - c_i )^2 ------- x(t+h) = x(t) + h x'(t) + 0.5 + x''(t) * h * h x'(t+h) = x'(t) + h x''(t) */ #define TIME_STEP 0.02 #define MAX_POS 1.0 #define MAX_VEL 4.0 #define MAX_ACTION 4.0 void field_apply_accn(rx,rv,a) float *rx,*rv,a; { *rx = ( *rx + *rv * TIME_STEP + 0.5 * a * TIME_STEP * TIME_STEP ); *rv = *rv + a * TIME_STEP; } typedef struct charge_struct { float charge; float center[MAX_DIM]; } charge; #define MAX_CHARGES 40 typedef struct field_struct { int sdim; /* Dimension of state space = 2 * pdim */ int pdim; /* Dimension of physical space */ int size; /* Number of charges */ float pgain,vgain; /* Position and velocity gains */ charge charges[MAX_CHARGES]; } field; void load_field_contents(fld,fname) field *fld; char *fname; /* Format: [0] .. [pdim-1] [0] .. [pdim-1] . . */ { FILE *s = safe_fopen(fname,"r"); if ( 3 != fscanf(s,"%d %f %f",&(fld->pdim),&(fld->pgain),&(fld->vgain)) ) my_error("load_field_contents syntax error"); else { bool finished = FALSE; int i; fld -> sdim = 2 * fld->pdim; for ( i = 0 ; !finished && i < MAX_CHARGES ; i++ ) { int j; charge *cg = &(fld->charges[i]); finished = 1 != fscanf(s,"%f",&cg->charge); for ( j = 0 ; !finished && j < fld->pdim ; j++ ) { float x; if ( 1 != fscanf(s,"%f",&x) ) my_error("load_field_contents loop"); cg->center[j] = x; } } fld -> size = i; } fclose(s); } field *create_and_load_field(fname) char *fname; /* Format: See above */ { field *fld = AM_MALLOC(field); load_field_contents(fld,fname); return(fld); } float field_charge(fld,chnum) field *fld; int chnum; { if ( chnum < 0 || chnum > fld->size ) my_error("isybvcis"); else return(fld->charges[chnum].charge); } float *field_center(fld,chnum) field *fld; int chnum; { if ( chnum < 0 || chnum > fld->size ) my_error("isybvcis"); else return(fld->charges[chnum].center); } void compute_accn(fld,nstate,wac,accn) field *fld; float *nstate,*wac,*accn; { int chnum; copy_floats(wac,accn,fld->pdim); for ( chnum = 0 ; chnum < fld->size ; chnum++ ) { float min_mag = MAX_POS/10.0; float diff[MAX_DIM]; float mag_sqd,scale,mag; floats_subtract(field_center(fld,chnum),nstate,fld->pdim,diff); mag_sqd = floats_magnitude_sqd(diff,fld->pdim); mag = sqrt(mag_sqd); mag_sqd = real_max(mag_sqd,min_mag * min_mag); /* To prevent things being too wild near center */ mag = real_max(mag,0.001); /* To prevent numerical overflow */ scale = field_charge(fld,chnum) / (mag_sqd * mag); floats_scalar_multiply(diff,fld->pdim,scale,diff); floats_add(accn,diff,fld->pdim,accn); } } /****************** NOW THE WRLD-RELATED PART ******************/ #define RUNNING_MODE 0 #define GOAL_MODE 1 void field_running_next_worst(wld,wst,wac,next_wst) world *wld; worst *wst; float *wac; worst *next_wst; { mode_spec *msp = mode_ref(wld,RUNNING_MODE); field *fld = (field *) msp -> data; float accn[MAX_DIM]; int i; compute_accn(fld,wst->nstate,wac,accn); next_wst -> mode = wst -> mode; for ( i = 0 ; i < fld->pdim ; i++ ) { float x = wst->nstate[i]; float v = wst->nstate[i+fld->pdim]; field_apply_accn(&x,&v,accn[i]); next_wst->nstate[i] = x; next_wst->nstate[i+fld->pdim] = v; } for ( i = 0 ; i < fld->sdim ; i++ ) { next_wst->nstate[i] = real_max(msp->middle[i]-msp->scales[i],next_wst->nstate[i]); next_wst->nstate[i] = real_min(msp->middle[i]+msp->scales[i],next_wst->nstate[i]); } } void field_running_local_control(wld,wst,goal_wst,wac) world *wld; worst *wst,*goal_wst; float *wac; { field *fld = (field *) mode_ref(wld,wst->mode) -> data; int i; float aim[MAX_DIM]; float h = TIME_STEP; if ( wst->mode == goal_wst -> mode ) copy_floats(goal_wst->nstate,aim,state_dim(wld,wst->mode)); else middle_of_region(mode_ref(wld,wst->mode)->mode_transitions->region,aim); for ( i = 0 ; i < fld->pdim ; i++ ) wac[i] = fld->pgain * (aim[i] - wst->nstate[i]) / (h * h) + fld->vgain * (aim[i+fld->pdim] - wst->nstate[i+fld->pdim]) / h; normalize_to_legality(wld,wst->mode,wac); } void field_running_draw_worst(gp,wld,wst) gproj *gp; world *wld; worst *wst; { gp_draw_floats_dot(gp,wst->nstate); } bool field_running_is_stuck(wld,wh) world *wld; worst_hist *wh; { return ( wh -> length > 98 ); } bool field_running_should_remove_trans(wld,msp,hy_me,hy_neigh) world *wld; mode_spec *msp; hype *hy_me,*hy_neigh; /* Okay, if for any physical dimension i: the velocity with hy_me is all the same sign and all paths from hy_me to hy_neigh are in the opposite physical direction then its hopeless to even attempt to travel from hy_me to hy_neigh */ { field *fld = (field *) mode_ref(wld,RUNNING_MODE) -> data; bool result = FALSE; int i; for ( i = 0 ; !result && i < fld->pdim ; i++ ) { int vel_comp = i + fld->pdim; bool positive_vel = !hype_low_limit_is_below(hy_me,vel_comp,0.0); bool negative_vel = !hype_high_limit_is_above(hy_me,vel_comp,0.0); if ( positive_vel && negative_vel ) my_error("+ and -"); if ( positive_vel ) { result = hype_has_limit(hy_me,i,LO) && hype_has_limit(hy_neigh,i,HI) && hype_ref(hy_me,i,LO) >= hype_ref(hy_neigh,i,HI) - 0.0001; } else if ( negative_vel ) { result = hype_has_limit(hy_me,i,HI) && hype_has_limit(hy_neigh,i,LO) && hype_ref(hy_me,i,HI) <= hype_ref(hy_neigh,i,LO) + 0.0001; } } return(result); } void field_goal_draw_worst(gp,wld,wst) gproj *gp; world *wld; worst *wst; { gp_draw_region_border(gp, mode_ref(wld,RUNNING_MODE)->mode_transitions->region ); } void field_draw_structure(gp,wld,wst) gproj *gp; world *wld; worst *wst; { field *fld = (field *) mode_ref(wld,RUNNING_MODE) -> data; int chnum; for ( chnum = 0 ; chnum < fld->size ; chnum++ ) { float center[MAX_DIM]; float r_pixels = 20.0 * fabs(field_charge(fld,chnum)); set_floats_constant(center,2,0.0); /* Zero out first two components in case pdim is less than two */ copy_floats(field_center(fld,chnum),center,fld->pdim); if ( field_charge(fld,chnum) > 0.0 ) gp_draw_floats_circle(gp,center,r_pixels); else gp_draw_floats_disc(gp,center,r_pixels); } } mode_spec *make_field_running_mode(wld,wname,argc,argv) world *wld; char *wname; int argc; char *argv[]; { field *fld = create_and_load_field(argv[2]); mode_spec *msp = create_empty_mode_spec(fld->sdim,fld->pdim); hype *goal_zone = create_hype(msp->state.dim); worst *goal_wst = create_worst(GOAL_MODE,0); float goal_nstate[MAX_DIM]; int i; msp -> name = "running"; for ( i = 0 ; i < fld->pdim ; i++ ) { hype_update(msp->state.bound,i,LO,-MAX_POS); hype_update(msp->state.bound,i,HI,MAX_POS); hype_update(msp->state.bound,i+fld->pdim,LO,-MAX_VEL); hype_update(msp->state.bound,i+fld->pdim,HI,MAX_VEL); hype_update(msp->action.bound,i,LO,-MAX_ACTION); hype_update(msp->action.bound,i,HI,MAX_ACTION); } for ( i = 0 ; i < fld->sdim ; i++ ) { msp->splittable_attributes[i] = TRUE; goal_nstate[i] = (i < fld->pdim) ? 0.6666666666 : 0.0; } i = index_of_arg("-goal",argc,argv); if ( i >= 0 && i + fld->sdim < argc ) { int j; for ( j = 0 ; j < fld->sdim ; j++ ) goal_nstate[j] = atof(argv[i+j+1]); } m_and_s_from_hype(msp->state.bound,msp->middle,msp->scales); for ( i = 0 ; i < fld->pdim ; i++ ) { int vi = i + fld->pdim; hype_update(goal_zone,i,LO,goal_nstate[i] - msp->scales[i]/10.0); hype_update(goal_zone,i,HI,goal_nstate[i] + msp->scales[i]/10.0); hype_update(goal_zone,vi,LO,goal_nstate[vi] - msp->scales[vi]); hype_update(goal_zone,vi,HI,goal_nstate[vi] + msp->scales[vi]); } msp -> mode_transitions = add_to_mtrans(create_in_hype_region(goal_zone), goal_wst, msp->mode_transitions ); msp -> next_worst = field_running_next_worst; msp -> local_control = field_running_local_control; msp -> draw_worst = field_running_draw_worst; msp -> is_stuck = field_running_is_stuck; msp -> should_remove_trans = field_running_should_remove_trans; msp -> data = (char *) fld; return(msp); } mode_spec *make_field_goal_mode(wld,wname,argc,argv) world *wld; char *wname; int argc; char *argv[]; /* PRE: Running mode has been created */ { mode_spec *msp = create_empty_mode_spec(0,0); msp -> name = "goal"; msp -> draw_worst = field_goal_draw_worst; return(msp); } void load_field(wld,wname,argc,argv) world *wld; char *wname; int argc; char *argv[]; { if ( argc < 3 ) { printf("%s field [options] [arm-options]\n", argv[0] ); printf("Field options:\n"); printf("-goal .. .. \n"); my_error("load_field(): argv[2] should be the charge file.\n"); } else { init_empty_mode_spec_array(wld,2); printf("mode array done\n"); wld -> modes[RUNNING_MODE] = make_field_running_mode(wld,wname,argc,argv); printf("run_mode done\n"); wld -> modes[GOAL_MODE] = make_field_goal_mode(wld,wname,argc,argv); printf("goal mode done\n"); wld -> goal = create_worst(GOAL_MODE,state_dim(wld,GOAL_MODE)); printf("goal done\n"); wld -> draw_structure = field_draw_structure; } }