Skip to content

Commit 07221cb

Browse files
Support for skid-steering and articulations. (o3de#589)
* Fix articulation limits handling in URDFs * Support vehicle dynamics and articulation --------- Signed-off-by: Michał Pełka <[email protected]> Co-authored-by: lumberyard-employee-dm <[email protected]>
1 parent 6c61b49 commit 07221cb

File tree

7 files changed

+98
-90
lines changed

7 files changed

+98
-90
lines changed

Gems/ROS2/Code/Source/RobotImporter/URDF/ArticulationsMaker.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,27 @@ namespace ROS2
6262

6363
if (jointAxis)
6464
{
65-
if (type == PhysX::ArticulationJointType::Hinge)
65+
using LimitType = decltype(jointAxis->Upper());
66+
const bool enableJointLimits = jointAxis->Upper() != AZStd::numeric_limits<LimitType>::infinity() ||
67+
jointAxis->Lower() != -AZStd::numeric_limits<LimitType>::infinity();
68+
articulationLinkConfiguration.m_isLimited = enableJointLimits;
69+
if (enableJointLimits)
6670
{
67-
const double limitUpper = AZ::RadToDeg(jointAxis->Upper());
68-
const double limitLower = AZ::RadToDeg(jointAxis->Lower());
69-
articulationLinkConfiguration.m_angularLimitNegative = limitLower;
70-
articulationLinkConfiguration.m_angularLimitPositive = limitUpper;
71-
}
72-
else if (type == PhysX::ArticulationJointType::Prismatic)
73-
{
74-
articulationLinkConfiguration.m_linearLimitLower = jointAxis->Upper();
75-
articulationLinkConfiguration.m_linearLimitUpper = jointAxis->Lower();
71+
if (type == PhysX::ArticulationJointType::Hinge)
72+
{
73+
if (enableJointLimits)
74+
{
75+
const double limitUpper = AZ::RadToDeg(jointAxis->Upper());
76+
const double limitLower = AZ::RadToDeg(jointAxis->Lower());
77+
articulationLinkConfiguration.m_angularLimitNegative = limitLower;
78+
articulationLinkConfiguration.m_angularLimitPositive = limitUpper;
79+
}
80+
}
81+
else if (type == PhysX::ArticulationJointType::Prismatic)
82+
{
83+
articulationLinkConfiguration.m_linearLimitLower = jointAxis->Upper();
84+
articulationLinkConfiguration.m_linearLimitUpper = jointAxis->Lower();
85+
}
7686
}
7787
}
7888
else

Gems/ROS2/Code/Source/VehicleDynamics/DriveModels/AckermannDriveModel.cpp

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,22 +140,10 @@ namespace ROS2::VehicleDynamics
140140

141141
for (const auto& wheelData : m_driveWheelsData)
142142
{
143-
const auto wheelEntity = wheelData.m_wheelEntity;
144143
float wheelRadius = wheelData.m_wheelRadius;
145-
const auto jointComponent = wheelData.m_wheelJoint;
146-
const auto id = AZ::EntityComponentIdPair(wheelEntity, jointComponent);
147144
AZ_Assert(wheelRadius != 0, "wheelRadius must be non-zero");
148145
auto desiredAngularSpeedX = (m_speedCommand / wheelRadius);
149-
150-
if (wheelData.m_isArticulation)
151-
{
152-
PhysX::ArticulationJointRequestBus::Event(
153-
wheelEntity, &PhysX::ArticulationJointRequests::SetDriveTargetVelocity, wheelData.m_axis, desiredAngularSpeedX);
154-
}
155-
else
156-
{
157-
PhysX::JointRequestBus::Event(id, &PhysX::JointRequests::SetVelocity, desiredAngularSpeedX);
158-
}
146+
VehicleDynamics::Utilities::SetWheelRotationSpeed(wheelData, desiredAngularSpeedX);
159147
}
160148
}
161149

Gems/ROS2/Code/Source/VehicleDynamics/DriveModels/SkidSteeringDriveModel.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,8 @@ namespace ROS2::VehicleDynamics
5151
AZ::ComponentApplicationBus::BroadcastResult(wheelEntityPtr, &AZ::ComponentApplicationRequests::FindEntity, wheelEntityId);
5252
AZ_Assert(wheelEntityPtr, "The wheelEntity should not be null here");
5353
auto* wheelControllerComponentPtr = Utils::GetGameOrEditorComponent<WheelControllerComponent>(wheelEntityPtr);
54-
auto hingeId = VehicleDynamics::Utilities::GetWheelPhysxHinge(wheelEntityId);
55-
AZ::Transform hingeTransform{ AZ::Transform::Identity() };
56-
PhysX::JointRequestBus::EventResult(hingeTransform, hingeId, &PhysX::JointRequests::GetTransform);
54+
auto wheelData = VehicleDynamics::Utilities::GetWheelData(wheelEntityId, axle.m_wheelRadius);
55+
AZ::Transform hingeTransform = Utilities::GetJointTransform(wheelData);
5756
if (wheelControllerComponentPtr)
5857
{
5958
const float normalizedWheelId = -1.f + 2.f * wheelNumber / (wheelCount - 1);
@@ -136,8 +135,8 @@ namespace ROS2::VehicleDynamics
136135
}
137136
for (const auto& wheel : axle.m_axleWheels)
138137
{
139-
auto hinge = VehicleDynamics::Utilities::GetWheelPhysxHinge(wheel);
140-
m_wheelsData[wheel] = hinge;
138+
auto wheelData = VehicleDynamics::Utilities::GetWheelData(wheel, axle.m_wheelRadius);
139+
m_wheelsData[wheel] = wheelData;
141140
}
142141
AZ_Warning(
143142
"SkidSteeringDriveModel",
@@ -158,17 +157,13 @@ namespace ROS2::VehicleDynamics
158157
}
159158
for (size_t wheelId = 0; wheelId < wheelCount; wheelId++)
160159
{
161-
const auto& wheel = axle.m_axleWheels[wheelId];
162-
auto hingePtr = m_wheelsData.find(wheel);
163-
if (hingePtr == m_wheelsData.end())
164-
{
165-
continue;
166-
}
160+
const auto& wheelEntityId = axle.m_axleWheels[wheelId];
167161
float normalizedWheelId = -1.f + 2.f * wheelId / (wheelCount - 1);
168162
float wheelBase = normalizedWheelId * m_config.m_wheelbase / 2.f;
169163
AZ_Assert(axle.m_wheelRadius != 0, "axle.m_wheelRadius must be non-zero");
170164
float wheelRate = (m_currentLinearVelocity + m_currentAngularVelocity * wheelBase) / axle.m_wheelRadius;
171-
PhysX::JointRequestBus::Event(hingePtr->second, &PhysX::JointRequests::SetVelocity, wheelRate);
165+
const auto& wheelData = m_wheelsData[wheelEntityId];
166+
VehicleDynamics::Utilities::SetWheelRotationSpeed(wheelData, wheelRate);
172167
}
173168
}
174169
}

Gems/ROS2/Code/Source/VehicleDynamics/DriveModels/SkidSteeringDriveModel.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ namespace ROS2::VehicleDynamics
5252
int wheelNumber, const AxleConfiguration& axle, const int axisCount) const;
5353

5454
SkidSteeringModelLimits m_limits;
55-
AZStd::unordered_map<AZ::EntityId, AZ::EntityComponentIdPair> m_wheelsData;
55+
AZStd::unordered_map<AZ::EntityId, VehicleDynamics::WheelDynamicsData> m_wheelsData;
5656
AZStd::vector<AZStd::tuple<VehicleDynamics::WheelControllerComponent*, AZ::Vector2, AZ::Vector3>> m_wheelColumns;
5757
VehicleConfiguration m_config;
5858
float m_currentLinearVelocity = 0.0f;

Gems/ROS2/Code/Source/VehicleDynamics/Utilities.cpp

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -180,73 +180,48 @@ namespace ROS2::VehicleDynamics::Utilities
180180
continue;
181181
}
182182

183-
PhysX::HingeJointComponent* hingeComponent{ nullptr };
184-
hingeComponent = wheelEntity->FindComponent<PhysX::HingeJointComponent>();
185-
186-
PhysX::ArticulationLinkComponent* articulation{ nullptr };
187-
articulation = wheelEntity->FindComponent<PhysX::ArticulationLinkComponent>();
188-
189-
if (!hingeComponent && !articulation)
190-
{
191-
AZ_Warning(
192-
"GetAllDriveWheelsData",
193-
false,
194-
"Wheel entity for axle %s does not have a HingeJointComponent nor an ArticulationLinkComponent, ignoring",
195-
axle.m_axleTag.c_str());
196-
continue;
197-
}
198-
199-
if (articulation)
200-
{
201-
if (articulation->m_config.m_articulationJointType != PhysX::ArticulationJointType::Hinge)
202-
{
203-
AZ_Warning(
204-
"GetAllDriveWheelsData",
205-
false,
206-
"Wheel entity for axle %s has an Articulation Link, but it's not a hinge joint, ignoring",
207-
wheel.ToString().c_str());
208-
continue;
209-
}
210-
}
211-
212-
VehicleDynamics::WheelDynamicsData wheelData;
213-
wheelData.m_isArticulation = articulation;
214-
wheelData.m_wheelEntity = wheel;
215-
if (articulation)
216-
{
217-
wheelData.m_wheelJoint = articulation->GetId();
218-
const bool hasFreeAxis = Utils::TryGetFreeArticulationAxis(wheelData.m_wheelEntity, wheelData.m_axis);
219-
220-
AZ_Error("VehicleDynamics::Utilities", hasFreeAxis, "Articulation wheel has no free axis somehow");
221-
}
222-
else
223-
{
224-
wheelData.m_wheelJoint = hingeComponent->GetId();
225-
}
226-
wheelData.m_wheelRadius = axle.m_wheelRadius;
183+
VehicleDynamics::WheelDynamicsData wheelData = GetWheelData(wheel, axle.m_wheelRadius);
227184
driveWheelEntities.push_back(wheelData);
228185
}
229186
}
230187
return driveWheelEntities;
231188
}
232189

233-
AZ::EntityComponentIdPair GetWheelPhysxHinge(const AZ::EntityId wheelEntityId)
190+
VehicleDynamics::WheelDynamicsData GetWheelData(const AZ::EntityId wheelEntityId, float wheelRadius)
234191
{
192+
VehicleDynamics::WheelDynamicsData wheelData;
193+
wheelData.m_wheelEntity = wheelEntityId;
194+
wheelData.m_wheelRadius = wheelRadius;
235195
AZ::Entity* wheelEntity = nullptr;
236196
AZ::ComponentApplicationBus::BroadcastResult(wheelEntity, &AZ::ComponentApplicationRequests::FindEntity, wheelEntityId);
237197
if (!wheelEntity)
238198
{
239199
AZ_Warning("GetWheelDynamicData", false, "Entity %s was not found", wheelEntityId.ToString().c_str());
240-
return AZ::EntityComponentIdPair();
200+
return wheelData;
241201
}
242202
PhysX::HingeJointComponent* hingeComponent{ nullptr };
243203
hingeComponent = wheelEntity->FindComponent<PhysX::HingeJointComponent>();
244-
if (!hingeComponent)
204+
205+
PhysX::ArticulationLinkComponent* articulationComponent{ nullptr };
206+
articulationComponent = wheelEntity->FindComponent<PhysX::ArticulationLinkComponent>();
207+
208+
if (hingeComponent)
209+
{
210+
wheelData.m_isArticulation = false;
211+
wheelData.m_wheelJoint = hingeComponent->GetId();
212+
return wheelData;
213+
}
214+
if (articulationComponent)
245215
{
246-
AZ_Warning("GetWheelDynamicData", false, "Entity %s has no PhysX::HingeJointComponent", wheelEntityId.ToString().c_str());
247-
return AZ::EntityComponentIdPair();
216+
wheelData.m_isArticulation = true;
217+
Utils::TryGetFreeArticulationAxis(wheelEntityId, wheelData.m_axis);
218+
wheelData.m_wheelJoint = articulationComponent->GetId();
219+
return wheelData;
220+
248221
}
249-
return AZ::EntityComponentIdPair(wheelEntityId, hingeComponent->GetId());
222+
223+
AZ_Warning("GetWheelDynamicData", false, "Entity %s has no PhysX::HingeJointComponent", wheelEntityId.ToString().c_str());
224+
return wheelData;
250225
}
251226

252227
float ComputeRampVelocity(float targetVelocty, float lastVelocity, AZ::u64 deltaTimeNs, float acceleration, float maxVelocity)
@@ -268,4 +243,27 @@ namespace ROS2::VehicleDynamics::Utilities
268243
}
269244
return AZStd::clamp(commandVelocity, -maxVelocity, maxVelocity);
270245
}
246+
247+
void SetWheelRotationSpeed(const VehicleDynamics::WheelDynamicsData& data, float wheelRotationSpeed)
248+
{
249+
if (data.m_isArticulation)
250+
{
251+
PhysX::ArticulationJointRequestBus::Event(
252+
data.m_wheelEntity, &PhysX::ArticulationJointRequests::SetDriveTargetVelocity, data.m_axis, wheelRotationSpeed);
253+
}
254+
else
255+
{
256+
PhysX::JointRequestBus::Event( AZ::EntityComponentIdPair(data.m_wheelEntity,data.m_wheelJoint), &PhysX::JointRequests::SetVelocity, wheelRotationSpeed);
257+
}
258+
}
259+
260+
AZ::Transform GetJointTransform(const VehicleDynamics::WheelDynamicsData& data)
261+
{
262+
AZ::Transform hingeTransform{ AZ::Transform::Identity() };
263+
if (!data.m_isArticulation)
264+
{
265+
PhysX::JointRequestBus::EventResult(hingeTransform, AZ::EntityComponentIdPair(data.m_wheelEntity,data.m_wheelJoint), &PhysX::JointRequests::GetTransform);
266+
}
267+
return hingeTransform;
268+
}
271269
} // namespace ROS2::VehicleDynamics::Utilities

Gems/ROS2/Code/Source/VehicleDynamics/Utilities.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ namespace ROS2::VehicleDynamics::Utilities
5353
//! Wheels need a WheelControllerComponent, and the axle must be a drive axle.
5454
AZStd::vector<VehicleDynamics::WheelDynamicsData> GetAllDriveWheelsData(const VehicleConfiguration& vehicleConfig);
5555

56-
//! Get Physx Hinge Joint Component AZ::EntityComponentIdPair from Entity
57-
//! @param wheelEntityId id of entity that has Physx Hinge Joint Controller
58-
//! @returns EntityComponentIdPair that contains id of Hinge component and @param wheelEntityId
59-
AZ::EntityComponentIdPair GetWheelPhysxHinge(const AZ::EntityId wheelEntityId);
56+
57+
//! Retrieve wheel data for a given wheel entity (components id for joints and free axis)
58+
//! @param wheelEntityId Wheel entity to process.
59+
//! @param wheelRadius Radius of the wheel in meters.
60+
//! @returns struct with wheel data.
61+
VehicleDynamics::WheelDynamicsData GetWheelData(const AZ::EntityId wheelEntityId, float wheelRadius);
6062

6163
//! Computes ramped velocity.
6264
//! @param targetVelocity Last commanded velocity to send to robot (in eg m/s or rad/s)
@@ -67,4 +69,11 @@ namespace ROS2::VehicleDynamics::Utilities
6769
//! @returns ramped velocity according to time, acceleration and clamped to @param maxVelocity
6870
float ComputeRampVelocity(float targetVelocity, float lastVelocity, AZ::u64 deltaTimeNs, float acceleration, float maxVelocity);
6971

72+
73+
void SetWheelRotationSpeed(const VehicleDynamics::WheelDynamicsData& data, float wheelRotationSpeed);
74+
75+
AZ::Transform GetJointTransform(const VehicleDynamics::WheelDynamicsData& data);
76+
77+
78+
7079
} // namespace ROS2::VehicleDynamics::Utilities
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Amazon": {
3+
"Physics": {
4+
"EnableReducedCoordinateArticulations": true
5+
}
6+
}
7+
}
8+

0 commit comments

Comments
 (0)