Skip to content

Commit f7ad62d

Browse files
authored
Fixed parenting of SDF model and link entity transforms (o3de#591)
* Fixed parenting of SDF model and link entity transforms The pose information from the SDF is a value relative to it's attached frame. The `GetLocalTransfromURDF` function was misnamed and returning the a local transform for any SDF link supplied to the function. Also the `PrefabMakerUtils::SetEntityParent` function which is used to attach a child entity to a parent entity, preserves the world transform of the child entity. Therefore any pose information on child entities such as nested models or links on a model would be set in world space and not in local space. * Fixed the setup of the parent child entity hierarchy between SDF links The parent child hierarchy is established through an SDF joint element which references a child and a parent link. The hieararchy itself shouldn't be used to adjust the world transform between the two links, only to have the child link entity to be under parent link entity for organization purposes. --------- Signed-off-by: lumberyard-employee-dm <[email protected]>
1 parent 5e2a13b commit f7ad62d

File tree

6 files changed

+53
-18
lines changed

6 files changed

+53
-18
lines changed

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

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ namespace ROS2::PrefabMakerUtils
7777
}
7878

7979
AZ_Trace("CreateEntity", "Processing entity id: %s with name: %s\n", entityId.ToString().c_str(), name.c_str());
80-
80+
8181
// If the parent is invalid, parent to the container of the currently focused prefab if one exists.
8282
if (!parentEntityId.IsValid())
8383
{
@@ -90,12 +90,14 @@ namespace ROS2::PrefabMakerUtils
9090
}
9191
}
9292

93-
SetEntityParent(entityId, parentEntityId);
93+
// Default the entity world transform to be the same as the parent entity world transform
94+
// Calling SetEntityParent would have the transform be at world origin
95+
SetEntityParentRelative(entityId, parentEntityId);
9496

9597
return entityId;
9698
}
9799

98-
void SetEntityParent(AZ::EntityId entityId, AZ::EntityId parentEntityId)
100+
static void SetEntityParentInternal(AZ::EntityId entityId, AZ::EntityId parentEntityId, bool useLocalTransform)
99101
{
100102
auto* entity = AzToolsFramework::GetEntityById(entityId);
101103
AZ_Assert(entity, "Unknown entity %s", entityId.ToString().c_str());
@@ -105,10 +107,29 @@ namespace ROS2::PrefabMakerUtils
105107

106108
if (auto* transformComponent = entity->FindComponent<AzToolsFramework::Components::TransformComponent>(); transformComponent)
107109
{
108-
transformComponent->SetParent(parentEntityId);
110+
if (useLocalTransform)
111+
{
112+
transformComponent->SetParentRelative(parentEntityId);
113+
}
114+
else
115+
{
116+
transformComponent->SetParent(parentEntityId);
117+
}
109118
}
110119
}
111120

121+
void SetEntityParent(AZ::EntityId entityId, AZ::EntityId parentEntityId)
122+
{
123+
constexpr bool useLocalTransform = false;
124+
return SetEntityParentInternal(entityId, parentEntityId, useLocalTransform);
125+
}
126+
127+
void SetEntityParentRelative(AZ::EntityId entityId, AZ::EntityId parentEntityId)
128+
{
129+
constexpr bool useLocalTransform = true;
130+
return SetEntityParentInternal(entityId, parentEntityId, useLocalTransform);
131+
}
132+
112133
void AddRequiredComponentsToEntity(AZ::EntityId entityId)
113134
{
114135
AZ::Entity* entity = AzToolsFramework::GetEntityById(entityId);

Gems/ROS2/Code/Source/RobotImporter/URDF/PrefabMakerUtils.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,20 @@ namespace ROS2::PrefabMakerUtils
4141
//! @return a result which is either a created prefab entity or an error.
4242
AzToolsFramework::Prefab::PrefabEntityResult CreateEntity(AZ::EntityId parentEntityId, const AZStd::string& name);
4343

44-
//! Set the parent entity for an entity. The entity getting parent is expected to be inactive.
44+
//! Set the parent entity for an entity. The entity being attached to the parent entity is expected to be inactive.
45+
//! NOTE: This uses the world transform of the entity when updating the transform
46+
//! The world location of the entity will not change
4547
//! @param entityId the id for entity that needs a parent.
4648
//! @param parentEntityId the id for the parent entity.
4749
void SetEntityParent(AZ::EntityId entityId, AZ::EntityId parentEntityId);
4850

51+
//! Set the parent entity for an entity. The entity being attached to the parent is expected to be inactive.
52+
//! NOTE: This uses the local transform of the entity when updating the transform
53+
//! and therefore allows the entity to relocate based on the parent world transform
54+
//! @param entityId the id for entity that needs a parent.
55+
//! @param parentEntityId the id for the parent entity.
56+
void SetEntityParentRelative(AZ::EntityId entityId, AZ::EntityId parentEntityId);
57+
4958
//! Create an entity name from arguments.
5059
//! @param rootName root of entity's name.
5160
//! @param type type of entity, depending on corresponding SDF tag. For example, "visual".

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,9 @@ namespace ROS2
228228
// set the current model transform component parent to the parent model
229229
if (parentModelEntityId.IsValid() && modelEntityId.IsValid())
230230
{
231-
PrefabMakerUtils::SetEntityParent(modelEntityId, parentModelEntityId);
231+
// The model entity local transform should be used
232+
// to allow it to move, rotate, scale relative to the parent
233+
PrefabMakerUtils::SetEntityParentRelative(modelEntityId, parentModelEntityId);
232234
}
233235
}
234236

@@ -277,7 +279,7 @@ namespace ROS2
277279
{
278280
AZ::EntityId createdEntityId = createLinkEntityResult.GetValue();
279281
std::string linkName = linkPtr->Name();
280-
AZ::Transform tf = Utils::GetWorldTransformURDF(linkPtr);
282+
AZ::Transform tf = Utils::GetLocalTransformURDF(linkPtr);
281283
auto* entity = AzToolsFramework::GetEntityById(createdEntityId);
282284
if (entity)
283285
{
@@ -296,7 +298,7 @@ namespace ROS2
296298
tf.GetRotation().GetY(),
297299
tf.GetRotation().GetZ(),
298300
tf.GetRotation().GetW());
299-
transformInterface->SetWorldTM(tf);
301+
transformInterface->SetLocalTM(tf);
300302
}
301303
else
302304
{
@@ -354,7 +356,7 @@ namespace ROS2
354356

355357
// Use the first joint where this link is a child to locate the parent link pointer.
356358
const sdf::Joint* joint = jointsWhereLinkIsChild.front();
357-
std::string parentLinkName = joint->ParentName();
359+
std::string parentLinkName = joint->ParentName();
358360
AZStd::string parentName(parentLinkName.c_str(), parentLinkName.size());
359361

360362
// Lookup the entity created from the parent link using the JointMapper to locate the parent SDF link.
@@ -382,6 +384,9 @@ namespace ROS2
382384
linkPrefabResult.GetValue().ToString().c_str(),
383385
parentEntityIter->second.GetValue().ToString().c_str());
384386
AZ_Trace("CreatePrefabFromUrdfOrSdf", "Link %s setting parent to %s\n", linkName.c_str(), parentName.c_str());
387+
// The joint hierarchy which specifies how a parent and child link hierarchy is represented in an SDF document
388+
// is used to establish the entity parent child hiearachy, but does not modify the world location of the link entities
389+
// therefore SetEntityParent is used to maintain the world transform of the child link
385390
PrefabMakerUtils::SetEntityParent(linkPrefabResult.GetValue(), parentEntityIter->second.GetValue());
386391
}
387392

Gems/ROS2/Code/Source/RobotImporter/Utils/RobotImporterUtils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ namespace ROS2::Utils
8080
return isWheel;
8181
}
8282

83-
AZ::Transform GetWorldTransformURDF(const sdf::Link* link, AZ::Transform t)
83+
AZ::Transform GetLocalTransformURDF(const sdf::Link* link, AZ::Transform t)
8484
{
8585
// Determine if the pose is relative to another link
8686
// See doxygen at

Gems/ROS2/Code/Source/RobotImporter/Utils/RobotImporterUtils.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ namespace ROS2::Utils
3838
//! @return true if the link is likely a wheel link.
3939
bool IsWheelURDFHeuristics(const sdf::Model& model, const sdf::Link* link);
4040

41-
//! The recursive function for the given link goes through URDF and finds world-to-entity transformation for us.
42-
//! @param link pointer to URDF/SDF link that root of robot description
43-
//! @param t initial transform, should be identity for non-recursive call.
44-
//! @returns root to entity transform
45-
AZ::Transform GetWorldTransformURDF(const sdf::Link* link, AZ::Transform t = AZ::Transform::Identity());
41+
//! Returns an AZ::Transform converted from the link pose defined relative to another frame.
42+
//! @param link pointer to URDF/SDF link
43+
//! @param t initial transform, multiplied against link transform
44+
//! @returns Transform of link
45+
AZ::Transform GetLocalTransformURDF(const sdf::Link* link, AZ::Transform t = AZ::Transform::Identity());
4646

4747
//! Type Alias representing a "stack" of Model object that were visited on the way to the current Link/Joint Visitor Callback
4848
using ModelStack = AZStd::deque<AZStd::reference_wrapper<const sdf::Model>>;

Gems/ROS2/Code/Tests/UrdfParserTest.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -791,17 +791,17 @@ namespace UnitTest
791791
const AZ::Vector3 expected_translation_link2{ -1.2000000476837158, 2.0784599781036377, 0.0 };
792792
const AZ::Vector3 expected_translation_link3{ -2.4000000953674316, 0.0, 0.0 };
793793

794-
const AZ::Transform transform_from_urdf_link1 = ROS2::Utils::GetWorldTransformURDF(base_link_ptr);
794+
const AZ::Transform transform_from_urdf_link1 = ROS2::Utils::GetLocalTransformURDF(base_link_ptr);
795795
EXPECT_NEAR(expected_translation_link1.GetX(), transform_from_urdf_link1.GetTranslation().GetX(), 1e-5);
796796
EXPECT_NEAR(expected_translation_link1.GetY(), transform_from_urdf_link1.GetTranslation().GetY(), 1e-5);
797797
EXPECT_NEAR(expected_translation_link1.GetZ(), transform_from_urdf_link1.GetTranslation().GetZ(), 1e-5);
798798

799-
const AZ::Transform transform_from_urdf_link2 = ROS2::Utils::GetWorldTransformURDF(link2_ptr);
799+
const AZ::Transform transform_from_urdf_link2 = ROS2::Utils::GetLocalTransformURDF(link2_ptr);
800800
EXPECT_NEAR(expected_translation_link2.GetX(), transform_from_urdf_link2.GetTranslation().GetX(), 1e-5);
801801
EXPECT_NEAR(expected_translation_link2.GetY(), transform_from_urdf_link2.GetTranslation().GetY(), 1e-5);
802802
EXPECT_NEAR(expected_translation_link2.GetZ(), transform_from_urdf_link2.GetTranslation().GetZ(), 1e-5);
803803

804-
const AZ::Transform transform_from_urdf_link3 = ROS2::Utils::GetWorldTransformURDF(link3_ptr);
804+
const AZ::Transform transform_from_urdf_link3 = ROS2::Utils::GetLocalTransformURDF(link3_ptr);
805805
EXPECT_NEAR(expected_translation_link3.GetX(), transform_from_urdf_link3.GetTranslation().GetX(), 1e-5);
806806
EXPECT_NEAR(expected_translation_link3.GetY(), transform_from_urdf_link3.GetTranslation().GetY(), 1e-5);
807807
EXPECT_NEAR(expected_translation_link3.GetZ(), transform_from_urdf_link3.GetTranslation().GetZ(), 1e-5);

0 commit comments

Comments
 (0)