ROS2SystemComponent.cpp 8.9 KB

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