ROS2LidarSensorComponent.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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 <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
  9. #include <Atom/RPI.Public/RPISystemInterface.h>
  10. #include <Atom/RPI.Public/Scene.h>
  11. #include <AzCore/Component/Entity.h>
  12. #include <AzCore/Serialization/EditContext.h>
  13. #include <AzCore/Serialization/EditContextConstants.inl>
  14. #include <AzFramework/Physics/PhysicsSystem.h>
  15. #include <Lidar/LidarTemplateUtils.h>
  16. #include <Lidar/ROS2LidarSensorComponent.h>
  17. #include <ROS2/Frame/ROS2FrameComponent.h>
  18. #include <ROS2/ROS2Bus.h>
  19. #include <ROS2/Utilities/ROS2Names.h>
  20. namespace ROS2
  21. {
  22. namespace Internal
  23. {
  24. const char* kPointCloudType = "sensor_msgs::msg::PointCloud2";
  25. }
  26. void ROS2LidarSensorComponent::Reflect(AZ::ReflectContext* context)
  27. {
  28. LidarTemplate::Reflect(context);
  29. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  30. {
  31. serialize->Class<ROS2LidarSensorComponent, ROS2SensorComponent>()
  32. ->Version(1)
  33. ->Field("lidarModel", &ROS2LidarSensorComponent::m_lidarModel)
  34. ->Field("LidarParameters", &ROS2LidarSensorComponent::m_lidarParameters)
  35. ->Field("IgnoreLayer", &ROS2LidarSensorComponent::m_ignoreLayer)
  36. ->Field("IgnoredLayerIndex", &ROS2LidarSensorComponent::m_ignoredLayerIndex);
  37. if (AZ::EditContext* ec = serialize->GetEditContext())
  38. {
  39. ec->Class<ROS2LidarSensorComponent>("ROS2 Lidar Sensor", "Lidar sensor component")
  40. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  41. ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
  42. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  43. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &ROS2LidarSensorComponent::m_lidarModel, "Lidar Model", "Lidar model")
  44. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &ROS2LidarSensorComponent::OnLidarModelSelected)
  45. ->EnumAttribute(LidarTemplate::LidarModel::Generic3DLidar, "Generic Lidar")
  46. ->DataElement(
  47. AZ::Edit::UIHandlers::EntityId,
  48. &ROS2LidarSensorComponent::m_lidarParameters,
  49. "Lidar parameters",
  50. "Configuration of Generic lidar")
  51. ->Attribute(AZ::Edit::Attributes::Visibility, &ROS2LidarSensorComponent::IsConfigurationVisible)
  52. ->DataElement(
  53. AZ::Edit::UIHandlers::ComboBox,
  54. &ROS2LidarSensorComponent::m_ignoreLayer,
  55. "Ignore layer",
  56. "Should we ignore selected layer index")
  57. ->DataElement(
  58. AZ::Edit::UIHandlers::Default,
  59. &ROS2LidarSensorComponent::m_ignoredLayerIndex,
  60. "Ignored layer index",
  61. "Layer index to ignore");
  62. }
  63. }
  64. }
  65. bool ROS2LidarSensorComponent::IsConfigurationVisible() const
  66. {
  67. return m_lidarModel == LidarTemplate::LidarModel::Generic3DLidar;
  68. }
  69. AZ::Crc32 ROS2LidarSensorComponent::OnLidarModelSelected()
  70. {
  71. m_lidarParameters = LidarTemplateUtils::GetTemplate(m_lidarModel);
  72. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  73. }
  74. ROS2LidarSensorComponent::ROS2LidarSensorComponent()
  75. {
  76. TopicConfiguration pc;
  77. AZStd::string type = Internal::kPointCloudType;
  78. pc.m_type = type;
  79. pc.m_topic = "pc";
  80. m_sensorConfiguration.m_frequency = 10;
  81. m_sensorConfiguration.m_publishersConfigurations.insert(AZStd::make_pair(type, pc));
  82. }
  83. void ROS2LidarSensorComponent::Visualise()
  84. {
  85. if (m_visualisationPoints.empty())
  86. {
  87. return;
  88. }
  89. if (m_drawQueue)
  90. {
  91. const uint8_t pixelSize = 2;
  92. AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArgs;
  93. drawArgs.m_verts = m_visualisationPoints.data();
  94. drawArgs.m_vertCount = m_visualisationPoints.size();
  95. drawArgs.m_colors = &AZ::Colors::Red;
  96. drawArgs.m_colorCount = 1;
  97. drawArgs.m_opacityType = AZ::RPI::AuxGeomDraw::OpacityType::Opaque;
  98. drawArgs.m_size = pixelSize;
  99. m_drawQueue->DrawPoints(drawArgs);
  100. }
  101. }
  102. void ROS2LidarSensorComponent::SetPhysicsScene()
  103. {
  104. auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
  105. auto foundBody = physicsSystem->FindAttachedBodyHandleFromEntityId(GetEntityId());
  106. auto lidarPhysicsSceneHandle = foundBody.first;
  107. if (foundBody.first == AzPhysics::InvalidSceneHandle)
  108. {
  109. auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  110. lidarPhysicsSceneHandle = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName);
  111. }
  112. AZ_Assert(lidarPhysicsSceneHandle != AzPhysics::InvalidSceneHandle, "Invalid physics scene handle for entity");
  113. m_lidarRaycaster.SetRaycasterScene(lidarPhysicsSceneHandle);
  114. }
  115. void ROS2LidarSensorComponent::Activate()
  116. {
  117. auto ros2Node = ROS2Interface::Get()->GetNode();
  118. AZ_Assert(m_sensorConfiguration.m_publishersConfigurations.size() == 1, "Invalid configuration of publishers for lidar sensor");
  119. const TopicConfiguration& publisherConfig = m_sensorConfiguration.m_publishersConfigurations[Internal::kPointCloudType];
  120. AZStd::string fullTopic = ROS2Names::GetNamespacedName(GetNamespace(), publisherConfig.m_topic);
  121. m_pointCloudPublisher = ros2Node->create_publisher<sensor_msgs::msg::PointCloud2>(fullTopic.data(), publisherConfig.GetQoS());
  122. SetPhysicsScene();
  123. if (m_sensorConfiguration.m_visualise)
  124. {
  125. auto* entityScene = AZ::RPI::Scene::GetSceneForEntityId(GetEntityId());
  126. m_drawQueue = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(entityScene);
  127. }
  128. m_lidarRaycaster.SetAddPointsMaxRange(m_lidarParameters.m_addPointsAtMax);
  129. ROS2SensorComponent::Activate();
  130. }
  131. void ROS2LidarSensorComponent::Deactivate()
  132. {
  133. ROS2SensorComponent::Deactivate();
  134. m_pointCloudPublisher.reset();
  135. }
  136. void ROS2LidarSensorComponent::FrequencyTick()
  137. {
  138. float distance = m_lidarParameters.m_maxRange;
  139. auto entityTransform = GetEntity()->FindComponent<AzFramework::TransformComponent>();
  140. const auto directions =
  141. LidarTemplateUtils::PopulateRayDirections(m_lidarParameters, entityTransform->GetWorldTM().GetEulerRadians());
  142. AZ::Vector3 start = entityTransform->GetWorldTM().GetTranslation();
  143. start.SetZ(start.GetZ());
  144. auto worldToLidarTransform = entityTransform->GetWorldTM();
  145. worldToLidarTransform.Invert();
  146. m_lastScanResults =
  147. m_lidarRaycaster.PerformRaycast(start, directions, worldToLidarTransform, distance, m_ignoreLayer, m_ignoredLayerIndex);
  148. if (m_lastScanResults.empty())
  149. {
  150. AZ_TracePrintf("Lidar Sensor Component", "No results from raycast\n");
  151. return;
  152. }
  153. if (m_sensorConfiguration.m_visualise)
  154. { // Store points for visualisation purposes, in global frame
  155. auto localToWorldTM = entityTransform->GetWorldTM();
  156. m_visualisationPoints = m_lastScanResults;
  157. for (AZ::Vector3& point : m_visualisationPoints)
  158. {
  159. point = localToWorldTM.TransformPoint(point);
  160. }
  161. }
  162. auto* ros2Frame = Utils::GetGameOrEditorComponent<ROS2FrameComponent>(GetEntity());
  163. auto message = sensor_msgs::msg::PointCloud2();
  164. message.header.frame_id = ros2Frame->GetFrameID().data();
  165. message.header.stamp = ROS2Interface::Get()->GetROSTimestamp();
  166. message.height = 1;
  167. message.width = m_lastScanResults.size();
  168. message.point_step = sizeof(AZ::Vector3);
  169. message.row_step = message.width * message.point_step;
  170. AZStd::array<const char*, 3> point_field_names = { "x", "y", "z" };
  171. for (int i = 0; i < point_field_names.size(); i++)
  172. {
  173. sensor_msgs::msg::PointField pf;
  174. pf.name = point_field_names[i];
  175. pf.offset = i * 4;
  176. pf.datatype = sensor_msgs::msg::PointField::FLOAT32;
  177. pf.count = 1;
  178. message.fields.push_back(pf);
  179. }
  180. size_t sizeInBytes = m_lastScanResults.size() * sizeof(AZ::Vector3);
  181. message.data.resize(sizeInBytes);
  182. AZ_Assert(message.row_step * message.height == sizeInBytes, "Inconsistency in the size of point cloud data");
  183. memcpy(message.data.data(), m_lastScanResults.data(), sizeInBytes);
  184. m_pointCloudPublisher->publish(message);
  185. }
  186. } // namespace ROS2