Skip to content

Commit 527f7fa

Browse files
srmainwaringIceShuttle
authored andcommitted
EnvironmentPreload visualise static environments (gazebosim#3138)
## Summary This PR fixes an issue where a static environment loaded by the EnvironmentPreload system would not display using the Environment Visualization Resolution and Point Cloud GUI widgets. There is also a fix that prevents segmentation faults in the VisualizationTool caused by copying a DataFrame which may invalidate the session iterators. ## Details Before this change, if the `<ignore_time>` element is added to the EnvironmentPreload system xml it is ignored when visualising the point cloud data. ```xml <plugin filename="gz-sim-environment-preload-system" name="gz::sim::systems::EnvironmentPreload"> <data>environmental_data.csv</data> <dimensions> <ignore_time>1</ignore_time> <time>timestamp</time> <space> <x>x</x> <y>y</y> <z>z</z> </space> </dimensions> </plugin> ``` This means that if only the time zero data is provided, for example: ```bash # environmental_data.csv - modified to only contain data for timestamp = 0 timestamp,humidity,x,y,z 0,80,-1,-1,-1 0,80,-1,-1, 1 0,80,-1, 1,-1 0,80,-1, 1, 1 0,90, 1,-1,-1 0,90, 1,-1, 1 0,90, 1, 1,-1 0,90, 1, 1, 1 ``` the VisualizationTool would never display the point cloud data. A workaround for this is to set a very large timestamp for a second copy of data. This is unsatisfactory because it doubles the size of the environment data required (which may be significant for large vector fields), and also requires a temporal interpolation when none is required, which results in a performance penalty. The other fix is to pay attention to unintended copies when using the auto keyword to obtain aliases to the data frame objects. The main change is ```diff - auto frame = _data->frame[this->pubs.begin()->first]; + const auto &frame = _data->frame[this->pubs.begin()->first]; ``` The problem is that the session iterators will in general be invalid for the copied frame, and this may result in a segmentation fault when they are dereferenced later in the code. In this particular case the check that the iterator was in range in the interpolation code failed resulting in a out of range access violation a few lines later. This type of error can be difficult to trace. With this change, and some other related fixes to the environmental data system, large static vector fields can be loaded and viewed in the GUI. Below is an example for a wind field containing 64000 rows of data. <img width="1312" height="940" alt="05-1-gz-wind-pc-x-axis" src="https://github.com/user-attachments/assets/72d9bd99-3640-456d-975a-9fc2d6387884" /> --------- Signed-off-by: Rhys Mainwaring <[email protected]>
1 parent 2a1c354 commit 527f7fa

File tree

4 files changed

+65
-46
lines changed

4 files changed

+65
-46
lines changed

examples/worlds/environmental_sensor.sdf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
name="gz::sim::systems::EnvironmentPreload">
3737
<data>environmental_data.csv</data>
3838
<dimensions>
39+
<ignore_time>0</ignore_time>
3940
<time>timestamp</time>
4041
<space>
4142
<x>x</x>

src/systems/environment_preload/EnvironmentPreload.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class gz::sim::systems::EnvironmentPreloadPrivate
120120
}
121121
this->samples = converted;
122122
this->visualize = true;
123-
this->visualizationPtr->resample = true;
123+
this->visualizationPtr->Resample();
124124
}
125125

126126
//////////////////////////////////////////////////
@@ -282,7 +282,7 @@ class gz::sim::systems::EnvironmentPreloadPrivate
282282
using ComponentT = components::Environment;
283283
auto component = ComponentT{std::move(data)};
284284
_ecm.CreateComponent(worldEntity(_ecm), std::move(component));
285-
this->visualizationPtr->resample = true;
285+
this->visualizationPtr->Resample();
286286
this->fileLoaded = true;
287287
}
288288
catch (const std::invalid_argument &exc)
@@ -337,7 +337,7 @@ void EnvironmentPreload::PreUpdate(
337337
scopedName(world, _ecm) + "/environment/visualize/res"),
338338
&EnvironmentPreloadPrivate::OnVisualResChanged, this->dataPtr.get());
339339

340-
this->dataPtr->visualizationPtr->resample = true;
340+
this->dataPtr->visualizationPtr->Resample();
341341
this->dataPtr->ReadSdf(_ecm);
342342
}
343343

@@ -349,7 +349,7 @@ void EnvironmentPreload::PreUpdate(
349349
if (this->dataPtr->visualize)
350350
{
351351
std::lock_guard<std::mutex> lock(this->dataPtr->mtx);
352-
auto samples = this->dataPtr->samples;
352+
const auto &samples = this->dataPtr->samples;
353353
this->dataPtr->visualizationPtr->Step(_info, _ecm, this->dataPtr->envData,
354354
samples.X(), samples.Y(), samples.Z());
355355
}

src/systems/environment_preload/VisualizationTool.cc

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ void EnvironmentVisualizationTool::CreatePointCloudTopics(
3737
this->pubs.emplace(key, node.Advertise<gz::msgs::Float_V>(key));
3838
gz::msgs::Float_V msg;
3939
this->floatFields.emplace(key, msg);
40+
4041
const double time = std::chrono::duration<double>(_info.simTime).count();
41-
auto sess = _data->frame[key].CreateSession(time);
42+
const auto sess = _data->staticTime ?
43+
_data->frame[key].CreateSession(0.0) :
44+
_data->frame[key].CreateSession(time);
4245
if (!_data->frame[key].IsValid(sess))
4346
{
4447
gzerr << key << "data is out of time bounds. Nothing will be published"
@@ -53,21 +56,30 @@ void EnvironmentVisualizationTool::CreatePointCloudTopics(
5356
/////////////////////////////////////////////////
5457
void EnvironmentVisualizationTool::FileReloaded()
5558
{
59+
std::lock_guard<std::mutex> lock(this->mutex);
5660
this->finishedTime = false;
5761
}
5862

63+
/////////////////////////////////////////////////
64+
void EnvironmentVisualizationTool::Resample()
65+
{
66+
std::lock_guard<std::mutex> lock(this->mutex);
67+
this->resample = true;
68+
}
69+
5970
/////////////////////////////////////////////////
6071
void EnvironmentVisualizationTool::Step(
6172
const UpdateInfo &_info,
6273
const EntityComponentManager &_ecm,
6374
const std::shared_ptr<components::EnvironmentalData> &_data,
6475
unsigned int _xSamples, unsigned int _ySamples, unsigned int _zSamples)
6576
{
77+
std::lock_guard<std::mutex> lock(this->mutex);
6678
if (this->finishedTime)
6779
{
6880
return;
6981
}
70-
auto now = std::chrono::steady_clock::now();
82+
const auto now = std::chrono::steady_clock::now();
7183
std::chrono::duration<double> dt(now - this->lastTick);
7284

7385
if (this->resample)
@@ -82,23 +94,25 @@ void EnvironmentVisualizationTool::Step(
8294
this->lastTick = now;
8395
}
8496

85-
// Progress session pointers to next time stamp
86-
for (auto &it : this->sessions)
97+
if (!_data->staticTime)
8798
{
88-
auto res =
89-
_data->frame[it.first].StepTo(it.second,
90-
std::chrono::duration<double>(_info.simTime).count());
91-
if (res.has_value())
92-
{
93-
it.second = res.value();
94-
}
95-
else
99+
// Progress session pointers to next time stamp
100+
for (auto &it : this->sessions)
96101
{
97-
gzerr << "Data does not exist beyond this time."
98-
<< " Not publishing new environment visualization data."
99-
<< std::endl;
100-
this->finishedTime = true;
101-
return;
102+
const auto time = std::chrono::duration<double>(_info.simTime).count();
103+
const auto res = _data->frame[it.first].StepTo(it.second, time);
104+
if (res.has_value())
105+
{
106+
it.second = res.value();
107+
}
108+
else
109+
{
110+
gzerr << "Data does not exist beyond this time (t = " << time << ")."
111+
<< " Not publishing new environment visualization data."
112+
<< std::endl;
113+
this->finishedTime = true;
114+
return;
115+
}
102116
}
103117
}
104118

@@ -118,24 +132,24 @@ void EnvironmentVisualizationTool::Visualize(
118132
{
119133
for (auto key : _data->frame.Keys())
120134
{
121-
const auto session = this->sessions[key];
122-
auto frame = _data->frame[key];
123-
auto [lower_bound, upper_bound] = frame.Bounds(session);
124-
auto step = upper_bound - lower_bound;
125-
auto dx = step.X() / _xSamples;
126-
auto dy = step.Y() / _ySamples;
127-
auto dz = step.Z() / _zSamples;
135+
const auto &session = this->sessions[key];
136+
const auto &frame = _data->frame[key];
137+
const auto [lower_bound, upper_bound] = frame.Bounds(session);
138+
const auto step = upper_bound - lower_bound;
139+
const auto dx = step.X() / _xSamples;
140+
const auto dy = step.Y() / _ySamples;
141+
const auto dz = step.Z() / _zSamples;
128142
std::size_t idx = 0;
129143
for (std::size_t x_steps = 0; x_steps < _xSamples; x_steps++)
130144
{
131-
auto x = lower_bound.X() + x_steps * dx;
145+
const auto x = lower_bound.X() + x_steps * dx;
132146
for (std::size_t y_steps = 0; y_steps < _ySamples; y_steps++)
133147
{
134-
auto y = lower_bound.Y() + y_steps * dy;
148+
const auto y = lower_bound.Y() + y_steps * dy;
135149
for (std::size_t z_steps = 0; z_steps < _zSamples; z_steps++)
136150
{
137-
auto z = lower_bound.Z() + z_steps * dz;
138-
auto res = frame.LookUp(session, math::Vector3d(x, y, z));
151+
const auto z = lower_bound.Z() + z_steps * dz;
152+
const auto res = frame.LookUp(session, math::Vector3d(x, y, z));
139153

140154
if (res.has_value())
141155
{
@@ -176,21 +190,21 @@ void EnvironmentVisualizationTool::ResizeCloud(
176190
// Assume all data have same point cloud.
177191
gz::msgs::InitPointCloudPacked(pcMsg, "some_frame", true,
178192
{{"xyz", gz::msgs::PointCloudPacked::Field::FLOAT32}});
179-
auto numberOfPoints = _numXSamples * _numYSamples * _numZSamples;
193+
const auto numberOfPoints = _numXSamples * _numYSamples * _numZSamples;
180194
std::size_t dataSize{
181195
static_cast<std::size_t>(numberOfPoints * pcMsg.point_step())};
182196
pcMsg.mutable_data()->resize(dataSize);
183197
pcMsg.set_height(1);
184198
pcMsg.set_width(numberOfPoints);
185199

186-
auto session = this->sessions[this->pubs.begin()->first];
187-
auto frame = _data->frame[this->pubs.begin()->first];
188-
auto [lower_bound, upper_bound] = frame.Bounds(session);
200+
const auto &session = this->sessions[this->pubs.begin()->first];
201+
const auto &frame = _data->frame[this->pubs.begin()->first];
202+
const auto [lower_bound, upper_bound] = frame.Bounds(session);
189203

190-
auto step = upper_bound - lower_bound;
191-
auto dx = step.X() / _numXSamples;
192-
auto dy = step.Y() / _numYSamples;
193-
auto dz = step.Z() / _numZSamples;
204+
const auto step = upper_bound - lower_bound;
205+
const auto dx = step.X() / _numXSamples;
206+
const auto dy = step.Y() / _numYSamples;
207+
const auto dz = step.Z() / _numZSamples;
194208

195209
// Populate point cloud
196210
gz::msgs::PointCloudPackedIterator<float> xIter(pcMsg, "x");
@@ -199,22 +213,23 @@ void EnvironmentVisualizationTool::ResizeCloud(
199213

200214
for (std::size_t x_steps = 0; x_steps < _numXSamples; x_steps++)
201215
{
202-
auto x = lower_bound.X() + x_steps * dx;
216+
const auto x = lower_bound.X() + x_steps * dx;
203217
for (std::size_t y_steps = 0; y_steps < _numYSamples; y_steps++)
204218
{
205-
auto y = lower_bound.Y() + y_steps * dy;
219+
const auto y = lower_bound.Y() + y_steps * dy;
206220
for (std::size_t z_steps = 0; z_steps < _numZSamples; z_steps++)
207221
{
208-
auto z = lower_bound.Z() + z_steps * dz;
209-
auto coords = getGridFieldCoordinates(_ecm, math::Vector3d{x, y, z},
222+
const auto z = lower_bound.Z() + z_steps * dz;
223+
const auto coords = getGridFieldCoordinates(
224+
_ecm, math::Vector3d{x, y, z},
210225
_data);
211226

212227
if (!coords.has_value())
213228
{
214229
continue;
215230
}
216231

217-
auto pos = coords.value();
232+
const auto pos = coords.value();
218233
*xIter = pos.X();
219234
*yIter = pos.Y();
220235
*zIter = pos.Z();

src/systems/environment_preload/VisualizationTool.hh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class EnvironmentVisualizationTool
6262
private: bool first{true};
6363

6464
/// \brief Enable resampling
65-
public: std::atomic<bool> resample{true};
65+
private: bool resample{true};
6666

6767
/// \brief Time has come to an end.
6868
private: bool finishedTime{false};
@@ -78,6 +78,9 @@ class EnvironmentVisualizationTool
7878
/// \brief Invoke when new file is made available.
7979
public: void FileReloaded();
8080

81+
/// \brief Invoke when new file is made available.
82+
public: void Resample();
83+
8184
/// \brief Step the visualizations
8285
/// \param[in] _info The simulation info including timestep
8386
/// \param[in] _ecm The Entity-Component-Manager

0 commit comments

Comments
 (0)