ROS2ContactSensorComponent.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "ROS2ContactSensorComponent.h"
  9. #include <AzFramework/Physics/Collision/CollisionEvents.h>
  10. #include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
  11. #include <AzFramework/Physics/PhysicsSystem.h>
  12. #include <ROS2/Frame/ROS2FrameComponent.h>
  13. #include <ROS2/ROS2GemUtilities.h>
  14. #include <ROS2/Utilities/ROS2Conversions.h>
  15. #include <ROS2/Utilities/ROS2Names.h>
  16. #include <geometry_msgs/msg/wrench.hpp>
  17. namespace ROS2
  18. {
  19. namespace
  20. {
  21. constexpr float ContactMaximumSeparation = 0.0001f;
  22. }
  23. ROS2ContactSensorComponent::ROS2ContactSensorComponent()
  24. {
  25. TopicConfiguration tc;
  26. AZStd::string type = "gazebo_msgs::msg::ContactsState";
  27. tc.m_type = type;
  28. tc.m_topic = "contact_sensor";
  29. m_sensorConfiguration.m_frequency = 15;
  30. m_sensorConfiguration.m_publishersConfigurations.insert(AZStd::make_pair(AZStd::move(type), AZStd::move(tc)));
  31. }
  32. void ROS2ContactSensorComponent::Reflect(AZ::ReflectContext* context)
  33. {
  34. if (auto* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  35. {
  36. serialize->Class<ROS2ContactSensorComponent, SensorBaseType>()->Version(2);
  37. if (auto* editContext = serialize->GetEditContext())
  38. {
  39. editContext->Class<ROS2ContactSensorComponent>("ROS2 Contact Sensor", "Contact detection controller")
  40. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  41. ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
  42. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  43. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/ROS2ContactSensor.svg")
  44. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/ROS2ContactSensor.svg");
  45. }
  46. }
  47. }
  48. void ROS2ContactSensorComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  49. {
  50. required.push_back(AZ_CRC_CE("PhysicsColliderService"));
  51. required.push_back(AZ_CRC_CE("ROS2Frame"));
  52. }
  53. void ROS2ContactSensorComponent::Activate()
  54. {
  55. ROS2SensorComponentBase::Activate();
  56. m_entityId = GetEntityId();
  57. AZ::Entity* entity = nullptr;
  58. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, m_entityId);
  59. m_entityName = entity->GetName();
  60. auto ros2Node = ROS2Interface::Get()->GetNode();
  61. AZ_Assert(m_sensorConfiguration.m_publishersConfigurations.size() == 1, "Invalid configuration of publishers for Contact sensor");
  62. const auto publisherConfig = m_sensorConfiguration.m_publishersConfigurations["gazebo_msgs::msg::ContactsState"];
  63. const auto fullTopic = ROS2Names::GetNamespacedName(GetNamespace(), publisherConfig.m_topic);
  64. m_contactsPublisher = ros2Node->create_publisher<gazebo_msgs::msg::ContactsState>(fullTopic.data(), publisherConfig.GetQoS());
  65. m_onCollisionBeginHandler = AzPhysics::SimulatedBodyEvents::OnCollisionBegin::Handler(
  66. [this]([[maybe_unused]] AzPhysics::SimulatedBodyHandle bodyHandle, const AzPhysics::CollisionEvent& event)
  67. {
  68. AddNewContact(event);
  69. });
  70. m_onCollisionPersistHandler = AzPhysics::SimulatedBodyEvents::OnCollisionPersist::Handler(
  71. [this]([[maybe_unused]] AzPhysics::SimulatedBodyHandle bodyHandle, const AzPhysics::CollisionEvent& event)
  72. {
  73. AddNewContact(event);
  74. });
  75. m_onCollisionEndHandler = AzPhysics::SimulatedBodyEvents::OnCollisionEnd::Handler(
  76. [this]([[maybe_unused]] AzPhysics::SimulatedBodyHandle bodyHandle, const AzPhysics::CollisionEvent& event)
  77. {
  78. AZStd::lock_guard<AZStd::mutex> lock(m_activeContactsMutex);
  79. m_activeContacts.erase(event.m_body2->GetEntityId());
  80. });
  81. StartSensor(
  82. m_sensorConfiguration.m_frequency,
  83. [this](auto&&... args)
  84. {
  85. if (!m_sensorConfiguration.m_publishingEnabled)
  86. {
  87. return;
  88. }
  89. FrequencyTick();
  90. });
  91. }
  92. void ROS2ContactSensorComponent::Deactivate()
  93. {
  94. StopSensor();
  95. m_activeContacts.clear();
  96. m_contactsPublisher.reset();
  97. m_onCollisionBeginHandler.Disconnect();
  98. m_onCollisionPersistHandler.Disconnect();
  99. m_onCollisionEndHandler.Disconnect();
  100. ROS2SensorComponentBase::Deactivate();
  101. }
  102. void ROS2ContactSensorComponent::FrequencyTick()
  103. {
  104. // Connects the collision handlers if not already connected
  105. AzPhysics::SystemInterface* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
  106. if (!physicsSystem)
  107. {
  108. return;
  109. }
  110. if (!m_onCollisionBeginHandler.IsConnected() || !m_onCollisionPersistHandler.IsConnected() ||
  111. !m_onCollisionEndHandler.IsConnected())
  112. {
  113. AZStd::pair<AzPhysics::SceneHandle, AzPhysics::SimulatedBodyHandle> foundBody =
  114. physicsSystem->FindAttachedBodyHandleFromEntityId(GetEntityId());
  115. AZ_Warning("Contact Sensor", foundBody.first != AzPhysics::InvalidSceneHandle, "Invalid scene handle")
  116. if (foundBody.first != AzPhysics::InvalidSceneHandle)
  117. {
  118. AzPhysics::SimulatedBodyEvents::RegisterOnCollisionBeginHandler(
  119. foundBody.first, foundBody.second, m_onCollisionBeginHandler);
  120. AzPhysics::SimulatedBodyEvents::RegisterOnCollisionPersistHandler(
  121. foundBody.first, foundBody.second, m_onCollisionPersistHandler);
  122. AzPhysics::SimulatedBodyEvents::RegisterOnCollisionEndHandler(foundBody.first, foundBody.second, m_onCollisionEndHandler);
  123. }
  124. }
  125. // Publishes all contacts
  126. gazebo_msgs::msg::ContactsState msg;
  127. const auto* ros2Frame = GetEntity()->FindComponent<ROS2FrameComponent>();
  128. AZ_Assert(ros2Frame, "Invalid component pointer value");
  129. msg.header.frame_id = ros2Frame->GetFrameID().data();
  130. msg.header.stamp = ROS2Interface::Get()->GetROSTimestamp();
  131. {
  132. // If there are no active collisions, then there is nothing to send
  133. AZStd::lock_guard<AZStd::mutex> lock(m_activeContactsMutex);
  134. if (!m_activeContacts.empty())
  135. {
  136. for (auto [id, contact] : m_activeContacts)
  137. {
  138. msg.states.push_back(AZStd::move(contact));
  139. }
  140. m_contactsPublisher->publish(AZStd::move(msg));
  141. m_activeContacts.clear();
  142. }
  143. }
  144. }
  145. void ROS2ContactSensorComponent::AddNewContact(const AzPhysics::CollisionEvent& event)
  146. {
  147. AZ::Entity* contactedEntity = nullptr;
  148. AZ::ComponentApplicationBus::BroadcastResult(
  149. contactedEntity, &AZ::ComponentApplicationRequests::FindEntity, event.m_body2->GetEntityId());
  150. gazebo_msgs::msg::ContactState state;
  151. AZ_Assert(contactedEntity, "Invalid entity pointer value");
  152. state.collision1_name = ("ID: " + m_entityId.ToString() + " Name:" + m_entityName).c_str();
  153. state.collision2_name = ("ID: " + event.m_body2->GetEntityId().ToString() + " Name:" + contactedEntity->GetName()).c_str();
  154. geometry_msgs::msg::Wrench totalWrench;
  155. for (auto& contact : event.m_contacts)
  156. {
  157. if (contact.m_separation < ContactMaximumSeparation)
  158. {
  159. state.contact_positions.emplace_back(ROS2Conversions::ToROS2Vector3(contact.m_position));
  160. state.contact_normals.emplace_back(ROS2Conversions::ToROS2Vector3(contact.m_normal));
  161. geometry_msgs::msg::Wrench contactWrench;
  162. contactWrench.force = ROS2Conversions::ToROS2Vector3(contact.m_impulse);
  163. state.wrenches.push_back(AZStd::move(contactWrench));
  164. totalWrench.force.x += contact.m_impulse.GetX();
  165. totalWrench.force.y += contact.m_impulse.GetY();
  166. totalWrench.force.z += contact.m_impulse.GetZ();
  167. state.depths.emplace_back(contact.m_separation);
  168. }
  169. }
  170. state.total_wrench = AZStd::move(totalWrench);
  171. if (!state.contact_positions.empty())
  172. {
  173. AZStd::lock_guard<AZStd::mutex> lock(m_activeContactsMutex);
  174. m_activeContacts[event.m_body2->GetEntityId()] = AZStd::move(state);
  175. }
  176. }
  177. } // namespace ROS2