RigidBodyTwistControlComponent.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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 "RigidBodyTwistControlComponent.h"
  9. #include <AzCore/Component/TransformBus.h>
  10. #include <AzCore/Math/MathUtils.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include <AzCore/Serialization/EditContextConstants.inl>
  13. #include <AzFramework/Physics/RigidBodyBus.h>
  14. #include <AzFramework/Physics/SimulatedBodies/RigidBody.h>
  15. namespace ROS2Controllers
  16. {
  17. namespace
  18. {
  19. AZ::Vector3 ComputeImpulse(const AZ::Vector3& errorVec, AZStd::array<PidConfiguration, 3>& controllers, float fixedDeltaTime)
  20. {
  21. AZ::Vector3 impulse = AZ::Vector3::CreateZero();
  22. for (size_t i = 0; i < 3; ++i)
  23. {
  24. const auto error = errorVec.GetElement(i);
  25. static constexpr float secondsToNanoSeconds = 1'000'000'000.0f;
  26. impulse.SetElement(i, controllers[i].ComputeCommand(error, fixedDeltaTime * secondsToNanoSeconds));
  27. }
  28. return impulse;
  29. }
  30. } // namespace
  31. void RigidBodyTwistControlComponent::Reflect(AZ::ReflectContext* context)
  32. {
  33. RigidBodyTwistControlComponentConfig::Reflect(context);
  34. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  35. {
  36. serialize->Class<RigidBodyTwistControlComponent, AZ::Component>()->Version(1)->Field(
  37. "Config", &RigidBodyTwistControlComponent::m_config);
  38. if (AZ::EditContext* ec = serialize->GetEditContext())
  39. {
  40. ec->Class<RigidBodyTwistControlComponent>("Rigid Body Twist Control", "Simple control through RigidBody")
  41. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  42. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  43. ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
  44. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/RigidBodyTwistControl.svg")
  45. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/RigidBodyTwistControl.svg")
  46. ->DataElement(
  47. AZ::Edit::UIHandlers::Default,
  48. &RigidBodyTwistControlComponent::m_config,
  49. "Configuration",
  50. "Configuration for the Rigid Body Twist Control Component")
  51. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
  52. }
  53. }
  54. }
  55. void RigidBodyTwistControlComponent::Activate()
  56. {
  57. AZ::TickBus::Handler::BusConnect();
  58. TwistNotificationBus::Handler::BusConnect(GetEntityId());
  59. for (auto& controller : m_config.m_linearControllers)
  60. {
  61. controller.InitializePid();
  62. }
  63. for (auto& controller : m_config.m_angularControllers)
  64. {
  65. controller.InitializePid();
  66. }
  67. }
  68. void RigidBodyTwistControlComponent::Deactivate()
  69. {
  70. TwistNotificationBus::Handler::BusDisconnect();
  71. if (m_sceneFinishSimHandler.IsConnected())
  72. {
  73. m_sceneFinishSimHandler.Disconnect();
  74. }
  75. if (AZ::TickBus::Handler::BusIsConnected())
  76. {
  77. AZ::TickBus::Handler::BusDisconnect();
  78. }
  79. }
  80. void RigidBodyTwistControlComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  81. {
  82. AzPhysics::SceneInterface* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  83. AZ_Assert(sceneInterface, "No scene interface");
  84. if (!sceneInterface)
  85. {
  86. return;
  87. }
  88. AzPhysics::SceneHandle defaultSceneHandle = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName);
  89. AZ_Assert(defaultSceneHandle != AzPhysics::InvalidSceneHandle, "Invalid default physics scene handle");
  90. AzPhysics::RigidBody* rigidBody = nullptr;
  91. Physics::RigidBodyRequestBus::EventResult(rigidBody, GetEntityId(), &Physics::RigidBodyRequests::GetRigidBody);
  92. AZ_Warning("RigidBodyTwistControlComponent", rigidBody, "No rigid body found for entity %s", GetEntity()->GetName().c_str());
  93. if (!rigidBody)
  94. {
  95. return;
  96. }
  97. m_bodyHandle = rigidBody->m_bodyHandle;
  98. m_sceneFinishSimHandler = AzPhysics::SceneEvents::OnSceneSimulationFinishHandler(
  99. [this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle, float fixedDeltaTime)
  100. {
  101. OnSceneSimulationFinish(sceneHandle, fixedDeltaTime);
  102. },
  103. aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Components));
  104. sceneInterface->RegisterSceneSimulationFinishHandler(defaultSceneHandle, m_sceneFinishSimHandler);
  105. AZ::TickBus::Handler::BusDisconnect();
  106. }
  107. void RigidBodyTwistControlComponent::OnSceneSimulationFinish(AzPhysics::SceneHandle sceneHandle, float fixedDeltaTime)
  108. {
  109. AzPhysics::SceneInterface* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  110. AZ_Assert(sceneInterface, "No scene interface");
  111. [[maybe_unused]] AzPhysics::SceneHandle defaultSceneHandle = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName);
  112. AZ_Assert(defaultSceneHandle != AzPhysics::InvalidSceneHandle, "Invalid default physics scene handle");
  113. if (m_bodyHandle == AzPhysics::InvalidSimulatedBodyHandle)
  114. {
  115. // This component is only interested in the default scene
  116. return;
  117. }
  118. auto* rigidBody = sceneInterface->GetSimulatedBodyFromHandle(sceneHandle, m_bodyHandle);
  119. AZ_Assert(rigidBody, "No body found for previously given handle");
  120. const AZ::Transform robotTransform = rigidBody->GetTransform();
  121. if (m_config.m_physicalApi == RigidBodyTwistControlComponentConfig::PhysicalApi::Kinematic)
  122. {
  123. // Convert local steering to world frame
  124. const auto linearVelocityGlobal = robotTransform.TransformVector(m_linearVelocityLocal);
  125. const auto angularVelocityGlobal = robotTransform.TransformVector(m_angularVelocityLocal);
  126. // Set the kinematic target for the rigid body
  127. // This will move the rigid body to the target position and orientation
  128. Physics::RigidBodyRequestBus::Event(GetEntityId(), &Physics::RigidBodyRequests::SetKinematic, true);
  129. AZ::Transform kinematicTarget = robotTransform;
  130. kinematicTarget.SetTranslation(kinematicTarget.GetTranslation() + linearVelocityGlobal * fixedDeltaTime);
  131. kinematicTarget.SetRotation(
  132. AZ::Quaternion::CreateFromScaledAxisAngle(angularVelocityGlobal * fixedDeltaTime) * kinematicTarget.GetRotation());
  133. Physics::RigidBodyRequestBus::Event(GetEntityId(), &Physics::RigidBodyRequests::SetKinematicTarget, kinematicTarget);
  134. }
  135. else if (m_config.m_physicalApi == RigidBodyTwistControlComponentConfig::PhysicalApi::Velocity)
  136. {
  137. // Convert local steering to world frame
  138. const auto linearVelocityGlobal = robotTransform.TransformVector(m_linearVelocityLocal);
  139. const auto angularVelocityGlobal = robotTransform.TransformVector(m_angularVelocityLocal);
  140. Physics::RigidBodyRequestBus::Event(GetEntityId(), &Physics::RigidBodyRequests::SetLinearVelocity, linearVelocityGlobal);
  141. Physics::RigidBodyRequestBus::Event(GetEntityId(), &Physics::RigidBodyRequests::SetAngularVelocity, angularVelocityGlobal);
  142. }
  143. else if (m_config.m_physicalApi == RigidBodyTwistControlComponentConfig::PhysicalApi::Force)
  144. {
  145. const AZ::Transform robotTransformInv = rigidBody->GetTransform().GetInverse();
  146. AZ::Vector3 currentLinearVelocityGlob = AZ::Vector3::CreateZero();
  147. AZ::Vector3 currentAngularVelocityGlob = AZ::Vector3::CreateZero();
  148. Physics::RigidBodyRequestBus::EventResult(
  149. currentLinearVelocityGlob, GetEntityId(), &Physics::RigidBodyRequests::GetLinearVelocity);
  150. Physics::RigidBodyRequestBus::EventResult(
  151. currentAngularVelocityGlob, GetEntityId(), &Physics::RigidBodyRequests::GetAngularVelocity);
  152. // Convert local steering to world frame
  153. const AZ::Vector3 currentLinearVelocityLocal = robotTransformInv.TransformVector(currentLinearVelocityGlob);
  154. const AZ::Vector3 angularVelocityLocal = robotTransformInv.TransformVector(currentAngularVelocityGlob);
  155. const AZ::Vector3 errorLinear = m_linearVelocityLocal - currentLinearVelocityLocal;
  156. const AZ::Vector3 errorAngular = m_angularVelocityLocal - angularVelocityLocal;
  157. // Compute the forces to apply based on the error
  158. AZ::Vector3 linearForceLocal = ComputeImpulse(errorLinear, m_config.m_linearControllers, fixedDeltaTime);
  159. AZ::Vector3 angularForceLocal = ComputeImpulse(errorAngular, m_config.m_angularControllers, fixedDeltaTime);
  160. const AZ::Vector3 linearForceGlobal = robotTransform.TransformVector(linearForceLocal);
  161. const AZ::Vector3 angularForceGlobal = robotTransform.TransformVector(angularForceLocal);
  162. Physics::RigidBodyRequestBus::Event(
  163. GetEntityId(), &Physics::RigidBodyRequests::ApplyLinearImpulse, linearForceGlobal * fixedDeltaTime);
  164. Physics::RigidBodyRequestBus::Event(
  165. GetEntityId(), &Physics::RigidBodyRequests::ApplyAngularImpulse, angularForceGlobal * fixedDeltaTime);
  166. }
  167. else
  168. {
  169. AZ_Error("RigidBodyTwistControlComponent", false, "Unknown physical API type %d", static_cast<int>(m_config.m_physicalApi));
  170. }
  171. }
  172. void RigidBodyTwistControlComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  173. {
  174. required.push_back(AZ_CRC_CE("ROS2RobotControl"));
  175. required.push_back(AZ_CRC_CE("PhysicsDynamicRigidBodyService"));
  176. }
  177. void RigidBodyTwistControlComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  178. {
  179. incompatible.push_back(AZ_CRC_CE("ArticulationLinkService"));
  180. }
  181. void RigidBodyTwistControlComponent::TwistReceived(const AZ::Vector3& linear, const AZ::Vector3& angular)
  182. {
  183. m_linearVelocityLocal = linear;
  184. m_angularVelocityLocal = angular;
  185. }
  186. } // namespace ROS2Controllers