ROS2ClockSystemComponent.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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 "ROS2ClockSystemComponent.h"
  9. #include "ROS2TimeSource.h"
  10. #include "RealTimeSource.h"
  11. #include "SimulationTimeSource.h"
  12. #include <AzCore/RTTI/BehaviorContext.h>
  13. #include <AzCore/Serialization/EditContext.h>
  14. #include <AzCore/Serialization/SerializeContext.h>
  15. #include <AzCore/Settings/SettingsRegistry.h>
  16. #include <AzCore/std/sort.h>
  17. #include <ROS2/ROS2Bus.h>
  18. #include <ROS2/Utilities/ROS2Conversions.h>
  19. #include <rclcpp/qos.hpp>
  20. namespace ROS2
  21. {
  22. constexpr AZStd::string_view ClockTypeConfigurationKey = "/O3DE/ROS2/ClockType";
  23. constexpr AZStd::string_view PublishClockConfigurationKey = "/O3DE/ROS2/PublishClock";
  24. constexpr AZStd::string_view RealtimeClockName = "realtime";
  25. constexpr AZStd::string_view SimulationClockName = "simulation";
  26. constexpr AZStd::string_view Ros2ClockName = "ros2";
  27. constexpr AZStd::string_view DefaultClock = SimulationClockName;
  28. constexpr size_t FramesNumberForStats = 60;
  29. AZ_COMPONENT_IMPL(ROS2ClockSystemComponent, "ROS2ClockSystemComponent", ROS2ClockSystemComponentTypeId);
  30. void ROS2ClockSystemComponent::Reflect(AZ::ReflectContext* context)
  31. {
  32. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  33. {
  34. serialize->Class<ROS2ClockSystemComponent, AZ::Component>()->Version(0);
  35. if (AZ::EditContext* ec = serialize->GetEditContext())
  36. {
  37. ec->Class<ROS2ClockSystemComponent>("ROS 2 Clock System Component", "This component provides ROS 2 clock functionality.")
  38. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  39. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
  40. ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
  41. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  42. }
  43. }
  44. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  45. {
  46. behaviorContext
  47. ->EBus<ROS2ClockRequestBus>("ROS2ClockRequestBus")
  48. ->Event("GetRosTimestampSec", &ROS2ClockRequestBus::Events::GetROSTimestampSec)
  49. ->Event("PublishTime", &ROS2ClockRequestBus::Events::PublishTime)
  50. ->Event("GetExpectedLoopTime", &ROS2ClockRequestBus::Events::GetExpectedLoopTime)
  51. ->Event("AdjustTimeSec", &ROS2ClockRequestBus::Events::AdjustTimeSec);
  52. }
  53. }
  54. ROS2ClockSystemComponent::ROS2ClockSystemComponent()
  55. {
  56. if (ROS2ClockInterface::Get() == nullptr)
  57. {
  58. ROS2ClockInterface::Register(this);
  59. }
  60. }
  61. ROS2ClockSystemComponent::~ROS2ClockSystemComponent()
  62. {
  63. if (ROS2ClockInterface::Get() == this)
  64. {
  65. ROS2ClockInterface::Unregister(this);
  66. }
  67. }
  68. void ROS2ClockSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  69. {
  70. provided.push_back(AZ_CRC_CE("ROS2ClockService"));
  71. }
  72. void ROS2ClockSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  73. {
  74. incompatible.push_back(AZ_CRC_CE("ROS2ClockService"));
  75. }
  76. void ROS2ClockSystemComponent::Activate()
  77. {
  78. // Read configuration from the settings registry
  79. AZStd::unordered_map<AZStd::string, AZStd::function<AZStd::unique_ptr<ITimeSource>()>> clocksMap;
  80. clocksMap[RealtimeClockName] = []()
  81. {
  82. AZ_Info("ROS2ClockSystemComponent", "Enabling realtime clock.\n");
  83. return AZStd::make_unique<RealTimeSource>();
  84. };
  85. clocksMap[SimulationClockName] = []()
  86. {
  87. AZ_Info("ROS2ClockSystemComponent", "Enabling simulation clock.\n");
  88. return AZStd::make_unique<SimulationTimeSource>();
  89. };
  90. clocksMap[Ros2ClockName] = []()
  91. {
  92. AZ_Info("ROS2ClockSystemComponent", "Enabling ros 2 clock.\n");
  93. return AZStd::make_unique<ROS2TimeSource>();
  94. };
  95. AZStd::string clockType{ "" };
  96. bool publishClock{ true };
  97. auto* registry = AZ::SettingsRegistry::Get();
  98. AZ_Assert(registry, "No Registry available");
  99. if (registry)
  100. {
  101. registry->Get(publishClock, PublishClockConfigurationKey);
  102. registry->Get(clockType, ClockTypeConfigurationKey);
  103. if (clocksMap.contains(clockType))
  104. {
  105. m_timeSource = clocksMap[clockType]();
  106. m_publishClock = publishClock;
  107. }
  108. }
  109. if (!m_timeSource)
  110. {
  111. AZ_Info(
  112. "ROS2SystemComponent",
  113. "Cannot read registry or the clock type '%s' is unknown, enabling %s clock.\n",
  114. clockType.c_str(),
  115. DefaultClock.data());
  116. m_timeSource = clocksMap[DefaultClock]();
  117. m_publishClock = true;
  118. }
  119. m_timeSource->Activate();
  120. m_simulationLoopTimes.clear();
  121. AZ::TickBus::Handler::BusConnect();
  122. ROS2ClockRequestBus::Handler::BusConnect();
  123. }
  124. void ROS2ClockSystemComponent::Deactivate()
  125. {
  126. ROS2ClockRequestBus::Handler::BusDisconnect();
  127. AZ::TickBus::Handler::BusDisconnect();
  128. // Deactivate the time source
  129. if (m_timeSource)
  130. {
  131. m_timeSource->Deactivate();
  132. }
  133. m_clockPublisher.reset();
  134. }
  135. builtin_interfaces::msg::Time ROS2ClockSystemComponent::GetROSTimestamp() const
  136. {
  137. AZ_Assert(m_timeSource, "No time source set for ROS2ClockSystemComponent");
  138. return m_timeSource->GetROSTimestamp();
  139. }
  140. double ROS2ClockSystemComponent::GetROSTimestampSec() const
  141. {
  142. AZ_Assert(m_timeSource, "No time source set for ROS2ClockSystemComponent");
  143. builtin_interfaces::msg::Time timeStamp = m_timeSource->GetROSTimestamp();
  144. return double(timeStamp.sec) + double(timeStamp.nanosec) * 1e-9;
  145. }
  146. AZ::Outcome<void, AZStd::string> ROS2ClockSystemComponent::AdjustTime(const builtin_interfaces::msg::Time& time)
  147. {
  148. AZ_Assert(m_timeSource, "No time source set for ROS2ClockSystemComponent");
  149. return m_timeSource->AdjustTime(time);
  150. }
  151. bool ROS2ClockSystemComponent::AdjustTimeSec(double time)
  152. {
  153. AZ_Assert(m_timeSource, "No time source set for ROS2ClockSystemComponent");
  154. builtin_interfaces::msg::Time timeStamp;
  155. timeStamp.sec = static_cast<int32_t>(time);
  156. timeStamp.nanosec = static_cast<uint32_t>((time - timeStamp.sec) * 1e9);
  157. const auto result = m_timeSource->AdjustTime(timeStamp);
  158. if (!result.IsSuccess())
  159. {
  160. AZ_Error("ROS2ClockSystemComponent", false, "Failed to adjust time: %s", result.GetError().c_str());
  161. }
  162. return result.IsSuccess();
  163. }
  164. void ROS2ClockSystemComponent::PublishTime()
  165. {
  166. if (!m_publishClock)
  167. {
  168. return;
  169. }
  170. if (!m_clockPublisher)
  171. {
  172. // Lazy construct
  173. auto ros2Node = ROS2Interface::Get()->GetNode();
  174. if (ros2Node)
  175. {
  176. rclcpp::ClockQoS qos;
  177. m_clockPublisher = ros2Node->create_publisher<rosgraph_msgs::msg::Clock>("/clock", qos);
  178. }
  179. }
  180. rosgraph_msgs::msg::Clock msg;
  181. msg.clock = GetROSTimestamp();
  182. m_clockPublisher->publish(msg);
  183. }
  184. float ROS2ClockSystemComponent::GetExpectedLoopTime() const
  185. {
  186. return m_simulationLoopTimeMedian;
  187. }
  188. void ROS2ClockSystemComponent::OnTick(float deltaTime, AZ::ScriptTimePoint time)
  189. {
  190. PublishTime();
  191. // Calculate simulation loop time
  192. const auto simulationTimestamp = GetROSTimestamp();
  193. const float deltaSimTime = ROS2Conversions::GetTimeDifference(m_lastSimulationTime, simulationTimestamp);
  194. // Filter out the outliers
  195. m_simulationLoopTimes.push_back(deltaSimTime);
  196. if (m_simulationLoopTimes.size() > FramesNumberForStats)
  197. {
  198. m_simulationLoopTimes.pop_front();
  199. }
  200. AZStd::vector<float> frameTimeSorted{ m_simulationLoopTimes.begin(), m_simulationLoopTimes.end() };
  201. AZStd::sort(frameTimeSorted.begin(), frameTimeSorted.end());
  202. m_simulationLoopTimeMedian = frameTimeSorted[frameTimeSorted.size() / 2];
  203. m_lastSimulationTime = simulationTimestamp;
  204. }
  205. } // namespace ROS2