pfuclt_omni_dataset.cpp
Go to the documentation of this file.
4 #include <tf2/utils.h>
5 
6 #define ROS_TDIFF(t) (t.toSec() - timeInit.toSec())
7 
8 namespace pfuclt_omni_dataset
9 {
10 int MY_ID;
14 std::vector<bool> PLAYING_ROBOTS;
15 float K1, K2; // coefficients for landmark observation covariance
16 float K3, K4, K5; // coefficients for target observation covariance
17 float ROB_HT; // Fixed height of the robots above ground in meters
18 std::vector<double> POS_INIT;
19 
20 bool USE_CUSTOM_VALUES = false; // If set to true via the parameter server, the custom values will be used
21 std::vector<double> CUSTOM_PARTICLE_INIT; // Used to set custom values when initiating the particle filter set (will still be a uniform distribution)
22 
23 bool DEBUG;
24 bool PUBLISH;
25 
26 // for ease of access
27 std::vector<Landmark> landmarks;
28 ros::Time timeInit;
29 
30 // Method definitions
31 
32 RobotFactory::RobotFactory(ros::NodeHandle& nh) : nh_(nh)
33 {
34  ParticleFilter::PFinitData initData(nh, MY_ID, NUM_TARGETS, STATES_PER_ROBOT,
35  MAX_ROBOTS, NUM_LANDMARKS, PLAYING_ROBOTS,
36  landmarks);
37 
38  if (PUBLISH)
39  pf = boost::shared_ptr<PFPublisher>(
40  new PFPublisher(initData, PFPublisher::PublishData(ROB_HT)));
41  else
42  pf = boost::shared_ptr<ParticleFilter>(new ParticleFilter(initData));
43 
44  timeInit = ros::Time::now();
45  ROS_INFO("Init time set to %f", timeInit.toSec());
46 
47  for (int rn = 0; rn < MAX_ROBOTS; rn++)
48  {
49  if (PLAYING_ROBOTS[rn])
50  {
51  robots_.push_back(
52  Robot_ptr(new Robot(nh_, this, pf->getPFReference(), rn)));
53  }
54  }
55 }
56 
58 {
59  if (!areAllRobotsActive())
60  return;
61 
62  pf->init(CUSTOM_PARTICLE_INIT, POS_INIT);
63 }
64 
66 {
67  std::string filename;
68 
69  // get the filename from parameter server
70  if (!readParam<std::string>(nh_, "/LANDMARKS_CONFIG", filename))
71  return;
72 
73  // parse the file and copy to vector of Landmarks
74  landmarks = getLandmarks(filename.c_str());
75  ROS_ERROR_COND(landmarks.empty(), "Couldn't open file \"%s\"",
76  filename.c_str());
77 
78  ROS_ERROR_COND((int)landmarks.size() != NUM_LANDMARKS,
79  "Read a number of landmarks different from the specified in "
80  "NUM_LANDMARKS");
81 
82  // iterate over the vector and print information
83  for (std::vector<Landmark>::iterator it = landmarks.begin();
84  it != landmarks.end(); ++it)
85  {
86  ROS_INFO("A fixed landmark with ID %d at position {x=%.2f, y=%.2f} \twas "
87  "created",
88  it->serial, it->x, it->y);
89  }
90 }
91 
93 {
94  for (std::vector<Robot_ptr>::iterator it = robots_.begin();
95  it != robots_.end(); ++it)
96  {
97  if (false == (*it)->hasStarted())
98  return false;
99  }
100  return true;
101 }
102 
104 {
105  timeStarted_ = ros::Time::now();
106  started_ = true;
107  ROS_INFO("OMNI%d has started %.2fs after the initial time", robotNumber_ + 1,
108  ROS_TDIFF(timeStarted_));
109 }
110 
111 Robot::Robot(ros::NodeHandle& nh, RobotFactory* parent, ParticleFilter* pf,
112  uint robotNumber)
113  : parent_(parent), pf_(pf), started_(false), robotNumber_(robotNumber)
114 {
115  std::string robotNamespace("/omni" +
116  boost::lexical_cast<std::string>(robotNumber + 1));
117 
118  // Subscribe to topics
119  sOdom_ = nh.subscribe<nav_msgs::Odometry>(
120  robotNamespace + "/odometry", 10,
121  boost::bind(&Robot::odometryCallback, this, _1));
122 
123  sBall_ = nh.subscribe<read_omni_dataset::BallData>(
124  robotNamespace + "/orangeball3Dposition", 10,
125  boost::bind(&Robot::targetCallback, this, _1));
126 
127  sLandmark_ = nh.subscribe<read_omni_dataset::LRMLandmarksData>(
128  robotNamespace + "/landmarkspositions", 10,
129  boost::bind(&Robot::landmarkDataCallback, this, _1));
130 
131  ROS_INFO("Created robot OMNI%d", robotNumber + 1);
132 }
133 
134 void Robot::odometryCallback(const nav_msgs::Odometry::ConstPtr& odometry)
135 {
136  if (!started_)
137  startNow();
138 
139  if (!pf_->isInitialized())
141 
142  Odometry odomStruct;
143  odomStruct.x = odometry->pose.pose.position.x;
144  odomStruct.y = odometry->pose.pose.position.y;
145  odomStruct.theta = tf2::getYaw(odometry->pose.pose.orientation);
146 
147  // ROS_DEBUG("OMNI%d odometry at time %d = {%f;%f;%f}", robotNumber_ + 1,
148  // odometry->header.stamp.sec, odomStruct.x, odomStruct.y,
149  // odomStruct.theta);
150 
151  // Call the particle filter predict step for this robot
152  pf_->predict(robotNumber_, odomStruct, odometry->header.stamp);
153 }
154 
155 void Robot::targetCallback(const read_omni_dataset::BallData::ConstPtr& target)
156 {
157  if (!started_)
158  startNow();
159 
160  // If needed, modify here to if(true) to go over the target occlusion from the dataset
161  if (target->found)
162  {
163  // ROS_DEBUG("OMNI%d ball data at time %d", robotNumber_ + 1,
164  // target->header.stamp.sec);
165 
166  TargetObservation obs;
167 
168  obs.found = true;
169  obs.x = target->x;
170  obs.y = target->y;
171  obs.z = target->z;
172  obs.d = Eigen::Vector2d(obs.x, obs.y).norm();
173  obs.r = Eigen::Vector3d(obs.x, obs.y, obs.z).norm();
174  obs.phi = atan2(target->y, target->x);
175 
176  // Auxiliary
177  const double cos2p = pow(cos(obs.phi), 2);
178  const double sin2p = pow(sin(obs.phi), 2);
179  const double d2 = pow(obs.d, 2);
180  const double r2 = pow(obs.r, 2);
181 
182  // 3D Model
183  static const float ballRadius = 0.1;
184  static const float ballr2 = pow(ballRadius, 2);
185  obs.covDD = K3 * (r2 * sin2p / (2 * ballr2)) +
186  K4 * (r2 * sin2p / (2 * (r2 - ballr2))) +
187  K3 * K4 * (r2 * cos2p / (4 * ballr2 * (r2 - ballr2)));
188  obs.covPP = K5 / (r2 - ballr2 * sin2p);
189 
190  // 2D Model
191  // obs.covDD = (double)(1 / target->mismatchFactor) *
192  // (K3 * obs.d + K4 * (obs.d * obs.d));
193 
194  // obs.covPP = K5 * (1 / (obs.d + 1));
195 
196  obs.covXX =
197  cos2p * obs.covDD + sin2p * (d2 * obs.covPP + obs.covDD * obs.covPP);
198  obs.covYY =
199  sin2p * obs.covDD + cos2p * (d2 * obs.covPP + obs.covDD * obs.covPP);
200 
201  // Save this observation
202  pf_->saveTargetObservation(robotNumber_, obs, target->header.stamp);
203  }
204  else
205  {
206  // ROS_DEBUG("OMNI%d didn't find the ball at time %d", robotNumber_ + 1,
207  // target->header.stamp.sec);
208 
210  }
211 
213 
214  // If this is the "self robot", update the iteration time
215  if (MY_ID == (int)robotNumber_ + 1)
216  pf_->updateTargetIterationTime(target->header.stamp);
217 }
218 
220  const read_omni_dataset::LRMLandmarksData::ConstPtr& landmarkData)
221 {
222  // ROS_DEBUG("OMNI%d landmark data at time %d", robotNumber_ + 1,
223  // landmarkData->header.stamp.sec);
224 
225  bool heuristicsFound[NUM_LANDMARKS];
226  for (int i = 0; i < NUM_LANDMARKS; i++)
227  heuristicsFound[i] = landmarkData->found[i];
228 
229  float distances[NUM_LANDMARKS];
230 
231  // d = sqrt(x^2+y^2)
232  for (int i = 0; i < NUM_LANDMARKS; i++)
233  {
234  distances[i] =
235  pow((pow(landmarkData->x[i], 2) + pow(landmarkData->y[i], 2)), 0.5);
236  }
237 
238  // Define heuristics if using custom values
239  if (USE_CUSTOM_VALUES)
240  {
241  // Huristic 1. If I see only 8 and not 9.... then I cannot see 7
242  if (landmarkData->found[8] && !landmarkData->found[9])
243  heuristicsFound[7] = false;
244 
245  // Huristic 2. If I see only 9 and not 8.... then I cannot see 6
246  if (!landmarkData->found[8] && landmarkData->found[9])
247  heuristicsFound[6] = false;
248 
249  // Huristic 3. If I see both 8 and 9.... then there are 2 subcases
250  if (landmarkData->found[8] && landmarkData->found[9])
251  {
252  // Huristic 3.1. If I am closer to 9.... then I cannot see 6
253  if (distances[9] < distances[8])
254  heuristicsFound[6] = false;
255 
256  // Huristic 3.2 If I am closer to 8.... then I cannot see 7
257  if (distances[8] < distances[9])
258  heuristicsFound[7] = false;
259  }
260 
261  float heuristicsThresh[] = HEURISTICS_THRESH_DEFAULT;
262 
263  if (robotNumber_ == 0)
264  {
265  heuristicsThresh[4] = 6.5;
266  heuristicsThresh[5] = 6.5;
267  heuristicsThresh[8] = 6.5;
268  heuristicsThresh[9] = 6.5;
269  }
270 
271  else if (robotNumber_ == 2)
272  {
273  heuristicsThresh[4] = 6.5;
274  heuristicsThresh[5] = 6.5;
275  heuristicsThresh[8] = 6.5;
276  heuristicsThresh[9] = 6.5;
277  }
278 
279  else if (robotNumber_ == 3)
280  {
281  heuristicsThresh[4] = 6.5;
282  heuristicsThresh[5] = 6.5;
283  heuristicsThresh[8] = 6.5;
284  heuristicsThresh[9] = 6.5;
285  }
286 
287  else if (robotNumber_ == 4)
288  {
289  heuristicsThresh[4] = 3.5;
290  heuristicsThresh[5] = 3.5;
291  heuristicsThresh[8] = 3.5;
292  heuristicsThresh[9] = 3.5;
293  }
294 
295  // Set landmark as not found if distance to it is above a certain threshold
296  for (int i = 0; i < NUM_LANDMARKS; i++)
297  {
298  if (distances[i] > heuristicsThresh[i])
299  heuristicsFound[i] = false;
300  }
301  }
302 
303  // End of heuristics, below uses the array but just for convenience
304 
305  for (int i = 0; i < NUM_LANDMARKS; i++)
306  {
307 
308  if (false == heuristicsFound[i])
310 
311  else
312  {
314  obs.found = true;
315  obs.x = landmarkData->x[i];
316  obs.y = landmarkData->y[i];
317  obs.d = sqrt(obs.x * obs.x + obs.y * obs.y);
318 
319  // If needed, this hack goes over the dataset threshold distance
320 // if (obs.d > 2.0)
321 // {
322 // pf_->saveLandmarkObservation(robotNumber_, i, false);
323 // continue;
324 // }
325 
326  obs.phi = atan2(obs.y, obs.x);
327  obs.covDD =
328  (K1 * fabs(1.0 - (landmarkData->AreaLandMarkActualinPixels[i] /
329  landmarkData->AreaLandMarkExpectedinPixels[i]))) *
330  (obs.d * obs.d);
331  obs.covPP = NUM_LANDMARKS * K2 * (1 / (obs.d + 1));
332  obs.covXX = pow(cos(obs.phi), 2) * obs.covDD +
333  pow(sin(obs.phi), 2) *
334  (pow(obs.d, 2) * obs.covPP + obs.covDD * obs.covPP);
335  obs.covYY = pow(sin(obs.phi), 2) * obs.covDD +
336  pow(cos(obs.phi), 2) *
337  (pow(obs.d, 2) * obs.covPP + obs.covDD * obs.covPP);
338 
340  landmarkData->header.stamp);
341  }
342  }
343 
345 }
346 
347 // end of namespace pfuclt_omni_dataset
348 }
349 
350 int main(int argc, char* argv[])
351 {
352  using namespace pfuclt_omni_dataset;
353 
354  // Parse input parameters
355  // TODO Consider using a library for this
356  std::cout << "Usage: pfuclt_omni_dataset --debug [true|FALSE] --publish [TRUE|false]" << std::endl;
357  if (argc > 2)
358  {
359  if (!strcmp(argv[2], "true"))
360  {
361  if (ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME,
362  ros::console::levels::Debug))
363  {
364  ros::console::notifyLoggerLevelsChanged();
365  }
366 
367  DEBUG = true;
368  }
369  else
370  DEBUG = false;
371  }
372  else
373  DEBUG = false;
374 
375  if (argc > 4)
376  {
377  if (!strcmp(argv[4], "true"))
378  {
379  PUBLISH = true;
380  }
381  else
382  PUBLISH = false;
383  }
384  else
385  PUBLISH = false;
386 
387  ROS_INFO_STREAM("DEBUG set to " << std::boolalpha << DEBUG << " and PUBLISH set to " << std::boolalpha << PUBLISH);
388 
389  ros::init(argc, argv, "pfuclt_omni_dataset");
390  ros::NodeHandle nh("~");
391 
392  // read parameters from param server
393  readParam<int>(nh, "MAX_ROBOTS", MAX_ROBOTS);
394  readParam<float>(nh, "ROB_HT", ROB_HT);
395  readParam<int>(nh, "NUM_TARGETS", NUM_TARGETS);
396  readParam<int>(nh, "NUM_LANDMARKS", NUM_LANDMARKS);
397  readParam<float>(nh, "LANDMARK_COV/K1", K1);
398  readParam<float>(nh, "LANDMARK_COV/K2", K2);
399  readParam<float>(nh, "LANDMARK_COV/K3", K3);
400  readParam<float>(nh, "LANDMARK_COV/K4", K4);
401  readParam<float>(nh, "LANDMARK_COV/K5", K5);
402  readParam<bool>(nh, "PLAYING_ROBOTS", PLAYING_ROBOTS);
403  readParam<double>(nh, "POS_INIT", POS_INIT);
404  readParam<bool>(nh, "USE_CUSTOM_VALUES", USE_CUSTOM_VALUES);
405  readParam<int>(nh, "MY_ID", MY_ID);
406 
407  uint total_size = (uint)MAX_ROBOTS * STATES_PER_ROBOT + NUM_TARGETS * STATES_PER_TARGET;
408 
409  readParam<double>(nh, "CUSTOM_PARTICLE_INIT", CUSTOM_PARTICLE_INIT);
410  if (CUSTOM_PARTICLE_INIT.size() != (total_size * 2))
411  {
412  ROS_ERROR("CUSTOM_PARTICLE_INIT given but not of correct size - should "
413  "have %d numbers and has %d",
414  total_size * 2, (int)CUSTOM_PARTICLE_INIT.size());
415  }
416 
417  ROS_INFO("Waiting for /clock");
418  ros::Time::waitForValid();
419  ROS_INFO("/clock message received");
420 
422 
424  {
425  ROS_WARN("OMNI2 not present in dataset.");
426  return EXIT_FAILURE;
427  }
428 
429  Factory.initializeFixedLandmarks();
430 
431  ros::spin();
432  return EXIT_SUCCESS;
433 }
void tryInitializeParticles()
tryInitializeParticles - checks if every robot is started, and if so, will initiate the particle filt...
boost::shared_ptr< Robot > Robot_ptr
int main(int argc, char *argv[])
void initializeFixedLandmarks()
initializeFixedLandmarks - will get a filename from the parameter server, and use its information to ...
#define HEURISTICS_THRESH_DEFAULT
std::vector< Landmark > landmarks
std::vector< double > POS_INIT
void updateTargetIterationTime(ros::Time tRos)
updateTargetIterationTime - the main robot should call this method after the target callback ...
#define STATES_PER_ROBOT
#define STATES_PER_TARGET
bool isInitialized()
isInitialized - simple interface to access private member initialized_
The PFPublisher class - implements publishing for the ParticleFilter class using ROS.
#define ROS_TDIFF(t)
std::vector< double > CUSTOM_PARTICLE_INIT
std::vector< bool > PLAYING_ROBOTS
void odometryCallback(const nav_msgs::Odometry::ConstPtr &)
odometryCallback - event-driven function which should be called when new odometry data is received ...
void saveTargetObservation(const uint robotNumber, const TargetObservation obs, ros::Time stamp)
saveTargetObservation - saves the target observation to a buffer of observations
The RobotFactory class - Creates and keeps information on the robot running the algorithm and its tea...
void landmarkDataCallback(const read_omni_dataset::LRMLandmarksData::ConstPtr &)
landmarkDataCallback - event-driven function which should be called when new landmark data is receive...
std::vector< Landmark > getLandmarks(const char *filename)
getLandmarks - read landmark configuration from CSV file
Definition: pfuclt_aux.cpp:7
bool areAllRobotsActive()
areAllTeammatesActive - uses each robot&#39;s public methods to check if they have started yet ...
boost::shared_ptr< ParticleFilter > pf
The PFinitData struct - provides encapsulation to the initial data necessary to construct a ParticleF...
The Robot class - Has the common variables and methods of all robots, and is the base class of any sp...
void saveAllTargetMeasurementsDone(const uint robotNumber)
saveAllTargetMeasurementsDone - call this function when all target measurements have been performed b...
void saveLandmarkObservation(const uint robotNumber, const uint landmarkNumber, const LandmarkObservation obs, ros::Time stamp)
saveLandmarkObservation - saves the landmark observation to a buffer of observations ...
Robot(ros::NodeHandle &nh, RobotFactory *parent, ParticleFilter *pf, uint robotNumber)
Robot - constructor, creates a new Robot instance.
void targetCallback(const read_omni_dataset::BallData::ConstPtr &)
targetCallBack - event-driven function which should be called when new target data is received ...
struct pfuclt_omni_dataset::odometry_s Odometry
void predict(const uint robotNumber, const Odometry odom, const ros::Time stamp)
predict - prediction step in the particle filter set with the received odometry
void startNow()
startNow - starts the robot
void saveAllLandmarkMeasurementsDone(const uint robotNumber)
saveAllLandmarkMeasurementsDone - call this function when all landmark measurements have been perform...
std::vector< Robot_ptr > robots_


pfuclt_omni_dataset
Author(s):
autogenerated on Tue Jul 4 2017 19:38:38