ROS2ContactSensorComponent.cpp 8.9 KB

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