Revision: 7534
http://playerstage.svn.sourceforge.net/playerstage/?rev=7534&view=rev
Author: rtv
Date: 2009-03-20 18:55:14 +0000 (Fri, 20 Mar 2009)
Log Message:
-----------
added missing file
Added Paths:
-----------
code/stage/trunk/examples/ctrl/fasr_plan.cc
Added: code/stage/trunk/examples/ctrl/fasr_plan.cc
===================================================================
--- code/stage/trunk/examples/ctrl/fasr_plan.cc (rev 0)
+++ code/stage/trunk/examples/ctrl/fasr_plan.cc 2009-03-20 18:55:14 UTC (rev
7534)
@@ -0,0 +1,775 @@
+#include "stage.hh"
+using namespace Stg;
+
+#include <FL/Fl_Shared_Image.H>
+#include <libstandalone_drivers/plan.h>
+
+int read_map_from_image(int* size_x, int* size_y, char** mapdata,
+ const char* fname, int negate );
+
+
+
+
+const bool verbose = false;
+
+// navigation control params
+const double cruisespeed = 0.4;
+const double avoidspeed = 0.05;
+const double avoidturn = 0.5;
+const double minfrontdistance = 0.7;
+const double stopdist = 0.5;
+const int avoidduration = 10;
+const int workduration = 20;
+const int payload = 1;
+
+double have[4][4] = {
+ // { -120, -180, 180, 180 },
+ //{ -90, -120, 180, 90 },
+ { 90, 180, 180, 180 },
+ { 90, -90, 180, 90 },
+ { 90, 90, 180, 90 },
+ { 0, 45, 0, 0}
+};
+
+double need[4][4] = {
+ { -120, -180, 180, 180 },
+ { -90, -120, 180, 90 },
+ { -90, -90, 180, 180 },
+ { -90, -180, -90, -90 }
+};
+
+double refuel[4][4] = {
+ { 0, 0, 45, 120 },
+ { 0,-90, -60, -160 },
+ { -90, -90, 180, 180 },
+ { -90, -180, -90, -90 }
+};
+
+typedef enum {
+ MODE_WORK=0,
+ MODE_DOCK,
+ MODE_UNDOCK
+} nav_mode_t;
+
+
+#define MAP_IDX(sx, i, j) ((sx) * (j) + (i))
+
+
+static guchar* pb_get_pixel( Fl_Shared_Image* img, int x, int y )
+{
+ guchar* pixels = (guchar*)(img->data()[0]);
+ unsigned int index = (y * img->w() * img->d()) + (x * img->d());
+ return( pixels + index );
+}
+
+// returns true if the value in the first channel is above threshold
+static gboolean pb_pixel_is_set( Fl_Shared_Image* img, int x, int y, int
threshold )
+{
+ guchar* pixel = pb_get_pixel( img,x,y );
+ return( pixel[0] > threshold );
+}
+
+void print_og( plan_t* plan )
+{
+ const int sx = plan->size_x;
+ const int sy = plan->size_y;
+
+ char c;
+ for(int j=0;j<sy;j++)
+ {
+ for(int i=0;i<sx;i++)
+ {
+ switch( plan->cells[i+j*sx].occ_state )
+ {
+ case 1: c = 'O'; break;
+ case 0: c = '?'; break;
+ case -1: c = '.'; break;
+ default: c = 'X'; break;
+ }
+
+ printf( "%c", c );
+ }
+ puts( "" );
+ }
+}
+
+void print_cspace( plan_t* plan )
+{
+ const int sx = plan->size_x;
+ const int sy = plan->size_y;
+
+ char c;
+ for(int j=0;j<sy;j++)
+ {
+ for(int i=0;i<sx;i++)
+ {
+ printf( "%.2f ", plan->cells[i+j*sx].occ_dist );
+ }
+ puts( "" );
+ }
+}
+
+plan_t* SetupPlan( double robot_radius,
+ double safety_dist,
+ double max_radius,
+ double dist_penalty,
+ const char* fname,
+ double widthm,
+ double originx,
+ double originy,
+ int threshold )
+{
+ Fl_Shared_Image *img = Fl_Shared_Image::get(fname);
+ assert( img );
+
+ int sx = img->w();
+ int sy = img->h();
+
+ plan_t* plan = NULL;
+ if( ! (plan = plan_alloc( robot_radius + safety_dist,
+ robot_radius + safety_dist,
+ max_radius,
+ dist_penalty,
+ 0.5)) )
+ {
+ puts("failed to allocate plan");
+ }
+
+ // map geometry
+ plan->scale = widthm / sx;
+ plan->size_x = sx;
+ plan->size_y = sy;
+ plan->origin_x = originx;
+ plan->origin_y = originy;
+
+ // allocate space for map cells
+ assert(plan->cells == NULL);
+ plan->cells = (plan_cell_t*)
+ realloc(plan->cells,
+ sx * sy * sizeof(plan_cell_t));
+
+ assert(plan->cells);
+
+ printf( "plan->max_radius %.3f\n", plan->max_radius );
+
+ // Copy over obstacle information from the image data that we read
+ for(int j=0;j<sy;j++)
+ for(int i=0;i<sx;i++)
+ {
+ plan_cell_t* cell = &plan->cells[i+j*sx];
+ cell->occ_dist = plan->max_radius;
+
+ cell->occ_state =
+ pb_pixel_is_set( img,i,sy-j-1, threshold) ? -1 : 1;
+
+ if( cell->occ_state >= 0 )
+ cell->occ_dist = 0;
+ }
+
+ img->release(); // frees all image resources
+
+ plan_init(plan);
+
+ plan_compute_cspace(plan);
+ //print_cspace(plan);
+
+// double x, y;
+// plan_convert_waypoint( plan, plan->cells, &x, &y );
+// printf( "cell 0 (%.2f, %.2f)\n", x, y );
+
+// plan_convert_waypoint( plan, plan->cells + sy*sx -1, &x, &y );
+// printf( "cell end (%.2f, %.2f)\n", x, y );
+
+// plan_convert_waypoint( plan, plan->cells + sx, &x, &y );
+// printf( "cell sx (%.2f, %.2f)\n", x, y );
+
+ //plan_convert_waypoint( plan, plan->cells, &x, &y );
+ //printf( "cell 0 (%.2f, %.2f)\n", x, y );
+
+
+ // getchar();
+
+
+ puts( "PLAN CREATED" );
+
+ return plan;
+}
+
+
+class Robot
+{
+private:
+ ModelPosition* pos;
+ ModelLaser* laser;
+ ModelRanger* ranger;
+ ModelFiducial* fiducial;
+ ModelBlobfinder* blobfinder;
+ ModelGripper* gripper;
+ Model *source, *sink;
+ int avoidcount, randcount;
+ int work_get, work_put;
+ bool charger_ahoy;
+ double charger_bearing;
+ double charger_range;
+ double charger_heading;
+ nav_mode_t mode;
+ bool at_dest;
+
+ plan_t* plan;
+ bool plan_done;
+
+public:
+ Robot( ModelPosition* pos,
+ Model* source,
+ Model* sink )
+ : pos(pos),
+ laser( (ModelLaser*)pos->GetUnusedModelOfType( MODEL_TYPE_LASER )),
+ ranger( (ModelRanger*)pos->GetUnusedModelOfType( MODEL_TYPE_RANGER )),
+ fiducial( (ModelFiducial*)pos->GetUnusedModelOfType( MODEL_TYPE_FIDUCIAL
)),
+ blobfinder( (ModelBlobfinder*)pos->GetUnusedModelOfType(
MODEL_TYPE_BLOBFINDER )),
+ gripper( (ModelGripper*)pos->GetUnusedModelOfType( MODEL_TYPE_GRIPPER )),
+ source(source),
+ sink(sink),
+ avoidcount(0),
+ randcount(0),
+ work_get(0),
+ work_put(0),
+ charger_ahoy(false),
+ charger_bearing(0),
+ charger_range(0),
+ charger_heading(0),
+ mode(MODE_WORK),
+ at_dest( false ),
+ plan( NULL ),
+ plan_done( false )
+ {
+ // need at least these models to get any work done
+ // (pos must be good, as we used it in the initialization list)
+ assert( laser );
+ assert( source );
+ assert( sink );
+
+ // PositionUpdate() checks to see if we reached source or sink
+ pos->AddUpdateCallback( (stg_model_callback_t)PositionUpdate, this );
+ pos->Subscribe();
+
+ // LaserUpdate() controls the robot, by reading from laser and
+ // writing to position
+ laser->AddUpdateCallback( (stg_model_callback_t)LaserUpdate, this );
+ laser->Subscribe();
+
+ fiducial->AddUpdateCallback( (stg_model_callback_t)FiducialUpdate, this );
+ fiducial->Subscribe();
+
+ //gripper->AddUpdateCallback( (stg_model_callback_t)GripperUpdate, this );
+ gripper->Subscribe();
+
+ if( blobfinder ) // optional
+ {
+ blobfinder->AddUpdateCallback( (stg_model_callback_t)BlobFinderUpdate,
this );
+ blobfinder->Subscribe();
+ }
+
+ //planif( !plan )
+ plan = SetupPlan( 0.3, // robot radius
+ 0.05, // safety dist
+ 0.5, // max radius
+ 1.0, // dist penalty
+
"/Users/vaughan/PS/stage/trunk/worlds/bitmaps/cave_compact.png",
+ 16.0, -8.0, -8.0, 250 );
+ }
+
+
+ void Dock()
+ {
+ // close the grippers so they can be pushed into the charger
+ ModelGripper::config_t gripper_data = gripper->GetConfig();
+
+ if( gripper_data.paddles != ModelGripper::PADDLE_CLOSED )
+ gripper->CommandClose();
+ else if( gripper_data.lift != ModelGripper::LIFT_UP )
+ gripper->CommandUp();
+
+ if( charger_ahoy )
+ {
+ double a_goal = normalize( charger_bearing );
+
+ // if( pos->Stalled() )
+ // {
+ // puts( "stalled. stopping" );
+ // pos->Stop();
+ // }
+ // else
+
+ if( charger_range > 0.5 )
+ {
+ if( !ObstacleAvoid() )
+ {
+ pos->SetXSpeed( cruisespeed );
+ pos->SetTurnSpeed( a_goal );
+ }
+ }
+ else
+ {
+ pos->SetTurnSpeed( a_goal );
+ pos->SetXSpeed( 0.02 ); // creep towards it
+
+ if( charger_range < 0.08 ) // close enough
+ pos->Stop();
+
+ if( pos->Stalled() ) // touching
+ pos->SetXSpeed( -0.01 ); // back off a bit
+
+ }
+ }
+ else
+ {
+ //printf( "docking but can't see a charger\n" );
+ pos->Stop();
+ mode = MODE_WORK; // should get us back on track eventually
+ }
+
+ // if the battery is charged, go back to work
+ if( Full() )
+ {
+ //printf( "fully charged, now back to work\n" );
+ mode = MODE_UNDOCK;
+ }
+ }
+
+
+ void UnDock()
+ {
+ const stg_meters_t gripper_distance = 0.2;
+ const stg_meters_t back_off_distance = 0.3;
+ const stg_meters_t back_off_speed = -0.05;
+
+ // back up a bit
+ if( charger_range < back_off_distance )
+ pos->SetXSpeed( back_off_speed );
+ else
+ pos->SetXSpeed( 0.0 );
+
+ // once we have backed off a bit, open and lower the gripper
+ ModelGripper::config_t gripper_data = gripper->GetConfig();
+ if( charger_range > gripper_distance )
+ {
+ if( gripper_data.paddles != ModelGripper::PADDLE_OPEN )
+ gripper->CommandOpen();
+ else if( gripper_data.lift != ModelGripper::LIFT_DOWN )
+ gripper->CommandDown();
+ }
+
+ // if the gripper is down and open and we're away from the charger, undock
is finished
+ if( gripper_data.paddles == ModelGripper::PADDLE_OPEN &&
+ gripper_data.lift == ModelGripper::LIFT_DOWN &&
+ charger_range > back_off_distance )
+ mode = MODE_WORK;
+ }
+
+ bool ObstacleAvoid()
+ {
+ bool obstruction = false;
+ bool stop = false;
+
+ // find the closest distance to the left and right and check if
+ // there's anything in front
+ double minleft = 1e6;
+ double minright = 1e6;
+
+ // Get the data
+ uint32_t sample_count=0;
+ stg_laser_sample_t* scan = laser->GetSamples( &sample_count );
+
+ for (uint32_t i = 0; i < sample_count; i++)
+ {
+ if( verbose ) printf( "%.3f ", scan[i].range );
+
+ if( (i > (sample_count/4))
+ && (i < (sample_count - (sample_count/4)))
+ && scan[i].range < minfrontdistance)
+ {
+ if( verbose ) puts( " obstruction!" );
+ obstruction = true;
+ }
+
+ if( scan[i].range < stopdist )
+ {
+ if( verbose ) puts( " stopping!" );
+ stop = true;
+ }
+
+ if( i > sample_count/2 )
+ minleft = MIN( minleft, scan[i].range );
+ else
+ minright = MIN( minright, scan[i].range );
+ }
+
+ if( verbose )
+ {
+ puts( "" );
+ printf( "minleft %.3f \n", minleft );
+ printf( "minright %.3f\n ", minright );
+ }
+
+ if( obstruction || stop || (avoidcount>0) )
+ {
+ if( verbose ) printf( "Avoid %d\n", avoidcount );
+
+ pos->SetXSpeed( stop ? 0.0 : avoidspeed );
+
+ /* once we start avoiding, select a turn direction and stick
+ with it for a few iterations */
+ if( avoidcount < 1 )
+ {
+ if( verbose ) puts( "Avoid START" );
+ avoidcount = random() % avoidduration + avoidduration;
+
+ if( minleft < minright )
+ {
+ pos->SetTurnSpeed( -avoidturn );
+ if( verbose ) printf( "turning right %.2f\n", -avoidturn );
+ }
+ else
+ {
+ pos->SetTurnSpeed( +avoidturn );
+ if( verbose ) printf( "turning left %2f\n", +avoidturn );
+ }
+ }
+
+ avoidcount--;
+
+ return true; // busy avoding obstacles
+ }
+
+ return false; // didn't have to avoid anything
+ }
+
+
+ void Work()
+ {
+ if( ! ObstacleAvoid() )
+ {
+ if( verbose ) puts( "Cruise" );
+
+ ModelGripper::config_t gdata = gripper->GetConfig();
+
+ //avoidcount = 0;
+ pos->SetXSpeed( cruisespeed );
+
+ Pose pose = pos->GetPose();
+
+ int x = (pose.x + 8) / 4;
+ int y = (pose.y + 8) / 4;
+
+ // oh what an awful bug - 5 hours to track this down. When using
+ // this controller in a world larger than 8*8 meters, a_goal can
+ // sometimes be NAN. Causing trouble WAY upstream.
+ if( x > 3 ) x = 3;
+ if( y > 3 ) y = 3;
+ if( x < 0 ) x = 0;
+ if( y < 0 ) y = 0;
+
+ double a_goal =
+ dtor( ( pos->GetFlagCount() || gdata.gripped ) ? have[y][x] :
need[y][x] );
+
+ // if we are low on juice - find the direction to the recharger instead
+ if( Hungry() )
+ {
+ //puts( "hungry - using refuel map" );
+
+ // use the refuel map
+ a_goal = dtor( refuel[y][x] );
+
+ if( charger_ahoy ) // I see a charger while hungry!
+ mode = MODE_DOCK;
+ }
+ else
+ {
+ if( ! at_dest )
+ {
+ if( gdata.beam[0] ) // inner break beam broken
+ gripper->CommandClose();
+ }
+ }
+
+ assert( ! isnan(a_goal ) );
+ assert( ! isnan(pose.a ) );
+
+ double a_error = normalize( a_goal - pose.a );
+
+ assert( ! isnan(a_error) );
+
+ pos->SetTurnSpeed( a_error );
+ }
+ }
+
+
+ // inspect the laser data and decide what to do
+ static int LaserUpdate( ModelLaser* laser, Robot* robot )
+ {
+ // if( laser->power_pack && laser->power_pack->charging )
+ // printf( "model %s power pack @%p is charging\n",
+ // laser->Token(), laser->power_pack );
+
+ uint32_t sample_count=0;
+ stg_laser_sample_t* scan = laser->GetSamples( &sample_count );
+
+ if( scan == NULL )
+ return 0;
+
+ Pose gpose = laser->GetGlobalPose();
+
+ // fill the dynamic map
+ double pts[2*sample_count];
+
+ // project hit points from each scan
+ // scan = this->scans;
+
+ int scan_points_count = 0;
+
+ //for(int i=0;i<this->scans_count;i++,scan++)
+ // {
+
+
+ stg_laser_cfg_t cfg = laser->GetConfig();
+
+ float b = -cfg.fov / 2.0;
+
+ for( unsigned int j=0; j<sample_count; j++ )
+ {
+ if( scan[j].range >= cfg.range_bounds.max )
+ continue;
+
+ double cs,sn;
+ cs = cos(gpose.a+b);
+ sn = sin(gpose.a+b);
+
+ double lx,ly;
+ lx = gpose.x + scan[j].range*cs;
+ ly = gpose.y + scan[j].range*sn;
+
+ //assert(this->scan_points_count*2 < this->scan_points_size);
+ pts[2*j ] = lx;
+ pts[2*j+1] = ly;
+ scan_points_count++;
+ }
+
+ //printf("setting %d hit points\n", this->scan_points_count);
+ plan_set_obstacles( robot->plan, pts, scan_points_count);
+
+
+
+ switch( robot->mode )
+ {
+ case MODE_DOCK:
+ //puts( "DOCK" );
+ robot->Dock();
+ break;
+
+ case MODE_WORK:
+ //puts( "WORK" );
+ robot->Work();
+ break;
+
+ case MODE_UNDOCK:
+ //puts( "UNDOCK" );
+ robot->UnDock();
+ break;
+
+ default:
+ printf( "unrecognized mode %u\n", robot->mode );
+ }
+
+ return 0;
+ }
+
+ bool Hungry()
+ {
+ return( pos->FindPowerPack()->ProportionRemaining() < 0.25 );
+ }
+
+ bool Full()
+ {
+ return( pos->FindPowerPack()->ProportionRemaining() > 0.95 );
+ }
+
+ static int PositionUpdate( ModelPosition* pos, Robot* robot )
+ {
+ Pose pose = pos->GetPose();
+
+ plan_t* plan = robot->plan;
+
+ //printf( "Pose: [%.2f %.2f %.2f %.2f]\n",
+ // pose.x, pose.y, pose.z, pose.a );
+
+ //printf( "planning from (%.2f %.2f) to (%.2f %.2f)\n",
+ // pose.x, pose.y, -4.0, 0.0 );
+
+
+ if( 1 )//! robot->plan_done )
+ {
+ if( plan_do_global( plan, pose.x, pose.y, -7,-7 ) < 0)
+ {
+ puts( "no global path" );
+ }
+ else
+ robot->plan_done = true;
+ }
+
+ //printf( "PATH_COUNT %d\n", plan->path_count );
+
+
+ plan_update_waypoints( plan, pose.x, pose.y );
+
+ //printf( "WAYPOINT_COUNT %d\n", plan->waypoint_count );
+
+
+ Waypoint* wps = NULL;
+ stg_color_t col = pos->GetColor();
+
+ if( plan->waypoint_count > 0 )
+ {
+ wps = new Waypoint[plan->waypoint_count];
+ for( int i=0; i<plan->waypoint_count; i++ )
+ {
+ //plan_convert_waypoint( plan, plan->path[i],
+ // &wps[i].pose.x,
+ // &wps[i].pose.y );
+
+ plan_convert_waypoint( plan, plan->waypoints[i],
+ &wps[i].pose.x,
+ &wps[i].pose.y );
+
+ wps[i].color = col;
+ }
+ }
+
+ Waypoint* old = pos->SetWaypoints( wps, plan->waypoint_count );
+
+ if( old )
+ delete[] old;
+
+ //pose.z += 0.0001;
+ //robot->pos->SetPose( pose );
+
+ if( pos->GetFlagCount() < payload &&
+ hypot( -7-pose.x, -7-pose.y ) < 2.0 )
+ {
+ if( ++robot->work_get > workduration )
+ {
+ // protect source from concurrent access
+ robot->source->Lock();
+
+ // transfer a chunk from source to robot
+ pos->PushFlag( robot->source->PopFlag() );
+ robot->source->Unlock();
+
+ robot->work_get = 0;
+ }
+ }
+
+ robot->at_dest = false;
+
+ if( hypot( 7-pose.x, 7-pose.y ) < 1.0 )
+ {
+ robot->at_dest = true;
+
+ robot->gripper->CommandOpen();
+
+ if( ++robot->work_put > workduration )
+ {
+ // protect sink from concurrent access
+ robot->sink->Lock();
+
+ //puts( "dropping" );
+ // transfer a chunk between robot and goal
+ robot->sink->PushFlag( pos->PopFlag() );
+ robot->sink->Unlock();
+
+ robot->work_put = 0;
+
+ }
+ }
+
+
+ return 0; // run again
+ }
+
+
+
+ static int FiducialUpdate( ModelFiducial* mod, Robot* robot )
+ {
+ robot->charger_ahoy = false;
+
+ for( unsigned int i = 0; i < mod->fiducial_count; i++ )
+ {
+ stg_fiducial_t* f = &mod->fiducials[i];
+
+ //printf( "fiducial %d is %d at %.2f m %.2f radians\n",
+ // i, f->id, f->range, f->bearing );
+
+ if( f->id == 2 ) // I see a charging station
+ {
+ // record that I've seen it and where it is
+ robot->charger_ahoy = true;
+ robot->charger_bearing = f->bearing;
+ robot->charger_range = f->range;
+ robot->charger_heading = f->geom.a;
+
+ //printf( "charger at %.2f radians\n", robot->charger_bearing );
+ break;
+ }
+ }
+
+ return 0; // run again
+ }
+
+ static int BlobFinderUpdate( ModelBlobfinder* blobmod, Robot* robot )
+ {
+ unsigned int blob_count = 0;
+ stg_blobfinder_blob_t* blobs = blobmod->GetBlobs( &blob_count );
+
+ if( blobs && (blob_count>0) )
+ {
+ printf( "%s sees %u blobs\n", blobmod->Token(), blob_count );
+ }
+
+ return 0;
+ }
+
+ static int GripperUpdate( ModelGripper* grip, Robot* robot )
+ {
+ ModelGripper::config_t gdata = grip->GetConfig();
+
+ printf( "BREAKBEAMS %s %s\n",
+ gdata.beam[0] ? gdata.beam[0]->Token() : "<null>",
+ gdata.beam[1] ? gdata.beam[1]->Token() : "<null>" );
+
+ printf( "CONTACTS %s %s\n",
+ gdata.contact[0] ? gdata.contact[0]->Token() : "<null>",
+ gdata.contact[1] ? gdata.contact[1]->Token() : "<null>");
+
+
+ return 0;
+ }
+
+
+};
+
+
+ //plan_t* Robot::plan = NULL;
+
+// Stage calls this when the model starts up
+extern "C" int Init( Model* mod )
+{
+ Robot* robot = new Robot( (ModelPosition*)mod,
+ mod->GetWorld()->GetModel( "source" ),
+ mod->GetWorld()->GetModel( "sink" ) );
+
+ return 0; //ok
+}
+
+
+
+
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
------------------------------------------------------------------------------
Apps built with the Adobe(R) Flex(R) framework and Flex Builder(TM) are
powering Web 2.0 with engaging, cross-platform capabilities. Quickly and
easily build your RIAs with Flex Builder, the Eclipse(TM)based development
software that enables intelligent coding and step-through debugging.
Download the free 60 day trial. http://p.sf.net/sfu/www-adobe-com
_______________________________________________
Playerstage-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-commit