(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Example of using a particle filter for localization in ROS by bfl library

Description: The tutorial demonstrates how to use the bfl library to create a particle filter for ROS. This particle filter will be used to track the pose of a robot against a known map. In this tutorial we will use the Gazebo model of AR.Drone equipmented three ultrasonic range sensors. However we can use this tutorial also with real AR.Drone provided that it is equipped with three ultrasonic range sensors or with any other model of robot.

Keywords: particle filter, bfl

Tutorial Level: ADVANCED

Prerequisite

As aim of the tutorial is to show how to use the bfl library in ROS, so I strongly recommend primarily to read the getting started guide of bfl library.

To communicate with AR.Drone you need this fork of “ardrone_autonomy” package. This fork contains the code which allow to receive data from ultrasonic ranges in ROS.

Also you need to install “gazebo_simulation” package from “ardroneum” repository.

Creating the particle filter in ROS

This section will guide you step-by-step through the implementation of a particle filter using a nonlinear system model and a non linear measurement model in ROS.

Creating the package

The first step is to create a new package which depends on ros_cpp, std_msgs, nav_msgs, bfl, geometry_msgs and ardrone_autonomy.

The second step is to add the following lines to CMakeList.txt.

# bfl (Bayesian Filtering Library) is a third party package that uses pkg-config
find_package(PkgConfig)
pkg_check_modules(BFL REQUIRED orocos-bfl)
include_directories(${BFL_INCLUDE_DIRS}/bfl)
message("BFL include dirs:" ${BFL_INCLUDE_DIRS})
message("BFL library dirs:" ${BFL_LIBRARY_DIRS})
link_directories(${BFL_LIBRARY_DIRS})

Creating of non linear system model

In this tutorial state of robot includes two coordinates (x, y) and orientation (theta). So the nonlinear system model is same as model described in this tutorial.

First of all, we have to create a new class inherited from ConditionalPdf which will implement general nonlinear conditional probability density function (pdf) which represents the probability of the predicted position given the current position of the mobile robot.

   1 #ifndef __NON_LINEAR_SYSTEM_MOBILE__
   2 #define __NON_LINEAR_SYSTEM_MOBILE__
   3 
   4 #include <pdf/conditionalpdf.h>
   5 #include <pdf/gaussian.h>
   6 
   7 namespace BFL
   8 {
   9  /// NonLinear Conditional Gaussian
  10  class NonlinearSystemPdf : public ConditionalPdf<MatrixWrapper::ColumnVector, MatrixWrapper::ColumnVector>
  11    {
  12    public:
  13      /// Constructor
  14      /** @param additiveNoise Pdf representing the additive Gaussian uncertainty
  15      */
  16      NonlinearSystemPdf( const Gaussian& additiveNoise);
  17 
  18      /// Destructor
  19      virtual ~NonlinearSystemPdf();
  20 
  21      // implement this virtual function for system model of a particle filter
  22      virtual bool SampleFrom (Sample<MatrixWrapper::ColumnVector>& one_sample, int method=DEFAULT, void * args=NULL) const;
  23 
  24    private:
  25      Gaussian _additiveNoise;
  26 
  27    };
  28 
  29 } // End namespace BFL
  30 
  31 #endif //
  32 

In this class we have to implement the ‘SampleFrom’ virtual function.

   1 #include "nonlinearSystemPdf.h"
   2 #include <wrappers/rng/rng.h> // Wrapper around several rng libraries
   3 
   4 #define SYSMODEL_NUMCONDARGUMENTS_MOBILE 2
   5 #define SYSMODEL_DIMENSION_MOBILE        3
   6 
   7 namespace BFL
   8 {
   9  using namespace MatrixWrapper;
  10 
  11  NonlinearSystemPdf::NonlinearSystemPdf(const Gaussian& additiveNoise)
  12    : ConditionalPdf<ColumnVector,ColumnVector>(SYSMODEL_DIMENSION_MOBILE,SYSMODEL_NUMCONDARGUMENTS_MOBILE)
  13  {
  14    _additiveNoise = additiveNoise;
  15  }
  16 
  17 
  18  NonlinearSystemPdf::~NonlinearSystemPdf(){}
  19 
  20 
  21 bool NonlinearSystemPdf::SampleFrom (Sample<ColumnVector>& one_sample, int method, void * args) const
  22  {
  23    ColumnVector state = ConditionalArgumentGet(0);
  24    ColumnVector vel   = ConditionalArgumentGet(1);
  25 
  26    // system update
  27    state(1) += cos(state(3)) * vel(1);
  28    state(2) += sin(state(3)) * vel(1);
  29    state(3) += vel(2);
  30 
  31    // sample from additive noise
  32    Sample<ColumnVector> noise;
  33    _additiveNoise.SampleFrom(noise, method, args);
  34 
  35    // store results in one_sample
  36    one_sample.ValueSet(state + noise.ValueGet());
  37 
  38    return true;
  39  }
  40 
  41 }//namespace BFL
  42 

Creating of nonlinear measurement model

As we have created the system model now we have to create a new class inhereted from ConditionalPdf. This class will implement a general nonlinear conditional probability density function (pdf) which represents the probability of the measured distances. Since we have three distance sensors we have to create an instrument to get a expected values of sensors. We will use a user-generated static map to get occupancy grid data (see the map_server package for documentation on building a map). We will use the map.h, map.c and map_range.c files from amcl node for internal representation of grid-based map and calculation of expected distances.

To generate our own class we start with the implementation of the header file: nonlinearMeasurementPdf.h.

   1 #ifndef __NON_LINEAR_MEAS_MOBILE__
   2 #define __NON_LINEAR_MEAS_MOBILE__
   3 
   4 #include <pdf/conditionalpdf.h>
   5 #include <pdf/gaussian.h>
   6 #include "map/map.h"
   7 #include <math.h>
   8 
   9 namespace BFL
  10 {
  11  /// Non Linear Conditional Gaussian
  12  class NonlinearMeasurementPdf : public ConditionalPdf<MatrixWrapper::ColumnVector, MatrixWrapper::ColumnVector>
  13    {
  14    public:
  15      /// Constructor
  16      /**
  17           @param additiveNoise Pdf representing the additive Gaussian uncertainty
  18      */
  19      NonlinearMeasurementPdf( const Gaussian& measNoise, map_t* map);
  20 
  21      /// Destructor
  22      virtual ~NonlinearMeasurementPdf();
  23 
  24      // implement this virtual function for measurement model of a particle filter
  25      virtual Probability ProbabilityGet(const MatrixWrapper::ColumnVector& measurement) const;
  26 
  27    private:
  28      Gaussian _measNoise;
  29      map_t* _map;
  30 
  31    };
  32 
  33 } // End namespace BFL
  34 
  35 #endif //
  36 

As you can see we add a pointer to map_t structure into own class and ‘map’ parameters into constructor for initialization. We have to implement ‘ProbabilityGet’ virtual function for our measurement model. This function have to return a probability the received measurements. We will use the gaussian of measurement noise with a difference between measurement and expected measurement vectors to calculate this probability. Expected measurements will be calculated by map_calc_range function

   1  double map_calc_range(map_t *map, double x, double y, double theta, double max_range)

   1 #include "nonlinearMeasurementPdf.h"
   2 #include <wrappers/rng/rng.h> // Wrapper around several rng libraries
   3 
   4 #define MEASMODEL_NUMCONDARGUMENTS_MOBILE 1
   5 #define MEASMODEL_DIMENSION_MOBILE        3
   6 
   7 namespace BFL
   8 {
   9  using namespace MatrixWrapper;
  10 
  11  NonlinearMeasurementPdf::NonlinearMeasurementPdf(const Gaussian& measNoise, map_t *map)
  12    : ConditionalPdf<ColumnVector,ColumnVector>(MEASMODEL_DIMENSION_MOBILE,MEASMODEL_NUMCONDARGUMENTS_MOBILE)
  13  {
  14    _measNoise = measNoise;
  15    _map =  map;
  16  }
  17 
  18 
  19  NonlinearMeasurementPdf::~NonlinearMeasurementPdf(){}
  20 
  21  Probability
  22  NonlinearMeasurementPdf::ProbabilityGet(const ColumnVector& measurement) const
  23  {
  24    ColumnVector state = ConditionalArgumentGet(0);
  25 
  26    ColumnVector expected_measurement(3);
  27 
  28    // Compute the range according to the map
  29    expected_measurement(1) = map_calc_range(_map, state(1), state(2), state(3) + 0, 10.0); //front
  30    expected_measurement(2) = map_calc_range(_map, state(1), state(2), state(3) + M_PI_2, 10.0); //left
  31    expected_measurement(2) = map_calc_range(_map, state(1), state(2), state(3) - M_PI_2, 10.0); //right
  32 
  33    Probability prb = _measNoise.ProbabilityGet(measurement-expected_measurement);
  34 
  35    return prb;
  36  }
  37 
  38 }//namespace BFL
  39 

Creating of custom particle filter class

In fact, there is a class for particles filter in BFL library. However, we will publish a particles cloud, so we need an access at weight samples of the filter. For that, we have to create an own class inherited from BootstrapFilter class. Create file customparticlefilter.h

   1 #ifndef CUSTOMPARTICLEFILTER_H
   2 #define CUSTOMPARTICLEFILTER_H
   3 
   4 #include <filter/bootstrapfilter.h>
   5 
   6 using namespace BFL;
   7 
   8 class CustomParticleFilter : public BootstrapFilter<MatrixWrapper::ColumnVector,MatrixWrapper::ColumnVector>
   9 {
  10 public:
  11    CustomParticleFilter(MCPdf<MatrixWrapper::ColumnVector> * prior,
  12                          int resampleperiod = 0,
  13                          double resamplethreshold = 0,
  14                          int resamplescheme = DEFAULT_RS);
  15 
  16    vector<WeightedSample<MatrixWrapper::ColumnVector> > getNewSamples();
  17 
  18 };
  19 
  20 #endif // CUSTOMPARTICLEFILTER_H
  21 

Then create customparticlefilter.cpp file

   1 #include "customparticlefilter.h"
   2 
   3 using namespace MatrixWrapper;
   4 using namespace BFL;
   5 
   6  CustomParticleFilter::CustomParticleFilter(MCPdf<ColumnVector> *prior, int resampleperiod, double resamplethreshold, int resamplescheme):
   7      BootstrapFilter<ColumnVector,ColumnVector>(prior, resampleperiod, resamplethreshold, resamplescheme)
   8 { }
   9 
  10  vector<WeightedSample<ColumnVector> > CustomParticleFilter::getNewSamples()
  11  {
  12      return _new_samples;
  13  }

Creating a node of particle filter

Now we have to create a class that implements a ROS node. This class will be responsible for:

  • creating an instance of the system model;
  • creating an instance of the measurement model;
  • creating an instance of the particle filter;
  • handling of map request;
  • handling of measurement messages;
  • handling of system update messages;
  • publish messages with current pose and particles cloud.

Create particlefilternode.cpp file.

   1 #include <filter/bootstrapfilter.h>
   2 #include <model/systemmodel.h>
   3 #include <model/measurementmodel.h>
   4 #include "nonlinearSystemPdf.h"
   5 #include "nonlinearMeasurementPdf.h"
   6 #include <iostream>
   7 #include <fstream>
   8 // Include file with properties
   9 #include "constants.h"
  10 #include "customparticlefilter.h"
  11 #include <ros/ros.h>
  12 #include <string>
  13 #include <std_msgs/Empty.h>
  14 
  15 #include <ardrone_autonomy/Navdata.h>
  16 #include <ardrone_autonomy/Ranges.h>
  17 #include <geometry_msgs/PoseArray.h>
  18 #include <geometry_msgs/PoseStamped.h>
  19 #include "nav_msgs/GetMap.h"
  20 #include "map/map.h"
  21 
  22 using namespace MatrixWrapper;
  23 using namespace BFL;
  24 using namespace std;
  25 
  26 /*
  27  The necessary SYSTEM MODEL is:
  28  x_k      = x_{k-1} + v_{k-1} * cos(theta) * delta_t
  29  y_k      = y_{k-1} + v_{k-1} * sin(theta) * delta_t
  30 */
  31 
  32 class ParticleFilterNode
  33 {
  34    ros::NodeHandle nh_;
  35    ros::Subscriber navi_sub;
  36    ros::Subscriber ranges_sub;
  37    ros::Publisher pose_pub;
  38    ros::Publisher particle_pub;
  39    map_t* map_;
  40    NonlinearSystemPdf *sys_pdf;
  41    SystemModel<ColumnVector> *sys_model;
  42    NonlinearMeasurementPdf *meas_pdf;
  43    MeasurementModel<ColumnVector,ColumnVector> *meas_model;
  44    MCPdf<ColumnVector> *prior_discr;
  45    CustomParticleFilter *filter;
  46    ros::Time prevNavDataTime;
  47    double dt;
  48    ardrone_autonomy::Navdata LastNavDataMsg;
  49 
  50 public:
  51    ParticleFilterNode()
  52    {
  53        navi_sub = nh_.subscribe("/ardrone/navdata", 1, &ParticleFilterNode::InputCb, this);
  54        ranges_sub = nh_.subscribe("ardrone/ranges", 1, &ParticleFilterNode::MeasurementCb, this);
  55        pose_pub = nh_.advertise<geometry_msgs::PoseStamped>("/pose_pf",1);
  56        particle_pub = nh_.advertise<geometry_msgs::PoseArray>("/particle_cloud",1);
  57        dt = 0.0;
  58 
  59        sys_model = NULL;
  60        meas_model = NULL;
  61        filter = NULL;
  62        map_ = NULL;
  63        requestMap();
  64 
  65    }
  66 
  67    ~ParticleFilterNode()
  68    {
  69        delete sys_model;
  70        delete meas_model;
  71        delete filter;
  72    }
  73 
  74    void CreateParticleFilter()
  75    {
  76        /****************************
  77         * NonLinear system model      *
  78         ***************************/
  79 
  80        // create gaussian
  81        ColumnVector sys_noise_Mu(STATE_SIZE);
  82        sys_noise_Mu(1) = MU_SYSTEM_NOISE_X;
  83        sys_noise_Mu(2) = MU_SYSTEM_NOISE_Y;
  84        sys_noise_Mu(3) = MU_SYSTEM_NOISE_THETA;
  85 
  86        SymmetricMatrix sys_noise_Cov(STATE_SIZE);
  87        sys_noise_Cov = 0.0;
  88        sys_noise_Cov(1,1) = SIGMA_SYSTEM_NOISE_X;
  89        sys_noise_Cov(1,2) = 0.0;
  90        sys_noise_Cov(1,3) = 0.0;
  91        sys_noise_Cov(2,1) = 0.0;
  92        sys_noise_Cov(2,2) = SIGMA_SYSTEM_NOISE_Y;
  93        sys_noise_Cov(2,3) = 0.0;
  94        sys_noise_Cov(3,1) = 0.0;
  95        sys_noise_Cov(3,2) = 0.0;
  96        sys_noise_Cov(3,3) = SIGMA_SYSTEM_NOISE_THETA;
  97 
  98        Gaussian system_Uncertainty(sys_noise_Mu, sys_noise_Cov);
  99 
 100        // create the nonlinear system model
 101        sys_pdf = new NonlinearSystemPdf(system_Uncertainty);
 102        sys_model = new SystemModel<ColumnVector> (sys_pdf);
 103 
 104 
 105        /*********************************
 106         * NonLinear Measurement model   *
 107         ********************************/
 108 
 109 
 110        // Construct the measurement noise (a scalar in this case)
 111        ColumnVector meas_noise_Mu(MEAS_SIZE);
 112        meas_noise_Mu(1) = MU_MEAS_NOISE;
 113        meas_noise_Mu(2) = MU_MEAS_NOISE;
 114        meas_noise_Mu(3) = MU_MEAS_NOISE;
 115        SymmetricMatrix meas_noise_Cov(MEAS_SIZE);
 116        meas_noise_Cov(1,1) = SIGMA_MEAS_NOISE;
 117        meas_noise_Cov(1,2) = 0.0;
 118        meas_noise_Cov(1,3) = 0.0;
 119        meas_noise_Cov(2,1) = 0.0;
 120        meas_noise_Cov(2,2) = SIGMA_MEAS_NOISE;
 121        meas_noise_Cov(2,3) = 0.0;
 122        meas_noise_Cov(3,1) = 0.0;
 123        meas_noise_Cov(3,2) = 0.0;
 124        meas_noise_Cov(3,3) = SIGMA_MEAS_NOISE;
 125 
 126        Gaussian measurement_Uncertainty(meas_noise_Mu, meas_noise_Cov);
 127 
 128 
 129        meas_pdf = new NonlinearMeasurementPdf(measurement_Uncertainty, map_);
 130        meas_model = new MeasurementModel<ColumnVector,ColumnVector>(meas_pdf);
 131 
 132        /****************************
 133         * Linear prior DENSITY     *
 134         ***************************/
 135        // Continuous Gaussian prior (for Kalman filters)
 136        ColumnVector prior_Mu(STATE_SIZE);
 137        prior_Mu(1) = PRIOR_MU_X;
 138        prior_Mu(2) = PRIOR_MU_Y;
 139        prior_Mu(3) = PRIOR_MU_THETA;
 140        SymmetricMatrix prior_Cov(STATE_SIZE);
 141        prior_Cov(1,1) = PRIOR_COV_X;
 142        prior_Cov(1,2) = 0.0;
 143        prior_Cov(1,3) = 0.0;
 144        prior_Cov(2,1) = 0.0;
 145        prior_Cov(2,2) = PRIOR_COV_Y;
 146        prior_Cov(2,3) = 0.0;
 147        prior_Cov(3,1) = 0.0;
 148        prior_Cov(3,2) = 0.0;
 149        prior_Cov(3,3) = PRIOR_COV_THETA;
 150        Gaussian prior_cont(prior_Mu,prior_Cov);
 151 
 152        // Discrete prior for Particle filter (using the continuous Gaussian prior)
 153        vector<Sample<ColumnVector> > prior_samples(NUM_SAMPLES);
 154        prior_discr = new MCPdf<ColumnVector>(NUM_SAMPLES,STATE_SIZE);
 155        prior_cont.SampleFrom(prior_samples,NUM_SAMPLES,CHOLESKY,NULL);
 156        prior_discr->ListOfSamplesSet(prior_samples);
 157 
 158        /******************************
 159         * Construction of the Filter *
 160         ******************************/
 161        filter = new CustomParticleFilter (prior_discr, 0.5, NUM_SAMPLES/4.0);
 162    }
 163 
 164    void MeasurementCb(ardrone_autonomy::Ranges msg)
 165    {
 166        ColumnVector measurement(3);
 167        measurement(1) = msg.range_front/100;
 168        measurement(2) = msg.range_left/100;
 169        measurement(3) = msg.range_right/100;
 170        //ROS_INFO("Measurement: %f",measurement(1));
 171        if (LastNavDataMsg.state==3 || LastNavDataMsg.state==7 || LastNavDataMsg.state==4)
 172        {
 173            filter->Update(meas_model, measurement);
 174            PublishParticles();
 175            PublishPose();
 176        }
 177    }
 178 
 179    void InputCb(ardrone_autonomy::Navdata msg)
 180    {
 181        if(!prevNavDataTime.isZero()) dt = (msg.header.stamp - prevNavDataTime).toSec();
 182        prevNavDataTime = msg.header.stamp;
 183        LastNavDataMsg = msg;
 184 
 185        ColumnVector input(2);
 186        input(1) = msg.vx*dt*0.001;
 187        input(2) = msg.vy*dt*0.001;
 188        if (LastNavDataMsg.state==3 || LastNavDataMsg.state==7 || LastNavDataMsg.state==4)
 189            filter->Update(sys_model,input);
 190    }
 191 
 192    void PublishParticles()
 193    {
 194        geometry_msgs::PoseArray particles_msg;
 195        particles_msg.header.stamp = ros::Time::now();
 196        particles_msg.header.frame_id = "/map";
 197 
 198        vector<WeightedSample<ColumnVector> >::iterator sample_it;
 199        vector<WeightedSample<ColumnVector> > samples;
 200 
 201        samples = filter->getNewSamples();
 202 
 203        for(sample_it = samples.begin(); sample_it<samples.end(); sample_it++)
 204        {
 205            geometry_msgs::Pose pose;
 206            ColumnVector sample = (*sample_it).ValueGet();
 207 
 208            pose.position.x = sample(1);
 209            pose.position.y = sample(2);
 210            pose.orientation.z = sample(3);
 211 
 212            particles_msg.poses.insert(particles_msg.poses.begin(), pose);
 213        }
 214        particle_pub.publish(particles_msg);
 215 
 216    }
 217 
 218    void PublishPose()
 219    {
 220        Pdf<ColumnVector> * posterior = filter->PostGet();
 221        ColumnVector pose = posterior->ExpectedValueGet();
 222        SymmetricMatrix pose_cov = posterior->CovarianceGet();
 223 
 224        geometry_msgs::PoseStamped pose_msg;
 225        pose_msg.header.stamp = ros::Time::now();
 226        pose_msg.header.frame_id = "/map";
 227 
 228        pose_msg.pose.position.x = pose(1);
 229        pose_msg.pose.position.y = pose(2);
 230        pose_msg.pose.orientation.z = pose(3);
 231 
 232        pose_pub.publish(pose_msg);
 233    }
 234 
 235    void requestMap()
 236    {
 237      // get map via RPC
 238      nav_msgs::GetMap::Request  req;
 239      nav_msgs::GetMap::Response resp;
 240      ROS_INFO("Requesting the map...");
 241      while(!ros::service::call("static_map", req, resp))
 242      {
 243        ROS_WARN("Request for map failed; trying again...");
 244        ros::Duration d(0.5);
 245        d.sleep();
 246      }
 247      handleMapMessage( resp.map );
 248    }
 249 
 250    void handleMapMessage(const nav_msgs::OccupancyGrid& msg)
 251    {
 252      ROS_INFO("Received a %d X %d map @ %.3f m/pix  Origin X %.3f Y %.3f\n",
 253               msg.info.width,
 254               msg.info.height,
 255               msg.info.resolution,
 256               msg.info.origin.position.x,
 257               msg.info.origin.position.y);
 258 
 259      freeMapDependentMemory();
 260      map_ = convertMap(msg);
 261      CreateParticleFilter();
 262    }
 263 
 264    void freeMapDependentMemory()
 265    {
 266      if( map_ != NULL ) {
 267        map_free( map_ );
 268        map_ = NULL;
 269      }
 270 
 271      if (sys_model)
 272        delete sys_model;
 273      if (meas_model)
 274        delete meas_model;
 275      if (filter)
 276        delete filter;
 277    }
 278 
 279    /**
 280     * Convert an OccupancyGrid map message into the internal
 281     * representation.  This allocates a map_t and returns it.
 282     */
 283    map_t* convertMap( const nav_msgs::OccupancyGrid& map_msg )
 284    {
 285      map_t* map = map_alloc();
 286      ROS_ASSERT(map);
 287 
 288      map->size_x = map_msg.info.width;
 289      map->size_y = map_msg.info.height;
 290      map->scale = map_msg.info.resolution;
 291      map->origin_x = map_msg.info.origin.position.x + (map->size_x / 2) * map->scale;
 292      map->origin_y = map_msg.info.origin.position.y + (map->size_y / 2) * map->scale;
 293 
 294      ROS_INFO(" Size X=%d Y=%d Scale=%f Origin X=%f Y=%f", map->size_x, map->size_y, map->scale, map->origin_x, map->origin_y);
 295      // Convert to player format
 296      map->cells = (map_cell_t*)malloc(sizeof(map_cell_t)*map->size_x*map->size_y);
 297      ROS_ASSERT(map->cells);
 298 
 299      for(int i=0;i<map->size_x * map->size_y;i++)
 300      {
 301        if(map_msg.data[i] == 0)
 302          map->cells[i].occ_state = -1;
 303        else if(map_msg.data[i] == 100)
 304          map->cells[i].occ_state = +1;
 305        else
 306          map->cells[i].occ_state = 0;
 307      }
 308      return map;
 309    }
 310 
 311 };
 312 
 313 
 314 int main(int argc, char** argv)
 315 {
 316    ros::init(argc, argv, "ParticleFilterNode");
 317    ParticleFilterNode pfNode;
 318    ros::spin();
 319    return 0;
 320 }

Testing the particle filter

Preparing of world file for Gazebo simulation

In this tutorial we will use a simple world (a rectangle room) for test.

https://docs.google.com/drawings/d/sez_lSe0peMphzybcfocqzw/image?w=688&h=323&rev=57&ac=1

You can download this world file here.

Preparing map filesNow we have to prepare a corresponding yaml and png files for map server. Yaml file have to contains name of png files, scale, coordinates of origin and some threshold.

image: newmap150x40.png

resolution: 0.1

origin: [-7.0, -2.0, 0.0]

negate: 0

occupied_thresh: 0.65

free_thresh: 0.196

As we have the 15x4 room and resolution of map is 0.1 m/px we have to create a image file with 150x40 dimensions. Obviously this image will contains only a black frame along borders. You can download this image file here.

Preparing launch file

First of all, we have to add a gazebo node with the simple world and spawn the model of AR.Drone.

<include file="$(find gazebo_simulation)/launch/start.launch">
           <arg name="world" value="$(find gazebo_simulation)/worlds/box.world"/>
        </include>
        <include file="$(find gazebo_simulation)/launch/spawn_quadrotor.launch" >
          <arg name="model" value="$(find gazebo_simulation)/urdf/ardrone_with_sonars.urdf.xacro"/>
        </include>

Now we have to add a node to control the AR.Drone. Here you can use any node like hector_quadrotor_teleop. We will use own control node (navigation) that is more complex than just teleop control. You can find this node in our repository.

Then we have to add a map_server node and tf node for static transformation

        <node name="map_node" pkg="map_server" type="map_server"
              args="$(find particles_filter_bfl)/map.yaml" respawn="true" output="screen" />
        <node pkg="tf" type="static_transform_publisher" name="map_base_link" args="0 0 0 0 0 0 map nav 100" />

Now we have to add own particle filter

<node pkg="particles_filter_bfl" type="particles_filter_node" name="particles_filter_node" output="screen" respawn="true">
</node>

Also we need to add rviz node for visualization of data

<node pkg="rviz" type="rviz" name="rviz" args="-d $(find particles_filter)/pf.rviz" >
</node>

The ready launch file you can download here.

Launch and result video To start you just need type the following command:

roslaunch particles_filter_bfl ardrone_gazebo_with_pf_bfl.launch

Then you need to publish start command for navigation node. Just type in new terminal the following command:

rostopic pub /start std_msgs/Empty

Example of working particle filter you can see on video below.

Wiki: bfl/Tutorials/Example of using a particle filter for localization by bfl library (last edited 2016-03-12 02:50:03 by Eric)