diff --git a/examples/worlds/CMakeLists.txt b/examples/worlds/CMakeLists.txt
index fee3a9e41c..258ee1529e 100644
--- a/examples/worlds/CMakeLists.txt
+++ b/examples/worlds/CMakeLists.txt
@@ -2,4 +2,8 @@ file(GLOB files "*.sdf")
install(FILES ${files}
DESTINATION ${GZ_DATA_INSTALL_DIR}/worlds)
+file(GLOB csv_files "*.csv")
+install(FILES ${csv_files}
+ DESTINATION ${GZ_DATA_INSTALL_DIR}/worlds)
+
add_subdirectory(thumbnails)
diff --git a/examples/worlds/advanced_lift_drag_system.sdf b/examples/worlds/advanced_lift_drag_system.sdf
new file mode 100644
index 0000000000..8b21318720
--- /dev/null
+++ b/examples/worlds/advanced_lift_drag_system.sdf
@@ -0,0 +1,686 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ 0 0 10 0 0 0
+ 1 1 1 1
+ 0.5 0.5 0.5 1
+
+ 1000
+ 0.9
+ 0.01
+ 0.001
+
+ -0.5 0.1 -0.9
+
+
+
+ true
+
+
+
+
+ 0 0 1
+ 100 100
+
+
+
+
+
+
+ 0 0 1
+ 100 100
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+
+
+
+ 0 0 0.246 0 0 0
+
+ 0 0 0 0 0 0
+
+ 0 0 0 0 0 0
+ 1
+
+ 0.197563
+ 0
+ 0
+ 0.1458929
+ 0
+ 0.1477
+
+
+
+ 0 0 -0.07 0 0 0
+
+
+ 0.47 0.47 0.11
+
+
+
+
+
+ 10
+ 0.01
+
+
+
+
+
+
+
+
+ 0.07 0 -0.08 0 0 0
+
+
+ 0.1 0.1 0.1
+ https://fuel.gazebosim.org/1.0/frede791/models/Advanced%20Plane/tip/files/meshes/body.dae
+
+
+
+ .175 .175 .175 1.0
+ .175 .175 .175 1.0
+
+
+ 1
+
+ 0
+
+
+ 1
+ 250
+
+
+
+ 1
+ 50
+
+
+
+ 0
+ 0.01
+
+
+
+
+
+
+
+ 0.3 0 0.0 0 1.57 0
+
+ 0 0 0 0 0 0
+ 0.005
+
+ 9.75e-07
+ 0
+ 0
+ 0.000166704
+ 0
+ 0.000167604
+
+
+
+ 0.0 0 0.0 0 0 0
+
+
+ 0.005
+ 0.1
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 -0.09 0 0 0
+
+
+ 1 1 1
+ https://fuel.gazebosim.org/1.0/frede791/models/Advanced%20Plane/tip/files/meshes/iris_prop_ccw.dae
+
+
+
+ .175 .175 .175 1.0
+ .175 .175 .175 1.0
+
+
+ 1
+
+ 0
+
+
+
+ rotor_puller
+ base_link
+
+ 1 0 0
+
+ -1e+16
+ 1e+16
+
+
+ 0
+ 0
+
+ 1
+
+
+
+
+
+ 0.00000001
+
+ 0.000001
+ 0.0
+ 0.000001
+ 0.0
+ 0.0
+ 0.000001
+
+ 0 0.3 0 0.00 0 0.0
+
+
+ 0.07 0.0 -0.08 0.00 0 0.0
+
+
+ 0.1 0.1 0.1
+ https://fuel.gazebosim.org/1.0/frede791/models/Advanced%20Plane/tip/files/meshes/left_aileron.dae
+
+
+
+ 1 0 0 1.0
+ 1 0 0 1.0
+
+
+
+
+
+
+ 0.00000001
+
+ 0.000001
+ 0.0
+ 0.000001
+ 0.0
+ 0.0
+ 0.000001
+
+ 0 -0.3 0 0.00 0 0.0
+
+
+ 0.07 0.0 -0.08 0.00 0 0.0
+
+
+ 0.1 0.1 0.1
+ https://fuel.gazebosim.org/1.0/frede791/models/Advanced%20Plane/tip/files/meshes/right_aileron.dae
+
+
+
+ 1 0 0 1.0
+ 1 0 0 1.0
+
+
+
+
+
+
+ 0.00000001
+
+ 0.000001
+ 0.0
+ 0.000001
+ 0.0
+ 0.0
+ 0.000001
+
+ 0 0.15 0 0.00 0 0.0
+
+
+ 0.07 0.0 -0.08 0.00 0 0.0
+
+
+ 0.1 0.1 0.1
+ https://fuel.gazebosim.org/1.0/frede791/models/Advanced%20Plane/tip/files/meshes/left_flap.dae
+
+
+
+ 1 0 0 1.0
+ 1 0 0 1.0
+
+
+
+
+
+
+ 0.00000001
+
+ 0.000001
+ 0.0
+ 0.000001
+ 0.0
+ 0.0
+ 0.000001
+
+ 0 -0.15 0 0.00 0 0.0
+
+
+ 0.07 0.0 -0.08 0.00 0 0.0
+
+
+ 0.1 0.1 0.1
+ https://fuel.gazebosim.org/1.0/frede791/models/Advanced%20Plane/tip/files/meshes/right_flap.dae
+
+
+
+ 1 0 0 1.0
+ 1 0 0 1.0
+
+
+
+
+
+
+ 0.00000001
+
+ 0.000001
+ 0.0
+ 0.000001
+ 0.0
+ 0.0
+ 0.000001
+
+ -0.5 0 0 0.00 0 0.0
+
+
+ 0.07 0.0 -0.08 0.00 0 0.0
+
+
+ 0.1 0.1 0.1
+ https://fuel.gazebosim.org/1.0/frede791/models/Advanced%20Plane/tip/files/meshes/elevators.dae
+
+
+
+ 1 0 0 1.0
+ 1 0 0 1.0
+
+
+
+
+
+
+ 0.00000001
+
+ 0.000001
+ 0.0
+ 0.000001
+ 0.0
+ 0.0
+ 0.000001
+
+ -0.5 0 0.05 0 0 0
+
+
+ 0.07 0.0 -0.08 0.00 0 0.0
+
+
+ 0.1 0.1 0.1
+ https://fuel.gazebosim.org/1.0/frede791/models/Advanced%20Plane/tip/files/meshes/rudder.dae
+
+
+
+ 1 0 0 1.0
+ 1 0 0 1.0
+
+
+
+
+
+ base_link
+ left_elevon
+ -0.07 0.4 0.08 0.00 0 0.0
+
+ 0 1 0
+
+
+ -0.53
+ 0.53
+
+
+ 1.000
+
+
+
+
+ 1
+
+
+
+
+
+ servo_0
+ servo_0
+ 10
+ 0
+ 0
+
+
+
+ base_link
+ right_elevon
+ -0.07 -0.4 0.08 0.00 0 0.0
+
+ 0 1 0
+
+
+ -0.53
+ 0.53
+
+
+ 1.000
+
+
+
+
+ 1
+
+
+
+
+
+ servo_1
+ servo_1
+ 10
+ 0
+ 0
+
+
+
+ base_link
+ left_flap
+ -0.07 0.2 0.08 0.00 0 0.0
+
+ 0 1 0
+
+
+ -0.53
+ 0.53
+
+
+ 1.000
+
+
+
+
+ 1
+
+
+
+
+
+ servo_4
+ servo_4
+ 10
+ 0
+ 0
+
+
+
+ base_link
+ right_flap
+ -0.07 -0.2 0.08 0.00 0 0.0
+
+ 0 1 0
+
+
+ -0.53
+ 0.53
+
+
+ 1.000
+
+
+
+
+ 1
+
+
+
+
+
+ servo_5
+ servo_5
+ 10
+ 0
+ 0
+
+
+
+ base_link
+ elevator
+ -0.5 0 0 0 0 0
+
+ 0 1 0
+
+
+ -0.53
+ 0.53
+
+
+ 1.000
+
+
+
+
+ 1
+
+
+
+
+
+ servo_2
+ servo_2
+ 10
+ 0
+ 0
+
+
+
+ base_link
+ rudder
+ -0.5 0 0.05 0.00 0 0.0
+
+ 0 0 1
+
+
+ -0.53
+ 0.53
+
+
+ 1.000
+
+
+
+
+ 1
+
+
+
+
+
+ servo_3
+ servo_3
+ 10
+ 0
+ 0
+
+
+
+
+
+
+ 0.0
+ 0.15188
+ 6.5
+ 0.97
+ 5.015
+ 0.029
+ 0.075
+ -0.463966
+ -0.258244
+ -0.039250
+ 0.100826
+ 0.0
+ 0.065861
+ 0.0
+ -0.487407
+ 0.0
+ -0.040416
+ 0.055166
+ 0.0
+ 7.971792
+ 0.0
+ -12.140140
+ 0.0
+ 0.0
+ 0.230299
+ 0.0
+ 0.078165
+ 0.0
+ -0.089947
+ 0.3391428111
+ -3.85
+ -0.9233984055
+ 0
+ -0.12 0.0 0.0
+ 0.34
+ 0.22
+ 1.2041
+ 1 0 0
+ 0 0 1
+ base_link
+ 4
+
+ servo_0
+ 0
+ 1
+ -0.000059
+ 0.000171
+ -0.011940
+ -0.003331
+ 0.001498
+ -0.000057
+
+
+ servo_1
+ 1
+ 1
+ -0.000059
+ -0.000171
+ -0.011940
+ 0.003331
+ 0.001498
+ 0.000057
+
+
+ servo_2
+ -1
+ 2
+ 0.000274
+ 0
+ 0.010696
+ 0.0
+ -0.025798
+ 0.0
+
+
+ servo_3
+ 1
+ 3
+ 0.0
+ -0.003913
+ 0.0
+ -0.000257
+ 0.0
+ 0.001613
+
+
+
+
+ rotor_puller_joint
+ rotor_puller
+ cw
+ 0.0125
+ 0.025
+ 3500
+ 8.54858e-06
+ 0.01
+ command/motor_speed
+ 0
+ 8.06428e-05
+ 1e-06
+ 10
+ velocity
+
+
+ servo_0
+
+
+
+
diff --git a/examples/worlds/environmental_sensor.sdf b/examples/worlds/environmental_sensor.sdf
index e1947c8591..8ef8ed35f1 100644
--- a/examples/worlds/environmental_sensor.sdf
+++ b/examples/worlds/environmental_sensor.sdf
@@ -1,5 +1,12 @@
-
+
+
@@ -104,6 +112,7 @@
1
30
+ sensors/humidity
diff --git a/src/gui/plugins/environment_loader/EnvironmentLoader.cc b/src/gui/plugins/environment_loader/EnvironmentLoader.cc
index 6ac8b464cf..cb688b3a8a 100644
--- a/src/gui/plugins/environment_loader/EnvironmentLoader.cc
+++ b/src/gui/plugins/environment_loader/EnvironmentLoader.cc
@@ -19,10 +19,11 @@
#include
#include
-#include
#include
+#include
#include
+#include
#include
#include
@@ -33,6 +34,11 @@
#include
#include
+#include
+
+#include
+#include
+
using namespace gz;
using namespace sim;
@@ -42,6 +48,11 @@ namespace sim
{
inline namespace GZ_SIM_VERSION_NAMESPACE
{
+const char* preload_plugin_name{
+ "gz::sim::systems::EnvironmentPreload"};
+const char* preload_plugin_filename{
+ "gz-sim-environment-preload-system"};
+using Units = msgs::DataLoadPathOptions_DataAngularUnits;
/// \brief Private data class for EnvironmentLoader
class EnvironmentLoaderPrivate
{
@@ -65,7 +76,7 @@ class EnvironmentLoaderPrivate
public: int zIndex{-1};
/// \brief Index of data dimension to be used as units.
- public: QString unit;
+ public: QString unit{"radians"};
public: using ReferenceT = math::SphericalCoordinates::CoordinateType;
@@ -76,12 +87,12 @@ class EnvironmentLoaderPrivate
{QString("ecef"), math::SphericalCoordinates::ECEF}};
/// \brief Map of supported spatial units.
- public: const QMap
+ public: const QMap
unitMap{
{QString("degree"),
- components::EnvironmentalData::ReferenceUnits::DEGREES},
+ Units::DataLoadPathOptions_DataAngularUnits_DEGREES},
{QString("radians"),
- components::EnvironmentalData::ReferenceUnits::RADIANS}
+ Units::DataLoadPathOptions_DataAngularUnits_RADIANS}
};
/// \brief Spatial reference.
@@ -92,6 +103,12 @@ class EnvironmentLoaderPrivate
/// \brief Whether to attempt an environmental data load.
public: std::atomic needsLoad{false};
+
+ /// \brief Gz transport node
+ public: transport::Node node;
+
+ /// \brief publisher
+ public: std::optional pub{std::nullopt};
};
}
}
@@ -123,38 +140,54 @@ void EnvironmentLoader::LoadConfig(const tinyxml2::XMLElement *)
void EnvironmentLoader::Update(const UpdateInfo &,
EntityComponentManager &_ecm)
{
- if (this->dataPtr->needsLoad)
+ auto world = worldEntity(_ecm);
+
+ if (!this->dataPtr->pub.has_value())
{
- std::lock_guard lock(this->dataPtr->mutex);
- this->dataPtr->needsLoad = false;
-
- /// TODO(arjo): Handle the case where we are loading a file in windows.
- /// Should SDFormat files support going from windows <=> unix paths?
- std::ifstream dataFile(this->dataPtr->dataPath.toStdString());
- gzmsg << "Loading environmental data from "
- << this->dataPtr->dataPath.toStdString()
- << std::endl;
- try
+ auto topic = transport::TopicUtils::AsValidTopic(
+ scopedName(world, _ecm) + "/" + "environment");
+ this->dataPtr->pub =
+ {this->dataPtr->node.Advertise(topic)};
+ }
+
+ static bool warned = false;
+ if (!this->dataPtr->pub->HasConnections() && !warned)
+ {
+ warned = true;
+ gzwarn << "Could not find a subscriber for the environment. "
+ << "Attempting to load environmental preload plugin."
+ << std::endl;
+
+ auto nameComp = _ecm.Component(world);
+ if (nullptr == nameComp) {
+ gzerr << "Failed to get world name" << std::endl;
+ return;
+ }
+ auto worldName = nameComp->Data();
+ msgs::EntityPlugin_V req;
+ req.mutable_entity()->set_id(world);
+ auto plugin = req.add_plugins();
+ plugin->set_name(preload_plugin_name);
+ plugin->set_filename(preload_plugin_filename);
+ plugin->set_innerxml("");
+ msgs::Boolean res;
+ bool result;
+ const unsigned int timeout = 5000;
+ const auto service = transport::TopicUtils::AsValidTopic(
+ "/world/" + worldName + "/entity/system/add");
+ if (service.empty())
+ {
+ gzerr << "Unable to request " << service << std::endl;
+ return;
+ }
+
+ if (this->dataPtr->node.Request(service, req, timeout, res, result))
{
- using ComponentDataT = components::EnvironmentalData;
- auto data = ComponentDataT::MakeShared(
- common::IO::ReadFrom(
- common::CSVIStreamIterator(dataFile),
- common::CSVIStreamIterator(),
- this->dataPtr->timeIndex, {
- static_cast(this->dataPtr->xIndex),
- static_cast(this->dataPtr->yIndex),
- static_cast(this->dataPtr->zIndex)}),
- this->dataPtr->referenceMap[this->dataPtr->reference],
- this->dataPtr->unitMap[this->dataPtr->unit]);
-
- using ComponentT = components::Environment;
- _ecm.CreateComponent(worldEntity(_ecm), ComponentT{std::move(data)});
+ gzdbg << "Added plugin successfully" << std::endl;
}
- catch (const std::invalid_argument &exc)
+ else
{
- gzerr << "Failed to load environmental data" << std::endl
- << exc.what() << std::endl;
+ gzerr << "Failed to load plugin" << std::endl;
}
}
}
@@ -162,7 +195,25 @@ void EnvironmentLoader::Update(const UpdateInfo &,
/////////////////////////////////////////////////
void EnvironmentLoader::ScheduleLoad()
{
- this->dataPtr->needsLoad = this->IsConfigured();
+ if(this->IsConfigured() && this->dataPtr->pub.has_value())
+ {
+ msgs::DataLoadPathOptions data;
+ data.set_path(this->dataPtr->dataPath.toStdString());
+ data.set_time(
+ this->dataPtr->dimensionList[this->dataPtr->timeIndex].toStdString());
+ data.set_x(
+ this->dataPtr->dimensionList[this->dataPtr->xIndex].toStdString());
+ data.set_y(
+ this->dataPtr->dimensionList[this->dataPtr->yIndex].toStdString());
+ data.set_z(
+ this->dataPtr->dimensionList[this->dataPtr->zIndex].toStdString());
+ auto referenceFrame = this->dataPtr->referenceMap[this->dataPtr->reference];
+
+ data.set_coordinate_type(msgs::ConvertCoord(referenceFrame));
+ data.set_units(this->dataPtr->unitMap[this->dataPtr->unit]);
+
+ this->dataPtr->pub->Publish(data);
+ }
}
/////////////////////////////////////////////////
diff --git a/src/gui/plugins/environment_visualization/EnvironmentVisualization.cc b/src/gui/plugins/environment_visualization/EnvironmentVisualization.cc
index 3ed5f6f9ce..c9c040f454 100644
--- a/src/gui/plugins/environment_visualization/EnvironmentVisualization.cc
+++ b/src/gui/plugins/environment_visualization/EnvironmentVisualization.cc
@@ -36,9 +36,7 @@
#include
-#include
-#include
-#include
+#include
#include
using namespace gz;
@@ -51,201 +49,44 @@ namespace sim
inline namespace GZ_SIM_VERSION_NAMESPACE
{
/// \brief Private data class for EnvironmentVisualization
-class EnvironmentVisualizationPrivate
+class EnvironmentVisualizationTool
{
- public: EnvironmentVisualizationPrivate()
- {
- this->pcPub =
- this->node.Advertise("/point_cloud");
- }
- /// \brief To synchronize member access.
- public: std::mutex mutex;
-
- /// \brief first load we need to scan for existing data sensor
- public: bool first {true};
-
- public: std::atomic resample{true};
/////////////////////////////////////////////////
- public: void CreatePointCloudTopics(
- std::shared_ptr data) {
- this->pubs.clear();
- this->sessions.clear();
- for (auto key : data->frame.Keys())
- {
- this->pubs.emplace(key, node.Advertise(key));
- gz::msgs::Float_V msg;
- this->floatFields.emplace(key, msg);
- this->sessions.emplace(key, data->frame[key].CreateSession());
- }
- }
-
- /////////////////////////////////////////////////
- public: void Step(
- const UpdateInfo &_info,
- std::shared_ptr &data,
- const EntityComponentManager& _ecm,
- double xSamples, double ySamples, double zSamples)
+ public: void Initiallize(
+ const EntityComponentManager &_ecm)
{
- auto now = std::chrono::steady_clock::now();
- std::chrono::duration dt(now - this->lastTick);
-
- if (this->resample)
- {
- this->CreatePointCloudTopics(data);
- this->ResizeCloud(data, _ecm, xSamples, ySamples, zSamples);
- this->resample = false;
- this->lastTick = now;
- }
-
- for (auto &it : this->sessions)
- {
- auto res =
- data->frame[it.first].StepTo(it.second,
- std::chrono::duration(_info.simTime).count());
- if (res.has_value())
- {
- it.second = res.value();
- }
- }
-
- // Publish at 2 hz for now. In future make reconfigureable.
- if (dt.count() > 0.5)
- {
- this->Visualize(data, xSamples, ySamples, zSamples);
- this->Publish();
- lastTick = now;
- }
+ auto world = worldEntity(_ecm);
+ auto topic =
+ common::joinPaths(
+ scopedName(world, _ecm), "environment", "visualize", "res");
+ std::lock_guard lock(this->mutex);
+ this->pcPub = node.Advertise(topic);
}
/////////////////////////////////////////////////
public: void Visualize(
- std::shared_ptr data,
- double xSamples, double ySamples, double zSamples) {
-
- for (auto key : data->frame.Keys())
- {
- const auto session = this->sessions[key];
- auto frame = data->frame[key];
- auto [lower_bound, upper_bound] = frame.Bounds(session);
- auto step = upper_bound - lower_bound;
- auto dx = step.X() / xSamples;
- auto dy = step.Y() / ySamples;
- auto dz = step.Z() / zSamples;
- std::size_t idx = 0;
- for (std::size_t x_steps = 0; x_steps < ceil(xSamples); x_steps++)
- {
- auto x = lower_bound.X() + x_steps * dx;
- for (std::size_t y_steps = 0; y_steps < ceil(ySamples); y_steps++)
- {
- auto y = lower_bound.Y() + y_steps * dy;
- for (std::size_t z_steps = 0; z_steps < ceil(zSamples); z_steps++)
- {
- auto z = lower_bound.Z() + z_steps * dz;
- auto res = frame.LookUp(session, math::Vector3d(x, y, z));
-
- if (res.has_value())
- {
- this->floatFields[key].mutable_data()->Set(idx,
- static_cast(res.value()));
- }
- else
- {
- this->floatFields[key].mutable_data()->Set(idx, std::nanf(""));
- }
- idx++;
- }
- }
- }
- }
- }
-
- /////////////////////////////////////////////////
- public: void Publish()
+ double xSamples, double ySamples, double zSamples)
{
- pcPub.Publish(this->pcMsg);
- for(auto &[key, pub] : this->pubs)
- {
- pub.Publish(this->floatFields[key]);
- }
+ std::lock_guard lock(this->mutex);
+ this->vec = msgs::Convert(math::Vector3d(xSamples, ySamples, zSamples));
+ this->pcPub.Publish(vec);
}
- /////////////////////////////////////////////////
- public: void ResizeCloud(
- std::shared_ptr data,
- const EntityComponentManager& _ecm,
- unsigned int xSamples, unsigned int ySamples, unsigned int zSamples)
- {
- assert (pubs.size() > 0);
-
- // Assume all data have same point cloud.
- gz::msgs::InitPointCloudPacked(pcMsg, "some_frame", true,
- {{"xyz", gz::msgs::PointCloudPacked::Field::FLOAT32}});
- auto numberOfPoints = xSamples * ySamples * zSamples;
- std::size_t dataSize{
- static_cast(numberOfPoints * pcMsg.point_step())};
- pcMsg.mutable_data()->resize(dataSize);
- pcMsg.set_height(1);
- pcMsg.set_width(numberOfPoints);
-
- auto session = this->sessions[this->pubs.begin()->first];
- auto frame = data->frame[this->pubs.begin()->first];
- auto [lower_bound, upper_bound] =
- frame.Bounds(session);
+ /// \brief The sample resolution
+ public: gz::msgs::Vector3d vec;
- auto step = upper_bound - lower_bound;
- auto dx = step.X() / xSamples;
- auto dy = step.Y() / ySamples;
- auto dz = step.Z() / zSamples;
-
- // Populate point cloud
- gz::msgs::PointCloudPackedIterator xIter(pcMsg, "x");
- gz::msgs::PointCloudPackedIterator yIter(pcMsg, "y");
- gz::msgs::PointCloudPackedIterator zIter(pcMsg, "z");
-
- for (std::size_t x_steps = 0; x_steps < xSamples; x_steps++)
- {
- auto x = lower_bound.X() + x_steps * dx;
- for (std::size_t y_steps = 0; y_steps < ySamples; y_steps++)
- {
- auto y = lower_bound.Y() + y_steps * dy;
- for (std::size_t z_steps = 0; z_steps < zSamples; z_steps++)
- {
- auto z = lower_bound.Z() + z_steps * dz;
- auto coords = getGridFieldCoordinates(
- _ecm, math::Vector3d{x, y, z},
- data);
+ /// \brief Publisher to publish sample resolution
+ public: transport::Node::Publisher pcPub;
- if (!coords.has_value())
- {
- continue;
- }
+ /// \brief Gz transport node
+ public: transport::Node node;
- auto pos = coords.value();
- *xIter = pos.X();
- *yIter = pos.Y();
- *zIter = pos.Z();
- ++xIter;
- ++yIter;
- ++zIter;
- }
- }
- }
- for (auto key : data->frame.Keys())
- {
- this->floatFields[key].mutable_data()->Resize(
- numberOfPoints, std::nanf(""));
- }
- }
+ /// \brief To synchronize member access.
+ public: std::mutex mutex;
- public: transport::Node::Publisher pcPub;
- public: std::unordered_map pubs;
- public: std::unordered_map floatFields;
- public: transport::Node node;
- public: gz::msgs::PointCloudPacked pcMsg;
- public: std::unordered_map> sessions;
- public: std::chrono::time_point lastTick;
+ /// \brief first load we need to scan for existing data sensor
+ public: bool first{true};
};
}
}
@@ -253,10 +94,14 @@ class EnvironmentVisualizationPrivate
/////////////////////////////////////////////////
EnvironmentVisualization::EnvironmentVisualization()
- : GuiSystem(), dataPtr(new EnvironmentVisualizationPrivate)
+ : GuiSystem(), dataPtr(new EnvironmentVisualizationTool)
{
gui::App()->Engine()->rootContext()->setContextProperty(
"EnvironmentVisualization", this);
+ this->qtimer = new QTimer(this);
+ connect(qtimer, &QTimer::timeout,
+ this, &EnvironmentVisualization::ResamplePointcloud);
+ this->qtimer->start(1000);
}
/////////////////////////////////////////////////
@@ -274,38 +119,23 @@ void EnvironmentVisualization::LoadConfig(const tinyxml2::XMLElement *)
}
/////////////////////////////////////////////////
-void EnvironmentVisualization::Update(const UpdateInfo &_info,
+void EnvironmentVisualization::Update(const UpdateInfo &,
EntityComponentManager &_ecm)
{
- _ecm.EachNew(
- [this](
- const Entity &/*_entity*/,
- const components::Environment* /*environment*/
- ) {
- this->dataPtr->resample = true;
- return true;
- }
- );
-
- auto environData =
- _ecm.Component(
- worldEntity(_ecm));
-
- if (environData == nullptr)
+ if (this->dataPtr->first)
{
- return;
+ this->dataPtr->Initiallize(_ecm);
+ this->dataPtr->first = false;
+ this->ResamplePointcloud();
}
-
- this->dataPtr->Step(
- _info, environData->Data(), _ecm,
- this->xSamples, this->ySamples, this->zSamples
- );
}
/////////////////////////////////////////////////
void EnvironmentVisualization::ResamplePointcloud()
{
- this->dataPtr->resample = true;
+ this->dataPtr->Visualize(
+ this->xSamples, this->ySamples, this->zSamples
+ );
}
diff --git a/src/gui/plugins/environment_visualization/EnvironmentVisualization.hh b/src/gui/plugins/environment_visualization/EnvironmentVisualization.hh
index 91cd420eb9..e5a3eb4397 100644
--- a/src/gui/plugins/environment_visualization/EnvironmentVisualization.hh
+++ b/src/gui/plugins/environment_visualization/EnvironmentVisualization.hh
@@ -30,7 +30,7 @@ namespace sim
// Inline bracket to help doxygen filtering.
inline namespace GZ_SIM_VERSION_NAMESPACE
{
- class EnvironmentVisualizationPrivate;
+ class EnvironmentVisualizationTool;
/// \class EnvironmentVisualization EnvironmentVisualization.hh
/// gz/sim/systems/EnvironmentVisualization.hh
@@ -66,13 +66,15 @@ inline namespace GZ_SIM_VERSION_NAMESPACE
/// \internal
/// \brief Pointer to private data
- private: std::unique_ptr dataPtr;
+ private: std::unique_ptr dataPtr;
public: unsigned int xSamples{10};
public: unsigned int ySamples{10};
public: unsigned int zSamples{10};
+
+ private: QTimer* qtimer;
};
}
}
diff --git a/src/systems/CMakeLists.txt b/src/systems/CMakeLists.txt
index 7eb7786908..d75a20663d 100644
--- a/src/systems/CMakeLists.txt
+++ b/src/systems/CMakeLists.txt
@@ -96,6 +96,7 @@ endfunction()
add_subdirectory(ackermann_steering)
add_subdirectory(acoustic_comms)
+add_subdirectory(advanced_lift_drag)
add_subdirectory(air_pressure)
add_subdirectory(air_speed)
add_subdirectory(altimeter)
diff --git a/src/systems/advanced_lift_drag/AdvancedLiftDrag.cc b/src/systems/advanced_lift_drag/AdvancedLiftDrag.cc
new file mode 100644
index 0000000000..42d6efc189
--- /dev/null
+++ b/src/systems/advanced_lift_drag/AdvancedLiftDrag.cc
@@ -0,0 +1,840 @@
+/*
+ * Copyright (C) 2019 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @author: Karthik Srivatsan, Frederik Markus
+ * @version: 1.1
+ *
+ * @brief: this plugin models the lift and drag of an aircraft
+ * as a single body, using stability, aerodynamic, and control derivatives.
+ * It takes in a specified number of control surfaces and control
+ * derivatives are defined/specified with respect to the deflection
+ * of individual control surfaces. Coefficients for this plugin can be
+ * obtained using Athena Vortex Lattice (AVL) by Mark Drela
+ * https://nps.edu/web/adsc/aircraft-aerodynamics2
+ * The sign conventions used in this plugin are therefore written
+ * in a way to be compatible with AVL.
+ * Force equations are computed in the body, while
+ * moment equations are computed in the stability frame.
+ * Has been adapted for Gazebo (Ignition) using the ECS.
+ *
+ *
+ */
+
+#include "AdvancedLiftDrag.hh"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "gz/sim/Link.hh"
+#include "gz/sim/Model.hh"
+#include "gz/sim/Util.hh"
+
+#include "gz/sim/components/AngularVelocity.hh"
+#include "gz/sim/components/Inertial.hh"
+#include "gz/sim/components/Joint.hh"
+#include "gz/sim/components/JointPosition.hh"
+#include "gz/sim/components/LinearVelocity.hh"
+#include "gz/sim/components/Link.hh"
+#include "gz/sim/components/Name.hh"
+#include "gz/sim/components/ExternalWorldWrenchCmd.hh"
+#include "gz/sim/components/Pose.hh"
+
+using namespace gz;
+using namespace sim;
+using namespace systems;
+
+class gz::sim::systems::AdvancedLiftDragPrivate
+{
+ // Initialize the system
+ public: void Load(const EntityComponentManager &_ecm,
+ const sdf::ElementPtr &_sdf);
+
+ /// \brief Initializes lift and drag forces in order to later
+ /// update the corresponding components
+ /// \param[in] _ecm Immutable reference to the EntityComponentManager
+ public: void Update(EntityComponentManager &_ecm);
+
+ /// \brief Compute Control Surface effects
+ /// \param[in] _ecm Immutable reference to the EntityComponentManager
+ public: void Comp_Ctrl_surf_eff(EntityComponentManager &_ecm);
+
+ /// \brief Model interface
+ public: Model model{kNullEntity};
+
+ /// \brief Lift Coefficient at zero angle of attack
+ public: double CL0 = 0.0;
+
+ /// \brief Drag coefficient at zero angle of attack
+ public: double CD0 = 0.0;
+
+ /// \brief Pitching moment coefficient at zero angle of attack
+ public: double Cem0 = 0.0;
+
+ // Get angle-of-attack (alpha) derivatives
+ /// \brief dCL/da (slope of CL-alpha curve)
+ public: double CLa = 0.0;
+
+ /// \brief dCy/da (sideforce slope wrt alpha)
+ public: double CYa = 0.0;
+
+ /// \brief dCl/da (roll moment slope wrt alpha)
+ public: double Cella = 0.0;
+
+ /// \brief dCm/da (pitching moment slope wrt alpha - before stall)
+ public: double Cema = 0.0;
+
+ /// \brief dCn/da (yaw moment slope wrt alpha)
+ public: double Cena = 0.0;
+
+ // Get sideslip angle (beta) derivatives
+ /// \brief dCL/dbeta (lift coefficient slope wrt beta)
+ public: double CLb = 0.0;
+
+ /// \brief dCY/dbeta (side force slope wrt beta)
+ public: double CYb = 0.0;
+
+ /// \brief dCl/dbeta (roll moment slope wrt beta)
+ public: double Cellb = 0.0;
+
+ /// \brief dCm/dbeta (pitching moment slope wrt beta)
+ public: double Cemb = 0.0;
+
+ /// \brief dCn/dbeta (yaw moment slope wrt beta)
+ public: double Cenb = 0.0;
+
+ /// \brief angle of attack when airfoil stalls
+ public: double alphaStall = GZ_PI_2;
+
+ /// \brief The angle of attack
+ public: double alpha = 0.0;
+
+ /// \brief The sideslip angle
+ public: double beta = 0.0;
+
+ /// \brief Slope of the Cm-alpha curve after stall
+ public: double CemaStall = 0.0;
+
+ /// \brief AVL reference point (it replaces the center of pressure
+ /// in the original LiftDragPlugin)
+ public: gz::math::Vector3d cp = math::Vector3d::Zero;
+
+ // Get the derivatives with respect to non-dimensional rates.
+ // In the next few lines, if you see "roll/pitch/yaw rate", remember it is in
+ // a non-dimensional form- it is not the actual body rate.
+ // Also, keep in mind that this CDp is not parasitic drag: that is CD0.
+
+ /// \brief dCD/dp (drag coefficient slope wrt roll rate)
+ public: double CDp = 0.0;
+
+ /// \brief dCY/dp (sideforce slope wrt roll rate)
+ public: double CYp = 0.0;
+
+ /// \brief dCL/dp (lift coefficient slope wrt roll rate)
+ public: double CLp = 0.0;
+
+ /// \brief dCl/dp (roll moment slope wrt roll rate)
+ public: double Cellp = 0.0;
+
+ /// \brief dCm/dp (pitching moment slope wrt roll rate)
+ public: double Cemp = 0.0;
+
+ /// \brief dCn/dp (yaw moment slope wrt roll rate)
+ public: double Cenp = 0.0;
+
+ /// \brief dCD/dq (drag coefficient slope wrt pitching rate)
+ public: double CDq = 0.0;
+
+ /// \brief dCY/dq (side force slope wrt pitching rate)
+ public: double CYq = 0.0;
+
+ /// \brief dCL/dq (lift coefficient slope wrt pitching rate)
+ public: double CLq = 0.0;
+
+ /// \brief dCl/dq (roll moment slope wrt pitching rate)
+ public: double Cellq = 0.0;
+
+ /// \brief dCm/dq (pitching moment slope wrt pitching rate)
+ public: double Cemq = 0.0;
+
+ /// \brief dCn/dq (yaw moment slope wrt pitching rate)
+ public: double Cenq = 0.0;
+
+ /// \brief dCD/dr (drag coefficient slope wrt yaw rate)
+ public: double CDr = 0.0;
+
+ /// \brief dCY/dr (side force slope wrt yaw rate)
+ public: double CYr = 0.0;
+
+ /// \brief dCL/dr (lift coefficient slope wrt yaw rate)
+ public: double CLr = 0.0;
+
+ /// \brief dCl/dr (roll moment slope wrt yaw rate)
+ public: double Cellr = 0.0;
+
+ /// \brief dCm/dr (pitching moment slope wrt yaw rate)
+ public: double Cemr = 0.0;
+
+ /// \brief dCn/dr (yaw moment slope wrt yaw rate)
+ public: double Cenr = 0.0;
+
+ /// \brief Number of present control surfaces
+ public: int num_ctrl_surfaces = 0;
+
+ /// Initialize storage of control surface properties
+ /// \brief Joint that the control surface connects to
+ public: std::vector controlJoints;
+
+ /// \brief Direction the control surface deflects to
+ public: std::vector ctrl_surface_direction;
+
+ /// \brief Effect of the control surface's deflection on drag
+ public: std::vector CD_ctrl;
+
+ /// \brief Effect of the control surface's deflection on side force
+ public: std::vector CY_ctrl;
+
+ /// \brief Effect of the control surface's deflection on lift
+ public: std::vector CL_ctrl;
+
+ /// \brief Effect of the control surface's deflection on roll moment
+ public: std::vector Cell_ctrl;
+
+ /// \brief Effect of the control surface's deflection on pitching moment
+ public: std::vector Cem_ctrl;
+
+ /// \brief Effect of the control surface's deflection on yaw moment
+ public: std::vector Cen_ctrl;
+
+ /// \brief Add aspect ratio (should that be computed?)
+ public: double AR = 0.0;
+
+ /// \brief Add mean-aerodynamic chord
+ public: double mac = 0.0;
+
+ /// \brief Add wing efficiency (Oswald efficiency factor for a 3D wing)
+ public: double eff = 0.0;
+
+ /// \brief The sigmoid blending parameter
+ public: double M = 15.0;
+
+ /// \brief coefficients for the flat plate drag model
+ public: double CD_fp_k1 = -0.224;
+ public: double CD_fp_k2 = -0.115;
+
+
+ /// \brief air density
+ /// at 25 deg C it's about 1.1839 kg/m^3
+ /// At 20 °C and 101.325 kPa, dry air has a density of 1.2041 kg/m3.
+ public: double rho = 1.2041;
+
+ /// \brief if the shape is aerodynamically radially symmetric about
+ /// the forward direction. Defaults to false for wing shapes.
+ /// If set to true, the upward direction is determined by the
+ /// angle of attack.
+ public: bool radialSymmetry = false;
+
+ /// \brief effective planeform surface area
+ public: double area = 1.0;
+
+ /// \brief Normally, this is taken as a direction parallel to the chord
+ /// of the airfoil in zero angle of attack forward flight.
+ public: math::Vector3d forward = math::Vector3d::UnitX;
+
+ /// \brief A vector in the lift/drag plane, perpendicular to the forward
+ /// vector. Inflow velocity orthogonal to forward and upward vectors
+ /// is considered flow in the wing sweep direction.
+ public: math::Vector3d upward = math::Vector3d::UnitZ;
+
+ /// \brief how much to change CL per radian of control surface joint
+ /// value.
+ public: double controlJointRadToCL = 4.0;
+
+ /// \brief Link entity targeted this plugin.
+ public: Entity linkEntity;
+
+ /// \brief Joint entity that actuates a control surface for this lifting body
+ public: Entity controlJointEntity;
+
+ /// \brief Set during Load to true if the configuration for the system is
+ /// valid and the post-update can run
+ public: bool validConfig{false};
+
+ /// \brief Copy of the sdf configuration used for this plugin
+ public: sdf::ElementPtr sdfConfig;
+
+ /// \brief Initialization flag
+ public: bool initialized{false};
+};
+
+//////////////////////////////////////////////////
+void AdvancedLiftDragPrivate::Load(const EntityComponentManager &_ecm,
+ const sdf::ElementPtr &_sdf)
+{
+ this->CL0 = _sdf->Get("CL0", this->CL0).first;
+ this->CD0 = _sdf->Get("CD0", this->CD0).first;
+ this->Cem0 = _sdf->Get("Cem0", this->Cem0).first;
+ this->CLa = _sdf->Get("CLa", this->CLa).first;
+ this->CYa = _sdf->Get("CYa", this->CYa).first;
+ this->Cella = _sdf->Get("Cella", this->Cella).first;
+ this->Cema = _sdf->Get("Cema", this->Cema).first;
+ this->Cena = _sdf->Get("Cena", this->Cena).first;
+ this->CLb = _sdf->Get("CLb", this->CLb).first;
+ this->CYb = _sdf->Get("CYb", this->CYb).first;
+ this->Cellb = _sdf->Get("Cellb", this->Cellb).first;
+ this->Cemb = _sdf->Get("Cemb", this->Cemb).first;
+ this->Cenb = _sdf->Get("Cenb", this->Cenb).first;
+ this->alphaStall = _sdf->Get("alpha_stall", this->alphaStall).first;
+ this->CemaStall = _sdf->Get("Cema_stall", this->CemaStall).first;
+ this->cp = _sdf->Get("cp", this->cp).first;
+ this->CDp = _sdf->Get("CDp", this->CDp).first;
+ this->CYp = _sdf->Get("CYp", this->CYp).first;
+ this->CLp = _sdf->Get("CLp", this->CLp).first;
+ this->Cellp = _sdf->Get("Cellp", this->Cellp).first;
+ this->Cemp = _sdf->Get("Cemp", this->Cemp).first;
+ this->Cenp = _sdf->Get("Cenp", this->Cenp).first;
+
+ this->CDq = _sdf->Get("CDq", this->CDq).first;
+ this->CYq = _sdf->Get("CYq", this->CYq).first;
+ this->CLq = _sdf->Get("CLq", this->CLq).first;
+ this->Cellq = _sdf->Get("Cellq", this->Cellq).first;
+ this->Cemq = _sdf->Get("Cemq", this->Cemq).first;
+ this->Cenq = _sdf->Get("Cenq", this->Cenq).first;
+ this->CDr = _sdf->Get("CDr", this->CDr).first;
+ this->CYr = _sdf->Get("CYr", this->CYr).first;
+ this->CLr = _sdf->Get("CLr", this->CLr).first;
+ this->Cellr = _sdf->Get("Cellr", this->Cellr).first;
+ this->Cemr = _sdf->Get("Cemr", this->Cemr).first;
+ this->Cenr = _sdf->Get("Cenr", this->Cenr).first;
+ this->num_ctrl_surfaces = _sdf->Get(
+ "num_ctrl_surfaces", this->num_ctrl_surfaces).first;
+
+ /*
+ Next, get the properties of each control surface: which joint it connects to,
+ which direction it deflects in, and the effect of its deflection on the
+ coefficient of drag, side force, lift, roll moment, pitching moment, and yaw moment.
+ */
+
+ while( _sdf->HasElement("control_surface") )
+ {
+ auto curr_ctrl_surface = _sdf->GetElement("control_surface");
+ auto ctrl_surface_name = curr_ctrl_surface->GetElement(
+ "name")->Get();
+ auto entities =
+ entitiesFromScopedName(ctrl_surface_name, _ecm, this->model.Entity());
+
+ if (entities.empty())
+ {
+ gzerr << "Joint with name[" << ctrl_surface_name << "] not found. "
+ << "The LiftDrag will not generate forces.\n";
+ this->validConfig = false;
+ return;
+ }
+ else if (entities.size() > 1)
+ {
+ gzwarn << "Multiple joint entities with name[" << ctrl_surface_name
+ << "] found. Using the first one.\n";
+ }
+
+ controlJointEntity = *entities.begin();
+ if (!_ecm.EntityHasComponentType(this->controlJointEntity,
+ components::Joint::typeId))
+ {
+ this->controlJointEntity = kNullEntity;
+ gzerr << "Entity with name[" << ctrl_surface_name
+ << "] is not a joint.\n";
+ this->validConfig = false;
+ return;
+ }
+
+ this->controlJoints.push_back(controlJointEntity);
+ this->ctrl_surface_direction.push_back(
+ std::stod(((curr_ctrl_surface->GetElement(
+ "direction"))->GetValue())->GetAsString()));
+ this->CD_ctrl.push_back(
+ std::stod(((curr_ctrl_surface->GetElement(
+ "CD_ctrl"))->GetValue())->GetAsString()));
+ this->CY_ctrl.push_back(
+ std::stod(((curr_ctrl_surface->GetElement(
+ "CY_ctrl"))->GetValue())->GetAsString()));
+ this->CL_ctrl.push_back(
+ std::stod(((curr_ctrl_surface->GetElement(
+ "CL_ctrl"))->GetValue())->GetAsString()));
+ this->Cell_ctrl.push_back(
+ std::stod(((curr_ctrl_surface->GetElement(
+ "Cell_ctrl"))->GetValue())->GetAsString()));
+ this->Cem_ctrl.push_back(
+ std::stod(((curr_ctrl_surface->GetElement(
+ "Cem_ctrl"))->GetValue())->GetAsString()));
+ this->Cen_ctrl.push_back(
+ std::stod(((curr_ctrl_surface->GetElement(
+ "Cen_ctrl"))->GetValue())->GetAsString()));
+ _sdf->RemoveChild(curr_ctrl_surface);
+ }
+
+ this->AR = _sdf->Get("AR", this->AR).first;
+ this->mac = _sdf->Get("mac", this->mac).first;
+ this->eff = _sdf->Get("eff", this->eff).first;
+ this->rho = _sdf->Get("air_density", this->rho).first;
+ this->radialSymmetry = _sdf->Get("radial_symmetry",
+ this->radialSymmetry).first;
+ this->area = _sdf->Get("area", this->area).first;
+
+ // blade forward (-drag) direction in link frame
+ this->forward =
+ _sdf->Get("forward", this->forward).first;
+ if(std::fabs(this->forward.Length()) >= 1e-9){
+ this->forward.Normalize();
+ }
+
+ else
+ {
+ gzerr << "Forward vector length is zero. This is not valid.\n";
+ this->validConfig = false;
+ return;
+ }
+
+ // blade upward (+lift) direction in link frame
+ this->upward = _sdf->Get(
+ "upward", this->upward) .first;
+ this->upward.Normalize();
+
+ this->controlJointRadToCL = _sdf->Get(
+ "control_joint_rad_to_cl", this->controlJointRadToCL).first;
+
+ if (_sdf->HasElement("link_name"))
+ {
+ sdf::ElementPtr elem = _sdf->GetElement("link_name");
+ auto linkName = elem->Get();
+ auto entities =
+ entitiesFromScopedName(linkName, _ecm, this->model.Entity());
+
+ if (entities.empty())
+ {
+ gzerr << "Link with name[" << linkName << "] not found. "
+ << "The AdvancedLiftDrag will not generate forces.\n";
+ this->validConfig = false;
+ return;
+ }
+ else if (entities.size() > 1)
+ {
+ gzwarn << "Multiple link entities with name[" << linkName << "] found. "
+ << "Using the first one.\n";
+ }
+
+ this->linkEntity = *entities.begin();
+ if (!_ecm.EntityHasComponentType(this->linkEntity,
+ components::Link::typeId))
+ {
+ this->linkEntity = kNullEntity;
+ gzerr << "Entity with name[" << linkName << "] is not a link.\n";
+ this->validConfig = false;
+ return;
+ }
+ }
+ else
+ {
+ gzerr << "AdvancedLiftDrag system requires the 'link_name' parameter.\n";
+ this->validConfig = false;
+ return;
+ }
+
+ // If we reached here, we have a valid configuration
+ this->validConfig = true;
+}
+
+//////////////////////////////////////////////////
+AdvancedLiftDrag::AdvancedLiftDrag()
+ : System(), dataPtr(std::make_unique())
+{
+}
+
+//////////////////////////////////////////////////
+void AdvancedLiftDragPrivate::Update(EntityComponentManager &_ecm)
+{
+ GZ_PROFILE("AdvancedLiftDragPrivate::Update");
+ // get linear velocity at cp in world frame
+ const auto worldLinVel =
+ _ecm.Component(this->linkEntity);
+ const auto worldAngVel =
+ _ecm.Component(this->linkEntity);
+ const auto worldPose =
+ _ecm.Component(this->linkEntity);
+
+
+ std::vector controlJointPosition_vec(
+ this->num_ctrl_surfaces);
+ // Generate a new vector that contains the current positions for all joints
+ for(int i = 0; i < this->num_ctrl_surfaces; i++){
+ components::JointPosition *controlJointPosition = nullptr;
+ if(this->controlJoints[i] != kNullEntity){
+ controlJointPosition = _ecm.Component(
+ this->controlJoints[i]);
+ controlJointPosition_vec[i] = controlJointPosition;
+ }
+ }
+
+ if (!worldLinVel || !worldAngVel || !worldPose)
+ return;
+
+ const auto &pose = worldPose->Data();
+ const auto cpWorld = pose.Rot().RotateVector(this->cp);
+ const auto air_velocity = worldLinVel->Data() + worldAngVel->Data().Cross(
+ cpWorld);
+
+ // Define body frame: X forward, Z downward, Y out the right wing
+ gz::math::Vector3d body_x_axis = pose.Rot().RotateVector(this->forward);
+ gz::math::Vector3d body_z_axis = -1*(pose.Rot().RotateVector(this->upward));
+ gz::math::Vector3d body_y_axis = body_z_axis.Cross(body_x_axis);
+
+ // Get the in-plane velocity (remove spanwise velocity from the velocity
+ // vector air_velocity)
+ gz::math::Vector3d velInLDPlane = air_velocity - air_velocity.Dot(
+ body_y_axis)*body_y_axis;
+
+ // Compute dynamic pressure
+ const double speedInLDPlane = velInLDPlane.Length();
+
+ // Define stability frame: X is in-plane velocity, Y is the same as body Y,
+ // and Z perpendicular to both
+ if(speedInLDPlane <= 1e-9){
+ return;
+ }
+ gz::math::Vector3d stability_x_axis = velInLDPlane/speedInLDPlane;
+ gz::math::Vector3d stability_y_axis = body_y_axis;
+ gz::math::Vector3d stability_z_axis = stability_x_axis.Cross(
+ stability_y_axis);
+
+ double span = std::sqrt(this->area*this->AR);
+ double epsilon = 1e-9;
+ if (fabs(this->mac - 0.0) <= epsilon){
+ // Computing the mean aerodynamic chord involves integrating the square of
+ // the chord along the span. If this parameter has not been input, this
+ // plugin will approximate MAC as mean chord. This works for rectangular
+ // and trapezoidal wings, but for more complex wing shapes, doing the
+ // integral is preferred.
+ this->mac = this->area/span;
+ }
+
+ // Get non-dimensional body rates. Gazebo uses ENU, so some have to be flipped
+ // gz::math::Vector3d body_rates = this->link->GetRelativeAngularVel();
+ components::AngularVelocity *AngVel = nullptr;
+ if (this->linkEntity != kNullEntity)
+ {
+ AngVel = _ecm.Component(this->linkEntity);
+ }
+ if(AngVel == nullptr){
+ gzerr << "Angular Velocity cannot be null.\n";
+ this->validConfig = false;
+ return;
+ }
+
+ double rr = AngVel->Data()[0]; // Roll rate
+ double pr = -1*AngVel->Data()[1]; // Pitch rate
+ double yr = -1*AngVel->Data()[2]; // Yaw rate
+
+ // Compute angle of attack, alpha, using the stability and body axes
+ // Project stability x onto body x and z, then take arctan to find alpha
+ double stabx_proj_bodyx = stability_x_axis.Dot(body_x_axis);
+ double stabx_proj_bodyz = stability_x_axis.Dot(body_z_axis);
+ this->alpha = atan2(stabx_proj_bodyz, stabx_proj_bodyx);
+
+ double sinAlpha = sin(this->alpha);
+ double cosAlpha = cos(this->alpha);
+
+ // Compute sideslip angle, beta
+ double velSW = air_velocity.Dot(body_y_axis);
+ double velFW = air_velocity.Dot(body_x_axis);
+ this->beta = (atan2(velSW, velFW));
+
+ // Compute dynamic pressure
+ double dyn_pres = 0.5 * this->rho * speedInLDPlane * speedInLDPlane;
+ double half_rho_vel = 0.5 * this->rho * speedInLDPlane;
+
+ // Compute CL at cp, check for stall
+ double CL{0.0};
+
+ // Use a sigmoid function to blend pre- and post-stall models
+ double sigma = (1+exp(-1*this->M*(this->alpha-this->alphaStall))+exp(
+ this->M*(this->alpha+this->alphaStall)))/((1+exp(
+ -1*this->M*(this->alpha-this->alphaStall)))*(1+exp(
+ this->M*(this->alpha+this->alphaStall)))); // blending function
+
+ // The lift coefficient (as well as all the other coefficients) are
+ // defined with the coefficient build-up method and using a quasi-steady
+ // approach. The first thing that is calculated is the CL of the wing in
+ // steady conditions (normal CL-alpha curve). Secondly, the effect of the
+ // roll, pitch, and yaw is added through the AVL stability coefficients.
+ // Finally, the effect of the control surfaces is added.
+
+ // The lift coefficient of the wing is defined as a combination of a linear
+ // function for the pre-stall regime and a combination of exponents a
+ // trigonometric functions for the post-stall regime. Both functions are
+ // blended in with the sigmoid function.
+ // CL_prestall = this->CL0 + this->CLa*ths->alpha
+ // CL_poststall = 2*(this->alpha/abs(this->alpha))*pow(sinAlpha,2.0)*cosAlpha
+
+ CL = (1-sigma)*(this->CL0 + this->CLa*this->alpha) + sigma*(
+ 2*(this->alpha/abs(this->alpha))*sinAlpha*sinAlpha*cosAlpha);
+
+ // Add sideslip effect, if any
+ CL = CL + this->CLb*this->beta;
+
+ // Compute control surface effects
+ double CL_ctrl_tot = 0;
+ double CD_ctrl_tot = 0;
+ double CY_ctrl_tot = 0;
+ double Cell_ctrl_tot = 0;
+ double Cem_ctrl_tot = 0;
+ double Cen_ctrl_tot = 0;
+
+ for(int i = 0; i < this->num_ctrl_surfaces; i++){
+ double controlAngle = 0.0;
+ if (controlJointPosition_vec[i] && !controlJointPosition_vec[
+ i]->Data().empty())
+ {
+ components::JointPosition *tmp_controlJointPosition =
+ controlJointPosition_vec[i];
+ controlAngle = tmp_controlJointPosition->Data()[0] * 180/M_PI;
+ }
+
+ // AVL's and Gazebo's direction of "positive" deflection may be different.
+ // Future users, keep an eye on this.
+ CL_ctrl_tot += controlAngle*this->CL_ctrl[i]*
+ this->ctrl_surface_direction[i];
+ CD_ctrl_tot += controlAngle*this->CD_ctrl[i]*
+ this->ctrl_surface_direction[i];
+ CY_ctrl_tot += controlAngle*this->CY_ctrl[i]*
+ this->ctrl_surface_direction[i];
+ Cell_ctrl_tot += controlAngle*this->Cell_ctrl[i]*
+ this->ctrl_surface_direction[i];
+ Cem_ctrl_tot += controlAngle*this->Cem_ctrl[i]*
+ this->ctrl_surface_direction[i];
+ Cen_ctrl_tot += controlAngle*this->Cen_ctrl[i]*
+ this->ctrl_surface_direction[i];
+ }
+
+ // AVL outputs a "CZ_elev", but the Z axis is down. This plugin
+ // uses CL_elev, which is the negative of CZ_elev
+ CL = CL+CL_ctrl_tot;
+
+ // Compute lift force at cp
+ gz::math::Vector3d lift = (CL * dyn_pres + (this->CLp * (
+ rr*span/2) * half_rho_vel) + (this->CLq * (pr*this->mac/2) *
+ half_rho_vel) + (this->CLr * (yr*span/2) * half_rho_vel)) *
+ (this->area * (-1 * stability_z_axis));
+
+ // Compute CD at cp, check for stall
+ double CD{0.0};
+
+ // Add in quadratic model and a high-angle-of-attack (Newtonian) model
+ // The post stall model used below has two parts. The first part, the
+ // (estimated) flat plate drag, comes from the data in Ostowari and Naik,
+ // Post-Stall Wind Tunnel Data for NACA 44XX Series Airfoil Sections.
+ // https://www.nrel.gov/docs/legosti/old/2559.pdf
+ // The second part (0.5-0.5cos(2*alpha)) is fitted using data from
+ // Stringer et al,
+ // A new 360° airfoil model for predicting airfoil thrust potential in
+ // vertical-axis wind turbine designs.
+ // https://aip.scitation.org/doi/pdf/10.1063/1.5011207
+ // I halved the drag numbers to make sure it would work with my
+ // flat plate drag model.
+
+
+ // To estimate the flat plate coefficient of drag, I fit a sigmoid function
+ // to the data in Ostowari and Naik. The form I used was:
+ // CD_FP = 2/(1+exp(k1+k2*AR)).
+ // The coefficients k1 and k2 might need some tuning.
+ // I chose a sigmoid because the CD would initially increase quickly
+ // with aspect ratio, but that rate of increase would slow down as AR
+ // goes to infinity.
+
+ double CD_fp = 2 / (1 + exp(this->CD_fp_k1 + this->CD_fp_k2 * (
+ std::max(this->AR, 1 / this->AR))));
+ CD = (1 - sigma) * (this->CD0 + (CL*CL) / (M_PI * this->AR *
+ this->eff)) + sigma * abs(
+ CD_fp * (0.5 - 0.5 * cos(2 * this->alpha)));
+
+ // Add in control surface terms
+ CD = CD + CD_ctrl_tot;
+
+ // Place drag at cp
+ gz::math::Vector3d drag = (CD * dyn_pres + (this->CDp * (rr*span/2) *
+ half_rho_vel) + (
+ this->CDq * (pr*this->mac/2) * half_rho_vel) +
+ (this->CDr * (yr*span/2) * half_rho_vel)) * (this->area *
+ (-1*stability_x_axis));
+
+ // Compute sideforce coefficient, CY
+ // Start with angle of attack, sideslip, and control terms
+ double CY = this->CYa * this->alpha + this->CYb * this->beta + CY_ctrl_tot;
+
+ gz::math::Vector3d sideforce = (CY * dyn_pres + (this->CYp * (
+ rr*span/2) * half_rho_vel) + (this->CYq * (pr*this->mac/2) *
+ half_rho_vel) + (this->CYr * (yr*span/2) * half_rho_vel)) *
+ (this->area * stability_y_axis);
+
+ // The Cm is divided in three sections: alpha>stall angle, alpha<-stall
+ // angle-stall anglestall angle region the Cm is assumed to always be positive or
+ // zero, in the alpha<-stall angle the Cm is assumed to always be
+ // negative or zero.
+
+ double Cem{0.0};
+
+ if (alpha > this->alphaStall)
+ {
+ Cem = this->Cem0 + (this->Cema * this->alphaStall +
+ this->CemaStall * (this->alpha - this->alphaStall));
+ }
+ else if (alpha < -this->alphaStall)
+ {
+ Cem = this->Cem0 + (-this->Cema * this->alphaStall +
+ this->CemaStall * (this->alpha + this->alphaStall));
+ }
+ else
+ {
+ Cem = this->Cem0 + this->Cema * this->alpha;
+ }
+ // Add sideslip effect, if any
+ Cem = this->Cemb * this->beta;
+
+ Cem += Cem_ctrl_tot;
+
+ gz::math::Vector3d pm = ((Cem * dyn_pres) + (this->Cemp * (
+ rr*span/2) * half_rho_vel) + (this->Cemq * (pr*this->mac/2) *
+ half_rho_vel) + (this->Cemr * (yr*span/2) * half_rho_vel)) *
+ (this->area * this->mac * body_y_axis);
+
+ // Compute roll moment coefficient, Cell
+ // Start with angle of attack, sideslip, and control terms
+ double Cell = this-> Cella * this->alpha + this->Cellb *
+ this-> beta + Cell_ctrl_tot;
+ gz::math::Vector3d rm = ((Cell * dyn_pres) + (this->Cellp * (
+ rr*span/2) * half_rho_vel) + (this->Cellq * (pr*this->mac/2) *
+ half_rho_vel) + (this->Cellr * (yr*span/2) * half_rho_vel)) *
+ (this->area * span * body_x_axis);
+
+ // Compute yaw moment coefficient, Cen
+ // Start with angle of attack, sideslip, and control terms
+ double Cen = this->Cena * this->alpha + this->Cenb * this->beta +
+ Cen_ctrl_tot;
+ gz::math::Vector3d ym = ((Cen * dyn_pres) + (this->Cenp * (
+ rr*span/2) * half_rho_vel) + (this->Cenq * (pr*this->mac/2) *
+ half_rho_vel) + (this->Cenr * (yr*span/2) * half_rho_vel)) *
+ (this->area * span * body_z_axis);
+
+ // Compute moment (torque)
+ gz::math::Vector3d moment = pm+rm+ym;
+
+ // Compute force about cg in inertial frame
+ gz::math::Vector3d force = lift + drag + sideforce;
+ gz::math::Vector3d torque = moment;
+
+ // Correct for nan or inf
+ force.Correct();
+ this->cp.Correct();
+ torque.Correct();
+
+ // positions
+ const auto totalTorque = torque + cpWorld.Cross(force);
+ Link link(this->linkEntity);
+ link.AddWorldWrench(_ecm, force, totalTorque);
+}
+
+//////////////////////////////////////////////////
+void AdvancedLiftDrag::Configure(const Entity &_entity,
+ const std::shared_ptr &_sdf,
+ EntityComponentManager &_ecm, EventManager &)
+{
+ this->dataPtr->model = Model(_entity);
+ if (!this->dataPtr->model.Valid(_ecm))
+ {
+ gzerr << "Advanced LiftDrag system should be attached to a model entity."
+ << "Failed to initialize." << std::endl;
+ return;
+ }
+ this->dataPtr->sdfConfig = _sdf->Clone();
+}
+
+//////////////////////////////////////////////////
+void AdvancedLiftDrag::PreUpdate(const UpdateInfo &_info,
+ EntityComponentManager &_ecm)
+{
+ GZ_PROFILE("AdvancedLiftDrag::PreUpdate");
+
+ // \TODO(anyone) Support rewind
+ if (_info.dt < std::chrono::steady_clock::duration::zero())
+ {
+ gzwarn << "Detected jump back in time ["
+ << std::chrono::duration_cast(_info.dt).count()
+ << "s]. System may not work properly." << std::endl;
+ }
+
+ if (!this->dataPtr->initialized)
+ {
+ // We call Load here instead of Configure because we can't be guaranteed
+ // that all entities have been created when Configure is called
+ this->dataPtr->Load(_ecm, this->dataPtr->sdfConfig);
+ this->dataPtr->initialized = true;
+
+ if (this->dataPtr->validConfig)
+ {
+ Link link(this->dataPtr->linkEntity);
+ link.EnableVelocityChecks(_ecm, true);
+
+ for(int i = 0; i < this->dataPtr->num_ctrl_surfaces; i++){
+
+ if ((this->dataPtr->controlJoints[i]!= kNullEntity) &&
+ !_ecm.Component(this->dataPtr->
+ controlJoints[i]))
+ {
+ _ecm.CreateComponent(this->dataPtr->controlJoints[i],
+ components::JointPosition());
+ }
+ }
+ }
+ }
+
+ if (_info.paused)
+ return;
+
+ // This is not an "else" because "initialized" can be set in the if block
+ // above
+ if (this->dataPtr->initialized && this->dataPtr->validConfig)
+ {
+
+ this->dataPtr->Update(_ecm);
+ }
+}
+
+GZ_ADD_PLUGIN(AdvancedLiftDrag,
+ System,
+ AdvancedLiftDrag::ISystemConfigure,
+ AdvancedLiftDrag::ISystemPreUpdate)
+
+GZ_ADD_PLUGIN_ALIAS(AdvancedLiftDrag,
+ "gz::sim::systems::AdvancedLiftDrag")
diff --git a/src/systems/advanced_lift_drag/AdvancedLiftDrag.hh b/src/systems/advanced_lift_drag/AdvancedLiftDrag.hh
new file mode 100644
index 0000000000..0a5be89e8f
--- /dev/null
+++ b/src/systems/advanced_lift_drag/AdvancedLiftDrag.hh
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GZ_SIM_SYSTEMS_ADVANCED_LIFT_DRAG_HH_
+#define GZ_SIM_SYSTEMS_ADVANCED_LIFT_DRAG_HH_
+
+#include
+#include
+
+namespace gz
+{
+namespace sim
+{
+// Inline bracket to help doxygen filtering.
+inline namespace GZ_SIM_VERSION_NAMESPACE {
+namespace systems
+{
+ // Forward declaration
+ class AdvancedLiftDragPrivate;
+
+ /// \brief The LiftDrag system computes lift and drag forces enabling
+ /// simulation of aerodynamic robots.
+ ///
+ ///
+ /// A tool can be found at the link below that automatically generates
+ /// the Advanced Lift Drag plugin for you. All that is needed is to
+ /// provide some physical parameters of the model. The README contains
+ /// all necessary setup and usage steps.
+ ///
+ /// https://github.com/PX4/PX4-Autopilot/blob/main/Tools/simulation/
+ /// gz/tools/avl_automation/
+ ///
+ ///
+ /// ## System Parameters
+ ///
+ /// - ``: Name of the link affected by the group of lift/drag
+ /// properties. This can be a scoped name to reference links in
+ /// nested models. \sa entitiesFromScopedName to learn more
+ /// about scoped names.
+ /// - ``: Density of the fluid this model is suspended in.
+ /// - ``: Surface area of the link.
+ /// - ``: Lift Coefficient at zero angle of attack
+ /// - ``: Pitching moment coefficient at zero angle of attack
+ /// - ``: Drag Coefficient at zero angle of attack
+ /// - ``: dCL/da (slope of CL-alpha curve) Slope of the first
+ /// portion of the alpha-lift coefficient curve.
+ /// - ``: dCy/da (sideforce slope wrt alpha)
+ /// - ``: dCl/da (roll moment slope wrt alpha)
+ /// - ``: dCm/da (pitching moment slope wrt alpha - before stall)
+ /// - ``: dCn/da (yaw moment slope wrt alpha)
+ /// - ``: dCL/dbeta (lift coefficient slope wrt beta)
+ /// - ``: dCY/dbeta (side force slope wrt beta)
+ /// - ``: dCl/dbeta (roll moment slope wrt beta)
+ /// - ``: dCm/dbeta (pitching moment slope wrt beta)
+ /// - ``: dCn/dbeta (yaw moment slope wrt beta)
+ /// - ``: angle of attack when airfoil stalls
+ /// - ``: Slope of the Cm-alpha curve after stall
+ /// - ``: 3-vector replacing the center of pressure in the original
+ /// LiftDragPlugin.
+ /// - ``: dCD/dp (drag coefficient slope wrt roll rate)
+ /// - ``: dCY/dp (sideforce slope wrt roll rate)
+ /// - ``: dCL/dp (lift coefficient slope wrt roll rate)
+ /// - ``: dCl/dp (roll moment slope wrt roll rate)
+ /// - ``: dCn/dp (yaw moment slope wrt roll rate)
+ /// - ``: dCD/dq (drag coefficient slope wrt pitching rate)
+ /// - ``: dCY/dq (side force slope wrt pitching rate)
+ /// - ``: dCL/dq (lift coefficient slope wrt pitching rate)
+ /// - ``: dCl/dq (roll moment slope wrt pitching rate)
+ /// - ``: dCm/dq (pitching moment slope wrt pitching rate)
+ /// - ``: dCn/dq (yaw moment slope wrt pitching rate)
+ /// - ``: dCD/dr (drag coefficient slope wrt yaw rate)
+ /// - ``: dCY/dr (side force slope wrt yaw rate)
+ /// - ``: dCL/dr (lift coefficient slope wrt yaw rate)
+ /// - ``: dCl/dr (roll moment slope wrt yaw rate)
+ /// - ``: dCm/dr (pitching moment slope wrt yaw rate)
+ /// - ``: dCn/dr (yaw moment slope wrt yaw rate)
+ /// - ``: Number of control surfaces
+ /// - ``: Vector that points to the joints that connect to the
+ /// control surface
+ /// - ``: Vectors of control surface deflections
+ /// - ``: Vector of the effect of the deflection on the coefficient
+ /// of drag
+ /// - ``: Vector of the effect of the deflection on the coefficient
+ /// of side force
+ /// - ``: Vector of the effect of the deflection on the coefficient
+ /// of lift
+ /// - ``: Vector of the effect of the deflection on the coefficient
+ /// of roll moment
+ /// - ``: Vector of the effect of the deflection on the coefficient
+ /// of pitching moment
+ /// - ``: Vector of the effect of the deflection on the coefficient
+ /// of yaw moment
+ /// - ``: Aspect ratio
+ /// - ``: The mean-aerodynamic chord
+ /// - ``: Wing efficiency (This is the Oswald efficiency factor for a
+ /// 3D wing)
+ /// - ``: The ratio of the coefficient of drag and alpha slope before
+ /// stall.
+ /// - ``: 3-vector representing the forward direction of motion in
+ /// the link frame.
+ /// - ``: 3-vector representing the direction of lift or drag.
+ /// - ``: Angle of attack at stall point; peak angle of attack.
+ /// - ``: The angle of attack
+ /// - ``: The sideslip angle
+ /// - ``: The sigmoid blending parameter
+ /// - ``: The first of the flat plate drag model coefficients
+ /// - ``: The second of the flat plate drag model coefficients
+ /// - ``: Copy of the sdf configuration used for this plugin
+
+ class AdvancedLiftDrag
+ : public System,
+ public ISystemConfigure,
+ public ISystemPreUpdate
+ {
+ /// \brief Constructor
+ public: AdvancedLiftDrag();
+
+ /// \brief Destructor
+ public: ~AdvancedLiftDrag() override = default;
+
+ // Documentation inherited
+ public: void Configure(const Entity &_entity,
+ const std::shared_ptr &_sdf,
+ EntityComponentManager &_ecm,
+ EventManager &_eventMgr) override;
+
+ /// Documentation inherited
+ public: void PreUpdate(const UpdateInfo &_info,
+ EntityComponentManager &_ecm) final;
+
+ /// \brief Private data pointer
+ private: std::unique_ptr dataPtr;
+ };
+ }
+}
+}
+}
+#endif
diff --git a/src/systems/advanced_lift_drag/CMakeLists.txt b/src/systems/advanced_lift_drag/CMakeLists.txt
new file mode 100644
index 0000000000..271f9d06a1
--- /dev/null
+++ b/src/systems/advanced_lift_drag/CMakeLists.txt
@@ -0,0 +1,4 @@
+gz_add_system(advanced-lift-drag
+ SOURCES
+ AdvancedLiftDrag.cc
+)
diff --git a/src/systems/environment_preload/CMakeLists.txt b/src/systems/environment_preload/CMakeLists.txt
index 178ec5ec32..ed6a4b8efe 100644
--- a/src/systems/environment_preload/CMakeLists.txt
+++ b/src/systems/environment_preload/CMakeLists.txt
@@ -1,6 +1,7 @@
gz_add_system(environment-preload
SOURCES
EnvironmentPreload.cc
+ VisualizationTool.cc
PUBLIC_LINK_LIBS
gz-common${GZ_COMMON_VER}::gz-common${GZ_COMMON_VER}
gz-common${GZ_COMMON_VER}::io
diff --git a/src/systems/environment_preload/EnvironmentPreload.cc b/src/systems/environment_preload/EnvironmentPreload.cc
index 5c678aa2db..5192fa46df 100644
--- a/src/systems/environment_preload/EnvironmentPreload.cc
+++ b/src/systems/environment_preload/EnvironmentPreload.cc
@@ -15,8 +15,10 @@
*
*/
#include "EnvironmentPreload.hh"
+#include "VisualizationTool.hh"
#include
+#include
#include
#include
#include
@@ -27,6 +29,11 @@
#include
+#include
+
+#include
+#include
+
#include "gz/sim/components/Environment.hh"
#include "gz/sim/components/World.hh"
#include "gz/sim/Util.hh"
@@ -35,72 +42,114 @@ using namespace gz;
using namespace sim;
using namespace systems;
+using Units = msgs::DataLoadPathOptions_DataAngularUnits;
//////////////////////////////////////////////////
class gz::sim::systems::EnvironmentPreloadPrivate
{
+ /// \brief Is the file loaded
public: bool loaded{false};
+ /// \brief SDF Description
public: std::shared_ptr sdf;
-};
-//////////////////////////////////////////////////
-EnvironmentPreload::EnvironmentPreload()
- : System(), dataPtr(new EnvironmentPreloadPrivate)
-{
-}
+ /// \brief GzTransport node
+ public: transport::Node node;
-//////////////////////////////////////////////////
-EnvironmentPreload::~EnvironmentPreload() = default;
+ /// \brief Data descriptions
+ public: msgs::DataLoadPathOptions dataDescription;
-//////////////////////////////////////////////////
-void EnvironmentPreload::Configure(
- const Entity &/*_entity*/,
- const std::shared_ptr &_sdf,
- EntityComponentManager &/*_ecm*/,
- EventManager &/*_eventMgr*/)
-{
- this->dataPtr->sdf = _sdf;
-}
+ /// \brief mutex to protect the samples and data description
+ public: std::mutex mtx;
-//////////////////////////////////////////////////
-void EnvironmentPreload::PreUpdate(
- const gz::sim::UpdateInfo &,
- gz::sim::EntityComponentManager &_ecm)
-{
- if (!std::exchange(this->dataPtr->loaded, true))
+ /// \brief Do we need to reload the system.
+ public: std::atomic needsReload{false};
+
+ /// \brief Visualization Helper
+ public: std::unique_ptr visualizationPtr;
+
+ /// \brief Are visualizations enabled
+ public: bool visualize{false};
+
+ /// \brief Sample resolutions
+ public: math::Vector3d samples;
+
+ /// \brief Is the file loaded
+ public: bool fileLoaded{false};
+
+ /// \brief File loading error logger
+ public: bool logFileLoadError{true};
+
+ /// \brief Reference to data
+ public: std::shared_ptr envData;
+
+ //////////////////////////////////////////////////
+ public: EnvironmentPreloadPrivate() :
+ visualizationPtr(new EnvironmentVisualizationTool) {}
+
+ //////////////////////////////////////////////////
+ public: void OnLoadCommand(const msgs::DataLoadPathOptions &_msg)
+ {
+ std::lock_guard lock(this->mtx);
+ this->dataDescription = _msg;
+ this->needsReload = true;
+ this->logFileLoadError = true;
+ this->visualizationPtr->FileReloaded();
+ gzdbg << "Loading file " << _msg.path() << "\n";
+ }
+
+ //////////////////////////////////////////////////
+ public: void OnVisualResChanged(const msgs::Vector3d &_resChanged)
+ {
+ std::lock_guard lock(this->mtx);
+ if (!this->fileLoaded)
+ {
+ // Only visualize if a file exists
+ return;
+ }
+ auto converted = msgs::Convert(_resChanged);
+ if (this->samples == converted)
+ {
+ // If the sample has not changed return.
+ // This is because resampling is expensive.
+ return;
+ }
+ this->samples = converted;
+ this->visualize = true;
+ this->visualizationPtr->resample = true;
+ }
+
+ //////////////////////////////////////////////////
+ public: void ReadSdf(EntityComponentManager &_ecm)
{
- if (!this->dataPtr->sdf->HasElement("data"))
+ if (!this->sdf->HasElement("data"))
{
- gzerr << "No environmental data file was specified";
+ gzerr << "No environmental data file was specified" << std::endl;
return;
}
+ std::lock_guard lock(mtx);
std::string dataPath =
- this->dataPtr->sdf->Get("data");
+ this->sdf->Get("data");
+
if (common::isRelativePath(dataPath))
{
- auto * component =
+ auto *component =
_ecm.Component(worldEntity(_ecm));
const std::string rootPath =
common::parentPath(component->Data().Element()->FilePath());
dataPath = common::joinPaths(rootPath, dataPath);
}
+ this->dataDescription.set_path(dataPath);
- std::ifstream dataFile(dataPath);
- if (!dataFile.is_open())
- {
- gzerr << "No environmental data file was found at " << dataPath;
- return;
- }
-
- components::EnvironmentalData::ReferenceUnits unit{
- components::EnvironmentalData::ReferenceUnits::RADIANS};
+ this->dataDescription.set_units(
+ Units::DataLoadPathOptions_DataAngularUnits_RADIANS);
std::string timeColumnName{"t"};
bool ignoreTime = false;
std::array spatialColumnNames{"x", "y", "z"};
- auto spatialReference = math::SphericalCoordinates::GLOBAL;
sdf::ElementConstPtr elem =
- this->dataPtr->sdf->FindElement("dimensions");
+ this->sdf->FindElement("dimensions");
+ msgs::SphericalCoordinatesType spatialReference =
+ msgs::SphericalCoordinatesType::GLOBAL;
if (elem)
{
if (elem->HasElement("ignore_time"))
@@ -120,27 +169,28 @@ void EnvironmentPreload::PreUpdate(
elem->Get("reference");
if (referenceName == "global")
{
- spatialReference = math::SphericalCoordinates::GLOBAL;
+ spatialReference = msgs::SphericalCoordinatesType::GLOBAL;
}
else if (referenceName == "spherical")
{
- spatialReference = math::SphericalCoordinates::SPHERICAL;
+ spatialReference = msgs::SphericalCoordinatesType::SPHERICAL;
if (elem->HasAttribute("units"))
{
std::string unitName = elem->Get("units");
if (unitName == "degrees")
{
- unit = components::EnvironmentalData::ReferenceUnits::DEGREES;
+ this->dataDescription.set_units(
+ Units::DataLoadPathOptions_DataAngularUnits_DEGREES);
}
else if (unitName != "radians")
{
- gzerr << "Unrecognized unit " << unitName << "\n";
+ ignerr << "Unrecognized unit " << unitName << "\n";
}
}
}
else if (referenceName == "ecef")
{
- spatialReference = math::SphericalCoordinates::ECEF;
+ spatialReference = msgs::SphericalCoordinatesType::ECEF;
}
else
{
@@ -160,26 +210,143 @@ void EnvironmentPreload::PreUpdate(
}
}
+ this->dataDescription.set_static_time(ignoreTime);
+ this->dataDescription.set_coordinate_type(spatialReference);
+ this->dataDescription.set_time(timeColumnName);
+ this->dataDescription.set_x(spatialColumnNames[0]);
+ this->dataDescription.set_y(spatialColumnNames[1]);
+ this->dataDescription.set_z(spatialColumnNames[2]);
+
+ this->needsReload = true;
+ }
+
+ //////////////////////////////////////////////////
+ public: components::EnvironmentalData::ReferenceUnits ConvertUnits(
+ const Units &_unit)
+ {
+ switch (_unit)
+ {
+ case Units::DataLoadPathOptions_DataAngularUnits_DEGREES:
+ return components::EnvironmentalData::ReferenceUnits::DEGREES;
+ case Units::DataLoadPathOptions_DataAngularUnits_RADIANS:
+ return components::EnvironmentalData::ReferenceUnits::RADIANS;
+ default:
+ gzerr << "Invalid unit conversion. Defaulting to radians." << std::endl;
+ return components::EnvironmentalData::ReferenceUnits::RADIANS;
+ }
+ }
+
+ //////////////////////////////////////////////////
+ public: void LoadEnvironment(EntityComponentManager &_ecm)
+ {
try
{
- gzmsg << "Loading Environment Data\n";
+ std::lock_guard lock(this->mtx);
+ std::array spatialColumnNames{
+ this->dataDescription.x(),
+ this->dataDescription.y(),
+ this->dataDescription.z()};
+
+ math::SphericalCoordinates::CoordinateType spatialReference =
+ msgs::Convert(this->dataDescription.coordinate_type());
+ auto units = this->ConvertUnits(this->dataDescription.units());
+
+ std::ifstream dataFile(this->dataDescription.path());
+ if (!dataFile.is_open())
+ {
+ if (this->logFileLoadError)
+ {
+ gzerr << "No environmental data file was found at " <<
+ this->dataDescription.path() << std::endl;
+ logFileLoadError = false;
+ }
+ return;
+ }
+
+ gzmsg << "Loading Environment Data " << this->dataDescription.path() <<
+ std::endl;
+
using ComponentDataT = components::EnvironmentalData;
auto data = ComponentDataT::MakeShared(
common::IO::ReadFrom(
common::CSVIStreamIterator(dataFile),
common::CSVIStreamIterator(),
- timeColumnName, spatialColumnNames),
- spatialReference, unit, ignoreTime);
-
+ this->dataDescription.time(), spatialColumnNames),
+ spatialReference, units, this->dataDescription.static_time());
+ this->envData = data;
using ComponentT = components::Environment;
auto component = ComponentT{std::move(data)};
_ecm.CreateComponent(worldEntity(_ecm), std::move(component));
+ this->visualizationPtr->resample = true;
+ this->fileLoaded = true;
}
catch (const std::invalid_argument &exc)
{
- gzerr << "Failed to load environment data" << std::endl
- << exc.what() << std::endl;
+ if (this->logFileLoadError)
+ {
+ gzerr << "Failed to load environment data" << std::endl
+ << exc.what() << std::endl;
+ this->logFileLoadError = false;
+ }
}
+
+ this->needsReload = false;
+ }
+};
+
+//////////////////////////////////////////////////
+EnvironmentPreload::EnvironmentPreload()
+ : System(), dataPtr(new EnvironmentPreloadPrivate)
+{
+}
+
+//////////////////////////////////////////////////
+EnvironmentPreload::~EnvironmentPreload() = default;
+
+//////////////////////////////////////////////////
+void EnvironmentPreload::Configure(
+ const Entity &/*_entity*/,
+ const std::shared_ptr &_sdf,
+ EntityComponentManager &/*_ecm*/,
+ EventManager &/*_eventMgr*/)
+{
+ this->dataPtr->sdf = _sdf;
+}
+
+//////////////////////////////////////////////////
+void EnvironmentPreload::PreUpdate(
+ const gz::sim::UpdateInfo &_info,
+ gz::sim::EntityComponentManager &_ecm)
+{
+ if (!std::exchange(this->dataPtr->loaded, true))
+ {
+ auto world = worldEntity(_ecm);
+
+ // See https://github.com/gazebosim/gz-sim/issues/1786
+ this->dataPtr->node.Subscribe(
+ transport::TopicUtils::AsValidTopic(
+ scopedName(world, _ecm) + "/environment"),
+ &EnvironmentPreloadPrivate::OnLoadCommand, this->dataPtr.get());
+ this->dataPtr->node.Subscribe(
+ transport::TopicUtils::AsValidTopic(
+ scopedName(world, _ecm) + "/environment/visualize/res"),
+ &EnvironmentPreloadPrivate::OnVisualResChanged, this->dataPtr.get());
+
+ this->dataPtr->visualizationPtr->resample = true;
+ this->dataPtr->ReadSdf(_ecm);
+ }
+
+ if (this->dataPtr->needsReload)
+ {
+ this->dataPtr->LoadEnvironment(_ecm);
+ }
+
+ if (this->dataPtr->visualize)
+ {
+ std::lock_guard lock(this->dataPtr->mtx);
+ auto samples = this->dataPtr->samples;
+ this->dataPtr->visualizationPtr->Step(_info, _ecm, this->dataPtr->envData,
+ samples.X(), samples.Y(), samples.Z());
}
}
diff --git a/src/systems/environment_preload/VisualizationTool.cc b/src/systems/environment_preload/VisualizationTool.cc
new file mode 100644
index 0000000000..75b37b6557
--- /dev/null
+++ b/src/systems/environment_preload/VisualizationTool.cc
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2022 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "VisualizationTool.hh"
+
+/////////////////////////////////////////////////
+EnvironmentVisualizationTool::EnvironmentVisualizationTool()
+{
+ this->pcPub =
+ this->node.Advertise("/point_cloud");
+}
+
+/////////////////////////////////////////////////
+void EnvironmentVisualizationTool::CreatePointCloudTopics(
+ const std::shared_ptr &_data,
+ const UpdateInfo &_info)
+{
+ this->pubs.clear();
+ this->sessions.clear();
+
+ for (auto key : _data->frame.Keys())
+ {
+ this->pubs.emplace(key, node.Advertise(key));
+ gz::msgs::Float_V msg;
+ this->floatFields.emplace(key, msg);
+ const double time = std::chrono::duration(_info.simTime).count();
+ auto sess = _data->frame[key].CreateSession(time);
+ if (!_data->frame[key].IsValid(sess))
+ {
+ gzerr << key << "data is out of time bounds. Nothing will be published"
+ <finishedTime = true;
+ continue;
+ }
+ this->sessions.emplace(key, sess);
+ }
+}
+
+/////////////////////////////////////////////////
+void EnvironmentVisualizationTool::FileReloaded()
+{
+ this->finishedTime = false;
+}
+
+/////////////////////////////////////////////////
+void EnvironmentVisualizationTool::Step(
+ const UpdateInfo &_info,
+ const EntityComponentManager &_ecm,
+ const std::shared_ptr &_data,
+ double _xSamples, double _ySamples, double _zSamples)
+{
+ if (this->finishedTime)
+ {
+ return;
+ }
+ auto now = std::chrono::steady_clock::now();
+ std::chrono::duration dt(now - this->lastTick);
+
+ if (this->resample)
+ {
+ this->CreatePointCloudTopics(_data, _info);
+ if (this->finishedTime) {
+ this->resample = false;
+ return;
+ }
+ this->ResizeCloud(_data, _ecm, _xSamples, _ySamples, _zSamples);
+ this->resample = false;
+ this->lastTick = now;
+ }
+
+ // Progress session pointers to next time stamp
+ for (auto &it : this->sessions)
+ {
+ auto res =
+ _data->frame[it.first].StepTo(it.second,
+ std::chrono::duration(_info.simTime).count());
+ if (res.has_value())
+ {
+ it.second = res.value();
+ }
+ else
+ {
+ gzerr << "Data does not exist beyond this time."
+ << " Not publishing new environment visuallization data."
+ << std::endl;
+ this->finishedTime = true;
+ return;
+ }
+ }
+
+ // Publish at 2 hz for now. In future make reconfigureable.
+ if (dt.count() > 0.5 && !this->finishedTime)
+ {
+ this->Visualize(_data, _xSamples, _ySamples, _zSamples);
+ this->Publish();
+ lastTick = now;
+ }
+}
+
+/////////////////////////////////////////////////
+void EnvironmentVisualizationTool::Visualize(
+ const std::shared_ptr &_data,
+ double _xSamples, double _ySamples, double _zSamples)
+{
+ for (auto key : _data->frame.Keys())
+ {
+ const auto session = this->sessions[key];
+ auto frame = _data->frame[key];
+ auto [lower_bound, upper_bound] = frame.Bounds(session);
+ auto step = upper_bound - lower_bound;
+ auto dx = step.X() / _xSamples;
+ auto dy = step.Y() / _ySamples;
+ auto dz = step.Z() / _zSamples;
+ std::size_t idx = 0;
+ for (std::size_t x_steps = 0; x_steps < ceil(_xSamples); x_steps++)
+ {
+ auto x = lower_bound.X() + x_steps * dx;
+ for (std::size_t y_steps = 0; y_steps < ceil(_ySamples); y_steps++)
+ {
+ auto y = lower_bound.Y() + y_steps * dy;
+ for (std::size_t z_steps = 0; z_steps < ceil(_zSamples); z_steps++)
+ {
+ auto z = lower_bound.Z() + z_steps * dz;
+ auto res = frame.LookUp(session, math::Vector3d(x, y, z));
+
+ if (res.has_value())
+ {
+ this->floatFields[key].mutable_data()->Set(idx,
+ static_cast(res.value()));
+ }
+ else
+ {
+ this->floatFields[key].mutable_data()->Set(idx, std::nanf(""));
+ }
+ idx++;
+ }
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////
+void EnvironmentVisualizationTool::Publish()
+{
+ pcPub.Publish(this->pcMsg);
+ for (auto &[key, pub] : this->pubs)
+ {
+ pub.Publish(this->floatFields[key]);
+ }
+}
+
+/////////////////////////////////////////////////
+void EnvironmentVisualizationTool::ResizeCloud(
+ const std::shared_ptr &_data,
+ const EntityComponentManager &_ecm,
+ unsigned int _numXSamples,
+ unsigned int _numYSamples,
+ unsigned int _numZSamples)
+{
+ assert(pubs.size() > 0);
+
+ // Assume all data have same point cloud.
+ gz::msgs::InitPointCloudPacked(pcMsg, "some_frame", true,
+ {{"xyz", gz::msgs::PointCloudPacked::Field::FLOAT32}});
+ auto numberOfPoints = _numXSamples * _numYSamples * _numZSamples;
+ std::size_t dataSize{
+ static_cast(numberOfPoints * pcMsg.point_step())};
+ pcMsg.mutable_data()->resize(dataSize);
+ pcMsg.set_height(1);
+ pcMsg.set_width(numberOfPoints);
+
+ auto session = this->sessions[this->pubs.begin()->first];
+ auto frame = _data->frame[this->pubs.begin()->first];
+ auto [lower_bound, upper_bound] = frame.Bounds(session);
+
+ auto step = upper_bound - lower_bound;
+ auto dx = step.X() / _numXSamples;
+ auto dy = step.Y() / _numYSamples;
+ auto dz = step.Z() / _numZSamples;
+
+ // Populate point cloud
+ gz::msgs::PointCloudPackedIterator xIter(pcMsg, "x");
+ gz::msgs::PointCloudPackedIterator yIter(pcMsg, "y");
+ gz::msgs::PointCloudPackedIterator zIter(pcMsg, "z");
+
+ for (std::size_t x_steps = 0; x_steps < _numXSamples; x_steps++)
+ {
+ auto x = lower_bound.X() + x_steps * dx;
+ for (std::size_t y_steps = 0; y_steps < _numYSamples; y_steps++)
+ {
+ auto y = lower_bound.Y() + y_steps * dy;
+ for (std::size_t z_steps = 0; z_steps < _numZSamples; z_steps++)
+ {
+ auto z = lower_bound.Z() + z_steps * dz;
+ auto coords = getGridFieldCoordinates(_ecm, math::Vector3d{x, y, z},
+ _data);
+
+ if (!coords.has_value())
+ {
+ continue;
+ }
+
+ auto pos = coords.value();
+ *xIter = pos.X();
+ *yIter = pos.Y();
+ *zIter = pos.Z();
+ ++xIter;
+ ++yIter;
+ ++zIter;
+ }
+ }
+ }
+ for (auto key : _data->frame.Keys())
+ {
+ this->floatFields[key].mutable_data()->Resize(
+ numberOfPoints, std::nanf(""));
+ }
+}
diff --git a/src/systems/environment_preload/VisualizationTool.hh b/src/systems/environment_preload/VisualizationTool.hh
new file mode 100644
index 0000000000..bb2e2fce1b
--- /dev/null
+++ b/src/systems/environment_preload/VisualizationTool.hh
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#ifndef GZ_SIM_SYSTEMS_ENVIRONMENTPRELOAD_VIZTOOL_HH_
+#define GZ_SIM_SYSTEMS_ENVIRONMENTPRELOAD_VIZTOOL_HH_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "gz/sim/components/Environment.hh"
+#include "gz/sim/Util.hh"
+
+using namespace gz;
+using namespace sim;
+
+namespace gz
+{
+namespace sim
+{
+inline namespace GZ_SIM_VERSION_NAMESPACE
+{
+
+/// \brief This class helps handle point cloud visuallizations
+/// of environment data.
+class EnvironmentVisualizationTool
+{
+ /// \brief Environment constructor
+ public: EnvironmentVisualizationTool();
+
+ /// \brief To synchronize member access.
+ private: std::mutex mutex;
+
+ /// \brief First load we need to scan for existing data sensor
+ private: bool first{true};
+
+ /// \brief Enable resampling
+ public: std::atomic resample{true};
+
+ /// \brief Time has come to an end.
+ private: bool finishedTime{false};
+
+ /// \brief Create publisher structures whenever a new environment is made
+ /// available.
+ /// \param[in] _data Data to be visuallized
+ /// \param[in] _info simulation info for current time step
+ private: void CreatePointCloudTopics(
+ const std::shared_ptr &_data,
+ const UpdateInfo &_info);
+
+ /// \brief Invoke when new file is made available.
+ public: void FileReloaded();
+
+ /// \brief Step the visualizations
+ /// \param[in] _info The simulation info including timestep
+ /// \param[in] _ecm The Entity-Component-Manager
+ /// \param[in] _data The data to be visuallized
+ /// \param[in] _xSample Samples along x
+ /// \param[in] _ySample Samples along y
+ /// \param[in] _zSample Samples along z
+ public: void Step(
+ const UpdateInfo &_info,
+ const EntityComponentManager &_ecm,
+ const std::shared_ptr &_data,
+ double _xSamples, double _ySamples, double _zSamples);
+
+ /// \brief Publishes a sample of the data
+ /// \param[in] _data The data to be visuallized
+ /// \param[in] _xSample Samples along x
+ /// \param[in] _ySample Samples along y
+ /// \param[in] _zSample Samples along z
+ private: void Visualize(
+ const std::shared_ptr