ROS2SystemComponent.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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 <signal.h>
  9. #include "ROS2SystemComponent.h"
  10. #include <ROS2/Clock/ROS2TimeSource.h>
  11. #include <ROS2/Clock/RealTimeSource.h>
  12. #include <ROS2/Clock/SimulationTimeSource.h>
  13. #include <ROS2/Communication/PublisherConfiguration.h>
  14. #include <ROS2/Communication/QoS.h>
  15. #include <ROS2/Communication/TopicConfiguration.h>
  16. #include <ROS2/ROS2TypeIds.h>
  17. #include <ROS2/Sensor/SensorConfiguration.h>
  18. #include <ROS2/Utilities/ROS2Conversions.h>
  19. #include <AzCore/Serialization/EditContext.h>
  20. #include <AzCore/Serialization/EditContextConstants.inl>
  21. #include <AzCore/Serialization/SerializeContext.h>
  22. #include <AzCore/Settings/SettingsRegistry.h>
  23. #include <AzCore/Time/ITime.h>
  24. #include <AzCore/std/smart_ptr/make_shared.h>
  25. #include <AzCore/std/sort.h>
  26. #include <AzCore/std/string/string_view.h>
  27. #include <AzFramework/API/ApplicationAPI.h>
  28. namespace ROS2
  29. {
  30. constexpr AZStd::string_view ClockTypeConfigurationKey = "/O3DE/ROS2/ClockType";
  31. constexpr AZStd::string_view PublishClockConfigurationKey = "/O3DE/ROS2/PublishClock";
  32. constexpr size_t FramesNumberForStats = 60;
  33. AZ_COMPONENT_IMPL(ROS2SystemComponent, "ROS2SystemComponent", ROS2SystemComponentTypeId);
  34. void ROS2SystemComponent::Reflect(AZ::ReflectContext* context)
  35. {
  36. // Reflect structs not strictly owned by any single component
  37. QoS::Reflect(context);
  38. TopicConfiguration::Reflect(context);
  39. PublisherConfiguration::Reflect(context);
  40. SensorConfiguration::Reflect(context);
  41. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  42. {
  43. serialize->Class<ROS2SystemComponent, AZ::Component>()->Version(0);
  44. if (AZ::EditContext* ec = serialize->GetEditContext())
  45. {
  46. ec->Class<ROS2SystemComponent>(
  47. "ROS 2 System Component",
  48. "This component is responsible for creating ROS 2 node and executor, provides ROS 2 interfaces, manages ROS 2 clock "
  49. "and publishes transforms.")
  50. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  51. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
  52. ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
  53. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  54. }
  55. }
  56. }
  57. void ROS2SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  58. {
  59. provided.push_back(AZ_CRC_CE("ROS2Service"));
  60. }
  61. void ROS2SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  62. {
  63. incompatible.push_back(AZ_CRC_CE("ROS2Service"));
  64. }
  65. void ROS2SystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  66. {
  67. required.push_back(AZ_CRC("AssetDatabaseService", 0x3abf5601));
  68. }
  69. void ROS2SystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
  70. {
  71. }
  72. ROS2SystemComponent::ROS2SystemComponent()
  73. {
  74. if (ROS2Interface::Get() == nullptr)
  75. {
  76. ROS2Interface::Register(this);
  77. }
  78. }
  79. ROS2SystemComponent::~ROS2SystemComponent()
  80. {
  81. if (ROS2Interface::Get() == this)
  82. {
  83. ROS2Interface::Unregister(this);
  84. }
  85. rclcpp::shutdown();
  86. }
  87. void ROS2SystemComponent::Init()
  88. {
  89. rclcpp::init(0, 0);
  90. // handle signals, e.g. via `Ctrl+C` hotkey or `kill` command
  91. auto handler = [](int sig)
  92. {
  93. rclcpp::shutdown(); // shutdown rclcpp
  94. std::raise(sig); // shutdown o3de
  95. };
  96. signal(SIGINT, handler);
  97. signal(SIGTERM, handler);
  98. }
  99. void ROS2SystemComponent::InitClock()
  100. {
  101. AZStd::unordered_map<AZStd::string, AZStd::function<AZStd::unique_ptr<ITimeSource>()>> clocksMap;
  102. clocksMap["realtime"] = []()
  103. {
  104. AZ_Info("ROS2SystemComponent", "Enabling realtime clock.");
  105. return AZStd::make_unique<RealTimeSource>();
  106. };
  107. clocksMap["simulation"] = []()
  108. {
  109. AZ_Info("ROS2SystemComponent", "Enabling simulation clock.");
  110. return AZStd::make_unique<SimulationTimeSource>();
  111. };
  112. clocksMap["ros2"] = []()
  113. {
  114. AZ_Info("ROS2SystemComponent", "Enabling ros 2 clock.");
  115. return AZStd::make_unique<ROS2TimeSource>();
  116. };
  117. AZStd::string clockType{ "" };
  118. bool publishClock{ true };
  119. auto* registry = AZ::SettingsRegistry::Get();
  120. AZ_Assert(registry, "No Registry available");
  121. if (registry)
  122. {
  123. registry->Get(publishClock, PublishClockConfigurationKey);
  124. registry->Get(clockType, ClockTypeConfigurationKey);
  125. if (clocksMap.contains(clockType))
  126. {
  127. m_simulationClock = AZStd::make_unique<ROS2Clock>(clocksMap[clockType](), publishClock);
  128. return;
  129. }
  130. }
  131. AZ_Info(
  132. "ROS2SystemComponent", "Cannot read registry or the clock type '%s' is unknown, enabling simulation clock.", clockType.c_str());
  133. m_simulationClock = AZStd::make_unique<ROS2Clock>(clocksMap["simulation"](), publishClock);
  134. }
  135. void ROS2SystemComponent::Activate()
  136. {
  137. InitClock();
  138. m_simulationClock->Activate();
  139. m_ros2Node = std::make_shared<rclcpp::Node>("o3de_ros2_node");
  140. m_executor = AZStd::make_shared<rclcpp::executors::SingleThreadedExecutor>();
  141. m_executor->add_node(m_ros2Node);
  142. m_staticTFBroadcaster = AZStd::make_unique<tf2_ros::StaticTransformBroadcaster>(m_ros2Node);
  143. m_dynamicTFBroadcaster = AZStd::make_unique<tf2_ros::TransformBroadcaster>(m_ros2Node);
  144. ROS2RequestBus::Handler::BusConnect();
  145. AZ::TickBus::Handler::BusConnect();
  146. m_nodeChangedEvent.Signal(m_ros2Node);
  147. }
  148. void ROS2SystemComponent::Deactivate()
  149. {
  150. AZ::TickBus::Handler::BusDisconnect();
  151. ROS2RequestBus::Handler::BusDisconnect();
  152. if (m_simulationClock)
  153. {
  154. m_simulationClock->Deactivate();
  155. }
  156. m_dynamicTFBroadcaster.reset();
  157. m_staticTFBroadcaster.reset();
  158. if (m_executor)
  159. {
  160. if (m_ros2Node)
  161. {
  162. m_executor->remove_node(m_ros2Node);
  163. }
  164. m_executor.reset();
  165. }
  166. m_simulationClock.reset();
  167. m_ros2Node.reset();
  168. m_nodeChangedEvent.Signal(m_ros2Node);
  169. }
  170. builtin_interfaces::msg::Time ROS2SystemComponent::GetROSTimestamp() const
  171. {
  172. return m_simulationClock->GetROSTimestamp();
  173. }
  174. std::shared_ptr<rclcpp::Node> ROS2SystemComponent::GetNode() const
  175. {
  176. return m_ros2Node;
  177. }
  178. void ROS2SystemComponent::ConnectOnNodeChanged(NodeChangedEvent::Handler& handler)
  179. {
  180. handler.Connect(m_nodeChangedEvent);
  181. }
  182. const ROS2Clock& ROS2SystemComponent::GetSimulationClock() const
  183. {
  184. return *m_simulationClock;
  185. }
  186. void ROS2SystemComponent::BroadcastTransform(const geometry_msgs::msg::TransformStamped& t, bool isDynamic)
  187. {
  188. if (isDynamic)
  189. {
  190. m_frameTransforms.push_back(t);
  191. }
  192. else
  193. {
  194. m_staticTFBroadcaster->sendTransform(t);
  195. }
  196. }
  197. void ROS2SystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  198. {
  199. if (rclcpp::ok())
  200. {
  201. m_dynamicTFBroadcaster->sendTransform(m_frameTransforms);
  202. m_frameTransforms.clear();
  203. m_simulationClock->Tick();
  204. m_executor->spin_some();
  205. }
  206. // Calculate simulation loop time
  207. const auto simulationTimestamp = m_simulationClock->GetROSTimestamp();
  208. const float deltaSimTime = ROS2Conversions::GetTimeDifference(m_lastSimulationTime, simulationTimestamp);
  209. // Filter out the outliers
  210. m_simulationLoopTimes.push_back(deltaSimTime);
  211. if (m_simulationLoopTimes.size() > FramesNumberForStats)
  212. {
  213. m_simulationLoopTimes.pop_front();
  214. }
  215. AZStd::vector<float> frameTimeSorted{ m_simulationLoopTimes.begin(), m_simulationLoopTimes.end() };
  216. AZStd::sort(frameTimeSorted.begin(), frameTimeSorted.end());
  217. m_simulationLoopTimeMedian = frameTimeSorted[frameTimeSorted.size() / 2];
  218. m_lastSimulationTime = simulationTimestamp;
  219. }
  220. float ROS2SystemComponent::GetExpectedSimulationLoopTime() const
  221. {
  222. return m_simulationLoopTimeMedian;
  223. }
  224. } // namespace ROS2