00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030
00031 #include <sys/types.h>
00032 #include <sys/stat.h>
00033 #include <unistd.h>
00034 #include <signal.h>
00035
00036
00037
00038 #include <stage.hh>
00039
00040
00041 #include <ros/ros.h>
00042 #include <boost/thread/mutex.hpp>
00043 #include <boost/thread/thread.hpp>
00044 #include <sensor_msgs/LaserScan.h>
00045 #include <nav_msgs/Odometry.h>
00046 #include <geometry_msgs/Twist.h>
00047 #include <rosgraph_msgs/Clock.h>
00048
00049 #include "tf/transform_broadcaster.h"
00050
00051 #define USAGE "stageros <worldfile>"
00052 #define ODOM "odom"
00053 #define BASE_SCAN "base_scan"
00054 #define BASE_POSE_GROUND_TRUTH "base_pose_ground_truth"
00055 #define CMD_VEL "cmd_vel"
00056
00057
00058 class StageNode
00059 {
00060 private:
00061
00062 sensor_msgs::LaserScan *laserMsgs;
00063 nav_msgs::Odometry *odomMsgs;
00064 nav_msgs::Odometry *groundTruthMsgs;
00065 rosgraph_msgs::Clock clockMsg;
00066
00067
00068 ros::NodeHandle n_;
00069
00070
00071 boost::mutex msg_lock;
00072
00073
00074 std::vector<Stg::ModelRanger *> lasermodels;
00075 std::vector<Stg::ModelPosition *> positionmodels;
00076 std::vector<ros::Publisher> laser_pubs_;
00077 std::vector<ros::Publisher> odom_pubs_;
00078 std::vector<ros::Publisher> ground_truth_pubs_;
00079 std::vector<ros::Subscriber> cmdvel_subs_;
00080 ros::Publisher clock_pub_;
00081
00082
00083
00084 static void ghfunc(Stg::Model* mod, StageNode* node);
00085
00086 static bool s_update(Stg::World* world, StageNode* node)
00087 {
00088 node->WorldCallback();
00089
00090
00091 return false;
00092 }
00093
00094
00095
00096 const char *mapName(const char *name, size_t robotID);
00097
00098 tf::TransformBroadcaster tf;
00099
00100
00101 ros::Time base_last_cmd;
00102 ros::Duration base_watchdog_timeout;
00103
00104
00105 ros::Time sim_time;
00106
00107 public:
00108
00109
00110 StageNode(int argc, char** argv, bool gui, const char* fname);
00111 ~StageNode();
00112
00113
00114
00115
00116 int SubscribeModels();
00117
00118
00119 void WorldCallback();
00120
00121
00122
00123 bool UpdateWorld();
00124
00125
00126 void cmdvelReceived(int idx, const boost::shared_ptr<geometry_msgs::Twist const>& msg);
00127
00128
00129 Stg::World* world;
00130 };
00131
00132
00133 const char *
00134 StageNode::mapName(const char *name, size_t robotID)
00135 {
00136 if (positionmodels.size() > 1)
00137 {
00138 static char buf[100];
00139 snprintf(buf, sizeof(buf), "/robot_%u/%s", (unsigned int)robotID, name);
00140 return buf;
00141 }
00142 else
00143 return name;
00144 }
00145
00146 void
00147 StageNode::ghfunc(Stg::Model* mod, StageNode* node)
00148 {
00149 if (dynamic_cast<Stg::ModelRanger *>(mod))
00150 node->lasermodels.push_back(dynamic_cast<Stg::ModelRanger *>(mod));
00151 if (dynamic_cast<Stg::ModelPosition *>(mod))
00152 node->positionmodels.push_back(dynamic_cast<Stg::ModelPosition *>(mod));
00153 }
00154
00155 void
00156 StageNode::cmdvelReceived(int idx, const boost::shared_ptr<geometry_msgs::Twist const>& msg)
00157 {
00158 boost::mutex::scoped_lock lock(msg_lock);
00159 this->positionmodels[idx]->SetSpeed(msg->linear.x,
00160 msg->linear.y,
00161 msg->angular.z);
00162 this->base_last_cmd = this->sim_time;
00163 }
00164
00165 StageNode::StageNode(int argc, char** argv, bool gui, const char* fname)
00166 {
00167 this->sim_time.fromSec(0.0);
00168 this->base_last_cmd.fromSec(0.0);
00169 double t;
00170 ros::NodeHandle localn("~");
00171 if(!localn.getParam("base_watchdog_timeout", t))
00172 t = 0.2;
00173 this->base_watchdog_timeout.fromSec(t);
00174
00175
00176
00177
00178 struct stat s;
00179 if(stat(fname, &s) != 0)
00180 {
00181 ROS_FATAL("The world file %s does not exist.", fname);
00182 ROS_BREAK();
00183 }
00184
00185
00186 Stg::Init( &argc, &argv );
00187
00188 if(gui)
00189 this->world = new Stg::WorldGui(800, 700, "Stage (ROS)");
00190 else
00191 this->world = new Stg::World();
00192
00193
00194
00195
00196
00197 this->world->Load(fname);
00198
00199
00200
00201 this->world->AddUpdateCallback((Stg::world_callback_t)s_update, this);
00202
00203 this->world->ForEachDescendant((Stg::model_callback_t)ghfunc, this);
00204 if (lasermodels.size() != positionmodels.size())
00205 {
00206 ROS_FATAL("number of position models and laser models must be equal in "
00207 "the world file.");
00208 ROS_BREAK();
00209 }
00210 size_t numRobots = positionmodels.size();
00211 ROS_INFO("found %u position/laser pair%s in the file",
00212 (unsigned int)numRobots, (numRobots==1) ? "" : "s");
00213
00214 this->laserMsgs = new sensor_msgs::LaserScan[numRobots];
00215 this->odomMsgs = new nav_msgs::Odometry[numRobots];
00216 this->groundTruthMsgs = new nav_msgs::Odometry[numRobots];
00217 }
00218
00219
00220
00221
00222
00223
00224
00225
00226 int
00227 StageNode::SubscribeModels()
00228 {
00229 n_.setParam("/use_sim_time", true);
00230
00231 for (size_t r = 0; r < this->positionmodels.size(); r++)
00232 {
00233 if(this->lasermodels[r])
00234 {
00235 this->lasermodels[r]->Subscribe();
00236 }
00237 else
00238 {
00239 ROS_ERROR("no laser");
00240 return(-1);
00241 }
00242 if(this->positionmodels[r])
00243 {
00244 this->positionmodels[r]->Subscribe();
00245 }
00246 else
00247 {
00248 ROS_ERROR("no position");
00249 return(-1);
00250 }
00251 laser_pubs_.push_back(n_.advertise<sensor_msgs::LaserScan>(mapName(BASE_SCAN,r), 10));
00252 odom_pubs_.push_back(n_.advertise<nav_msgs::Odometry>(mapName(ODOM,r), 10));
00253 ground_truth_pubs_.push_back(n_.advertise<nav_msgs::Odometry>(mapName(BASE_POSE_GROUND_TRUTH,r), 10));
00254 cmdvel_subs_.push_back(n_.subscribe<geometry_msgs::Twist>(mapName(CMD_VEL,r), 10, boost::bind(&StageNode::cmdvelReceived, this, r, _1)));
00255 }
00256 clock_pub_ = n_.advertise<rosgraph_msgs::Clock>("/clock",10);
00257 return(0);
00258 }
00259
00260 StageNode::~StageNode()
00261 {
00262 delete[] laserMsgs;
00263 delete[] odomMsgs;
00264 delete[] groundTruthMsgs;
00265 }
00266
00267 bool
00268 StageNode::UpdateWorld()
00269 {
00270 return this->world->UpdateAll();
00271 }
00272
00273 void
00274 StageNode::WorldCallback()
00275 {
00276 boost::mutex::scoped_lock lock(msg_lock);
00277
00278 this->sim_time.fromSec(world->SimTimeNow() / 1e6);
00279
00280
00281 if(this->sim_time.sec == 0 && this->sim_time.nsec == 0)
00282 {
00283 ROS_DEBUG("Skipping initial simulation step, to avoid publishing clock==0");
00284 return;
00285 }
00286
00287
00288 if((this->base_watchdog_timeout.toSec() > 0.0) &&
00289 ((this->sim_time - this->base_last_cmd) >= this->base_watchdog_timeout))
00290 {
00291 for (size_t r = 0; r < this->positionmodels.size(); r++)
00292 this->positionmodels[r]->SetSpeed(0.0, 0.0, 0.0);
00293 }
00294
00295
00296 for (size_t r = 0; r < this->lasermodels.size(); r++)
00297 {
00298 const std::vector<Stg::ModelRanger::Sensor>& sensors = this->lasermodels[r]->GetSensors();
00299
00300 if( sensors.size() > 1 )
00301 ROS_WARN( "ROS Stage currently supports rangers with 1 sensor only." );
00302
00303
00304
00305 const Stg::ModelRanger::Sensor& s = sensors[0];
00306
00307 if( s.ranges.size() )
00308 {
00309
00310 this->laserMsgs[r].angle_min = -s.fov/2.0;
00311 this->laserMsgs[r].angle_max = +s.fov/2.0;
00312 this->laserMsgs[r].angle_increment = s.fov/(double)(s.sample_count-1);
00313 this->laserMsgs[r].range_min = s.range.min;
00314 this->laserMsgs[r].range_max = s.range.max;
00315 this->laserMsgs[r].ranges.resize(s.ranges.size());
00316 this->laserMsgs[r].intensities.resize(s.intensities.size());
00317
00318 for(unsigned int i=0; i<s.ranges.size(); i++)
00319 {
00320 this->laserMsgs[r].ranges[i] = s.ranges[i];
00321 this->laserMsgs[r].intensities[i] = (uint8_t)s.intensities[i];
00322 }
00323
00324 this->laserMsgs[r].header.frame_id = mapName("base_laser_link", r);
00325 this->laserMsgs[r].header.stamp = sim_time;
00326 this->laser_pubs_[r].publish(this->laserMsgs[r]);
00327 }
00328
00329
00330
00331 Stg::Pose lp = this->lasermodels[r]->GetPose();
00332 tf::Quaternion laserQ;
00333 laserQ.setRPY(0.0, 0.0, lp.a);
00334 tf::Transform txLaser = tf::Transform(laserQ,
00335 tf::Point(lp.x, lp.y, 0.15));
00336 tf.sendTransform(tf::StampedTransform(txLaser, sim_time,
00337 mapName("base_link", r),
00338 mapName("base_laser_link", r)));
00339
00340
00341 tf::Transform txIdentity(tf::createIdentityQuaternion(),
00342 tf::Point(0, 0, 0));
00343 tf.sendTransform(tf::StampedTransform(txIdentity,
00344 sim_time,
00345 mapName("base_footprint", r),
00346 mapName("base_link", r)));
00347
00348
00349
00350 this->odomMsgs[r].pose.pose.position.x = this->positionmodels[r]->est_pose.x;
00351 this->odomMsgs[r].pose.pose.position.y = this->positionmodels[r]->est_pose.y;
00352 this->odomMsgs[r].pose.pose.orientation = tf::createQuaternionMsgFromYaw(this->positionmodels[r]->est_pose.a);
00353 Stg::Velocity v = this->positionmodels[r]->GetVelocity();
00354 this->odomMsgs[r].twist.twist.linear.x = v.x;
00355 this->odomMsgs[r].twist.twist.linear.y = v.y;
00356 this->odomMsgs[r].twist.twist.angular.z = v.a;
00357
00358
00359
00360
00361 this->odomMsgs[r].header.frame_id = mapName("odom", r);
00362 this->odomMsgs[r].header.stamp = sim_time;
00363
00364 this->odom_pubs_[r].publish(this->odomMsgs[r]);
00365
00366
00367 tf::Quaternion odomQ;
00368 tf::quaternionMsgToTF(odomMsgs[r].pose.pose.orientation, odomQ);
00369 tf::Transform txOdom(odomQ,
00370 tf::Point(odomMsgs[r].pose.pose.position.x,
00371 odomMsgs[r].pose.pose.position.y, 0.0));
00372 tf.sendTransform(tf::StampedTransform(txOdom, sim_time,
00373 mapName("odom", r),
00374 mapName("base_footprint", r)));
00375
00376
00377 Stg::Pose gpose = this->positionmodels[r]->GetGlobalPose();
00378 Stg::Velocity gvel = this->positionmodels[r]->GetGlobalVelocity();
00379
00380 tf::Quaternion q_gpose;
00381 q_gpose.setRPY(0.0, 0.0, gpose.a-M_PI/2.0);
00382 tf::Transform gt(q_gpose, tf::Point(gpose.y, -gpose.x, 0.0));
00383 tf::Quaternion q_gvel;
00384 q_gvel.setRPY(0.0, 0.0, gvel.a-M_PI/2.0);
00385 tf::Transform gv(q_gvel, tf::Point(gvel.y, -gvel.x, 0.0));
00386
00387 this->groundTruthMsgs[r].pose.pose.position.x = gt.getOrigin().x();
00388 this->groundTruthMsgs[r].pose.pose.position.y = gt.getOrigin().y();
00389 this->groundTruthMsgs[r].pose.pose.position.z = gt.getOrigin().z();
00390 this->groundTruthMsgs[r].pose.pose.orientation.x = gt.getRotation().x();
00391 this->groundTruthMsgs[r].pose.pose.orientation.y = gt.getRotation().y();
00392 this->groundTruthMsgs[r].pose.pose.orientation.z = gt.getRotation().z();
00393 this->groundTruthMsgs[r].pose.pose.orientation.w = gt.getRotation().w();
00394 this->groundTruthMsgs[r].twist.twist.linear.x = gv.getOrigin().x();
00395 this->groundTruthMsgs[r].twist.twist.linear.y = gv.getOrigin().y();
00396
00397
00398
00399 this->groundTruthMsgs[r].twist.twist.angular.z = gvel.a;
00400
00401 this->groundTruthMsgs[r].header.frame_id = mapName("odom", r);
00402 this->groundTruthMsgs[r].header.stamp = sim_time;
00403
00404 this->ground_truth_pubs_[r].publish(this->groundTruthMsgs[r]);
00405 }
00406
00407 this->clockMsg.clock = sim_time;
00408 this->clock_pub_.publish(this->clockMsg);
00409 }
00410
00411 int
00412 main(int argc, char** argv)
00413 {
00414 if( argc < 2 )
00415 {
00416 puts(USAGE);
00417 exit(-1);
00418 }
00419
00420 ros::init(argc, argv, "stageros");
00421
00422 bool gui = true;
00423 for(int i=0;i<(argc-1);i++)
00424 {
00425 if(!strcmp(argv[i], "-g"))
00426 gui = false;
00427 }
00428
00429 StageNode sn(argc-1,argv,gui,argv[argc-1]);
00430
00431 if(sn.SubscribeModels() != 0)
00432 exit(-1);
00433
00434 boost::thread t = boost::thread(boost::bind(&ros::spin));
00435
00436
00437 sn.world->Start();
00438
00439
00440
00441 ros::WallRate r(10.0);
00442 while(ros::ok() && !sn.world->TestQuit())
00443 {
00444 if(gui)
00445 Fl::wait(r.expectedCycleTime().toSec());
00446 else
00447 {
00448 sn.UpdateWorld();
00449 r.sleep();
00450 }
00451 }
00452 t.join();
00453
00454 exit(0);
00455 }
00456