perception_scoring_plugin.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2019 Open Source Robotics Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16 */
17 
18 #include <algorithm>
19 #include <mutex>
20 #include <string>
21 #include <vector>
22 #include <gazebo/common/Assert.hh>
23 #include <gazebo/common/Console.hh>
24 #include <gazebo/common/Events.hh>
25 #include <ignition/math/config.hh>
26 #include <ignition/math/Matrix4.hh>
27 #include <ignition/math/Pose3.hh>
28 #include <gazebo/physics/Link.hh>
29 #include <gazebo/physics/Model.hh>
30 #include <gazebo/transport/transport.hh>
31 #include <sdf/sdf.hh>
33 
34 /* The version of ignition math in Ubuntu Xenial is 2.2.3 and lacks of
35  * some features added after that version in the 2.x series */
36 
37 /* There is a bug in versions for ign-math in Bionic that does not
38  * define properly IGNITION_MATH_MAJOR_VERSION. Some magic to check
39  * defined variables but empty and assume 3.0.x series */
40 #define DO_EXPAND(VAL) VAL ## 1
41 #define EXPAND(VAL) DO_EXPAND(VAL)
42 #if (EXPAND(IGNITION_MATH_MAJOR_VERSION) == 1)
43  #define MAJOR_VERSION 3
44  #define MINOR_VERSION 0
45 #else
46  #define MAJOR_VERSION IGNITION_MATH_MAJOR_VERSION
47  #define MINOR_VERSION IGNITION_MATH_MINOR_VERSION
48 #endif
49 
50 #if MAJOR_VERSION == 2 && MINOR_VERSION < 3
51  #define ign_math_vector3d_zero ignition::math::Vector3d(0, 0, 0)
52 #else
53  #define ign_math_vector3d_zero ignition::math::Vector3d::Zero
54 #endif
55 
56 
57 
60  const double& _duration,
61  const std::string& _type,
62  const std::string& _name,
63  const ignition::math::Pose3d& _trialPose,
64  const gazebo::physics::WorldPtr _world)
65 {
66  this->time = _time;
67  this->duration = _duration;
68  this->type = _type;
69  this->name = _name;
70  this->trialPose = _trialPose;
71  #if GAZEBO_MAJOR_VERSION >= 8
72  this->modelPtr = _world->EntityByName(this->name);
73  #else
74  this->modelPtr = _world->GetEntity(this->name);
75  #endif
76  if (modelPtr)
77  {
78  #if GAZEBO_MAJOR_VERSION >= 8
79  this->origPose = this->modelPtr->WorldPose();
80  #else
81  this->origPose = this->modelPtr->GetWorldPose().Ign();
82  #endif
83  }
84 }
85 
86 std::string PerceptionObject::Str()
87 {
88  std::string rtn = "\nname: ";
89  rtn += this->name;
90  rtn += "\ntype: ";
91  rtn += this->type;
92  rtn += "\ntime: ";
93  rtn += std::to_string(this->time);
94  rtn += "\nduration: ";
95  rtn += std::to_string(this->duration);
96  rtn += "\nerror: ";
97  rtn += std::to_string(this->error);
98  return rtn;
99 }
100 
102 void PerceptionObject::SetError(const double& _error)
103 {
104  if (this->active)
105  this->error = std::min(2.0, std::min(this->error, _error));
106 }
107 
109 void PerceptionObject::StartTrial(const gazebo::physics::EntityPtr& _frame)
110 {
111  // Set object pose relative to the specified frame (e.g., the wam-v)
112  // Pitch and roll are set to zero as a hack to deal with
113  // transients associated with spawning buoys with significant attitude.
114  #if GAZEBO_MAJOR_VERSION >= 8
115  ignition::math::Pose3d framePose(
116  _frame->WorldPose().Pos(),
117  ignition::math::Quaterniond(0.0, 0.0,
118  _frame->WorldPose().Rot().Yaw()));
119  #else
120  ignition::math::Pose3d framePose(
121  _frame->GetWorldPose().pos.Ign(),
122  ignition::math::Quaterniond(0.0, 0.0,
123  _frame->GetWorldPose().rot.Ign().Yaw()));
124  #endif
125  ignition::math::Matrix4d transMat(framePose);
126  ignition::math::Matrix4d pose_local(this->trialPose);
127 
128  this->modelPtr->SetWorldPose((transMat * pose_local).Pose());
129  this->modelPtr->SetWorldTwist(ign_math_vector3d_zero,
131  this->active = true;
132  gzmsg << "PerceptionScoringPlugin: spawning " << this->name << std::endl;
133 }
134 
137 {
138  this->modelPtr->SetWorldPose(this->origPose);
139  this->modelPtr->SetWorldTwist(ign_math_vector3d_zero,
141  this->active = false;
142  gzmsg << "PerceptionScoringPlugin: despawning " << this->name << std::endl;
143 }
144 
146 
149 {
150  gzmsg << "PerceptionScoringPlugin loaded" << std::endl;
151 }
152 
155 {
156 }
157 
159 void PerceptionScoringPlugin::Load(gazebo::physics::WorldPtr _world,
160  sdf::ElementPtr _sdf)
161 {
162  // Base class, also binds the update method for the base class
163  ScoringPlugin::Load(_world, _sdf);
164 
165  this->world = _world;
166  this->sdf = _sdf;
167 
168  if (_sdf->HasElement("loop_forever"))
169  {
170  sdf::ElementPtr loopElem = _sdf->GetElement("loop_forever");
171  this->loopForever = loopElem->Get<bool>();
172  }
173 
174  if (_sdf->HasElement("frame"))
175  {
176  this->frameName = _sdf->Get<std::string>("frame");
177  }
178 
179  if (!_sdf->HasElement("object_sequence"))
180  {
181  gzerr << "PerceptionScoringPlugin: Unable to find <object_sequence> "
182  "element\n";
183  return;
184  }
185 
186  sdf::ElementPtr sequence = _sdf->GetElement("object_sequence");
187 
188  sdf::ElementPtr objectElem = NULL;
189  if (sequence->HasElement("object"))
190  {
191  objectElem = sequence->GetElement("object");
192  }
193 
194  while (objectElem)
195  {
196  // Parse the time.
197  if (!objectElem->HasElement("time"))
198  {
199  gzerr << "PerceptionScoringPlugin: Unable to find <time> in object\n";
200  objectElem = objectElem->GetNextElement("object");
201  continue;
202  }
203  sdf::ElementPtr timeElement = objectElem->GetElement("time");
204  double time = timeElement->Get<double>();
205 
206  double duration = 5;
207  if (objectElem->HasElement("duration"))
208  {
209  sdf::ElementPtr durationElement = objectElem->GetElement("duration");
210  duration = durationElement->Get<double>();
211  }
212 
213  // Parse the object type.
214  if (!objectElem->HasElement("type"))
215  {
216  gzerr << "PerceptionScoringPlugin: Unable to find <type> in object.\n";
217  objectElem = objectElem->GetNextElement("object");
218  continue;
219  }
220  sdf::ElementPtr typeElement = objectElem->GetElement("type");
221  std::string type = typeElement->Get<std::string>();
222 
223  // Parse the object name - this is what must be matched id success.
224  if (!objectElem->HasElement("name"))
225  {
226  gzerr << "PerceptionScoringPlugin: Unable to find <name> in object.\n";
227  objectElem = objectElem->GetNextElement("object");
228  continue;
229  }
230  sdf::ElementPtr nameElement = objectElem->GetElement("name");
231  std::string name = nameElement->Get<std::string>();
232 
233  // Parse the object pose
234  if (!objectElem->HasElement("pose"))
235  {
236  gzerr << "PerceptionScoringPlugin: Unable to find <pose> in object.\n";
237  objectElem = objectElem->GetNextElement("object");
238  continue;
239  }
240  sdf::ElementPtr poseElement = objectElem->GetElement("pose");
241  ignition::math::Pose3d pose = poseElement->Get<ignition::math::Pose3d>();
242 
243  // Add the object to the collection.
244  PerceptionObject obj(time, duration, type, name, pose, _world);
245  this->objects.push_back(obj);
246 
247  objectElem = objectElem->GetNextElement("object");
248  }
249 
250  #if GAZEBO_MAJOR_VERSION >= 8
251  this->lastUpdateTime = this->world->SimTime();
252  #else
253  this->lastUpdateTime = this->world->GetSimTime();
254  #endif
255 
256  // Optional: ROS namespace.
257  if (_sdf->HasElement("robot_namespace"))
258  this->ns = _sdf->GetElement("robot_namespace")->Get<std::string>();
259 
260  // Optional: ROS topic.
261  this->objectTopic = "/vrx/perception/landmark";
262  if (_sdf->HasElement("landmark_topic"))
263  {
264  this->objectTopic = _sdf->GetElement("landmark_topic")->Get<std::string>();
265  }
266 
267  this->Restart();
268 
269  this->connection = gazebo::event::Events::ConnectWorldUpdateEnd(
270  boost::bind(&PerceptionScoringPlugin::OnUpdate, this));
271 }
272 
275 {
276  for (auto& obj : this->objects)
277  {
278  // reset all objects' errors
279  obj.error = 10.0;
280  // bump all objs time to start again
281  #if GAZEBO_MAJOR_VERSION >= 8
282  obj.time += this->world->SimTime().Double();
283  #else
284  obj.time += this->world->GetSimTime().Double();
285  #endif
286  }
287  gzmsg << "Object population restarted" << std::endl;
288 }
289 
292 {
293  // if have not finished the load, skip
294  if (!this->frameName.empty())
295  {
296  // get frame of robot
297  #if GAZEBO_MAJOR_VERSION >= 8
298  this->frame =
299  this->world->EntityByName(this->frameName);
300  #else
301  this->frame =
302  this->world->GetEntity(this->frameName);
303  #endif
304  // make sure it is a real frame
305  if (!this->frame)
306  {
307  gzwarn << std::string("The frame '") << this->frameName
308  << "' does not exist" << std::endl;
309  return;
310  }
311  if (!this->frame->HasType(gazebo::physics::Base::LINK) &&
312  !this->frame->HasType(gazebo::physics::Base::MODEL))
313  {
314  gzwarn << "'frame' tag must list the name of a link or model"
315  << std::endl;
316  return;
317  }
318  }
319  // look at all objects
320  for (auto& obj : this->objects)
321  {
322  // if time to spawn an object
323  if (this->ElapsedTime() > obj.time &&
324  this->ElapsedTime() < obj.time + obj.duration &&
325  !obj.active)
326  {
327  // increment the atempt balance for this new obj
328  this->attemptBal += 1;
329  obj.StartTrial(this->frame);
330  ROS_INFO_NAMED("PerceptionScoring",
331  "New Attempt Balance: %d", this->attemptBal);
332  }
333  // if time to despawn and object
334  if (this->ElapsedTime() > obj.time + obj.duration &&
335  obj.active)
336  {
337  // prevent negative attemp balance
338  if (this->attemptBal > 0)
339  this->attemptBal -= 1;
340  // inc objects despawned
341  this->objectsDespawned += 1;
342  obj.EndTrial();
343 
344  // Add the score for this object.
345  this->SetScore(this->Score() + obj.error);
346 
347  ROS_INFO_NAMED("PerceptionScoring",
348  "New Attempt Balance: %d", this->attemptBal);
349  }
350  }
351  // if we have finished
352  if (this->objectsDespawned == this->objects.size() &&
353  this->TaskState() != "finished")
354  {
355  // publish string summarizing the objects
356  for (auto& obj : this->objects)
357  {
358  ROS_INFO_NAMED("PerceptionScoring", "%s", obj.Str().c_str());
359  }
360 
361  // Run score is the mean error per object.
362  this->SetScore(this->Score() / this->objects.size());
363  ROS_INFO_NAMED("Perception run score: ", "%f", this->Score());
364 
365  // if loop, restart
366  if (this->loopForever)
367  {
368  this->objectsDespawned = 0;
369  this->Restart();
370  }
371  else
372  {
373  this->Finish();
374  }
375  }
376 }
377 
380  const geographic_msgs::GeoPoseStamped::ConstPtr &_msg)
381 {
382  // only accept an attempt if there are any in the attempt balance
383  if (this->attemptBal == 0)
384  {
385  ROS_WARN_NAMED("PerceptionScoring",
386  "Attempt Balance is 0, no attempts currently allowed. Ignoring.");
387  return;
388  }
389  else
390  {
391  // burn one attempt
392  this->attemptBal -= 1;
393  ROS_INFO_NAMED("PerceptionScoring",
394  "New Attempt Balance: %d", this->attemptBal);
395  }
396  for (auto& obj : this->objects)
397  {
398  // if attempt correct type
399  if (obj.type == _msg->header.frame_id)
400  {
401  // Convert geo pose to Gazebo pose
402  // Note - this is used in a few different VRX plugins, may want to have a
403  // separate library?
404  // Convert lat/lon to local
405  // Snippet from UUV Simulator SphericalCoordinatesROSInterfacePlugin.cc
406  ignition::math::Vector3d scVec(_msg->pose.position.latitude,
407  _msg->pose.position.longitude, 0);
408  #if GAZEBO_MAJOR_VERSION >= 8
409  ignition::math::Vector3d cartVec =
410  this->world->SphericalCoords()->LocalFromSpherical(scVec);
411  #else
412  ignition::math::Vector3d cartVec =
413  this->world->GetSphericalCoordinates()->LocalFromSpherical(scVec);
414  #endif
415  // Get current pose of the current object
416  #if GAZEBO_MAJOR_VERSION >= 8
417  ignition::math::Pose3d truePose = obj.modelPtr->WorldPose();
418  #else
419  ignition::math::Pose3d truePose = obj.modelPtr->GetWorldPose().Ign();
420  #endif
421  // 2D Error
422  obj.SetError(sqrt(pow(cartVec.X() - truePose.Pos().X(), 2)+
423  pow(cartVec.Y() - truePose.Pos().Y(), 2)));
424  }
425  }
426 }
427 
430 {
431  gzmsg << "OnRunning" << std::endl;
432  // Quit if ros plugin was not loaded
433  if (!ros::isInitialized())
434  {
435  ROS_ERROR("ROS was not initialized.");
436  return;
437  }
438 
439  // Subscribe
440  this->nh = ros::NodeHandle(this->ns);
441  this->objectSub = this->nh.subscribe(this->objectTopic, 1,
443 }
444 
447 {
448  // Avoid releasing the vehicle by overriding this function.
449  return;
450 }
#define ROS_INFO_NAMED(name,...)
std::string name
PerceptionObject type.
#define ROS_WARN_NAMED(name,...)
GZ_REGISTER_WORLD_PLUGIN(UsvWindPlugin)
virtual void Load(gazebo::physics::WorldPtr _world, sdf::ElementPtr _sdf)
void OnUpdate()
Update the plugin.
#define ign_math_vector3d_zero
std::string type
PerceptionObject type.
ROSCPP_DECL bool isInitialized()
void Restart()
Restart the object population list.
void SetError(const double &_error)
set the error of this boject if this object is active and this is the lowest seen error ...
A plugin that allows models to be spawned at a given location in a specific simulation time and then ...
void Load(gazebo::physics::WorldPtr _world, sdf::ElementPtr _sdf)
double duration
amount of time in which the object should be spawned.
virtual ~PerceptionScoringPlugin()
Destructor.
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
void EndTrial()
move the object back to its original location and make inactive
Class to store information about each object to be populated.
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
double error
error associated with the guess of a moel
void OnAttempt(const geographic_msgs::GeoPoseStamped::ConstPtr &_msg)
void StartTrial(const gazebo::physics::EntityPtr &_frame)
move the object to where it is supposed to be relative to the frame
ignition::math::Pose3d trialPose
Pose in which the object should be placed in wam-v&#39;s frame.
void ReleaseVehicle() override
Tries to release the vehicle in case is locked.
double time
Simulation time in which the object should be spawned.
void OnRunning() override
Callback executed when the task state transition into "running".
gazebo::physics::EntityPtr modelPtr
ModelPtr to the model that this object is representing.
PerceptionObject(const double &_time, const double &_duration, const std::string &_type, const std::string &_name, const ignition::math::Pose3d &_trialPose, const gazebo::physics::WorldPtr _world)
constructor of perception object
ignition::math::Pose3d origPose
Pose in which the object should be placed in global frame.
#define ROS_ERROR(...)
bool active
bool to tell weather or not the object is open for attempts


vrx_gazebo
Author(s): Brian Bingham , Carlos Aguero
autogenerated on Thu May 7 2020 03:54:56