stageros.cpp
Go to the documentation of this file.
1 /*
2  * stageros
3  * Copyright (c) 2008, Willow Garage, Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <signal.h>
35 
36 
37 // libstage
38 #include <stage.hh>
39 
40 
41 // roscpp
42 #include <ros/ros.h>
43 #include <boost/thread/mutex.hpp>
44 #include <boost/thread/thread.hpp>
45 #include <sensor_msgs/LaserScan.h>
46 #include <sensor_msgs/Image.h>
48 #include <sensor_msgs/CameraInfo.h>
49 #include <nav_msgs/Odometry.h>
50 #include <geometry_msgs/Twist.h>
51 #include <rosgraph_msgs/Clock.h>
52 
53 #include <std_srvs/Empty.h>
54 
56 
57 #define USAGE "stageros <worldfile>"
58 #define IMAGE "image"
59 #define DEPTH "depth"
60 #define CAMERA_INFO "camera_info"
61 #define ODOM "odom"
62 #define BASE_SCAN "base_scan"
63 #define BASE_POSE_GROUND_TRUTH "base_pose_ground_truth"
64 #define CMD_VEL "cmd_vel"
65 
66 // Our node
67 class StageNode
68 {
69 private:
70 
71  // roscpp-related bookkeeping
73 
74  // A mutex to lock access to fields that are used in message callbacks
75  boost::mutex msg_lock;
76 
77  // The models that we're interested in
78  std::vector<Stg::ModelCamera *> cameramodels;
79  std::vector<Stg::ModelRanger *> lasermodels;
80  std::vector<Stg::ModelPosition *> positionmodels;
81 
82  //a structure representing a robot inthe simulator
83  struct StageRobot
84  {
85  //stage related models
86  Stg::ModelPosition* positionmodel; //one position
87  std::vector<Stg::ModelCamera *> cameramodels; //multiple cameras per position
88  std::vector<Stg::ModelRanger *> lasermodels; //multiple rangers per position
89 
90  //ros publishers
92  ros::Publisher ground_truth_pub; //one ground truth
93 
94  std::vector<ros::Publisher> image_pubs; //multiple images
95  std::vector<ros::Publisher> depth_pubs; //multiple depths
96  std::vector<ros::Publisher> camera_pubs; //multiple cameras
97  std::vector<ros::Publisher> laser_pubs; //multiple lasers
98 
99  ros::Subscriber cmdvel_sub; //one cmd_vel subscriber
100  };
101 
102  std::vector<StageRobot const *> robotmodels_;
103 
104  // Used to remember initial poses for soft reset
105  std::vector<Stg::Pose> initial_poses;
107 
109 
112 
113  // A helper function that is executed for each stage model. We use it
114  // to search for models of interest.
115  static void ghfunc(Stg::Model* mod, StageNode* node);
116 
117  static bool s_update(Stg::World* world, StageNode* node)
118  {
119  node->WorldCallback();
120  // We return false to indicate that we want to be called again (an
121  // odd convention, but that's the way that Stage works).
122  return false;
123  }
124 
125  // Appends the given robot ID to the given message name. If omitRobotID
126  // is true, an unaltered copy of the name is returned.
127  const char *mapName(const char *name, size_t robotID, Stg::Model* mod) const;
128  const char *mapName(const char *name, size_t robotID, size_t deviceID, Stg::Model* mod) const;
129 
131 
132  // Last time that we received a velocity command
135 
136  // Current simulation time
138 
139  // Last time we saved global position (for velocity calculation).
141  // Last published global pose of each robot
142  std::vector<Stg::Pose> base_last_globalpos;
143 
144 public:
145  // Constructor; stage itself needs argc/argv. fname is the .world file
146  // that stage should load.
147  StageNode(int argc, char** argv, bool gui, const char* fname, bool use_model_names);
148  ~StageNode();
149 
150  // Subscribe to models of interest. Currently, we find and subscribe
151  // to the first 'laser' model and the first 'position' model. Returns
152  // 0 on success (both models subscribed), -1 otherwise.
153  int SubscribeModels();
154 
155  // Our callback
156  void WorldCallback();
157 
158  // Do one update of the world. May pause if the next update time
159  // has not yet arrived.
160  bool UpdateWorld();
161 
162  // Message callback for a MsgBaseVel message, which set velocities.
164 
165  // Service callback for soft reset
166  bool cb_reset_srv(std_srvs::Empty::Request& request, std_srvs::Empty::Response& response);
167 
168  // The main simulator object
169  Stg::World* world;
170 };
171 
172 // since stageros is single-threaded, this is OK. revisit if that changes!
173 const char *
174 StageNode::mapName(const char *name, size_t robotID, Stg::Model* mod) const
175 {
176  //ROS_INFO("Robot %lu: Device %s", robotID, name);
177  bool umn = this->use_model_names;
178 
179  if ((positionmodels.size() > 1 ) || umn)
180  {
181  static char buf[100];
182  std::size_t found = std::string(((Stg::Ancestor *) mod)->Token()).find(":");
183 
184  if ((found==std::string::npos) && umn)
185  {
186  snprintf(buf, sizeof(buf), "/%s/%s", ((Stg::Ancestor *) mod)->Token(), name);
187  }
188  else
189  {
190  snprintf(buf, sizeof(buf), "/robot_%u/%s", (unsigned int)robotID, name);
191  }
192 
193  return buf;
194  }
195  else
196  return name;
197 }
198 
199 const char *
200 StageNode::mapName(const char *name, size_t robotID, size_t deviceID, Stg::Model* mod) const
201 {
202  //ROS_INFO("Robot %lu: Device %s:%lu", robotID, name, deviceID);
203  bool umn = this->use_model_names;
204 
205  if ((positionmodels.size() > 1 ) || umn)
206  {
207  static char buf[100];
208  std::size_t found = std::string(((Stg::Ancestor *) mod)->Token()).find(":");
209 
210  if ((found==std::string::npos) && umn)
211  {
212  snprintf(buf, sizeof(buf), "/%s/%s_%u", ((Stg::Ancestor *) mod)->Token(), name, (unsigned int)deviceID);
213  }
214  else
215  {
216  snprintf(buf, sizeof(buf), "/robot_%u/%s_%u", (unsigned int)robotID, name, (unsigned int)deviceID);
217  }
218 
219  return buf;
220  }
221  else
222  {
223  static char buf[100];
224  snprintf(buf, sizeof(buf), "/%s_%u", name, (unsigned int)deviceID);
225  return buf;
226  }
227 }
228 
229 void
230 StageNode::ghfunc(Stg::Model* mod, StageNode* node)
231 {
232  //printf( "inspecting %s, parent\n", mod->Token() );
233 
234  if (dynamic_cast<Stg::ModelRanger *>(mod)) {
235  node->lasermodels.push_back(dynamic_cast<Stg::ModelRanger *>(mod));
236  }
237  if (dynamic_cast<Stg::ModelPosition *>(mod)) {
238  Stg::ModelPosition * p = dynamic_cast<Stg::ModelPosition *>(mod);
239  // remember initial poses
240  node->positionmodels.push_back(p);
241  node->initial_poses.push_back(p->GetGlobalPose());
242  }
243  if (dynamic_cast<Stg::ModelCamera *>(mod)) {
244  node->cameramodels.push_back(dynamic_cast<Stg::ModelCamera *>(mod));
245  }
246 }
247 
248 
249 
250 
251 bool
252 StageNode::cb_reset_srv(std_srvs::Empty::Request& request, std_srvs::Empty::Response& response)
253 {
254  ROS_INFO("Resetting stage!");
255  for (size_t r = 0; r < this->positionmodels.size(); r++) {
256  this->positionmodels[r]->SetPose(this->initial_poses[r]);
257  this->positionmodels[r]->SetStall(false);
258  }
259  return true;
260 }
261 
262 
263 
264 void
266 {
267  boost::mutex::scoped_lock lock(msg_lock);
268  this->positionmodels[idx]->SetSpeed(msg->linear.x,
269  msg->linear.y,
270  msg->angular.z);
271  this->base_last_cmd = this->sim_time;
272 }
273 
274 StageNode::StageNode(int argc, char** argv, bool gui, const char* fname, bool use_model_names)
275 {
276  this->use_model_names = use_model_names;
277  this->sim_time.fromSec(0.0);
278  this->base_last_cmd.fromSec(0.0);
279  double t;
280  ros::NodeHandle localn("~");
281  if(!localn.getParam("base_watchdog_timeout", t))
282  t = 0.2;
284 
285  if(!localn.getParam("is_depth_canonical", isDepthCanonical))
286  isDepthCanonical = true;
287 
288  // We'll check the existence of the world file, because libstage doesn't
289  // expose its failure to open it. Could go further with checks (e.g., is
290  // it readable by this user).
291  struct stat s;
292  if(stat(fname, &s) != 0)
293  {
294  ROS_FATAL("The world file %s does not exist.", fname);
295  ROS_BREAK();
296  }
297 
298  // initialize libstage
299  Stg::Init( &argc, &argv );
300 
301  if(gui)
302  this->world = new Stg::WorldGui(600, 400, "Stage (ROS)");
303  else
304  this->world = new Stg::World();
305 
306  this->world->Load(fname);
307 
308  // todo: reverse the order of these next lines? try it .
309 
310  this->world->AddUpdateCallback((Stg::world_callback_t)s_update, this);
311 
312  // inspect every model to locate the things we care about
313  this->world->ForEachDescendant((Stg::model_callback_t)ghfunc, this);
314 }
315 
316 
317 // Subscribe to models of interest. Currently, we find and subscribe
318 // to the first 'laser' model and the first 'position' model. Returns
319 // 0 on success (both models subscribed), -1 otherwise.
320 //
321 // Eventually, we should provide a general way to map stage models onto ROS
322 // topics, similar to Player .cfg files.
323 int
325 {
326  n_.setParam("/use_sim_time", true);
327 
328  for (size_t r = 0; r < this->positionmodels.size(); r++)
329  {
330  StageRobot* new_robot = new StageRobot;
331  new_robot->positionmodel = this->positionmodels[r];
332  new_robot->positionmodel->Subscribe();
333 
334  ROS_INFO( "Subscribed to Stage position model \"%s\"", this->positionmodels[r]->Token() );
335 
336  for (size_t s = 0; s < this->lasermodels.size(); s++)
337  {
338  if (this->lasermodels[s] and this->lasermodels[s]->Parent() == new_robot->positionmodel)
339  {
340  new_robot->lasermodels.push_back(this->lasermodels[s]);
341  this->lasermodels[s]->Subscribe();
342  ROS_INFO( "subscribed to Stage ranger \"%s\"", this->lasermodels[s]->Token() );
343  }
344  }
345 
346  for (size_t s = 0; s < this->cameramodels.size(); s++)
347  {
348  if (this->cameramodels[s] and this->cameramodels[s]->Parent() == new_robot->positionmodel)
349  {
350  new_robot->cameramodels.push_back(this->cameramodels[s]);
351  this->cameramodels[s]->Subscribe();
352 
353  ROS_INFO( "subscribed to Stage camera model \"%s\"", this->cameramodels[s]->Token() );
354  }
355  }
356 
357  // TODO - print the topic names nicely as well
358  ROS_INFO("Robot %s provided %lu rangers and %lu cameras",
359  new_robot->positionmodel->Token(),
360  new_robot->lasermodels.size(),
361  new_robot->cameramodels.size() );
362 
363  new_robot->odom_pub = n_.advertise<nav_msgs::Odometry>(mapName(ODOM, r, static_cast<Stg::Model*>(new_robot->positionmodel)), 10);
364  new_robot->ground_truth_pub = n_.advertise<nav_msgs::Odometry>(mapName(BASE_POSE_GROUND_TRUTH, r, static_cast<Stg::Model*>(new_robot->positionmodel)), 10);
365  new_robot->cmdvel_sub = n_.subscribe<geometry_msgs::Twist>(mapName(CMD_VEL, r, static_cast<Stg::Model*>(new_robot->positionmodel)), 10, boost::bind(&StageNode::cmdvelReceived, this, r, _1));
366 
367  for (size_t s = 0; s < new_robot->lasermodels.size(); ++s)
368  {
369  if (new_robot->lasermodels.size() == 1)
370  new_robot->laser_pubs.push_back(n_.advertise<sensor_msgs::LaserScan>(mapName(BASE_SCAN, r, static_cast<Stg::Model*>(new_robot->positionmodel)), 10));
371  else
372  new_robot->laser_pubs.push_back(n_.advertise<sensor_msgs::LaserScan>(mapName(BASE_SCAN, r, s, static_cast<Stg::Model*>(new_robot->positionmodel)), 10));
373 
374  }
375 
376  for (size_t s = 0; s < new_robot->cameramodels.size(); ++s)
377  {
378  if (new_robot->cameramodels.size() == 1)
379  {
380  new_robot->image_pubs.push_back(n_.advertise<sensor_msgs::Image>(mapName(IMAGE, r, static_cast<Stg::Model*>(new_robot->positionmodel)), 10));
381  new_robot->depth_pubs.push_back(n_.advertise<sensor_msgs::Image>(mapName(DEPTH, r, static_cast<Stg::Model*>(new_robot->positionmodel)), 10));
382  new_robot->camera_pubs.push_back(n_.advertise<sensor_msgs::CameraInfo>(mapName(CAMERA_INFO, r, static_cast<Stg::Model*>(new_robot->positionmodel)), 10));
383  }
384  else
385  {
386  new_robot->image_pubs.push_back(n_.advertise<sensor_msgs::Image>(mapName(IMAGE, r, s, static_cast<Stg::Model*>(new_robot->positionmodel)), 10));
387  new_robot->depth_pubs.push_back(n_.advertise<sensor_msgs::Image>(mapName(DEPTH, r, s, static_cast<Stg::Model*>(new_robot->positionmodel)), 10));
388  new_robot->camera_pubs.push_back(n_.advertise<sensor_msgs::CameraInfo>(mapName(CAMERA_INFO, r, s, static_cast<Stg::Model*>(new_robot->positionmodel)), 10));
389  }
390  }
391 
392  this->robotmodels_.push_back(new_robot);
393  }
394  clock_pub_ = n_.advertise<rosgraph_msgs::Clock>("/clock", 10);
395 
396  // advertising reset service
397  reset_srv_ = n_.advertiseService("reset_positions", &StageNode::cb_reset_srv, this);
398 
399  return(0);
400 }
401 
403 {
404  for (std::vector<StageRobot const*>::iterator r = this->robotmodels_.begin(); r != this->robotmodels_.end(); ++r)
405  delete *r;
406 }
407 
408 bool
410 {
411  return this->world->UpdateAll();
412 }
413 
414 void
416 {
417  if( ! ros::ok() ) {
418  ROS_INFO( "ros::ok() is false. Quitting." );
419  this->world->QuitAll();
420  return;
421  }
422 
423  boost::mutex::scoped_lock lock(msg_lock);
424 
425  this->sim_time.fromSec(world->SimTimeNow() / 1e6);
426  // We're not allowed to publish clock==0, because it used as a special
427  // value in parts of ROS, #4027.
428  if(this->sim_time.sec == 0 && this->sim_time.nsec == 0)
429  {
430  ROS_DEBUG("Skipping initial simulation step, to avoid publishing clock==0");
431  return;
432  }
433 
434  // TODO make this only affect one robot if necessary
435  if((this->base_watchdog_timeout.toSec() > 0.0) &&
436  ((this->sim_time - this->base_last_cmd) >= this->base_watchdog_timeout))
437  {
438  for (size_t r = 0; r < this->positionmodels.size(); r++)
439  this->positionmodels[r]->SetSpeed(0.0, 0.0, 0.0);
440  }
441 
442  //loop on the robot models
443  for (size_t r = 0; r < this->robotmodels_.size(); ++r)
444  {
445  StageRobot const * robotmodel = this->robotmodels_[r];
446 
447  //loop on the laser devices for the current robot
448  for (size_t s = 0; s < robotmodel->lasermodels.size(); ++s)
449  {
450  Stg::ModelRanger const* lasermodel = robotmodel->lasermodels[s];
451  const std::vector<Stg::ModelRanger::Sensor>& sensors = lasermodel->GetSensors();
452 
453  if( sensors.size() > 1 )
454  ROS_WARN( "ROS Stage currently supports rangers with 1 sensor only." );
455 
456  // for now we access only the zeroth sensor of the ranger - good
457  // enough for most laser models that have a single beam origin
458  const Stg::ModelRanger::Sensor& sensor = sensors[0];
459 
460  if( sensor.ranges.size() )
461  {
462  // Translate into ROS message format and publish
463  sensor_msgs::LaserScan msg;
464  msg.angle_min = -sensor.fov/2.0;
465  msg.angle_max = +sensor.fov/2.0;
466  msg.angle_increment = sensor.fov/(double)(sensor.sample_count-1);
467  msg.range_min = sensor.range.min;
468  msg.range_max = sensor.range.max;
469  msg.ranges.resize(sensor.ranges.size());
470  msg.intensities.resize(sensor.intensities.size());
471 
472  for(unsigned int i = 0; i < sensor.ranges.size(); i++)
473  {
474  msg.ranges[i] = sensor.ranges[i];
475  msg.intensities[i] = sensor.intensities[i];
476  }
477 
478  if (robotmodel->lasermodels.size() > 1)
479  msg.header.frame_id = mapName("base_laser_link", r, s, static_cast<Stg::Model*>(robotmodel->positionmodel));
480  else
481  msg.header.frame_id = mapName("base_laser_link", r, static_cast<Stg::Model*>(robotmodel->positionmodel));
482 
483  msg.header.stamp = sim_time;
484  robotmodel->laser_pubs[s].publish(msg);
485  }
486 
487  // Also publish the base->base_laser_link Tx. This could eventually move
488  // into being retrieved from the param server as a static Tx.
489  Stg::Pose lp = lasermodel->GetPose();
490  tf::Quaternion laserQ;
491  laserQ.setRPY(0.0, 0.0, lp.a);
492  tf::Transform txLaser = tf::Transform(laserQ, tf::Point(lp.x, lp.y, robotmodel->positionmodel->GetGeom().size.z + lp.z));
493 
494  if (robotmodel->lasermodels.size() > 1)
495  tf.sendTransform(tf::StampedTransform(txLaser, sim_time,
496  mapName("base_link", r, static_cast<Stg::Model*>(robotmodel->positionmodel)),
497  mapName("base_laser_link", r, s, static_cast<Stg::Model*>(robotmodel->positionmodel))));
498  else
499  tf.sendTransform(tf::StampedTransform(txLaser, sim_time,
500  mapName("base_link", r, static_cast<Stg::Model*>(robotmodel->positionmodel)),
501  mapName("base_laser_link", r, static_cast<Stg::Model*>(robotmodel->positionmodel))));
502  }
503 
504  //the position of the robot
506  sim_time,
507  mapName("base_footprint", r, static_cast<Stg::Model*>(robotmodel->positionmodel)),
508  mapName("base_link", r, static_cast<Stg::Model*>(robotmodel->positionmodel))));
509 
510  // Get latest odometry data
511  // Translate into ROS message format and publish
512  nav_msgs::Odometry odom_msg;
513  odom_msg.pose.pose.position.x = robotmodel->positionmodel->est_pose.x;
514  odom_msg.pose.pose.position.y = robotmodel->positionmodel->est_pose.y;
515  odom_msg.pose.pose.orientation = tf::createQuaternionMsgFromYaw(robotmodel->positionmodel->est_pose.a);
516  Stg::Velocity v = robotmodel->positionmodel->GetVelocity();
517  odom_msg.twist.twist.linear.x = v.x;
518  odom_msg.twist.twist.linear.y = v.y;
519  odom_msg.twist.twist.angular.z = v.a;
520 
521  //@todo Publish stall on a separate topic when one becomes available
522  //this->odomMsgs[r].stall = this->positionmodels[r]->Stall();
523  //
524  odom_msg.header.frame_id = mapName("odom", r, static_cast<Stg::Model*>(robotmodel->positionmodel));
525  odom_msg.header.stamp = sim_time;
526 
527  robotmodel->odom_pub.publish(odom_msg);
528 
529  // broadcast odometry transform
530  tf::Quaternion odomQ;
531  tf::quaternionMsgToTF(odom_msg.pose.pose.orientation, odomQ);
532  tf::Transform txOdom(odomQ, tf::Point(odom_msg.pose.pose.position.x, odom_msg.pose.pose.position.y, 0.0));
533  tf.sendTransform(tf::StampedTransform(txOdom, sim_time,
534  mapName("odom", r, static_cast<Stg::Model*>(robotmodel->positionmodel)),
535  mapName("base_footprint", r, static_cast<Stg::Model*>(robotmodel->positionmodel))));
536 
537  // Also publish the ground truth pose and velocity
538  Stg::Pose gpose = robotmodel->positionmodel->GetGlobalPose();
539  tf::Quaternion q_gpose;
540  q_gpose.setRPY(0.0, 0.0, gpose.a);
541  tf::Transform gt(q_gpose, tf::Point(gpose.x, gpose.y, 0.0));
542  // Velocity is 0 by default and will be set only if there is previous pose and time delta>0
543  Stg::Velocity gvel(0,0,0,0);
544  if (this->base_last_globalpos.size()>r){
545  Stg::Pose prevpose = this->base_last_globalpos.at(r);
546  double dT = (this->sim_time-this->base_last_globalpos_time).toSec();
547  if (dT>0)
548  gvel = Stg::Velocity(
549  (gpose.x - prevpose.x)/dT,
550  (gpose.y - prevpose.y)/dT,
551  (gpose.z - prevpose.z)/dT,
552  Stg::normalize(gpose.a - prevpose.a)/dT
553  );
554  this->base_last_globalpos.at(r) = gpose;
555  }else //There are no previous readings, adding current pose...
556  this->base_last_globalpos.push_back(gpose);
557 
558  nav_msgs::Odometry ground_truth_msg;
559  ground_truth_msg.pose.pose.position.x = gt.getOrigin().x();
560  ground_truth_msg.pose.pose.position.y = gt.getOrigin().y();
561  ground_truth_msg.pose.pose.position.z = gt.getOrigin().z();
562  ground_truth_msg.pose.pose.orientation.x = gt.getRotation().x();
563  ground_truth_msg.pose.pose.orientation.y = gt.getRotation().y();
564  ground_truth_msg.pose.pose.orientation.z = gt.getRotation().z();
565  ground_truth_msg.pose.pose.orientation.w = gt.getRotation().w();
566  ground_truth_msg.twist.twist.linear.x = gvel.x;
567  ground_truth_msg.twist.twist.linear.y = gvel.y;
568  ground_truth_msg.twist.twist.linear.z = gvel.z;
569  ground_truth_msg.twist.twist.angular.z = gvel.a;
570 
571  ground_truth_msg.header.frame_id = mapName("odom", r, static_cast<Stg::Model*>(robotmodel->positionmodel));
572  ground_truth_msg.header.stamp = sim_time;
573 
574  robotmodel->ground_truth_pub.publish(ground_truth_msg);
575 
576  //cameras
577  for (size_t s = 0; s < robotmodel->cameramodels.size(); ++s)
578  {
579  Stg::ModelCamera* cameramodel = robotmodel->cameramodels[s];
580  // Get latest image data
581  // Translate into ROS message format and publish
582  if (robotmodel->image_pubs[s].getNumSubscribers() > 0 && cameramodel->FrameColor())
583  {
584  sensor_msgs::Image image_msg;
585 
586  image_msg.height = cameramodel->getHeight();
587  image_msg.width = cameramodel->getWidth();
588  image_msg.encoding = "rgba8";
589  //this->imageMsgs[r].is_bigendian="";
590  image_msg.step = image_msg.width*4;
591  image_msg.data.resize(image_msg.width * image_msg.height * 4);
592 
593  memcpy(&(image_msg.data[0]), cameramodel->FrameColor(), image_msg.width * image_msg.height * 4);
594 
595  //invert the opengl weirdness
596  int height = image_msg.height - 1;
597  int linewidth = image_msg.width*4;
598 
599  char* temp = new char[linewidth];
600  for (int y = 0; y < (height+1)/2; y++)
601  {
602  memcpy(temp,&image_msg.data[y*linewidth],linewidth);
603  memcpy(&(image_msg.data[y*linewidth]),&(image_msg.data[(height-y)*linewidth]),linewidth);
604  memcpy(&(image_msg.data[(height-y)*linewidth]),temp,linewidth);
605  }
606 
607  if (robotmodel->cameramodels.size() > 1)
608  image_msg.header.frame_id = mapName("camera", r, s, static_cast<Stg::Model*>(robotmodel->positionmodel));
609  else
610  image_msg.header.frame_id = mapName("camera", r,static_cast<Stg::Model*>(robotmodel->positionmodel));
611  image_msg.header.stamp = sim_time;
612 
613  robotmodel->image_pubs[s].publish(image_msg);
614  }
615 
616  //Get latest depth data
617  //Translate into ROS message format and publish
618  //Skip if there are no subscribers
619  if (robotmodel->depth_pubs[s].getNumSubscribers()>0 && cameramodel->FrameDepth())
620  {
621  sensor_msgs::Image depth_msg;
622  depth_msg.height = cameramodel->getHeight();
623  depth_msg.width = cameramodel->getWidth();
625  //this->depthMsgs[r].is_bigendian="";
626  int sz = this->isDepthCanonical?sizeof(float):sizeof(uint16_t);
627  size_t len = depth_msg.width * depth_msg.height;
628  depth_msg.step = depth_msg.width * sz;
629  depth_msg.data.resize(len*sz);
630 
631  //processing data according to REP118
632  if (this->isDepthCanonical){
633  double nearClip = cameramodel->getCamera().nearClip();
634  double farClip = cameramodel->getCamera().farClip();
635  memcpy(&(depth_msg.data[0]),cameramodel->FrameDepth(),len*sz);
636  float * data = (float*)&(depth_msg.data[0]);
637  for (size_t i=0;i<len;++i)
638  if(data[i]<=nearClip)
639  data[i] = -INFINITY;
640  else if(data[i]>=farClip)
641  data[i] = INFINITY;
642  }
643  else{
644  int nearClip = (int)(cameramodel->getCamera().nearClip() * 1000);
645  int farClip = (int)(cameramodel->getCamera().farClip() * 1000);
646  for (size_t i=0;i<len;++i){
647  int v = (int)(cameramodel->FrameDepth()[i]*1000);
648  if (v<=nearClip || v>=farClip) v = 0;
649  ((uint16_t*)&(depth_msg.data[0]))[i] = (uint16_t) ((v<=nearClip || v>=farClip) ? 0 : v );
650  }
651  }
652 
653  //invert the opengl weirdness
654  int height = depth_msg.height - 1;
655  int linewidth = depth_msg.width*sz;
656 
657  char* temp = new char[linewidth];
658  for (int y = 0; y < (height+1)/2; y++)
659  {
660  memcpy(temp,&depth_msg.data[y*linewidth],linewidth);
661  memcpy(&(depth_msg.data[y*linewidth]),&(depth_msg.data[(height-y)*linewidth]),linewidth);
662  memcpy(&(depth_msg.data[(height-y)*linewidth]),temp,linewidth);
663  }
664 
665  if (robotmodel->cameramodels.size() > 1)
666  depth_msg.header.frame_id = mapName("camera", r, s, static_cast<Stg::Model*>(robotmodel->positionmodel));
667  else
668  depth_msg.header.frame_id = mapName("camera", r, static_cast<Stg::Model*>(robotmodel->positionmodel));
669  depth_msg.header.stamp = sim_time;
670  robotmodel->depth_pubs[s].publish(depth_msg);
671  }
672 
673  //sending camera's tf and info only if image or depth topics are subscribed to
674  if ((robotmodel->image_pubs[s].getNumSubscribers()>0 && cameramodel->FrameColor())
675  || (robotmodel->depth_pubs[s].getNumSubscribers()>0 && cameramodel->FrameDepth()))
676  {
677 
678  Stg::Pose lp = cameramodel->GetPose();
679  tf::Quaternion Q; Q.setRPY(
680  (cameramodel->getCamera().pitch()*M_PI/180.0)-M_PI,
681  0.0,
682  lp.a+(cameramodel->getCamera().yaw()*M_PI/180.0) - robotmodel->positionmodel->GetPose().a
683  );
684 
685  tf::Transform tr = tf::Transform(Q, tf::Point(lp.x, lp.y, robotmodel->positionmodel->GetGeom().size.z+lp.z));
686 
687  if (robotmodel->cameramodels.size() > 1)
688  tf.sendTransform(tf::StampedTransform(tr, sim_time,
689  mapName("base_link", r, static_cast<Stg::Model*>(robotmodel->positionmodel)),
690  mapName("camera", r, s, static_cast<Stg::Model*>(robotmodel->positionmodel))));
691  else
692  tf.sendTransform(tf::StampedTransform(tr, sim_time,
693  mapName("base_link", r, static_cast<Stg::Model*>(robotmodel->positionmodel)),
694  mapName("camera", r, static_cast<Stg::Model*>(robotmodel->positionmodel))));
695 
696  sensor_msgs::CameraInfo camera_msg;
697  if (robotmodel->cameramodels.size() > 1)
698  camera_msg.header.frame_id = mapName("camera", r, s, static_cast<Stg::Model*>(robotmodel->positionmodel));
699  else
700  camera_msg.header.frame_id = mapName("camera", r, static_cast<Stg::Model*>(robotmodel->positionmodel));
701  camera_msg.header.stamp = sim_time;
702  camera_msg.height = cameramodel->getHeight();
703  camera_msg.width = cameramodel->getWidth();
704 
705  double fx,fy,cx,cy;
706  cx = camera_msg.width / 2.0;
707  cy = camera_msg.height / 2.0;
708  double fovh = cameramodel->getCamera().horizFov()*M_PI/180.0;
709  double fovv = cameramodel->getCamera().vertFov()*M_PI/180.0;
710  //double fx_ = 1.43266615300557*this->cameramodels[r]->getWidth()/tan(fovh);
711  //double fy_ = 1.43266615300557*this->cameramodels[r]->getHeight()/tan(fovv);
712  fx = cameramodel->getWidth()/(2*tan(fovh/2));
713  fy = cameramodel->getHeight()/(2*tan(fovv/2));
714 
715  //ROS_INFO("fx=%.4f,%.4f; fy=%.4f,%.4f", fx, fx_, fy, fy_);
716 
717 
718  camera_msg.D.resize(4, 0.0);
719 
720  camera_msg.K[0] = fx;
721  camera_msg.K[2] = cx;
722  camera_msg.K[4] = fy;
723  camera_msg.K[5] = cy;
724  camera_msg.K[8] = 1.0;
725 
726  camera_msg.R[0] = 1.0;
727  camera_msg.R[4] = 1.0;
728  camera_msg.R[8] = 1.0;
729 
730  camera_msg.P[0] = fx;
731  camera_msg.P[2] = cx;
732  camera_msg.P[5] = fy;
733  camera_msg.P[6] = cy;
734  camera_msg.P[10] = 1.0;
735 
736  robotmodel->camera_pubs[s].publish(camera_msg);
737 
738  }
739 
740  }
741  }
742 
743  this->base_last_globalpos_time = this->sim_time;
744  rosgraph_msgs::Clock clock_msg;
745  clock_msg.clock = sim_time;
746  this->clock_pub_.publish(clock_msg);
747 }
748 
749 int
750 main(int argc, char** argv)
751 {
752  if( argc < 2 )
753  {
754  puts(USAGE);
755  exit(-1);
756  }
757 
758  ros::init(argc, argv, "stageros");
759 
760  bool gui = true;
761  bool use_model_names = false;
762  for(int i=0;i<(argc-1);i++)
763  {
764  if(!strcmp(argv[i], "-g"))
765  gui = false;
766  if(!strcmp(argv[i], "-u"))
767  use_model_names = true;
768  }
769 
770  StageNode sn(argc-1,argv,gui,argv[argc-1], use_model_names);
771 
772  if(sn.SubscribeModels() != 0)
773  exit(-1);
774 
775  boost::thread t = boost::thread(boost::bind(&ros::spin));
776 
777  sn.world->Start();
778 
779  Stg::World::Run();
780 
781  t.join();
782 
783  exit(0);
784 }
785 
#define BASE_POSE_GROUND_TRUTH
Definition: stageros.cpp:63
#define DEPTH
Definition: stageros.cpp:59
#define ROS_FATAL(...)
#define BASE_SCAN
Definition: stageros.cpp:62
bool cb_reset_srv(std_srvs::Empty::Request &request, std_srvs::Empty::Response &response)
Definition: stageros.cpp:252
const char * mapName(const char *name, size_t robotID, Stg::Model *mod) const
Definition: stageros.cpp:174
ros::Time sim_time
Definition: stageros.cpp:137
#define USAGE
Definition: stageros.cpp:57
bool isDepthCanonical
Definition: stageros.cpp:110
void publish(const boost::shared_ptr< M > &message) const
Stg::ModelPosition * positionmodel
Definition: stageros.cpp:86
bool use_model_names
Definition: stageros.cpp:111
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
std::vector< ros::Publisher > camera_pubs
Definition: stageros.cpp:96
XmlRpcServer s
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
boost::mutex msg_lock
Definition: stageros.cpp:75
void cmdvelReceived(int idx, const boost::shared_ptr< geometry_msgs::Twist const > &msg)
Definition: stageros.cpp:265
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
TFSIMD_FORCE_INLINE const tfScalar & y() const
#define ROS_WARN(...)
Stg::World * world
Definition: stageros.cpp:169
ros::Time base_last_cmd
Definition: stageros.cpp:133
std::vector< StageRobot const * > robotmodels_
Definition: stageros.cpp:102
Time & fromSec(double t)
ROSCPP_DECL void spin(Spinner &spinner)
std::vector< ros::Publisher > laser_pubs
Definition: stageros.cpp:97
TFSIMD_FORCE_INLINE const tfScalar & x() const
bool UpdateWorld()
Definition: stageros.cpp:409
static bool s_update(Stg::World *world, StageNode *node)
Definition: stageros.cpp:117
std::vector< Stg::ModelRanger * > lasermodels
Definition: stageros.cpp:88
ros::Publisher odom_pub
Definition: stageros.cpp:91
TFSIMD_FORCE_INLINE const tfScalar & z() const
#define CAMERA_INFO
Definition: stageros.cpp:60
void setRPY(const tfScalar &roll, const tfScalar &pitch, const tfScalar &yaw)
Duration & fromSec(double t)
static void quaternionMsgToTF(const geometry_msgs::Quaternion &msg, Quaternion &bt)
int main(int argc, char **argv)
Definition: stageros.cpp:750
#define ROS_INFO(...)
std::vector< Stg::ModelPosition * > positionmodels
Definition: stageros.cpp:80
const std::string TYPE_32FC1
int SubscribeModels()
Definition: stageros.cpp:324
#define CMD_VEL
Definition: stageros.cpp:64
ROSCPP_DECL bool ok()
TFSIMD_FORCE_INLINE const tfScalar & y() const
const std::string TYPE_16UC1
void WorldCallback()
Definition: stageros.cpp:415
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
ros::Publisher clock_pub_
Definition: stageros.cpp:108
TFSIMD_FORCE_INLINE Vector3 & getOrigin()
ros::Subscriber cmdvel_sub
Definition: stageros.cpp:99
ros::NodeHandle n_
Definition: stageros.cpp:72
std::vector< Stg::Pose > initial_poses
Definition: stageros.cpp:105
#define ODOM
Definition: stageros.cpp:61
ros::Publisher ground_truth_pub
Definition: stageros.cpp:92
static geometry_msgs::Quaternion createQuaternionMsgFromYaw(double yaw)
ros::Time base_last_globalpos_time
Definition: stageros.cpp:140
Quaternion getRotation() const
static void ghfunc(Stg::Model *mod, StageNode *node)
Definition: stageros.cpp:230
ros::ServiceServer reset_srv_
Definition: stageros.cpp:106
bool getParam(const std::string &key, std::string &s) const
std::vector< Stg::ModelCamera * > cameramodels
Definition: stageros.cpp:78
ros::Duration base_watchdog_timeout
Definition: stageros.cpp:134
StageNode(int argc, char **argv, bool gui, const char *fname, bool use_model_names)
Definition: stageros.cpp:274
std::vector< ros::Publisher > depth_pubs
Definition: stageros.cpp:95
static const Transform & getIdentity()
std::vector< ros::Publisher > image_pubs
Definition: stageros.cpp:94
std::vector< Stg::ModelCamera * > cameramodels
Definition: stageros.cpp:87
#define IMAGE
Definition: stageros.cpp:58
#define ROS_BREAK()
tf::TransformBroadcaster tf
Definition: stageros.cpp:130
std::vector< Stg::ModelRanger * > lasermodels
Definition: stageros.cpp:79
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
std::vector< Stg::Pose > base_last_globalpos
Definition: stageros.cpp:142
#define ROS_DEBUG(...)


stage_ros
Author(s): Brian Gerky
autogenerated on Thu Jun 6 2019 19:40:27