amcl.cc

00001 /*
00002  *  Player - One Hell of a Robot Server
00003  *  Copyright (C) 2000 Brian Gerkey and others
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  *
00019  */
00021 //
00022 // Desc: Adaptive Monte-Carlo localization
00023 // Author: Andrew Howard
00024 // Date: 6 Feb 2003
00025 // CVS: $Id: amcl.cc 4293 2007-12-07 02:43:10Z gerkey $
00026 //
00027 // Theory of operation:
00028 //  TODO
00029 //
00030 // Requires: position (odometry)
00031 // Provides: localization
00032 //
00034 
00279 #ifdef HAVE_CONFIG_H
00280 #include "config.h"
00281 #endif
00282 
00283 #include <assert.h>
00284 #include <errno.h>
00285 #include <string.h>
00286 #include <math.h>
00287 #include <stdlib.h>       // for atoi(3)
00288 #include <sys/types.h>
00289 #include <unistd.h>
00290 #include <sys/time.h>
00291 
00292 #define PLAYER_ENABLE_TRACE 1
00293 #define PLAYER_ENABLE_MSG 1
00294 
00295 #include <libplayercore/playercore.h>
00296 #include <libplayercore/error.h>
00297 
00298 #include "amcl.h"
00299 // Sensors
00300 #include "amcl_odom.h"
00301 #include "amcl_laser.h"
00302 //#include "amcl_fiducial.h"
00303 //#include "amcl_gps.h"
00304 //#include "amcl_imu.h"
00305 
00306 #define MAX_HYPS 8
00307 
00309 // Create an instance of the driver
00310 Driver* AdaptiveMCL_Init( ConfigFile* cf, int section)
00311 {
00312   return ((Driver*) (new AdaptiveMCL(cf, section)));
00313 }
00314 
00315 
00317 // Register the driver
00318 void AdaptiveMCL_Register(DriverTable* table)
00319 {
00320   table->AddDriver("amcl", AdaptiveMCL_Init);
00321   return;
00322 }
00323 
00325 // Useful macros
00326 #define AMCL_DATASIZE MAX(sizeof(player_localize_data_t), sizeof(player_position_data_t))
00327 
00328 
00330 // Constructor
00331 AdaptiveMCL::AdaptiveMCL( ConfigFile* cf, int section)
00332     : Driver(cf, section)
00333 {
00334   int i;
00335   double u[3];
00336   AMCLSensor *sensor;
00337 
00338   memset(&this->localize_addr, 0, sizeof(player_devaddr_t));
00339   memset(&this->position_addr, 0, sizeof(player_devaddr_t));
00340 
00341   // Do we create a localize interface?
00342   if(cf->ReadDeviceAddr(&(this->localize_addr), section, "provides",
00343                         PLAYER_LOCALIZE_CODE, -1, NULL) == 0)
00344   {
00345     if(this->AddInterface(this->localize_addr))
00346     {
00347       this->SetError(-1);
00348       return;
00349     }
00350   }
00351 
00352   // Do we create a position interface?
00353   if(cf->ReadDeviceAddr(&(this->position_addr), section, "provides",
00354                         PLAYER_POSITION2D_CODE, -1, NULL) == 0)
00355   {
00356     if(this->AddInterface(this->position_addr))
00357     {
00358       this->SetError(-1);
00359       return;
00360     }
00361   }
00362 
00363   this->init_sensor = -1;
00364   this->action_sensor = -1;
00365   this->sensor_count = 0;
00366 
00367   player_devaddr_t odom_addr;
00368   player_devaddr_t laser_addr;
00369   //player_devaddr_t fiducial_addr;
00370 
00371   // Create odometry sensor
00372   if(cf->ReadDeviceAddr(&odom_addr, section, "requires",
00373                         PLAYER_POSITION2D_CODE, -1, "odometry") == 0)
00374   {
00375     this->action_sensor = this->sensor_count;
00376     if (cf->ReadInt(section, "odom_init", 1))
00377       this->init_sensor = this->sensor_count;
00378     sensor = new AMCLOdom(*this,odom_addr);
00379     sensor->is_action = 1;
00380     this->sensors[this->sensor_count++] = sensor;
00381   }
00382 
00383   // Create laser sensor
00384   if(cf->ReadDeviceAddr(&laser_addr, section, "requires",
00385                         PLAYER_LASER_CODE, -1, NULL) == 0)
00386   {
00387     sensor = new AMCLLaser(*this,laser_addr);
00388     sensor->is_action = 0;
00389     this->sensors[this->sensor_count++] = sensor;
00390   }
00391 
00392 #if 0
00393   // Create fiducial sensor
00394   if(cf->ReadDeviceAddr(&fiducial_addr, section, "requires",
00395                         PLAYER_FIDUCIAL_CODE, -1, NULL) == 0)
00396   {
00397     sensor = new AMCLFiducial(fiducial_addr);
00398     sensor->is_action = 0;
00399     this->sensors[this->sensor_count++] = sensor;
00400   }
00401 #endif
00402 
00403   /*
00404   // Create GPS sensor
00405   if (cf->ReadInt(section, "gps_index", -1) >= 0)
00406   {
00407     if (cf->ReadInt(section, "gps_init", 1))
00408       this->init_sensor = this->sensor_count;
00409     this->sensors[this->sensor_count++] = new AMCLGps();
00410   }
00411 
00412   // Create IMU sensor
00413   if (cf->ReadInt(section, "imu_index", -1) >= 0)
00414     this->sensors[this->sensor_count++] = new AMCLImu();
00415   */
00416 
00417   // We need an "action" sensor
00418   if(this->action_sensor < 0)
00419   {
00420     PLAYER_ERROR("No action sensor");
00421     this->SetError(-1);
00422     return;
00423   }
00424 
00425   // Load sensor settings
00426   for (i = 0; i < this->sensor_count; i++)
00427     this->sensors[i]->Load(cf, section);
00428 
00429   // Create the sensor data queue
00430   this->q_len = 0;
00431   this->q_start = 0;
00432   this->q_size = 20000;
00433   this->q_data = new AMCLSensorData*[this->q_size];
00434   memset(this->q_data,0,sizeof(AMCLSensorData*)*this->q_size);
00435 
00436   // Particle filter settings
00437   this->pf = NULL;
00438   this->pf_min_samples = cf->ReadInt(section, "pf_min_samples", 100);
00439   this->pf_max_samples = cf->ReadInt(section, "pf_max_samples", 10000);
00440 
00441   // Adaptive filter parameters
00442   this->pf_err = cf->ReadFloat(section, "pf_err", 0.01);
00443   this->pf_z = cf->ReadFloat(section, "pf_z", 3);
00444 
00445   // Initial pose estimate
00446   this->pf_init_pose_mean = pf_vector_zero();
00447   this->pf_init_pose_mean.v[0] = cf->ReadTupleLength(section, "init_pose", 0, 0);
00448   this->pf_init_pose_mean.v[1] = cf->ReadTupleLength(section, "init_pose", 1, 0);
00449   this->pf_init_pose_mean.v[2] = cf->ReadTupleAngle(section, "init_pose", 2, 0);
00450 
00451   // Initial pose covariance
00452   u[0] = cf->ReadTupleLength(section, "init_pose_var", 0, 1);
00453   u[1] = cf->ReadTupleLength(section, "init_pose_var", 1, 1);
00454   u[2] = cf->ReadTupleAngle(section, "init_pose_var", 2, 2*M_PI);
00455   this->pf_init_pose_cov = pf_matrix_zero();
00456   this->pf_init_pose_cov.m[0][0] = u[0] * u[0];
00457   this->pf_init_pose_cov.m[1][1] = u[1] * u[1];
00458   this->pf_init_pose_cov.m[2][2] = u[2] * u[2];
00459 
00460   // Update distances
00461   this->min_dr = cf->ReadTupleLength(section, "update_thresh", 0, 0.20);
00462   this->min_da = cf->ReadTupleAngle(section, "update_thresh", 1, M_PI/6);
00463 
00464   // Initial hypothesis list
00465   this->hyp_count = 0;
00466   this->hyp_alloc = 0;
00467   this->hyps = NULL;
00468   pthread_mutex_init(&this->best_hyp_lock,NULL);
00469 
00470 #ifdef INCLUDE_RTKGUI
00471   // Enable debug gui
00472   this->enable_gui = cf->ReadInt(section, "enable_gui", 0);
00473 #endif
00474 
00475 
00476   return;
00477 }
00478 
00479 
00481 // Destructor
00482 AdaptiveMCL::~AdaptiveMCL(void)
00483 {
00484   int i;
00485 
00486   // Delete sensor data queue
00487   delete[] this->q_data;
00488   free(hyps);
00489 
00490   // Delete sensors
00491   for (i = 0; i < this->sensor_count; i++)
00492   {
00493     this->sensors[i]->Unload();
00494     delete this->sensors[i];
00495   }
00496   this->sensor_count = 0;
00497   pthread_mutex_destroy(&this->best_hyp_lock);
00498 
00499   return;
00500 }
00501 
00502 
00504 // Set up the device (called by server thread).
00505 int AdaptiveMCL::Setup(void)
00506 {
00507   PLAYER_MSG0(2, "setup");
00508 
00509   // Create the particle filter
00510   assert(this->pf == NULL);
00511   this->pf = pf_alloc(this->pf_min_samples, this->pf_max_samples);
00512   this->pf->pop_err = this->pf_err;
00513   this->pf->pop_z = this->pf_z;
00514 
00515   // Start sensors
00516   for (int i = 0; i < this->sensor_count; i++)
00517     if (this->sensors[i]->Setup() < 0)
00518     {
00519       PLAYER_ERROR1 ("failed to setup sensor %d", i);
00520       return -1;
00521     }
00522 
00523 
00524   // Start the driver thread.
00525   PLAYER_MSG0(2, "running");
00526   this->StartThread();
00527 
00528   return 0;
00529 }
00530 
00531 
00533 // Shutdown the device (called by server thread).
00534 int AdaptiveMCL::Shutdown(void)
00535 {
00536   int i;
00537 
00538   PLAYER_MSG0(2, "shutting down");
00539 
00540   // Stop the driver thread.
00541   this->StopThread();
00542 
00543   // Stop sensors
00544   for (i = 0; i < this->sensor_count; i++)
00545     this->sensors[i]->Shutdown();
00546 
00547   // Delete the particle filter
00548   pf_free(this->pf);
00549   this->pf = NULL;
00550 
00551   PLAYER_MSG0(2, "shutdown done");
00552   return 0;
00553 }
00554 
00555 
00557 // Check for updated sensor data
00558 void AdaptiveMCL::UpdateSensorData(void)
00559 {
00560 /*
00561   int i;
00562   AMCLSensorData *data;
00563   pf_vector_t pose, delta;
00564 
00565   assert(this->action_sensor >= 0 && this->action_sensor < this->sensor_count);
00566 
00567   // Check the sensors for new data
00568   for (i = 0; i < this->sensor_count; i++)
00569   {
00570     data = this->sensors[i]->GetData();
00571     if (data != NULL)
00572     {
00573       this->Push(data);
00574       if(this->sensors[i]->is_action)
00575       {
00576         if (this->pf_init)
00577         {
00578           // HACK: Assume that the action sensor is odometry
00579           pose = ((AMCLOdomData*)data)->pose;
00580 
00581           // Compute change in pose
00582           delta = pf_vector_coord_sub(pose, this->pf_odom_pose);
00583 
00584           // Publish new pose estimate
00585           this->PutDataPosition(delta);
00586         }
00587       }
00588     }
00589   }
00590   return;*/
00591 }
00592 
00593 
00595 // Push data onto the filter queue.
00596 void AdaptiveMCL::Push(AMCLSensorData *data)
00597 {
00598   int i;
00599 
00600   this->Lock();
00601 
00602   if (this->q_len >= this->q_size)
00603   {
00604     this->Unlock();
00605     PLAYER_ERROR("queue overflow");
00606     return;
00607   }
00608   i = (this->q_start + this->q_len++) % this->q_size;
00609 
00610   this->q_data[i] = data;
00611 
00612   this->Unlock();
00613   return;
00614 }
00615 
00616 
00618 // Take a peek at the queue
00619 AMCLSensorData *AdaptiveMCL::Peek(void)
00620 {
00621   int i;
00622 
00623   this->Lock();
00624 
00625   if (this->q_len == 0)
00626   {
00627     this->Unlock();
00628     return NULL;
00629   }
00630   i = this->q_start % this->q_size;
00631 
00632   this->Unlock();
00633   return this->q_data[i];
00634 }
00635 
00636 
00638 // Pop data from the filter queue
00639 AMCLSensorData *AdaptiveMCL::Pop(void)
00640 {
00641   int i;
00642 
00643   this->Lock();
00644 
00645   if (this->q_len == 0)
00646   {
00647     this->Unlock();
00648     return NULL;
00649   }
00650   i = this->q_start++ % this->q_size;
00651   this->q_len--;
00652 
00653   this->Unlock();
00654   return this->q_data[i];
00655 }
00656 
00657 
00659 // Main function for device thread
00660 void AdaptiveMCL::Main(void)
00661 {
00662   this->q_len = 0;
00663 
00664   // No data has yet been pushed, and the
00665   // filter has not yet been initialized
00666   this->pf_init = false;
00667 
00668   // Initial hypothesis list
00669   this->hyp_count = 0;
00670 
00671   // WARNING: this only works for Linux
00672   // Run at a lower priority
00673   nice(10);
00674 
00675 #ifdef INCLUDE_RTKGUI
00676   pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
00677 
00678   // Start the GUI
00679   if (this->enable_gui)
00680     this->SetupGUI();
00681 #endif
00682 
00683 #ifdef INCLUDE_OUTFILE
00684   // Open file for logging results
00685   this->outfile = fopen("amcl.out", "w+");
00686 #endif
00687 
00688   while (true)
00689   {
00690 #ifdef INCLUDE_RTKGUI
00691     if (this->enable_gui)
00692     {
00693       rtk_canvas_render(this->canvas);
00694       rtk_app_main_loop(this->app);
00695     }
00696     pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
00697 #endif
00698 
00699     // Sleep for 1ms (will actually take longer than this).
00700     const struct timespec sleeptime = {0, 1000000L};
00701     nanosleep(&sleeptime, NULL);
00702 
00703     // Test if we are supposed to cancel this thread.  This is the
00704     // only place we can cancel from if we are running the GUI.
00705     pthread_testcancel();
00706 
00707     // Process any pending messages
00708     this->ProcessMessages();
00709     //UpdateSensorData();
00710 
00711 #ifdef INCLUDE_RTKGUI
00712     pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
00713 #endif
00714 
00715     // Initialize the filter if we havent already done so
00716     if (!this->pf_init)
00717       this->InitFilter();
00718 
00719     // Update the filter
00720     if (this->UpdateFilter())
00721     {
00722 #ifdef INCLUDE_OUTFILE
00723       // Save the error values
00724       pf_vector_t mean;
00725       double var;
00726 
00727       pf_get_cep_stats(pf, &mean, &var);
00728 
00729       printf("amcl %.3f %.3f %f\n", mean.v[0], mean.v[1], mean.v[2] * 180 / M_PI);
00730 
00731       fprintf(this->outfile, "%d.%03d unknown 6665 localize 01 %d.%03d ",
00732               0, 0, 0, 0);
00733       fprintf(this->outfile, "1.0 %e %e %e %e 0 0 0 0 0 0 0 0 \n",
00734               mean.v[0], mean.v[1], mean.v[2], var);
00735       fflush(this->outfile);
00736 #endif
00737     }
00738   }
00739   return;
00740 }
00741 
00742 
00744 // Thread finalization
00745 void AdaptiveMCL::MainQuit()
00746 {
00747 #ifdef INCLUDE_RTKGUI
00748   // Stop the GUI
00749   if (this->enable_gui)
00750     this->ShutdownGUI();
00751 #endif
00752 
00753 #ifdef INCLUDE_OUTFILE
00754   // Close the log file
00755   fclose(this->outfile);
00756 #endif
00757 
00758   return;
00759 }
00760 
00761 
00763 // Initialize the filter
00764 void AdaptiveMCL::InitFilter(void)
00765 {
00766   ::pf_init(this->pf, this->pf_init_pose_mean, this->pf_init_pose_cov);
00767 
00768   return;
00769 }
00770 
00771 
00773 // Update the filter with new sensor data
00774 bool AdaptiveMCL::UpdateFilter(void)
00775 {
00776   int i;
00777   double ts;
00778   double weight;
00779   pf_vector_t pose, delta;
00780   pf_vector_t pose_mean;
00781   pf_matrix_t pose_cov;
00782   amcl_hyp_t *hyp;
00783   AMCLSensorData *data;
00784   bool update;
00785 
00786   //printf("q len %d\n", this->q_len);
00787 
00788   // Get the action data
00789   data = this->Pop();
00790   if (data == NULL)
00791     return false;
00792   if (!data->sensor->is_action)
00793   {
00794     delete data;
00795     return false;
00796   }
00797 
00798   // Use the action timestamp
00799   ts = data->time;
00800 
00801   // HACK
00802   pose = ((AMCLOdomData*) data)->pose;
00803   delta = pf_vector_zero();
00804   update = false;
00805 
00806   //printf("odom pose\n");
00807   //pf_vector_fprintf(pose, stdout, "%.3f");
00808 
00809   // Compute change in pose
00810   if (this->pf_init)
00811   {
00812     // Compute change in pose
00813     delta = pf_vector_coord_sub(pose, this->pf_odom_pose);
00814 
00815     // See if we should update the filter
00816     update = fabs(delta.v[0]) > this->min_dr ||
00817       fabs(delta.v[1]) > this->min_dr ||
00818       fabs(delta.v[2]) > this->min_da;
00819   }
00820 
00821   // If the filter has not been initialized
00822   if (!this->pf_init)
00823   {
00824     // Discard this action data
00825     delete data; data = NULL;
00826 
00827     // Pose at last filter update
00828     this->pf_odom_pose = pose;
00829 
00830     // Filter is now initialized
00831     this->pf_init = true;
00832 
00833     // Should update sensor data
00834     update = true;
00835 
00836     //printf("init\n");
00837     //pf_vector_fprintf(pose, stdout, "%.3f");
00838   }
00839 
00840   // If the robot has moved, update the filter
00841   else if (this->pf_init && update)
00842   {
00843     //printf("pose\n");
00844     //pf_vector_fprintf(pose, stdout, "%.3f");
00845 
00846     // HACK
00847     // Modify the delta in the action data so the filter gets
00848     // updated correctly
00849     ((AMCLOdomData*) data)->delta = delta;
00850 
00851     // Use the action data to update the filter
00852     data->sensor->UpdateAction(this->pf, data);
00853     delete data; data = NULL;
00854 
00855     // Pose at last filter update
00856     //this->pf_odom_pose = pose;
00857   }
00858   else
00859   {
00860     // Discard this action data
00861     delete data; data = NULL;
00862   }
00863 
00864   bool processed_first_sensor = false;
00865   // If the robot has moved, update the filter
00866   if (update)
00867   {
00868     // Process the remaining sensor data up to the next action data
00869     while (1)
00870     {
00871       data = this->Peek();
00872       if ((data == NULL ) || (data->sensor->is_action))
00873       {
00874         // HACK: Discard action data until we've processed at least one
00875         // sensor reading
00876         if(!processed_first_sensor)
00877         {
00878           if(data)
00879           {
00880             data = this->Pop();
00881             assert(data);
00882             delete data;
00883           }
00884           // avoid a busy loop while waiting for a sensor reading to
00885           // process
00886           //return false;
00887           usleep(1000);
00888           ProcessMessages();
00889           //UpdateSensorData();
00890           continue;
00891         }
00892         else
00893           break;
00894       }
00895       data = this->Pop();
00896       assert(data);
00897 
00898       // Use the data to update the filter
00899       // HACK: only use the first sensor reading
00900       if(!processed_first_sensor)
00901       {
00902         data->sensor->UpdateSensor(this->pf, data);
00903         processed_first_sensor = true;
00904         this->pf_odom_pose = pose;
00905       }
00906 
00907 #ifdef INCLUDE_RTKGUI
00908       // Draw the current sensor data
00909       if (this->enable_gui)
00910         data->sensor->UpdateGUI(this->canvas, this->robot_fig, data);
00911 #endif
00912 
00913       // Discard data
00914       delete data; data = NULL;
00915     }
00916 
00917     // Resample the particles
00918     pf_update_resample(this->pf);
00919 
00920     // Read out the current hypotheses
00921     double max_weight = 0.0;
00922     pf_vector_t max_weight_pose={{0.0,0.0,0.0}};
00923     this->hyp_count = 0;
00924     //for (i = 0; (size_t) i < sizeof(this->hyps) / sizeof(this->hyps[0]); i++)
00925     for (i = 0; MAX_HYPS; i++)
00926     {
00927       if (!pf_get_cluster_stats(this->pf, i, &weight, &pose_mean, &pose_cov))
00928         break;
00929 
00930       //pf_vector_fprintf(pose_mean, stdout, "%.3f");
00931 
00932       if (this->hyp_count +1 > this->hyp_alloc)
00933       {
00934         this->hyp_alloc = this->hyp_count+1;
00935         this->hyps = (amcl_hyp_t*)realloc(this->hyps, sizeof(amcl_hyp_t)*this->hyp_alloc);
00936       }
00937       hyp = this->hyps + this->hyp_count++;
00938       hyp->weight = weight;
00939       hyp->pf_pose_mean = pose_mean;
00940       hyp->pf_pose_cov = pose_cov;
00941 
00942       if(hyp->weight > max_weight)
00943       {
00944         max_weight = hyp->weight;
00945         max_weight_pose = hyp->pf_pose_mean;
00946       }
00947     }
00948 
00949     if(max_weight > 0.0)
00950     {
00951       pthread_mutex_lock(&this->best_hyp_lock);
00952       this->best_hyp = max_weight_pose;
00953       pthread_mutex_unlock(&this->best_hyp_lock);
00954     }
00955 
00956 #ifdef INCLUDE_RTKGUI
00957     // Update the GUI
00958     if (this->enable_gui)
00959       this->UpdateGUI();
00960 #endif
00961 
00962     // Encode data to send to server
00963     this->PutDataLocalize(ts);
00964     delta = pf_vector_zero();
00965     this->PutDataPosition(delta,ts);
00966 
00967     return true;
00968   }
00969   else
00970   {
00971     // Process the remaining sensor data up to the next action data
00972     while (1)
00973     {
00974       data = this->Peek();
00975       if (data == NULL)
00976         break;
00977       if (data->sensor->is_action)
00978         break;
00979       data = this->Pop();
00980       assert(data);
00981       delete data; data = NULL;
00982     }
00983 
00984     // Encode data to send to server; only the position interface
00985     // gets updated every cycle
00986     this->PutDataPosition(delta,ts);
00987 
00988     return false;
00989   }
00990 }
00991 
00992 // this function will be passed to qsort(3) to sort the hypoths before
00993 // sending them out.  the idea is to sort them in descending order of
00994 // weight.
00995 int
00996 hypoth_compare(const void* h1, const void* h2)
00997 {
00998   const player_localize_hypoth_t* hyp1 = (const player_localize_hypoth_t*)h1;
00999   const player_localize_hypoth_t* hyp2 = (const player_localize_hypoth_t*)h2;
01000   if(hyp1->alpha < hyp2->alpha)
01001     return(1);
01002   else if(hyp1->alpha == hyp2->alpha)
01003     return(0);
01004   else
01005     return(-1);
01006 }
01007 
01008 
01010 // Output data on the localize interface
01011 void AdaptiveMCL::PutDataLocalize(double time)
01012 {
01013   int i;
01014   amcl_hyp_t *hyp;
01015   pf_vector_t pose;
01016   pf_matrix_t pose_cov;
01017   //size_t datalen;
01018   player_localize_data_t data;
01019 
01020   // Record the number of pending observations
01021   data.pending_count = 0;
01022   data.pending_time = 0.0;
01023 
01024   // Encode the hypotheses
01025   data.hypoths_count = this->hyp_count;
01026   data.hypoths = new player_localize_hypoth_t[data.hypoths_count];
01027   for (i = 0; i < this->hyp_count; i++)
01028   {
01029     hyp = this->hyps + i;
01030 
01031     // Get the current estimate
01032     pose = hyp->pf_pose_mean;
01033     pose_cov = hyp->pf_pose_cov;
01034 
01035     // Check for bad values
01036     if (!pf_vector_finite(pose))
01037     {
01038       pf_vector_fprintf(pose, stderr, "%e");
01039       assert(0);
01040     }
01041     if (!pf_matrix_finite(pose_cov))
01042     {
01043       pf_matrix_fprintf(pose_cov, stderr, "%e");
01044       assert(0);
01045     }
01046 
01047     data.hypoths[i].alpha = hyp->weight;
01048 
01049     data.hypoths[i].mean.px = pose.v[0];
01050     data.hypoths[i].mean.py = pose.v[1];
01051     data.hypoths[i].mean.pa = pose.v[2];
01052 
01053     data.hypoths[i].cov[0] = pose_cov.m[0][0];
01054     data.hypoths[i].cov[1] = pose_cov.m[1][1];
01055     data.hypoths[i].cov[2] = pose_cov.m[2][2];
01056   }
01057 
01058   // sort according to weight
01059   qsort((void*)data.hypoths,data.hypoths_count,
01060         sizeof(player_localize_hypoth_t),&hypoth_compare);
01061 
01062   // Push data out
01063   this->Publish(this->localize_addr, 
01064                 PLAYER_MSGTYPE_DATA,
01065                 PLAYER_LOCALIZE_DATA_HYPOTHS,
01066                 (void*)&data);
01067   delete [] data.hypoths;
01068 }
01069 
01070 
01072 // Output data on the position interface
01073 void AdaptiveMCL::PutDataPosition(pf_vector_t delta, double time)
01074 {
01075   pf_vector_t pose;
01076   player_position2d_data_t data;
01077 
01078   memset(&data,0,sizeof(data));
01079 
01080   // Get the current estimate
01081   pthread_mutex_lock(&this->best_hyp_lock);
01082   pose = this->best_hyp;
01083   pthread_mutex_unlock(&this->best_hyp_lock);
01084 
01085   // Add the accumulated odometric change
01086   pose = pf_vector_coord_add(delta, pose);
01087 
01088   data.pos.px = pose.v[0];
01089   data.pos.py = pose.v[1];
01090   data.pos.pa = pose.v[2];
01091 
01092   // Push data out
01093   this->Publish(this->position_addr, 
01094                 PLAYER_MSGTYPE_DATA,
01095                 PLAYER_POSITION2D_DATA_STATE,
01096                 (void*)&data,sizeof(data),&time);
01097 }
01098 
01099 // MessageHandler
01100 int
01101 AdaptiveMCL::ProcessMessage(QueuePointer & resp_queue,
01102                             player_msghdr * hdr,
01103                             void * data)
01104 {
01105   player_localize_set_pose_t* setposereq;
01106 
01107   // Is it a request to set the filter's pose?
01108   if(Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ,
01109                            PLAYER_LOCALIZE_REQ_SET_POSE,
01110                            this->localize_addr))
01111   {
01112     setposereq = (player_localize_set_pose_t*)data;
01113 
01114     pf_vector_t pose;
01115     pf_matrix_t cov;
01116 
01117     pose.v[0] = setposereq->mean.px;
01118     pose.v[1] = setposereq->mean.py;
01119     pose.v[2] = setposereq->mean.pa;
01120 
01121     cov = pf_matrix_zero();
01122     cov.m[0][0] = setposereq->cov[0];
01123     cov.m[1][1] = setposereq->cov[1];
01124     cov.m[2][2] = setposereq->cov[2];
01125 
01126     // Re-initialize the filter
01127     this->pf_init_pose_mean = pose;
01128     this->pf_init_pose_cov = cov;
01129     this->pf_init = false;
01130 
01131     // Send an ACK
01132     this->Publish(this->localize_addr, resp_queue,
01133                   PLAYER_MSGTYPE_RESP_ACK,
01134                   PLAYER_LOCALIZE_REQ_SET_POSE);
01135     return(0);
01136   }
01137   // Is it a request for the current particle set?
01138   else if(Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ,
01139                                 PLAYER_LOCALIZE_REQ_GET_PARTICLES,
01140                                 this->localize_addr))
01141   {
01142     pf_vector_t mean;
01143     double var;
01144     player_localize_get_particles_t resp;
01145     pf_sample_set_t *set;
01146     pf_sample_t *sample;
01147     size_t i;
01148 
01149     pf_get_cep_stats(this->pf, &mean, &var);
01150 
01151     resp.mean.px = mean.v[0];
01152     resp.mean.py = mean.v[1];
01153     resp.mean.pa = mean.v[2];
01154     resp.variance = var;
01155 
01156     set = this->pf->sets + this->pf->current_set;
01157 
01158     resp.particles_count = set->sample_count;
01159     resp.particles = new player_localize_particle_t [resp.particles_count];
01160 
01161     // TODO: pick representative particles
01162     for(i=0;i<resp.particles_count;i++)
01163     {
01164       sample = set->samples + i;
01165       resp.particles[i].pose.px = sample->pose.v[0];
01166       resp.particles[i].pose.py = sample->pose.v[1];
01167       resp.particles[i].pose.pa = sample->pose.v[2];
01168       resp.particles[i].alpha = sample->weight;
01169     }
01170 
01171     this->Publish(this->localize_addr, resp_queue,
01172                   PLAYER_MSGTYPE_RESP_ACK,
01173                   PLAYER_LOCALIZE_REQ_GET_PARTICLES,
01174                   (void*)&resp);
01175     delete [] resp.particles;
01176     return(0);
01177   } else if(Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ,
01178                                   PLAYER_POSITION2D_REQ_GET_GEOM, device_addr))
01179   {
01180     assert(hdr->size == 0);
01181     ProcessGeom(resp_queue, hdr);
01182     return(0);
01183   }
01184 
01185   // pass on the rest of the messages to the sensors
01186   for (int i = 0; i < this->sensor_count; i++)
01187   {
01188     int ret = this->sensors[i]->ProcessMessage(resp_queue, hdr, data);
01189     if (ret >= 0)
01190         return ret;
01191   }
01192 
01193   return(-1);
01194 }
01195 
01196 void
01197 AdaptiveMCL::ProcessGeom(QueuePointer &resp_queue, player_msghdr_t* hdr)
01198 {
01199   player_position2d_geom_t geom={{0}};
01200   // just return a point so we don't get errors from playerv
01201   geom.size.sl = 0.01;
01202   geom.size.sw = 0.01;
01203 
01204   Publish(this->position_addr, resp_queue,
01205           PLAYER_MSGTYPE_RESP_ACK,
01206           PLAYER_POSITION2D_REQ_GET_GEOM,
01207           &geom, sizeof(geom), NULL);
01208 }
01209 
01210 #ifdef INCLUDE_RTKGUI
01211 
01213 // Set up the GUI
01214 int AdaptiveMCL::SetupGUI(void)
01215 {
01216   int i;
01217 
01218   // Initialize RTK
01219   rtk_init(NULL, NULL);
01220 
01221   this->app = rtk_app_create();
01222 
01223   this->canvas = rtk_canvas_create(this->app);
01224   rtk_canvas_title(this->canvas, "AdaptiveMCL");
01225 
01226   /* TODO: fix
01227   if (this->map != NULL)
01228   {
01229     rtk_canvas_size(this->canvas, this->map->size_x, this->map->size_y);
01230     rtk_canvas_scale(this->canvas, this->map->scale, this->map->scale);
01231   }
01232   else
01233   */
01234   {
01235     rtk_canvas_size(this->canvas, 400, 400);
01236     rtk_canvas_scale(this->canvas, 0.1, 0.1);
01237   }
01238 
01239   this->map_fig = rtk_fig_create(this->canvas, NULL, -1);
01240   this->pf_fig = rtk_fig_create(this->canvas, this->map_fig, 5);
01241 
01242   /* FIX
01243   // Draw the map
01244   if (this->map != NULL)
01245   {
01246     map_draw_occ(this->map, this->map_fig);
01247     //map_draw_cspace(this->map, this->map_fig);
01248 
01249     // HACK; testing
01250     map_draw_wifi(this->map, this->map_fig, 0);
01251   }
01252   */
01253 
01254   rtk_fig_line(this->map_fig, 0, -100, 0, +100);
01255   rtk_fig_line(this->map_fig, -100, 0, +100, 0);
01256 
01257   // Best guess figure
01258   this->robot_fig = rtk_fig_create(this->canvas, NULL, 9);
01259 
01260   // Draw the robot
01261   rtk_fig_color(this->robot_fig, 0.7, 0, 0);
01262   rtk_fig_rectangle(this->robot_fig, 0, 0, 0, 0.40, 0.20, 0);
01263 
01264   // TESTING
01265   //rtk_fig_movemask(this->robot_fig, RTK_MOVE_TRANS | RTK_MOVE_ROT);
01266 
01267   // Initialize the sensor GUI's
01268   for (i = 0; i < this->sensor_count; i++)
01269     this->sensors[i]->SetupGUI(this->canvas, this->robot_fig);
01270 
01271   rtk_app_main_init(this->app);
01272 
01273   return 0;
01274 }
01275 
01276 
01278 // Shut down the GUI
01279 int AdaptiveMCL::ShutdownGUI(void)
01280 {
01281   int i;
01282 
01283   // Finalize the sensor GUI's
01284   for (i = 0; i < this->sensor_count; i++)
01285     this->sensors[i]->ShutdownGUI(this->canvas, this->robot_fig);
01286 
01287   rtk_fig_destroy(this->robot_fig);
01288   rtk_fig_destroy(this->pf_fig);
01289   rtk_fig_destroy(this->map_fig);
01290 
01291   rtk_canvas_destroy(this->canvas);
01292   rtk_app_destroy(this->app);
01293 
01294   rtk_app_main_term(this->app);
01295 
01296   return 0;
01297 }
01298 
01299 
01301 // Update the GUI
01302 void AdaptiveMCL::UpdateGUI(void)
01303 {
01304   rtk_fig_clear(this->pf_fig);
01305   rtk_fig_color(this->pf_fig, 0, 0, 1);
01306   pf_draw_samples(this->pf, this->pf_fig, 1000);
01307   pf_draw_cep_stats(this->pf, this->pf_fig);
01308   //rtk_fig_color(this->pf_fig, 0, 1, 0);
01309   //pf_draw_stats(this->pf, this->pf_fig);
01310   //pf_draw_hist(this->pf, this->pf_fig);
01311 
01312   // Draw the best pose estimate
01313   this->DrawPoseEst();
01314 
01315   return;
01316 }
01317 
01318 
01320 // Draw the current best pose estimate
01321 void AdaptiveMCL::DrawPoseEst()
01322 {
01323   int i;
01324   double max_weight;
01325   amcl_hyp_t *hyp;
01326   pf_vector_t pose;
01327 
01328   this->Lock();
01329 
01330   max_weight = -1;
01331   for (i = 0; i < this->hyp_count; i++)
01332   {
01333     hyp = this->hyps + i;
01334 
01335     if (hyp->weight > max_weight)
01336     {
01337       max_weight = hyp->weight;
01338       pose = hyp->pf_pose_mean;
01339     }
01340   }
01341 
01342   this->Unlock();
01343 
01344   if (max_weight < 0.0)
01345     return;
01346 
01347   // Shift the robot figure
01348   rtk_fig_origin(this->robot_fig, pose.v[0], pose.v[1], pose.v[2]);
01349 
01350   return;
01351 }
01352 
01353 #endif
01354 
01355 

Last updated 12 September 2005 21:38:45