Skip to content
This repository was archived by the owner on Feb 3, 2025. It is now read-only.

Commit fbf99a2

Browse files
authored
Fix sensor update rate throttling when new sensors are spawned (#2784)
* fix sensor manager max update rate after spawning new sensors Signed-off-by: Ian Chen <ichen@osrfoundation.org> * changelog Signed-off-by: Ian Chen <ichen@osrfoundation.org> * revert changes in Visual.cc Signed-off-by: Ian Chen <ichen@osrfoundation.org> * revert changes Signed-off-by: Ian Chen <ichen@osrfoundation.org>
1 parent 28a4af5 commit fbf99a2

File tree

3 files changed

+143
-13
lines changed

3 files changed

+143
-13
lines changed

Changelog.md

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Gazebo 9.xx.x (202x-xx-xx)
44

5+
1. Fix sensor update rate throttling when new sensors are spawned
6+
* [Pull request #2784](https://github.com/osrf/gazebo/pull/2784)
7+
58
1. LensFlare: initialize OGRE compositors during plugin initialization
69
* [Pull request #2762](https://github.com/osrf/gazebo/pull/2762)
710

gazebo/sensors/SensorManager.cc

+35-13
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ using namespace sensors;
3535
/// for timing coordination.
3636
boost::mutex g_sensorTimingMutex;
3737

38+
/// \brief Flag to indicate if number of sensors has changed and that the
39+
/// max update rate needs to be recalculated
40+
bool g_sensorsDirty = true;
41+
3842
//////////////////////////////////////////////////
3943
SensorManager::SensorManager()
4044
: initialized(false), removeAllSensors(false)
@@ -527,23 +531,34 @@ void SensorManager::SensorContainer::RunLoop()
527531
return;
528532
}
529533

530-
{
531-
boost::recursive_mutex::scoped_lock lock(this->mutex);
532534

533-
// Get the minimum update rate from the sensors.
534-
for (Sensor_V::iterator iter = this->sensors.begin();
535-
iter != this->sensors.end() && !this->stop; ++iter)
535+
auto computeMaxUpdateRate = [&]()
536+
{
536537
{
537-
GZ_ASSERT((*iter) != nullptr, "Sensor is null");
538-
maxUpdateRate = std::max((*iter)->UpdateRate(), maxUpdateRate);
538+
boost::recursive_mutex::scoped_lock lock(this->mutex);
539+
540+
if (!g_sensorsDirty)
541+
return;
542+
543+
// Get the minimum update rate from the sensors.
544+
for (Sensor_V::iterator iter = this->sensors.begin();
545+
iter != this->sensors.end() && !this->stop; ++iter)
546+
{
547+
GZ_ASSERT((*iter) != nullptr, "Sensor is null");
548+
maxUpdateRate = std::max((*iter)->UpdateRate(), maxUpdateRate);
549+
}
550+
551+
g_sensorsDirty = false;
539552
}
540-
}
541553

542-
// Calculate an appropriate sleep time.
543-
if (maxUpdateRate > 0)
544-
sleepTime.Set(1.0 / (maxUpdateRate));
545-
else
546-
sleepTime.Set(0, 1e6);
554+
// Calculate an appropriate sleep time.
555+
if (maxUpdateRate > 0)
556+
sleepTime.Set(1.0 / (maxUpdateRate));
557+
else
558+
sleepTime.Set(0, 1e6);
559+
};
560+
561+
computeMaxUpdateRate();
547562

548563
while (!this->stop)
549564
{
@@ -556,6 +571,8 @@ void SensorManager::SensorContainer::RunLoop()
556571
return;
557572
}
558573

574+
computeMaxUpdateRate();
575+
559576
// Get the start time of the update.
560577
startTime = world->SimTime();
561578

@@ -653,6 +670,7 @@ void SensorManager::SensorContainer::AddSensor(SensorPtr _sensor)
653670
{
654671
boost::recursive_mutex::scoped_lock lock(this->mutex);
655672
this->sensors.push_back(_sensor);
673+
g_sensorsDirty = true;
656674
}
657675

658676
// Tell the run loop that we have received a sensor
@@ -682,6 +700,8 @@ bool SensorManager::SensorContainer::RemoveSensor(const std::string &_name)
682700
}
683701
}
684702

703+
g_sensorsDirty = true;
704+
685705
return removed;
686706
}
687707

@@ -717,6 +737,8 @@ void SensorManager::SensorContainer::RemoveSensors()
717737
(*iter)->Fini();
718738
}
719739

740+
g_sensorsDirty = true;
741+
720742
this->sensors.clear();
721743
}
722744

test/integration/sensor.cc

+105
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ class SensorTest : public ServerFixture
2222
{
2323
};
2424

25+
std::mutex g_mutex;
26+
unsigned int g_messageCount = 0;
27+
28+
////////////////////////////////////////////////////////////////////////
29+
void SensorCallback(const ConstIMUSensorPtr &/*_msg*/)
30+
{
31+
std::lock_guard<std::mutex> lock(g_mutex);
32+
g_messageCount++;
33+
}
34+
2535
/////////////////////////////////////////////////
2636
// This tests getting links from a model.
2737
TEST_F(SensorTest, GetScopedName)
@@ -48,6 +58,101 @@ TEST_F(SensorTest, FastSensor)
4858
// SensorManager::SensorContainer::RunLoop() is set improperly
4959
}
5060

61+
/////////////////////////////////////////////////
62+
// Make sure sensors update rates are respected
63+
// Spawn two sensors, one after another, with different update rates and
64+
// verify the rates are correctly throttled
65+
TEST_F(SensorTest, MaxUpdateRate)
66+
{
67+
Load("worlds/empty.world");
68+
69+
physics::WorldPtr world = physics::get_world("default");
70+
ASSERT_NE(nullptr, world);
71+
72+
auto spawnSensorWithUpdateRate = [&](const std::string &_name,
73+
const ignition::math::Pose3d &_pose, double _rate)
74+
{
75+
std::ostringstream newModelStr;
76+
newModelStr << "<sdf version='" << SDF_VERSION << "'>"
77+
<< "<model name ='" << _name << "'>\n"
78+
<< "<static>true</static>\n"
79+
<< "<pose>" << _pose << "</pose>\n"
80+
<< "<link name ='body'>\n"
81+
<< "<inertial>\n"
82+
<< "<mass>0.1</mass>\n"
83+
<< "</inertial>\n"
84+
<< "<collision name='parent_collision'>\n"
85+
<< " <pose>0 0 0.0205 0 0 0</pose>\n"
86+
<< " <geometry>\n"
87+
<< " <cylinder>\n"
88+
<< " <radius>0.021</radius>\n"
89+
<< " <length>0.029</length>\n"
90+
<< " </cylinder>\n"
91+
<< " </geometry>\n"
92+
<< "</collision>\n"
93+
<< " <sensor name ='" << _name << "' type ='imu'>\n"
94+
<< " <update_rate>" << _rate << "</update_rate>\n"
95+
<< " <topic>" << _name << "</topic>\n"
96+
<< " <imu>\n"
97+
<< " </imu>\n"
98+
<< " </sensor>\n"
99+
<< "</link>\n"
100+
<< "</model>\n"
101+
<< "</sdf>\n";
102+
103+
SpawnSDF(newModelStr.str());
104+
};
105+
106+
transport::NodePtr node = transport::NodePtr(new transport::Node());
107+
node->Init();
108+
109+
g_messageCount = 0;
110+
111+
// spawn first sensor with low update rate
112+
spawnSensorWithUpdateRate("sensor1", ignition::math::Pose3d::Zero, 5);
113+
114+
transport::SubscriberPtr sub = node->Subscribe("~/sensor1/body/sensor1/imu",
115+
SensorCallback);
116+
117+
// wait for messages
118+
int sleep = 0;
119+
int maxSleep = 1000;
120+
double t0 = 0.0;
121+
while (g_messageCount < 30 && sleep++ < maxSleep)
122+
{
123+
if (g_messageCount == 0)
124+
t0 = world->SimTime().Double();
125+
common::Time::MSleep(10);
126+
}
127+
128+
// verify update rate by checking the time it takes to receive n msgs
129+
double elapsed = world->SimTime().Double() - t0;
130+
EXPECT_NEAR(6.0, elapsed, 0.5);
131+
132+
// disconnect first sensor
133+
sub.reset();
134+
135+
g_messageCount = 0;
136+
137+
// spawn another sensor with higher update rate
138+
spawnSensorWithUpdateRate("sensor2", ignition::math::Pose3d::Zero, 10);
139+
sub = node->Subscribe("~/sensor2/body/sensor2/imu", SensorCallback);
140+
141+
// wait for more msgs
142+
sleep = 0;
143+
t0 = 0.0;
144+
while (g_messageCount < 30 && sleep++ < maxSleep)
145+
{
146+
if (g_messageCount == 0)
147+
t0 = world->SimTime().Double();
148+
common::Time::MSleep(10);
149+
}
150+
151+
// verify update rate by checking the time it takes to receive n msgs
152+
elapsed = world->SimTime().Double() - t0;
153+
EXPECT_NEAR(3.0, elapsed, 0.5);
154+
}
155+
51156
int main(int argc, char **argv)
52157
{
53158
::testing::InitGoogleTest(&argc, argv);

0 commit comments

Comments
 (0)