From 65a18cb860a1c75df6a5625b09819cc0435fe884 Mon Sep 17 00:00:00 2001 From: Nate Koenig Date: Thu, 25 Feb 2021 17:59:47 -0800 Subject: [PATCH 1/7] Limit scene broadcast publications when paused (#497) Signed-off-by: Nate Koenig Signed-off-by: Louise Poubel Co-authored-by: Nate Koenig Co-authored-by: Louise Poubel --- include/ignition/gazebo/gui/GuiRunner.hh | 4 ++ src/gui/GuiRunner.cc | 45 +++++++++++++++++-- src/systems/physics/Physics.cc | 34 +++++++------- .../scene_broadcaster/SceneBroadcaster.cc | 10 ++--- .../scene_broadcaster/SceneBroadcaster.hh | 1 + 5 files changed, 68 insertions(+), 26 deletions(-) diff --git a/include/ignition/gazebo/gui/GuiRunner.hh b/include/ignition/gazebo/gui/GuiRunner.hh index 43a33a795e..4ea88aa0be 100644 --- a/include/ignition/gazebo/gui/GuiRunner.hh +++ b/include/ignition/gazebo/gui/GuiRunner.hh @@ -63,6 +63,10 @@ class IGNITION_GAZEBO_VISIBLE GuiRunner : public QObject /// \param[in] _msg New state message. private: void OnState(const msgs::SerializedStepMap &_msg); + /// \brief Update the plugins. + /// \todo(anyone) Move to GuiRunner::Implementation when porting to v5 + private: void UpdatePlugins(); + /// \brief Entity-component manager. private: gazebo::EntityComponentManager ecm; diff --git a/src/gui/GuiRunner.cc b/src/gui/GuiRunner.cc index 88c7f4d46a..9d7ebeb16a 100644 --- a/src/gui/GuiRunner.cc +++ b/src/gui/GuiRunner.cc @@ -30,6 +30,16 @@ using namespace ignition; using namespace gazebo; +/// \todo(anyone) Move to GuiRunner::Implementation when porting to v5 +/// \brief Flag used to end the gUpdateThread. +static bool gRunning = false; + +/// \brief Mutex to protect the plugin update. +static std::mutex gUpdateMutex; + +/// \brief The plugin update thread.. +static std::thread gUpdateThread; + ///////////////////////////////////////////////// GuiRunner::GuiRunner(const std::string &_worldName) { @@ -51,10 +61,31 @@ GuiRunner::GuiRunner(const std::string &_worldName) << std::endl; this->RequestState(); + + // Periodically update the plugins + // \todo(anyone) Move the global variables to GuiRunner::Implementation on v5 + gRunning = true; + gUpdateThread = std::thread([&]() + { + while (gRunning) + { + { + std::lock_guard lock(gUpdateMutex); + this->UpdatePlugins(); + } + // This is roughly a 30Hz update rate. + std::this_thread::sleep_for(std::chrono::milliseconds(33)); + } + }); } ///////////////////////////////////////////////// -GuiRunner::~GuiRunner() = default; +GuiRunner::~GuiRunner() +{ + gRunning = false; + if (gUpdateThread.joinable()) + gUpdateThread.join(); +} ///////////////////////////////////////////////// void GuiRunner::RequestState() @@ -108,16 +139,22 @@ void GuiRunner::OnState(const msgs::SerializedStepMap &_msg) IGN_PROFILE_THREAD_NAME("GuiRunner::OnState"); IGN_PROFILE("GuiRunner::Update"); + std::lock_guard lock(gUpdateMutex); this->ecm.SetState(_msg.state()); // Update all plugins this->updateInfo = convert(_msg.stats()); + this->UpdatePlugins(); + this->ecm.ClearNewlyCreatedEntities(); + this->ecm.ProcessRemoveEntityRequests(); +} + +///////////////////////////////////////////////// +void GuiRunner::UpdatePlugins() +{ auto plugins = gui::App()->findChildren(); for (auto plugin : plugins) { plugin->Update(this->updateInfo, this->ecm); } - this->ecm.ClearNewlyCreatedEntities(); - this->ecm.ProcessRemoveEntityRequests(); } - diff --git a/src/systems/physics/Physics.cc b/src/systems/physics/Physics.cc index 84b5276e31..86ee7162a7 100644 --- a/src/systems/physics/Physics.cc +++ b/src/systems/physics/Physics.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -200,6 +201,10 @@ class ignition::gazebo::systems::PhysicsPrivate /// has drained. public: std::unordered_map entityOffMap; + /// \brief Entities whose pose commands have been processed and should be + /// deleted the following iteration. + public: std::unordered_set worldPoseCmdsToRemove; + /// \brief used to store whether physics objects have been created. public: bool initialized = false; @@ -1450,10 +1455,15 @@ void PhysicsPrivate::UpdatePhysics(EntityComponentManager &_ecm) }); // Update model pose + auto olderWorldPoseCmdsToRemove = std::move(this->worldPoseCmdsToRemove); + this->worldPoseCmdsToRemove.clear(); + _ecm.Each( [&](const Entity &_entity, const components::Model *, const components::WorldPoseCmd *_poseCmd) { + this->worldPoseCmdsToRemove.insert(_entity); + auto modelIt = this->entityModelMap.find(_entity); if (modelIt == this->entityModelMap.end()) return true; @@ -1509,6 +1519,13 @@ void PhysicsPrivate::UpdatePhysics(EntityComponentManager &_ecm) return true; }); + // Remove world commands from previous iteration. We let them rotate one + // iteration so other systems have a chance to react to them too. + for (const Entity &entity : olderWorldPoseCmdsToRemove) + { + _ecm.RemoveComponent(entity); + } + // Slip compliance on Collisions _ecm.Each( [&](const Entity &_entity, @@ -1641,23 +1658,6 @@ void PhysicsPrivate::UpdatePhysics(EntityComponentManager &_ecm) return true; }); - // Clear pending commands - // Note: Removing components from inside an Each call can be dangerous. - // Instead, we collect all the entities that have the desired components and - // remove the component from them afterward. - std::vector entitiesWorldCmd; - _ecm.Each( - [&](const Entity &_entity, components::WorldPoseCmd*) -> bool - { - entitiesWorldCmd.push_back(_entity); - return true; - }); - - for (const Entity &entity : entitiesWorldCmd) - { - _ecm.RemoveComponent(entity); - } - // Populate bounding box info // Only compute bounding box if component exists to avoid unnecessary // computations diff --git a/src/systems/scene_broadcaster/SceneBroadcaster.cc b/src/systems/scene_broadcaster/SceneBroadcaster.cc index 722d8f58ac..ac93da86b6 100644 --- a/src/systems/scene_broadcaster/SceneBroadcaster.cc +++ b/src/systems/scene_broadcaster/SceneBroadcaster.cc @@ -272,12 +272,12 @@ void SceneBroadcaster::PostUpdate(const UpdateInfo &_info, // Throttle here instead of using transport::AdvertiseMessageOptions so that // we can skip the ECM serialization bool jumpBackInTime = _info.dt < std::chrono::steady_clock::duration::zero(); - auto now = std::chrono::system_clock::now(); bool changeEvent = _manager.HasEntitiesMarkedForRemoval() || - _manager.HasNewEntities() || _manager.HasOneTimeComponentChanges() || - jumpBackInTime; - bool itsPubTime = now - this->dataPtr->lastStatePubTime > - this->dataPtr->statePublishPeriod; + _manager.HasNewEntities() || _manager.HasOneTimeComponentChanges() || + jumpBackInTime; + auto now = std::chrono::system_clock::now(); + bool itsPubTime = !_info.paused && (now - this->dataPtr->lastStatePubTime > + this->dataPtr->statePublishPeriod); auto shouldPublish = this->dataPtr->statePub.HasConnections() && (changeEvent || itsPubTime); diff --git a/src/systems/scene_broadcaster/SceneBroadcaster.hh b/src/systems/scene_broadcaster/SceneBroadcaster.hh index 0f19e23fe0..9506d080ba 100644 --- a/src/systems/scene_broadcaster/SceneBroadcaster.hh +++ b/src/systems/scene_broadcaster/SceneBroadcaster.hh @@ -55,6 +55,7 @@ namespace systems EntityComponentManager &_ecm, EventManager &_eventMgr) final; + // Documentation inherited public: void PostUpdate(const UpdateInfo &_info, const EntityComponentManager &_ecm) final; From 1a160849eb5fdd7f11d22025808a87178630f935 Mon Sep 17 00:00:00 2001 From: Nate Koenig Date: Mon, 1 Mar 2021 12:36:24 -0800 Subject: [PATCH 2/7] Merge pull request #651 from ignitionrobotics/particle_modification Support particle modification --- CMakeLists.txt | 2 +- src/rendering/RenderUtil.cc | 15 ++++++-- src/rendering/SceneManager.cc | 36 +++++++++++++------ .../particle_emitter/ParticleEmitter.cc | 21 ++++++----- test/integration/components.cc | 36 +++++++++---------- test/integration/particle_emitter.cc | 34 +++++++++--------- 6 files changed, 88 insertions(+), 56 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index abc409f202..378c5ef4bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ set(IGN_TRANSPORT_VER ${ignition-transport9_VERSION_MAJOR}) #-------------------------------------- # Find ignition-msgs -ign_find_package(ignition-msgs6 REQUIRED VERSION 6.3) +ign_find_package(ignition-msgs6 REQUIRED VERSION 6.4) set(IGN_MSGS_VER ${ignition-msgs6_VERSION_MAJOR}) #-------------------------------------- diff --git a/src/rendering/RenderUtil.cc b/src/rendering/RenderUtil.cc index bcc0c03429..204a1ad1e6 100644 --- a/src/rendering/RenderUtil.cc +++ b/src/rendering/RenderUtil.cc @@ -179,6 +179,9 @@ class ignition::gazebo::RenderUtilPrivate public: std::unordered_map newParticleEmittersCmds; + /// \brief A list of entities with particle emitter cmds to remove + public: std::vector particleCmdsToRemove; + /// \brief Map of ids of entites to be removed and sim iteration when the /// remove request is received public: std::unordered_map removeEntities; @@ -342,6 +345,13 @@ void RenderUtil::UpdateECM(const UpdateInfo &/*_info*/, { std::lock_guard lock(this->dataPtr->updateMutex); + // Remove the commands from the entity + // these are commands from the last iteration. We want to make sure all + // systems have a chance to process them first before they are removed. + for (const auto &entity : this->dataPtr->particleCmdsToRemove) + _ecm.RemoveComponent(entity); + this->dataPtr->particleCmdsToRemove.clear(); + // particle emitters commands _ecm.Each( [&](const Entity &_entity, @@ -349,7 +359,7 @@ void RenderUtil::UpdateECM(const UpdateInfo &/*_info*/, { // store emitter properties and update them in rendering thread this->dataPtr->newParticleEmittersCmds[_entity] = - _emitterCmd->Data(); + _emitterCmd->Data(); // update pose comp here if (_emitterCmd->Data().has_pose()) @@ -358,7 +368,8 @@ void RenderUtil::UpdateECM(const UpdateInfo &/*_info*/, if (poseComp) poseComp->Data() = msgs::Convert(_emitterCmd->Data().pose()); } - _ecm.RemoveComponent(_entity); + // Store the entity ids to clear outside of the `Each` loop. + this->dataPtr->particleCmdsToRemove.push_back(_entity); return true; }); diff --git a/src/rendering/SceneManager.cc b/src/rendering/SceneManager.cc index 48b0317b9b..763f21b59f 100644 --- a/src/rendering/SceneManager.cc +++ b/src/rendering/SceneManager.cc @@ -1059,7 +1059,7 @@ rendering::ParticleEmitterPtr SceneManager::UpdateParticleEmitter(Entity _id, } default: { - emitter->SetType(ignition::rendering::EmitterType::EM_POINT); + // Do nothing if type is not set. } } @@ -1068,20 +1068,28 @@ rendering::ParticleEmitterPtr SceneManager::UpdateParticleEmitter(Entity _id, emitter->SetEmitterSize(ignition::msgs::Convert(_emitter.size())); // Rate. - emitter->SetRate(_emitter.rate()); + if (_emitter.has_rate()) + emitter->SetRate(_emitter.rate().data()); // Duration. - emitter->SetDuration(_emitter.duration()); + if (_emitter.has_duration()) + emitter->SetDuration(_emitter.duration().data()); // Emitting. - emitter->SetEmitting(_emitter.emitting()); + if (_emitter.has_emitting()) { + emitter->SetEmitting(_emitter.emitting().data()); + } // Particle size. - emitter->SetParticleSize( - ignition::msgs::Convert(_emitter.particle_size())); + if (_emitter.has_particle_size()) + { + emitter->SetParticleSize( + ignition::msgs::Convert(_emitter.particle_size())); + } // Lifetime. - emitter->SetLifetime(_emitter.lifetime()); + if (_emitter.has_lifetime()) + emitter->SetLifetime(_emitter.lifetime().data()); // Material. if (_emitter.has_material()) @@ -1092,12 +1100,17 @@ rendering::ParticleEmitterPtr SceneManager::UpdateParticleEmitter(Entity _id, } // Velocity range. - emitter->SetVelocityRange(_emitter.min_velocity(), _emitter.max_velocity()); + if (_emitter.has_min_velocity() && _emitter.has_max_velocity()) + { + emitter->SetVelocityRange(_emitter.min_velocity().data(), + _emitter.max_velocity().data()); + } // Color range image. - if (!_emitter.color_range_image().empty()) + if (_emitter.has_color_range_image() && + !_emitter.color_range_image().data().empty()) { - emitter->SetColorRangeImage(_emitter.color_range_image()); + emitter->SetColorRangeImage(_emitter.color_range_image().data()); } // Color range. else if (_emitter.has_color_start() && _emitter.has_color_end()) @@ -1108,7 +1121,8 @@ rendering::ParticleEmitterPtr SceneManager::UpdateParticleEmitter(Entity _id, } // Scale rate. - emitter->SetScaleRate(_emitter.scale_rate()); + if (_emitter.has_scale_rate()) + emitter->SetScaleRate(_emitter.scale_rate().data()); // pose if (_emitter.has_pose()) diff --git a/src/systems/particle_emitter/ParticleEmitter.cc b/src/systems/particle_emitter/ParticleEmitter.cc index 697b260544..6c2de2a435 100644 --- a/src/systems/particle_emitter/ParticleEmitter.cc +++ b/src/systems/particle_emitter/ParticleEmitter.cc @@ -188,13 +188,16 @@ void ParticleEmitter::Configure(const Entity &_entity, ignition::msgs::Set(this->dataPtr->emitter.mutable_size(), size); // Rate. - this->dataPtr->emitter.set_rate(_sdf->Get("rate", 10).first); + this->dataPtr->emitter.mutable_rate()->set_data( + _sdf->Get("rate", 10).first); // Duration. - this->dataPtr->emitter.set_duration(_sdf->Get("duration", 0).first); + this->dataPtr->emitter.mutable_duration()->set_data( + _sdf->Get("duration", 0).first); // Emitting. - this->dataPtr->emitter.set_emitting(_sdf->Get("emitting", false).first); + this->dataPtr->emitter.mutable_emitting()->set_data( + _sdf->Get("emitting", false).first); // Particle size. size = ignition::math::Vector3d::One; @@ -203,7 +206,8 @@ void ParticleEmitter::Configure(const Entity &_entity, ignition::msgs::Set(this->dataPtr->emitter.mutable_particle_size(), size); // Lifetime. - this->dataPtr->emitter.set_lifetime(_sdf->Get("lifetime", 5).first); + this->dataPtr->emitter.mutable_lifetime()->set_data( + _sdf->Get("lifetime", 5).first); // Material. if (_sdf->HasElement("material")) @@ -216,11 +220,11 @@ void ParticleEmitter::Configure(const Entity &_entity, } // Min velocity. - this->dataPtr->emitter.set_min_velocity( + this->dataPtr->emitter.mutable_min_velocity()->set_data( _sdf->Get("min_velocity", 1).first); // Max velocity. - this->dataPtr->emitter.set_max_velocity( + this->dataPtr->emitter.mutable_max_velocity()->set_data( _sdf->Get("max_velocity", 1).first); // Color start. @@ -236,7 +240,7 @@ void ParticleEmitter::Configure(const Entity &_entity, ignition::msgs::Set(this->dataPtr->emitter.mutable_color_end(), color); // Scale rate. - this->dataPtr->emitter.set_scale_rate( + this->dataPtr->emitter.mutable_scale_rate()->set_data( _sdf->Get("scale_rate", 1).first); // Color range image. @@ -250,7 +254,8 @@ void ParticleEmitter::Configure(const Entity &_entity, systemPaths.SetFilePathEnv(kResourcePathEnv); auto absolutePath = systemPaths.FindFile(path); - this->dataPtr->emitter.set_color_range_image(absolutePath); + this->dataPtr->emitter.mutable_color_range_image()->set_data( + absolutePath); } igndbg << "Loading particle emitter:" << std::endl diff --git a/test/integration/components.cc b/test/integration/components.cc index 28b1053329..c341dfcc0d 100644 --- a/test/integration/components.cc +++ b/test/integration/components.cc @@ -1520,15 +1520,15 @@ TEST_F(ComponentsTest, ParticleEmitter) emitter1.mutable_size()->set_x(1); emitter1.mutable_size()->set_y(2); emitter1.mutable_size()->set_z(3); - emitter1.set_rate(4.0); - emitter1.set_duration(5.0); - emitter1.set_emitting(false); + emitter1.mutable_rate()->set_data(4.0); + emitter1.mutable_duration()->set_data(5.0); + emitter1.mutable_emitting()->set_data(false); emitter1.mutable_particle_size()->set_x(0.1); emitter1.mutable_particle_size()->set_y(0.2); emitter1.mutable_particle_size()->set_z(0.3); - emitter1.set_lifetime(6.0); - emitter1.set_min_velocity(7.0); - emitter1.set_max_velocity(8.0); + emitter1.mutable_lifetime()->set_data(6.0); + emitter1.mutable_min_velocity()->set_data(7.0); + emitter1.mutable_max_velocity()->set_data(8.0); emitter1.mutable_color_start()->set_r(1.0); emitter1.mutable_color_start()->set_g(0); emitter1.mutable_color_start()->set_b(0); @@ -1537,8 +1537,8 @@ TEST_F(ComponentsTest, ParticleEmitter) emitter1.mutable_color_end()->set_g(1.0); emitter1.mutable_color_end()->set_b(1.0); emitter1.mutable_color_end()->set_a(0); - emitter1.set_scale_rate(9.0); - emitter1.set_color_range_image("path_to_texture"); + emitter1.mutable_scale_rate()->set_data(9.0); + emitter1.mutable_color_range_image()->set_data("path_to_texture"); msgs::ParticleEmitter emitter2; emitter2.set_name("emitter2"); @@ -1547,15 +1547,15 @@ TEST_F(ComponentsTest, ParticleEmitter) emitter2.mutable_size()->set_x(1); emitter2.mutable_size()->set_y(2); emitter2.mutable_size()->set_z(3); - emitter2.set_rate(4.0); - emitter2.set_duration(5.0); - emitter2.set_emitting(false); + emitter2.mutable_rate()->set_data(4.0); + emitter2.mutable_duration()->set_data(5.0); + emitter2.mutable_emitting()->set_data(false); emitter2.mutable_particle_size()->set_x(0.1); emitter2.mutable_particle_size()->set_y(0.2); emitter2.mutable_particle_size()->set_z(0.3); - emitter2.set_lifetime(6.0); - emitter2.set_min_velocity(7.0); - emitter2.set_max_velocity(8.0); + emitter2.mutable_lifetime()->set_data(6.0); + emitter2.mutable_min_velocity()->set_data(7.0); + emitter2.mutable_max_velocity()->set_data(8.0); emitter2.mutable_color_start()->set_r(1.0); emitter2.mutable_color_start()->set_g(0); emitter2.mutable_color_start()->set_b(0); @@ -1564,8 +1564,8 @@ TEST_F(ComponentsTest, ParticleEmitter) emitter2.mutable_color_end()->set_g(1.0); emitter2.mutable_color_end()->set_b(1.0); emitter2.mutable_color_end()->set_a(0); - emitter2.set_scale_rate(9.0); - emitter2.set_color_range_image("path_to_texture"); + emitter2.mutable_scale_rate()->set_data(9.0); + emitter2.mutable_color_range_image()->set_data("path_to_texture"); // Create components. auto comp1 = components::ParticleEmitter(emitter1); @@ -1586,7 +1586,7 @@ TEST_F(ComponentsTest, ParticleEmitterCmd) { msgs::ParticleEmitter emitter1; emitter1.set_name("emitter1"); - emitter1.set_emitting(true); + emitter1.mutable_emitting()->set_data(true); // Create components. auto comp1 = components::ParticleEmitterCmd(emitter1); @@ -1598,6 +1598,6 @@ TEST_F(ComponentsTest, ParticleEmitterCmd) std::istringstream istr(ostr.str()); components::ParticleEmitter comp3; comp3.Deserialize(istr); - EXPECT_EQ(comp1.Data().emitting(), comp3.Data().emitting()); + EXPECT_EQ(comp1.Data().emitting().data(), comp3.Data().emitting().data()); EXPECT_EQ(comp1.Data().name(), comp3.Data().name()); } diff --git a/test/integration/particle_emitter.cc b/test/integration/particle_emitter.cc index 29dd17fa0c..c7ef738175 100644 --- a/test/integration/particle_emitter.cc +++ b/test/integration/particle_emitter.cc @@ -97,27 +97,29 @@ TEST_F(ParticleEmitterTest, SDFLoad) msgs::Convert(_emitter->Data().pose())); EXPECT_EQ(math::Vector3d(2, 2, 2), msgs::Convert(_emitter->Data().size())); - EXPECT_DOUBLE_EQ(5.0, _emitter->Data().rate()); - EXPECT_DOUBLE_EQ(1.0, _emitter->Data().duration()); - EXPECT_TRUE(_emitter->Data().emitting()); + EXPECT_DOUBLE_EQ(5.0, _emitter->Data().rate().data()); + EXPECT_DOUBLE_EQ(1.0, _emitter->Data().duration().data()); + EXPECT_TRUE(_emitter->Data().emitting().data()); EXPECT_EQ(math::Vector3d(3, 3, 3), msgs::Convert(_emitter->Data().particle_size())); - EXPECT_DOUBLE_EQ(2.0, _emitter->Data().lifetime()); + EXPECT_DOUBLE_EQ(2.0, _emitter->Data().lifetime().data()); // TODO(anyone) add material check here - EXPECT_DOUBLE_EQ(10.0, _emitter->Data().min_velocity()); - EXPECT_DOUBLE_EQ(20.0, _emitter->Data().max_velocity()); + EXPECT_DOUBLE_EQ(10.0, + _emitter->Data().min_velocity().data()); + EXPECT_DOUBLE_EQ(20.0, + _emitter->Data().max_velocity().data()); EXPECT_EQ(math::Color::Blue, msgs::Convert(_emitter->Data().color_start())); EXPECT_EQ(math::Color::Green, msgs::Convert(_emitter->Data().color_end())); - EXPECT_DOUBLE_EQ(10.0, _emitter->Data().scale_rate()); + EXPECT_DOUBLE_EQ(10.0, _emitter->Data().scale_rate().data()); // color range image is empty because the emitter system // will not be able to find a file that does not exist // TODO(anyone) this should return "/path/to/dummy_image.png" // and let rendering do the findFile instead EXPECT_EQ(std::string(), - _emitter->Data().color_range_image()); + _emitter->Data().color_range_image().data()); } else { @@ -133,21 +135,21 @@ TEST_F(ParticleEmitterTest, SDFLoad) msgs::Convert(_emitter->Data().pose())); EXPECT_EQ(math::Vector3d(1, 1, 1), msgs::Convert(_emitter->Data().size())); - EXPECT_DOUBLE_EQ(10.0, _emitter->Data().rate()); - EXPECT_DOUBLE_EQ(0.0, _emitter->Data().duration()); - EXPECT_FALSE(_emitter->Data().emitting()); + EXPECT_DOUBLE_EQ(10.0, _emitter->Data().rate().data()); + EXPECT_DOUBLE_EQ(0.0, _emitter->Data().duration().data()); + EXPECT_FALSE(_emitter->Data().emitting().data()); EXPECT_EQ(math::Vector3d(1, 1, 1), msgs::Convert(_emitter->Data().particle_size())); - EXPECT_DOUBLE_EQ(5.0, _emitter->Data().lifetime()); + EXPECT_DOUBLE_EQ(5.0, _emitter->Data().lifetime().data()); // TODO(anyone) add material check here - EXPECT_DOUBLE_EQ(1.0, _emitter->Data().min_velocity()); - EXPECT_DOUBLE_EQ(1.0, _emitter->Data().max_velocity()); + EXPECT_DOUBLE_EQ(1.0, _emitter->Data().min_velocity().data()); + EXPECT_DOUBLE_EQ(1.0, _emitter->Data().max_velocity().data()); EXPECT_EQ(math::Color::White, msgs::Convert(_emitter->Data().color_start())); EXPECT_EQ(math::Color::White, msgs::Convert(_emitter->Data().color_end())); - EXPECT_DOUBLE_EQ(1.0, _emitter->Data().scale_rate()); - EXPECT_EQ("", _emitter->Data().color_range_image()); + EXPECT_DOUBLE_EQ(1.0, _emitter->Data().scale_rate().data()); + EXPECT_EQ("", _emitter->Data().color_range_image().data()); } return true; From 2a73431ab4f0361ef37c59f4af5a29982d3af523 Mon Sep 17 00:00:00 2001 From: Louise Poubel Date: Mon, 1 Mar 2021 13:28:17 -0800 Subject: [PATCH 3/7] Cache top level and static to speed up physics system (#656) Signed-off-by: Louise Poubel --- src/systems/physics/Physics.cc | 407 ++++++++++++++++++--------------- 1 file changed, 226 insertions(+), 181 deletions(-) diff --git a/src/systems/physics/Physics.cc b/src/systems/physics/Physics.cc index 2a4a05cbbd..281cf7aeeb 100644 --- a/src/systems/physics/Physics.cc +++ b/src/systems/physics/Physics.cc @@ -197,6 +197,13 @@ class ignition::gazebo::systems::PhysicsPrivate /// associated with a physics Link public: std::unordered_map linkEntityMap; + /// \brief Cache the top-level model for each entity. + /// The key is an entity and the value is its top level model. + public: std::unordered_map topLevelModelMap; + + /// \brief Keep track of what entities are static (models and links). + public: std::unordered_set staticEntities; + /// \brief A map between model entity ids in the ECM to whether its battery /// has drained. public: std::unordered_map entityOffMap; @@ -713,6 +720,7 @@ void PhysicsPrivate::CreatePhysicsEntities(const EntityComponentManager &_ecm) if (staticComp && staticComp->Data()) { model.SetStatic(staticComp->Data()); + this->staticEntities.insert(_entity); } auto selfCollideComp = _ecm.Component(_entity); if (selfCollideComp && selfCollideComp ->Data()) @@ -747,11 +755,15 @@ void PhysicsPrivate::CreatePhysicsEntities(const EntityComponentManager &_ecm) } auto modelPtrPhys = nestedModelFeature->ConstructNestedModel(model); this->entityModelMap.insert(std::make_pair(_entity, modelPtrPhys)); + this->topLevelModelMap.insert(std::make_pair(_entity, + topLevelModel(_entity, _ecm))); } else { auto modelPtrPhys = worldPtrPhys->ConstructModel(model); this->entityModelMap.insert(std::make_pair(_entity, modelPtrPhys)); + this->topLevelModelMap.insert(std::make_pair(_entity, + topLevelModel(_entity, _ecm))); } } // check if parent is a model (nested model) @@ -783,14 +795,18 @@ void PhysicsPrivate::CreatePhysicsEntities(const EntityComponentManager &_ecm) auto parentStaticComp = _ecm.Component(_parent->Data()); if (parentStaticComp && parentStaticComp->Data()) + { model.SetStatic(true); - + this->staticEntities.insert(_entity); + } auto modelPtrPhys = nestedModelFeature->ConstructNestedModel(model); if (modelPtrPhys) { this->entityModelMap.insert( std::make_pair(_entity, modelPtrPhys)); + this->topLevelModelMap.insert(std::make_pair(_entity, + topLevelModel(_entity, _ecm))); } else { @@ -843,6 +859,12 @@ void PhysicsPrivate::CreatePhysicsEntities(const EntityComponentManager &_ecm) link.SetName(_name->Data()); link.SetRawPose(_pose->Data()); + if (this->staticEntities.find(_parent->Data()) != + this->staticEntities.end()) + { + this->staticEntities.insert(_entity); + } + // get link inertial auto inertial = _ecm.Component(_entity); if (inertial) @@ -853,6 +875,8 @@ void PhysicsPrivate::CreatePhysicsEntities(const EntityComponentManager &_ecm) auto linkPtrPhys = modelPtrPhys->ConstructLink(link); this->entityLinkMap.insert(std::make_pair(_entity, linkPtrPhys)); this->linkEntityMap.insert(std::make_pair(linkPtrPhys, _entity)); + this->topLevelModelMap.insert(std::make_pair(_entity, + topLevelModel(_entity, _ecm))); return true; }); @@ -985,6 +1009,8 @@ void PhysicsPrivate::CreatePhysicsEntities(const EntityComponentManager &_ecm) std::make_pair(_entity, collisionPtrPhys)); this->collisionEntityMap.insert( std::make_pair(collisionPtrPhys, _entity)); + this->topLevelModelMap.insert(std::make_pair(_entity, + topLevelModel(_entity, _ecm))); return true; }); @@ -1067,6 +1093,8 @@ void PhysicsPrivate::CreatePhysicsEntities(const EntityComponentManager &_ecm) // Some joints may not be supported, so only add them to the map if // the physics entity is valid this->entityJointMap.insert(std::make_pair(_entity, jointPtrPhys)); + this->topLevelModelMap.insert(std::make_pair(_entity, + topLevelModel(_entity, _ecm))); } return true; }); @@ -1157,6 +1185,8 @@ void PhysicsPrivate::CreatePhysicsEntities(const EntityComponentManager &_ecm) igndbg << "Creating detachable joint [" << _entity << "]" << std::endl; this->entityJointMap.insert(std::make_pair(_entity, jointPtrPhys)); + this->topLevelModelMap.insert(std::make_pair(_entity, + topLevelModel(_entity, _ecm))); } else { @@ -1196,6 +1226,7 @@ void PhysicsPrivate::RemovePhysicsEntities(const EntityComponentManager &_ecm) { this->collisionEntityMap.erase(collIt->second); this->entityCollisionMap.erase(collIt); + this->topLevelModelMap.erase(childCollision); } } // First erase the entry associated with this link from the @@ -1206,17 +1237,22 @@ void PhysicsPrivate::RemovePhysicsEntities(const EntityComponentManager &_ecm) this->linkEntityMap.erase(linkPhysIt->second); } this->entityLinkMap.erase(childLink); + this->topLevelModelMap.erase(childLink); + this->staticEntities.erase(childLink); } for (const auto &childJoint : _ecm.ChildrenByComponents(_entity, components::Joint())) { this->entityJointMap.erase(childJoint); + this->topLevelModelMap.erase(childJoint); } // Remove the model from the physics engine modelIt->second->Remove(); this->entityModelMap.erase(_entity); + this->topLevelModelMap.erase(_entity); + this->staticEntities.erase(_entity); } return true; }); @@ -1460,7 +1496,7 @@ void PhysicsPrivate::UpdatePhysics(EntityComponentManager &_ecm) return true; // world pose cmd currently not supported for nested models - if (_entity != topLevelModel(_entity, _ecm)) + if (_entity != this->topLevelModelMap[_entity]) { ignerr << "Unable to set world pose for nested models." << std::endl; @@ -1492,9 +1528,7 @@ void PhysicsPrivate::UpdatePhysics(EntityComponentManager &_ecm) linkPose)); // Process pose commands for static models here, as one-time changes - const components::Static *staticComp = - _ecm.Component(_entity); - if (staticComp && staticComp->Data()) + if (this->staticEntities.find(_entity) != this->staticEntities.end()) { auto worldPoseComp = _ecm.Component(_entity); if (worldPoseComp) @@ -1556,7 +1590,7 @@ void PhysicsPrivate::UpdatePhysics(EntityComponentManager &_ecm) return true; // angular vel cmd currently not supported for nested models - if (_entity != topLevelModel(_entity, _ecm)) + if (_entity != this->topLevelModelMap[_entity]) { ignerr << "Unable to set angular velocity for nested models." << std::endl; @@ -1604,7 +1638,7 @@ void PhysicsPrivate::UpdatePhysics(EntityComponentManager &_ecm) return true; // linear vel cmd currently not supported for nested models - if (_entity != topLevelModel(_entity, _ecm)) + if (_entity != this->topLevelModelMap[_entity]) { ignerr << "Unable to set linear velocity for nested models." << std::endl; @@ -1759,212 +1793,217 @@ void PhysicsPrivate::UpdateSim(EntityComponentManager &_ecm) { IGN_PROFILE("PhysicsPrivate::UpdateSim"); - // local pose + // Link poses, velocities... + IGN_PROFILE_BEGIN("Links"); _ecm.Each( [&](const Entity &_entity, components::Link * /*_link*/, components::Pose *_pose, const components::ParentEntity *_parent)->bool { // If parent is static, don't process pose changes as periodic - const auto *staticComp = - _ecm.Component(_parent->Data()); - - if (staticComp && staticComp->Data()) + if (this->staticEntities.find(_entity) != this->staticEntities.end()) return true; auto linkIt = this->entityLinkMap.find(_entity); - if (linkIt != this->entityLinkMap.end()) + if (linkIt == this->entityLinkMap.end()) { - // get top level model of this link - auto topLevelModelEnt = topLevelModel(_parent->Data(), _ecm); + ignerr << "Internal error: link [" << _entity + << "] not in entity map" << std::endl; + return true; + } - auto canonicalLink = - _ecm.Component(_entity); + IGN_PROFILE_BEGIN("Local pose"); + // get top level model of this link + auto topLevelModelEnt = this->topLevelModelMap[_parent->Data()]; - auto frameData = linkIt->second->FrameDataRelativeToWorld(); - const auto &worldPose = frameData.pose; + auto canonicalLink = + _ecm.Component(_entity); - if (canonicalLink) - { - // This is the canonical link, update the top level model. - // The pose of this link w.r.t its top level model never changes - // because it's "fixed" to the model. Instead, we change - // the top level model's pose here. The physics engine gives us the - // pose of this link relative to world so to set the top level - // model's pose, we have to post-multiply it by the inverse of the - // transform of the link w.r.t to its top level model. - math::Pose3d linkPoseFromTopLevelModel; - linkPoseFromTopLevelModel = - this->RelativePose(topLevelModelEnt, _entity, _ecm); - - // update top level model's pose - auto mutableModelPose = - _ecm.Component(topLevelModelEnt); - *(mutableModelPose) = components::Pose( - math::eigen3::convert(worldPose) * - linkPoseFromTopLevelModel.Inverse()); - - _ecm.SetChanged(topLevelModelEnt, components::Pose::typeId, - ComponentState::PeriodicChange); - } - else - { - // Compute the relative pose of this link from the top level model - // first get the world pose of the top level model - auto worldComp = - _ecm.Component(topLevelModelEnt); - // if the worldComp is a nullptr, something is wrong with ECS - if (!worldComp) - { - ignerr << "The parent component of " << topLevelModelEnt - << " could not be found. This should never happen!\n"; - return true; - } - math::Pose3d parentWorldPose = - this->RelativePose(worldComp->Data(), _parent->Data(), _ecm); - - // Unlike canonical links, pose of regular links can move relative. - // to the parent. Same for links inside nested models. - *_pose = components::Pose(math::eigen3::convert(worldPose) + - parentWorldPose.Inverse()); - _ecm.SetChanged(_entity, components::Pose::typeId, - ComponentState::PeriodicChange); - } + auto frameData = linkIt->second->FrameDataRelativeToWorld(); + const auto &worldPose = frameData.pose; - // Populate world poses, velocities and accelerations of the link. For - // now these components are updated only if another system has created - // the corresponding component on the entity. - auto worldPoseComp = _ecm.Component(_entity); - if (worldPoseComp) + if (canonicalLink) + { + // This is the canonical link, update the top level model. + // The pose of this link w.r.t its top level model never changes + // because it's "fixed" to the model. Instead, we change + // the top level model's pose here. The physics engine gives us the + // pose of this link relative to world so to set the top level + // model's pose, we have to post-multiply it by the inverse of the + // transform of the link w.r.t to its top level model. + math::Pose3d linkPoseFromTopLevelModel; + linkPoseFromTopLevelModel = + this->RelativePose(topLevelModelEnt, _entity, _ecm); + + // update top level model's pose + auto mutableModelPose = + _ecm.Component(topLevelModelEnt); + *(mutableModelPose) = components::Pose( + math::eigen3::convert(worldPose) * + linkPoseFromTopLevelModel.Inverse()); + + _ecm.SetChanged(topLevelModelEnt, components::Pose::typeId, + ComponentState::PeriodicChange); + } + else + { + // Compute the relative pose of this link from the top level model + // first get the world pose of the top level model + auto worldComp = + _ecm.Component(topLevelModelEnt); + // if the worldComp is a nullptr, something is wrong with ECS + if (!worldComp) { - auto state = - worldPoseComp->SetData(math::eigen3::convert(frameData.pose), - this->pose3Eql) ? - ComponentState::PeriodicChange : - ComponentState::NoChange; - _ecm.SetChanged(_entity, components::WorldPose::typeId, state); + ignerr << "The parent component of " << topLevelModelEnt + << " could not be found. This should never happen!\n"; + return true; } + math::Pose3d parentWorldPose = + this->RelativePose(worldComp->Data(), _parent->Data(), _ecm); + + // Unlike canonical links, pose of regular links can move relative. + // to the parent. Same for links inside nested models. + *_pose = components::Pose(math::eigen3::convert(worldPose) + + parentWorldPose.Inverse()); + _ecm.SetChanged(_entity, components::Pose::typeId, + ComponentState::PeriodicChange); + } + IGN_PROFILE_END(); - // Velocity in world coordinates - auto worldLinVelComp = - _ecm.Component(_entity); - if (worldLinVelComp) - { - auto state = worldLinVelComp->SetData( - math::eigen3::convert(frameData.linearVelocity), - this->vec3Eql) ? - ComponentState::PeriodicChange : - ComponentState::NoChange; - _ecm.SetChanged(_entity, - components::WorldLinearVelocity::typeId, state); - } + // Populate world poses, velocities and accelerations of the link. For + // now these components are updated only if another system has created + // the corresponding component on the entity. + auto worldPoseComp = _ecm.Component(_entity); + if (worldPoseComp) + { + auto state = + worldPoseComp->SetData(math::eigen3::convert(frameData.pose), + this->pose3Eql) ? + ComponentState::PeriodicChange : + ComponentState::NoChange; + _ecm.SetChanged(_entity, components::WorldPose::typeId, state); + } - // Angular velocity in world frame coordinates - auto worldAngVelComp = - _ecm.Component(_entity); - if (worldAngVelComp) - { - auto state = worldAngVelComp->SetData( - math::eigen3::convert(frameData.angularVelocity), + // Velocity in world coordinates + auto worldLinVelComp = + _ecm.Component(_entity); + if (worldLinVelComp) + { + auto state = worldLinVelComp->SetData( + math::eigen3::convert(frameData.linearVelocity), this->vec3Eql) ? ComponentState::PeriodicChange : ComponentState::NoChange; - _ecm.SetChanged(_entity, - components::WorldAngularVelocity::typeId, state); - } + _ecm.SetChanged(_entity, + components::WorldLinearVelocity::typeId, state); + } - // Acceleration in world frame coordinates - auto worldLinAccelComp = - _ecm.Component(_entity); - if (worldLinAccelComp) - { - auto state = worldLinAccelComp->SetData( - math::eigen3::convert(frameData.linearAcceleration), - this->vec3Eql) ? - ComponentState::PeriodicChange : - ComponentState::NoChange; - _ecm.SetChanged(_entity, - components::WorldLinearAcceleration::typeId, state); - } + // Angular velocity in world frame coordinates + auto worldAngVelComp = + _ecm.Component(_entity); + if (worldAngVelComp) + { + auto state = worldAngVelComp->SetData( + math::eigen3::convert(frameData.angularVelocity), + this->vec3Eql) ? + ComponentState::PeriodicChange : + ComponentState::NoChange; + _ecm.SetChanged(_entity, + components::WorldAngularVelocity::typeId, state); + } - // Angular acceleration in world frame coordinates - auto worldAngAccelComp = - _ecm.Component(_entity); + // Acceleration in world frame coordinates + auto worldLinAccelComp = + _ecm.Component(_entity); + if (worldLinAccelComp) + { + auto state = worldLinAccelComp->SetData( + math::eigen3::convert(frameData.linearAcceleration), + this->vec3Eql) ? + ComponentState::PeriodicChange : + ComponentState::NoChange; + _ecm.SetChanged(_entity, + components::WorldLinearAcceleration::typeId, state); + } - if (worldAngAccelComp) - { - auto state = worldAngAccelComp->SetData( - math::eigen3::convert(frameData.angularAcceleration), - this->vec3Eql) ? - ComponentState::PeriodicChange : - ComponentState::NoChange; - _ecm.SetChanged(_entity, - components::WorldAngularAcceleration::typeId, state); - } + // Angular acceleration in world frame coordinates + auto worldAngAccelComp = + _ecm.Component(_entity); - const Eigen::Matrix3d R_bs = worldPose.linear().transpose(); // NOLINT + if (worldAngAccelComp) + { + auto state = worldAngAccelComp->SetData( + math::eigen3::convert(frameData.angularAcceleration), + this->vec3Eql) ? + ComponentState::PeriodicChange : + ComponentState::NoChange; + _ecm.SetChanged(_entity, + components::WorldAngularAcceleration::typeId, state); + } - // Velocity in body-fixed frame coordinates - auto bodyLinVelComp = - _ecm.Component(_entity); - if (bodyLinVelComp) - { - Eigen::Vector3d bodyLinVel = R_bs * frameData.linearVelocity; - auto state = - bodyLinVelComp->SetData(math::eigen3::convert(bodyLinVel), - this->vec3Eql) ? - ComponentState::PeriodicChange : - ComponentState::NoChange; - _ecm.SetChanged(_entity, components::LinearVelocity::typeId, state); - } + const Eigen::Matrix3d R_bs = worldPose.linear().transpose(); // NOLINT - // Angular velocity in body-fixed frame coordinates - auto bodyAngVelComp = - _ecm.Component(_entity); - if (bodyAngVelComp) - { - Eigen::Vector3d bodyAngVel = R_bs * frameData.angularVelocity; - auto state = - bodyAngVelComp->SetData(math::eigen3::convert(bodyAngVel), - this->vec3Eql) ? - ComponentState::PeriodicChange : - ComponentState::NoChange; - _ecm.SetChanged(_entity, components::AngularVelocity::typeId, - state); - } + // Velocity in body-fixed frame coordinates + auto bodyLinVelComp = + _ecm.Component(_entity); + if (bodyLinVelComp) + { + Eigen::Vector3d bodyLinVel = R_bs * frameData.linearVelocity; + auto state = + bodyLinVelComp->SetData(math::eigen3::convert(bodyLinVel), + this->vec3Eql) ? + ComponentState::PeriodicChange : + ComponentState::NoChange; + _ecm.SetChanged(_entity, components::LinearVelocity::typeId, state); + } - // Acceleration in body-fixed frame coordinates - auto bodyLinAccelComp = - _ecm.Component(_entity); - if (bodyLinAccelComp) - { - Eigen::Vector3d bodyLinAccel = R_bs * frameData.linearAcceleration; - auto state = - bodyLinAccelComp->SetData(math::eigen3::convert(bodyLinAccel), - this->vec3Eql)? - ComponentState::PeriodicChange : - ComponentState::NoChange; - _ecm.SetChanged(_entity, components::LinearAcceleration::typeId, - state); - } + // Angular velocity in body-fixed frame coordinates + auto bodyAngVelComp = + _ecm.Component(_entity); + if (bodyAngVelComp) + { + Eigen::Vector3d bodyAngVel = R_bs * frameData.angularVelocity; + auto state = + bodyAngVelComp->SetData(math::eigen3::convert(bodyAngVel), + this->vec3Eql) ? + ComponentState::PeriodicChange : + ComponentState::NoChange; + _ecm.SetChanged(_entity, components::AngularVelocity::typeId, + state); + } - // Angular acceleration in world frame coordinates - auto bodyAngAccelComp = - _ecm.Component(_entity); - if (bodyAngAccelComp) - { - Eigen::Vector3d bodyAngAccel = R_bs * frameData.angularAcceleration; - auto state = - bodyAngAccelComp->SetData(math::eigen3::convert(bodyAngAccel), - this->vec3Eql) ? - ComponentState::PeriodicChange : - ComponentState::NoChange; - _ecm.SetChanged(_entity, components::AngularAcceleration::typeId, - state); - } + // Acceleration in body-fixed frame coordinates + auto bodyLinAccelComp = + _ecm.Component(_entity); + if (bodyLinAccelComp) + { + Eigen::Vector3d bodyLinAccel = R_bs * frameData.linearAcceleration; + auto state = + bodyLinAccelComp->SetData(math::eigen3::convert(bodyLinAccel), + this->vec3Eql)? + ComponentState::PeriodicChange : + ComponentState::NoChange; + _ecm.SetChanged(_entity, components::LinearAcceleration::typeId, + state); + } + + // Angular acceleration in world frame coordinates + auto bodyAngAccelComp = + _ecm.Component(_entity); + if (bodyAngAccelComp) + { + Eigen::Vector3d bodyAngAccel = R_bs * frameData.angularAcceleration; + auto state = + bodyAngAccelComp->SetData(math::eigen3::convert(bodyAngAccel), + this->vec3Eql) ? + ComponentState::PeriodicChange : + ComponentState::NoChange; + _ecm.SetChanged(_entity, components::AngularAcceleration::typeId, + state); } return true; }); + IGN_PROFILE_END(); // pose/velocity/acceleration of non-link entities such as sensors / // collisions. These get updated only if another system has created a @@ -1975,6 +2014,7 @@ void PhysicsPrivate::UpdateSim(EntityComponentManager &_ecm) // * AngularVelocity // * LinearAcceleration + IGN_PROFILE_BEGIN("Sensors / collisions"); // world pose _ecm.Each( @@ -2071,8 +2111,10 @@ void PhysicsPrivate::UpdateSim(EntityComponentManager &_ecm) return true; }); + IGN_PROFILE_END(); // Clear reset components + IGN_PROFILE_BEGIN("Clear / reset components"); std::vector entitiesPositionReset; _ecm.Each( [&](const Entity &_entity, components::JointPositionReset *) -> bool @@ -2127,8 +2169,10 @@ void PhysicsPrivate::UpdateSim(EntityComponentManager &_ecm) std::fill(_slip->Data().begin(), _slip->Data().end(), 0.0); return true; }); + IGN_PROFILE_END(); // Update joint positions + IGN_PROFILE_BEGIN("Joints"); _ecm.Each( [&](const Entity &_entity, components::Joint *, components::JointPosition *_jointPos) -> bool @@ -2163,6 +2207,7 @@ void PhysicsPrivate::UpdateSim(EntityComponentManager &_ecm) } return true; }); + IGN_PROFILE_END(); // TODO(louise) Skip this if there are no collision features this->UpdateCollisions(_ecm); From 6b124f8e74fee56e0e13685a960493ed3c2e33b7 Mon Sep 17 00:00:00 2001 From: Nate Koenig Date: Mon, 1 Mar 2021 13:44:15 -0800 Subject: [PATCH 4/7] Performer count (#652) * Support particle modification Signed-off-by: Nate Koenig * delay removal of particle emitter cmd component Signed-off-by: Ian Chen * clear cmds Signed-off-by: Ian Chen * Add entity count to performer detector events Signed-off-by: Nate Koenig * udpate documentation and test Signed-off-by: Nate Koenig * fix typo Signed-off-by: Ian Chen * Cache top level and static to speed up physics system Signed-off-by: Louise Poubel Co-authored-by: Nate Koenig Co-authored-by: Ian Chen Co-authored-by: Louise Poubel --- .../performer_detector/PerformerDetector.cc | 5 +++++ .../performer_detector/PerformerDetector.hh | 4 +++- test/integration/performer_detector.cc | 17 ++++++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/systems/performer_detector/PerformerDetector.cc b/src/systems/performer_detector/PerformerDetector.cc index 9aa1dc3a45..7da45abd73 100644 --- a/src/systems/performer_detector/PerformerDetector.cc +++ b/src/systems/performer_detector/PerformerDetector.cc @@ -242,6 +242,11 @@ void PerformerDetector::Publish( headerData->set_key("state"); headerData->add_value(std::to_string(_state)); } + { + auto *headerData = msg.mutable_header()->add_data(); + headerData->set_key("count"); + headerData->add_value(std::to_string(this->detectedEntities.size())); + } // Include the optional extra header data. for (const auto &data : this->extraHeaderData) diff --git a/src/systems/performer_detector/PerformerDetector.hh b/src/systems/performer_detector/PerformerDetector.hh index 58b6fdc326..24df42a580 100644 --- a/src/systems/performer_detector/PerformerDetector.hh +++ b/src/systems/performer_detector/PerformerDetector.hh @@ -51,7 +51,9 @@ namespace systems /// header will contain the key "frame_id" with a value set to the name of /// the model containing the PerformerDetector system and the key "state" with /// a value set to "1" if the performer is entering the detector's region and - /// "0" if the performer is leaving the region. + /// "0" if the performer is leaving the region. The `data` field of the + /// header will also contain the key "count" with a value set to the + /// number of performers currently in the region. /// /// The PerformerDetector has to be attached to a and it's region is /// centered on the containing model's origin. diff --git a/test/integration/performer_detector.cc b/test/integration/performer_detector.cc index 58f7be3af4..ff851cf0af 100644 --- a/test/integration/performer_detector.cc +++ b/test/integration/performer_detector.cc @@ -74,8 +74,9 @@ TEST_F(PerformerDetectorTest, MovingPerformer) transport::Node node; auto cmdVelPub = node.Advertise("/model/vehicle_blue/cmd_vel"); + std::string expectedCount = "1"; auto detectorCb = std::function( - [this](const auto &_msg) + [this, &expectedCount](const auto &_msg) { std::lock_guard lock(this->poseMsgsMutex); this->poseMsgs.push_back(_msg); @@ -89,6 +90,7 @@ TEST_F(PerformerDetectorTest, MovingPerformer) bool hasUniqueKey = false; bool hasDuplicateKey = false; + bool hasCount = false; for (int i = 0; i < _msg.header().data_size(); ++i) { EXPECT_NE(_msg.header().data(i).key(), "no_value"); @@ -104,18 +106,27 @@ TEST_F(PerformerDetectorTest, MovingPerformer) EXPECT_EQ(_msg.header().data(i).value(0), "second_value"); hasDuplicateKey = true; } + else if (_msg.header().data(i).key() == "count") + { + EXPECT_EQ(_msg.header().data(i).value(0), expectedCount); + hasCount = true; + } } if (detectorName == "detector1") { - EXPECT_EQ(4, _msg.header().data_size()); + EXPECT_EQ(5, _msg.header().data_size()); EXPECT_TRUE(hasDuplicateKey); EXPECT_TRUE(hasUniqueKey); + EXPECT_TRUE(hasCount); } else { - EXPECT_EQ(2, _msg.header().data_size()); + EXPECT_EQ(3, _msg.header().data_size()); EXPECT_FALSE(hasDuplicateKey); EXPECT_FALSE(hasUniqueKey); + EXPECT_TRUE(hasCount); + // Change the expected count after 'detector2' is triggered. + expectedCount = "0"; } }); From e592cae8ce81a91114a3dc4469bfc5e820bf4bb8 Mon Sep 17 00:00:00 2001 From: "Addisu Z. Taddese" Date: Mon, 1 Mar 2021 17:28:36 -0600 Subject: [PATCH 5/7] Use a custom data structure to manage entity feature maps (#586) * Use a custom data structure to manage entity feature maps This ensures that when a model is removed, all the related physics entities are removed as well causing the physics engine to clean up the memory used by the model and its children. This also fixes a segfault that occurs when ign-gazebo is terminated after a model is unloaded by the level manager. The segfault seems to be due to a bug in DART, but the bug is exposed due to the way children physics entity pointers outlived their parent models because they weren't removed from their entity map when the parent model was unloaded. Signed-off-by: Addisu Z. Taddese * Fix collide_bitmask by calling EntityCast after AddEntity This also uses the Entity of the collision instead of the parent Signed-off-by: Addisu Z. Taddese * Change FeatureLists to structs to improve compile time Signed-off-by: Addisu Z. Taddese * Cleanup and documentation Signed-off-by: Addisu Z. Taddese * Add test for EntityFeatureMap Signed-off-by: Addisu Z. Taddese * Fix documentation Signed-off-by: Addisu Z. Taddese --- src/systems/physics/CMakeLists.txt | 10 + src/systems/physics/EntityFeatureMap.hh | 290 +++++++++ src/systems/physics/EntityFeatureMap_TEST.cc | 173 +++++ src/systems/physics/Physics.cc | 637 ++++++++----------- src/systems/physics/Physics.hh | 47 -- 5 files changed, 740 insertions(+), 417 deletions(-) create mode 100644 src/systems/physics/EntityFeatureMap.hh create mode 100644 src/systems/physics/EntityFeatureMap_TEST.cc diff --git a/src/systems/physics/CMakeLists.txt b/src/systems/physics/CMakeLists.txt index 1a118c287c..8e4cfb072e 100644 --- a/src/systems/physics/CMakeLists.txt +++ b/src/systems/physics/CMakeLists.txt @@ -7,3 +7,13 @@ gz_add_system(physics ignition-physics${IGN_PHYSICS_VER}::ignition-physics${IGN_PHYSICS_VER} ) +set (gtest_sources + EntityFeatureMap_TEST.cc +) + +ign_build_tests(TYPE UNIT + SOURCES + ${gtest_sources} + LIB_DEPS + ignition-physics${IGN_PHYSICS_VER}::core +) diff --git a/src/systems/physics/EntityFeatureMap.hh b/src/systems/physics/EntityFeatureMap.hh new file mode 100644 index 0000000000..f9d8fe52c0 --- /dev/null +++ b/src/systems/physics/EntityFeatureMap.hh @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2021 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 IGNITION_GAZEBO_SYSTEMS_PHYSICS_ENTITY_FEATURE_MAP_HH_ +#define IGNITION_GAZEBO_SYSTEMS_PHYSICS_ENTITY_FEATURE_MAP_HH_ + +#include +#include +#include + +#include +#include +#include +#include + +#include "ignition/gazebo/Entity.hh" + +namespace ignition::gazebo +{ +inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE { +namespace systems::physics_system +{ + // \brief Helper class that associates Gazebo entities with Physics entities + // with required and optional features. It can be used to cast a physics + // entity with the required features to another physics entity with one of + // the optional features. This class was created to keep all physics entities + // in one place so that when a gazebo entity is removed, all the mapped + // physics entitities can be removed at the same time. This ensures that + // reference counts are properly zeroed out in the underlying physics engines + // and the memory associated with the physics entities can be freed. + // + // DEV WARNING: There is an implicit conversion between physics EntityPtr and + // std::size_t in ign-physics. This seems also implicitly convert between + // EntityPtr and gazebo Entity. Therefore, any member function that takes a + // gazebo Entity can accidentally take an EntityPtr. To prevent this, for + // every function that takes a gazebo Entity, we should always have an + // overload that also takes an EntityPtr with required features. We can do + // this because there's a 1:1 mapping between the two in maps contained in + // this class. + // + // \tparam PhysicsEntityT Type of entity, such as World, Model, or Link + // \tparam PolicyT Policy of the physics engine (2D, 3D) + // \tparam RequiredFeatureList Required features of the physics entity + // \tparam OptionalFeatureLists One or more optional feature lists of the + // physics entity. + template