throttling_camera.cpp
Go to the documentation of this file.
1 #include <gazebo/plugins/CameraPlugin.hh>
2 // physics definitions and functions: Required to perform slowdown
3 #include <gazebo/physics/physics.hh>
4 // ROS simulated camera class with most of the boilerplate already in place
6 #include <ros/ros.h>
7 #include <memory>
8 
9 #include <deque>
10 #include <algorithm>
11 
12 namespace
13 {
17 class AverageStat
18 {
19 private:
21  std::deque<double> samples;
23  size_t maxSamples;
25  double average = 0;
27  double stdev = 0;
29  double maxStDev;
30 
31 public:
32  AverageStat(size_t numSamples, double validStDev) :
33  samples(),
34  maxSamples(numSamples),
35  maxStDev(validStDev)
36  {}
37 
44  double update(double sample)
45  {
46  samples.push_back(sample);
47  if (samples.size() > maxSamples)
48  {
49  samples.pop_front();
50  }
51  average = std::accumulate(samples.begin(), samples.end(), 0.0) / samples.size();
52  double stdevSquared = std::accumulate(samples.begin(), samples.end(), 0.0,
53  [&](double sum, double xi) {
54  return sum + (xi - average) * (xi - average);
55  }) / samples.size();
56  stdev = std::sqrt(stdevSquared);
57  return average;
58  }
59 
65  double getAverage() const
66  {
67  return average;
68  }
69 
75  double getStDev() const
76  {
77  return stdev;
78  }
79 
85  bool isAdequate() const
86  {
87  return (samples.size() >= maxSamples) && (stdev < maxStDev);
88  }
89 
95  void reset()
96  {
97  samples.clear();
98  }
99 };
100 
101 }
102 
104 {
105 
112 class ThrottlingCamera : public gazebo::CameraPlugin, gazebo::GazeboRosCameraUtils
113 {
114 private:
116  gazebo::sensors::SensorPtr camPtr;
118  gazebo::physics::WorldPtr world;
120  gazebo::physics::PresetManagerPtr presetManager;
121 
124 
126  std::unique_ptr<AverageStat> timeSamples;
127 public:
128  ThrottlingCamera() = default;
129  ~ThrottlingCamera() override = default;
130 
137  void Load(gazebo::sensors::SensorPtr parent, sdf::ElementPtr sdf) override
138  {
139  if (!ros::isInitialized())
140  {
141  ROS_FATAL_NAMED("throttling_camera", "ROS node for Gazebo has not been initialized, unable to load plugin");
142  return;
143  }
144  ROS_DEBUG_NAMED("throttling_camera", "Initializing ROS throttling (stable-rate) camera");
145 
146  CameraPlugin::Load(parent, sdf);
147 
148  world = gazebo::physics::get_world(parent->WorldName());
149 #if GAZEBO_MAJOR_VERSION >= 8
150  presetManager = world->PresetMgr();
151 #else
152  presetManager = world->GetPresetManager();
153 #endif /* GAZEBO_MAJOR_VERSION */
154 
155  // Same as in PX4
156  if (presetManager->CurrentProfile() != "default_physics")
157  {
158  gzwarn << "Current physics profile is not default_physics, but actually is " << presetManager->CurrentProfile() << "\n";
159  if (!presetManager->CurrentProfile("default_physics"))
160  {
161  gzerr << "Could not set current profile to default_physics!\n";
162  }
163  }
164 
165  double minUpdateRate = parent->UpdateRate();
166  if (sdf->HasElement("minUpdateRate"))
167  {
168  minUpdateRate = sdf->Get<double>("minUpdateRate");
169  }
170  maxUpdateInterval = 1.0 / minUpdateRate;
171 
172  size_t windowSize = 10;
173  if (sdf->HasElement("windowSize"))
174  {
175  windowSize = sdf->Get<size_t>("windowSize");
176  }
177 
178  double maxStDev = 0.02;
179  if (sdf->HasElement("maxStDev"))
180  {
181  maxStDev = sdf->Get<double>("maxStDev");
182  }
183 
184  timeSamples.reset(new AverageStat(windowSize, maxStDev));
185 
186  camPtr = parent;
187 
188  parentSensor_ = camPtr;
189  width_ = width;
190  height_ = height;
191  depth_ = depth;
192  format_ = format;
193  camera_ = camera;
194 
196  }
197 
210  void OnNewFrame(const unsigned char *image, unsigned int width, unsigned int height, unsigned int depth,
211  const std::string &format) override {
212 
213  // Note: sensorUpdateTime uses simulated time
214  auto sensorUpdateTime = camPtr->LastMeasurementTime();
215  // If sensor was not active for some reason, we allow it to get new data on next frame
216  if (!camPtr->IsActive())
217  {
218  camPtr->SetActive(true);
219  last_update_time_ = sensorUpdateTime;
220  timeSamples->reset();
221  return;
222  }
223 
224  boost::mutex::scoped_lock lock(*image_connect_count_lock_);
225  if (*image_connect_count_ > 0)
226  {
227  if (sensorUpdateTime < last_update_time_)
228  {
229  ROS_WARN_NAMED("throttling_camera", "Negative sensor update time difference (world reset?)");
230  last_update_time_ = sensorUpdateTime;
231  timeSamples->reset();
232  }
233 
234  auto timeDelta = sensorUpdateTime - last_update_time_;
235  timeSamples->update(timeDelta.Double());
236 
237  // We want to throttle the simulation down if we have measurements too far apart
238  if (timeSamples->isAdequate() && timeSamples->getAverage() > maxUpdateInterval)
239  {
240  ROS_INFO_STREAM_NAMED("throttling_camera", "Had average update period of "
241  << timeSamples->getAverage() << " (stdev: " << timeSamples->getStDev() << ")"
242  << ", but desired update period is " << update_period_
243  << ", throttling simulation down");
244  boost::any currentRealTimeUpadteRateParam;
245  if (!presetManager->GetCurrentProfileParam("real_time_update_rate", currentRealTimeUpadteRateParam))
246  {
247  gzerr << "Failed to get real time update rate parameter!\n";
248  }
249  auto currentRate = boost::any_cast<double>(currentRealTimeUpadteRateParam);
250  // We are being somewhat aggressive here, maybe we could throttle the world
251  // down in steps?
252  double slowdownFactor = update_period_ / timeSamples->getAverage();
253  auto nextRate = currentRate * slowdownFactor;
254  if (!presetManager->SetCurrentProfileParam("real_time_update_rate", nextRate))
255  {
256  gzerr << "Failed to set real time update rate parameter!\n";
257  }
258  if (slowdownFactor < 0.5)
259  {
260  ROS_WARN_STREAM_NAMED("throttling_camera", "Simulation slowed down significantly; consider running"
261  "the simulation with a lower PX4_SIM_SPEED_FACTOR value (slowed down from "
262  << currentRate << " to " << nextRate << " updates per second)");
263  }
264  // We're discarding old samples to avoid extensive slowdown
265  timeSamples->reset();
266  }
267  PutCameraData(image, sensorUpdateTime);
268  PublishCameraInfo(sensorUpdateTime);
269  }
270  last_update_time_ = sensorUpdateTime;
271  }
272 };
273 }
274 
void OnNewFrame(const unsigned char *image, unsigned int width, unsigned int height, unsigned int depth, const std::string &format) override
#define ROS_WARN_NAMED(name,...)
ROSCPP_DECL bool isInitialized()
void Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf, const std::string &_camera_name_suffix="")
#define ROS_INFO_STREAM_NAMED(name, args)
std::unique_ptr< AverageStat > timeSamples
void update(const std::string &key, const XmlRpc::XmlRpcValue &v)
GZ_REGISTER_SENSOR_PLUGIN(throttling_camera::ThrottlingCamera)
#define ROS_DEBUG_NAMED(name,...)
gazebo::physics::PresetManagerPtr presetManager
void Load(gazebo::sensors::SensorPtr parent, sdf::ElementPtr sdf) override
#define ROS_FATAL_NAMED(name,...)
#define ROS_WARN_STREAM_NAMED(name, args)


clover_simulation
Author(s): Alexey Rogachevskiy, Andrey Ryabov, Arthur Golubtsov, Oleg Kalachev, Svyatoslav Zhuravlev
autogenerated on Mon Feb 28 2022 22:08:36