Skip to content

Commit

Permalink
Robot Importer: add sensor maker (o3de#503)
Browse files Browse the repository at this point in the history
* add SensorsMaker for SDFormat data
* publish hooks for processing
* revert output filename change
* code review: cache list of sensor hooks
---------
Signed-off-by: Jan Hanca <[email protected]>

Signed-off-by: Michał Pełka <[email protected]>
  • Loading branch information
jhanca-robotecai authored and michalpelka committed Sep 25, 2023
1 parent 51de032 commit 5ce38c9
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 28 deletions.
6 changes: 5 additions & 1 deletion Gems/ROS2/Code/Include/ROS2/RobotImporter/RobotImporterBus.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
*/
#pragma once

#include <AzCore/EBus/EBus.h>
#include <AzCore/EBus/Policies.h>
#include <AzCore/std/string/string.h>
#include <AzCore/EBus/EBus.h>
#include <ROS2/RobotImporter/SDFormatSensorImporterHook.h>

namespace ROS2
{
Expand All @@ -24,6 +25,9 @@ namespace ROS2
//! @param importAssetWithUrdf If true, the assets referenced in the urdf file will be imported
//! @param useArticulation If true, the prefab will be generated with articulation
virtual bool GeneratePrefabFromFile(const AZStd::string_view filePath, bool importAssetWithUrdf, bool useArticulation) = 0;

//! Return the reference to the list of sensor importer hooks
virtual const SDFormat::SensorImporterHooksStorage& GetSensorHooks() const = 0;
};

using RobotImporterRequestBus = AZ::EBus<RobotImporterRequest>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,7 @@ namespace ROS2::SDFormat
using ConvertSDFSensorCallback = AZStd::function<ConvertSensorOutcome(AZ::Entity&, const sdf::Sensor&)>;
ConvertSDFSensorCallback m_sdfSensorToComponentCallback;
};

//! Buffer for SensorImporterHook data
using SensorImporterHooksStorage = AZStd::vector<ROS2::SDFormat::SensorImporterHook>;
} // namespace ROS2::SDFormat
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
#include <AzCore/Utils/Utils.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/utility/move.h>
#include <AzToolsFramework/API/ViewPaneOptions.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <RobotImporter/URDF/UrdfParser.h>
#include <RobotImporter/Utils/ErrorUtils.h>
#include <RobotImporter/Utils/FilePath.h>
#include <SDFormat/ROS2SensorHooks.h>
#include <SdfAssetBuilder/SdfAssetBuilderSettings.h>

#include <sdf/sdf.hh>
Expand All @@ -31,7 +34,16 @@ namespace ROS2
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ROS2RobotImporterEditorSystemComponent, ROS2RobotImporterSystemComponent>()->Version(0);
const auto& importerHookCamera = ROS2::SDFormat::ROS2SensorHooks::ROS2CameraSensor();
const auto& importerHookGNSS = ROS2::SDFormat::ROS2SensorHooks::ROS2GNSSSensor();
const auto& importerHookImu = ROS2::SDFormat::ROS2SensorHooks::ROS2ImuSensor();
const auto& importerHookLidar = ROS2::SDFormat::ROS2SensorHooks::ROS2LidarSensor();
serializeContext->Class<ROS2RobotImporterEditorSystemComponent, ROS2RobotImporterSystemComponent>()->Version(0)->Attribute(
"SensorImporterHooks",
SDFormat::SensorImporterHooksStorage{ AZStd::move(importerHookCamera),
AZStd::move(importerHookGNSS),
AZStd::move(importerHookImu),
AZStd::move(importerHookLidar) });
}

if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
Expand Down Expand Up @@ -61,6 +73,26 @@ namespace ROS2
ROS2RobotImporterSystemComponent::Activate();
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
RobotImporterRequestBus::Handler::BusConnect();

auto serializeContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetSerializeContext();
serializeContext->EnumerateAll(
[&](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid& typeId) -> bool
{
auto* attribute = AZ::FindAttribute(AZ::Crc32("SensorImporterHooks"), classData->m_attributes);
if (attribute == nullptr)
{
return true;
}

AZ::AttributeReader reader(nullptr, attribute);
SDFormat::SensorImporterHooksStorage sensorHooks;
if (reader.Read<SDFormat::SensorImporterHooksStorage>(sensorHooks))
{
m_sensorHooks.insert(m_sensorHooks.end(), sensorHooks.begin(), sensorHooks.end());
}

return false;
});
}

void ROS2RobotImporterEditorSystemComponent::Deactivate()
Expand Down Expand Up @@ -223,4 +255,9 @@ namespace ROS2
return true;
}

const SDFormat::SensorImporterHooksStorage& ROS2RobotImporterEditorSystemComponent::GetSensorHooks() const
{
return m_sensorHooks;
}

} // namespace ROS2
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <AzCore/std/string/string.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <ROS2/RobotImporter/RobotImporterBus.h>
#include <ROS2/RobotImporter/SDFormatSensorImporterHook.h>
#include <RobotImporter/Utils/SourceAssetsStorage.h>
namespace ROS2
{
Expand Down Expand Up @@ -42,8 +43,12 @@ namespace ROS2

// RobotImporterRequestsBus::Handler overrides ..
bool GeneratePrefabFromFile(const AZStd::string_view filePath, bool importAssetWithUrdf, bool useArticulation) override;
const SDFormat::SensorImporterHooksStorage& GetSensorHooks() const override;

// Timeout for loop waiting for assets to be built
static constexpr AZStd::chrono::seconds assetLoopTimeout = AZStd::chrono::seconds(30);

// Cache for storing sensor importer hooks (read only once)
SDFormat::SensorImporterHooksStorage m_sensorHooks;
};
} // namespace ROS2
15 changes: 8 additions & 7 deletions Gems/ROS2/Code/Source/RobotImporter/RobotImporterWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,13 @@ namespace ROS2

if (m_importAssetWithUrdf)
{
m_urdfAssetsMapping = AZStd::make_shared<Utils::UrdfAssetMap>(
Utils::CopyAssetForURDFAndCreateAssetMap(m_meshNames, m_urdfPath.String(), collidersNames, visualNames, sdfBuilderSettings, dirSuffix));
m_urdfAssetsMapping = AZStd::make_shared<Utils::UrdfAssetMap>(Utils::CopyAssetForURDFAndCreateAssetMap(
m_meshNames, m_urdfPath.String(), collidersNames, visualNames, sdfBuilderSettings, dirSuffix));
}
else
{
m_urdfAssetsMapping = AZStd::make_shared<Utils::UrdfAssetMap>(Utils::FindAssetsForUrdf(m_meshNames, m_urdfPath.String(), sdfBuilderSettings));
m_urdfAssetsMapping =
AZStd::make_shared<Utils::UrdfAssetMap>(Utils::FindAssetsForUrdf(m_meshNames, m_urdfPath.String(), sdfBuilderSettings));
for (const AZStd::string& meshPath : m_meshNames)
{
if (m_urdfAssetsMapping->contains(meshPath))
Expand Down Expand Up @@ -313,7 +314,7 @@ namespace ROS2
void RobotImporterWidget::FillPrefabMakerPage()
{
// Use the URDF/SDF file name stem the prefab name
AZStd::string robotName = AZStd::string(m_urdfPath.Stem().Native());
AZStd::string robotName = AZStd::string(m_urdfPath.Stem().Native()) + ".prefab";
m_prefabMakerPage->setProposedPrefabName(robotName);
QWizard::button(PrefabCreationButtonId)->setText(tr("Create Prefab"));
QWizard::setOption(HavePrefabCreationButton, true);
Expand Down Expand Up @@ -400,11 +401,11 @@ namespace ROS2

void RobotImporterWidget::CreatePrefab(AZStd::string prefabName)
{
const AZ::IO::Path prefabPathRealative(AZ::IO::Path("Assets") / "Importer" / prefabName);
const AZ::IO::Path prefabPath(AZ::IO::Path(AZ::Utils::GetProjectPath()) / prefabPathRealative);
const AZ::IO::Path prefabPathRelative(AZ::IO::Path("Assets") / "Importer" / prefabName);
const AZ::IO::Path prefabPath(AZ::IO::Path(AZ::Utils::GetProjectPath()) / prefabPathRelative);
bool fileExists = AZ::IO::FileIOBase::GetInstance()->Exists(prefabPath.c_str());

if (CheckCyclicalDependency(prefabPathRealative))
if (CheckCyclicalDependency(prefabPathRelative))
{
m_prefabMakerPage->setSuccess(false);
return;
Expand Down
2 changes: 1 addition & 1 deletion Gems/ROS2/Code/Source/RobotImporter/RobotImporterWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ namespace ROS2

//! Checks if the importedPrefabFilename is the same as focused prefab name.
//! @param importedPrefabFilename name of imported prefab
//! @return True if names of prefabs are identical or an erorr occured during validation
//! @return True if names of prefabs are identical or an error occurred during validation
bool CheckCyclicalDependency(AZ::IO::Path importedPrefabFilename);

//! Report an error to the user.
Expand Down
47 changes: 47 additions & 0 deletions Gems/ROS2/Code/Source/RobotImporter/URDF/SensorsMaker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/

#include "SensorsMaker.h"

#include <AzCore/Component/EntityId.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <ROS2/RobotImporter/RobotImporterBus.h>
#include <ROS2/RobotImporter/SDFormatSensorImporterHook.h>

#include <sdf/Link.hh>
#include <sdf/Sensor.hh>

namespace ROS2
{
bool AddSensor(AZ::EntityId entityId, const sdf::Sensor* sensor)
{
SDFormat::SensorImporterHooksStorage sensorHooks;
ROS2::RobotImporterRequestBus::BroadcastResult(sensorHooks, &ROS2::RobotImporterRequest::GetSensorHooks);
for (const auto& hook : sensorHooks)
{
if (hook.m_sensorTypes.contains(sensor->Type()))
{
AZ::Entity* entity = AzToolsFramework::GetEntityById(entityId);
hook.m_sdfSensorToComponentCallback(*entity, *sensor);
return true;
}
}

return false;
}

void SensorsMaker::AddSensors(const sdf::Model& model, const sdf::Link* link, AZ::EntityId entityId) const
{
for (size_t si = 0; si < link->SensorCount(); ++si)
{
const auto* sensor = link->SensorByIndex(si);
const bool success = AddSensor(entityId, sensor);
AZ_Warning("SensorMaker", success, "Cannot find a sensor hook for sensor %d", sensor->Type());
}
}
} // namespace ROS2
32 changes: 32 additions & 0 deletions Gems/ROS2/Code/Source/RobotImporter/URDF/SensorsMaker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/

#pragma once

#include "UrdfParser.h"
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Outcome/Outcome.h>

#include <sdf/sdf.hh>

namespace ROS2
{
//! Populates a given entity with all the contents of the <sensor> tag.
//! Sensors are specified as children of link or joint in SDFormat.
class SensorsMaker
{
public:
//! Adds a sensor to an entity and sets it accordingly based on SDFormat description.
//! @param model A parsed SDF model which could hold information about sensor to be made.
//! @param link A parsed SDF tree link node used to identify link being currently processed.
//! @param entityId A non-active entity which will be affected.
//! @returns created components Id or string with fail
void AddSensors(const sdf::Model& model, const sdf::Link* link, AZ::EntityId entityId) const;
};
} // namespace ROS2
47 changes: 30 additions & 17 deletions Gems/ROS2/Code/Source/RobotImporter/URDF/URDFPrefabMaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemScriptingBus.h>
#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
Expand Down Expand Up @@ -149,7 +149,6 @@ namespace ROS2
constexpr bool visitNestedModels = true;
Utils::VisitModels(*m_root, GetAllLinksFromModel, visitNestedModels);


for (const auto& [name, linkPtr] : links)
{
createdLinks[name] = AddEntitiesForLink(linkPtr, AZ::EntityId{}, createdEntities);
Expand All @@ -176,8 +175,7 @@ namespace ROS2
// Set the transforms of links
for (const auto& [name, linkPtr] : links)
{
if (const auto thisEntry = createdLinks.at(name);
thisEntry.IsSuccess())
if (const auto thisEntry = createdLinks.at(name); thisEntry.IsSuccess())
{
AZ::Transform tf = Utils::GetWorldTransformURDF(linkPtr);
auto* entity = AzToolsFramework::GetEntityById(thisEntry.GetValue());
Expand All @@ -202,7 +200,8 @@ namespace ROS2
}
else
{
AZ_Trace("CreatePrefabFromUrdfOrSdf", "Setting transform failed: %s does not have transform interface\n", name.c_str());
AZ_Trace(
"CreatePrefabFromUrdfOrSdf", "Setting transform failed: %s does not have transform interface\n", name.c_str());
}
}
}
Expand Down Expand Up @@ -252,8 +251,8 @@ namespace ROS2
> defining the coordinate transformation from the parent link frame to the child link frame.
*/

AZStd::string parentName(jointsWhereLinkIsChild.front()->ParentName().c_str(),
jointsWhereLinkIsChild.front()->ParentName().size());
AZStd::string parentName(
jointsWhereLinkIsChild.front()->ParentName().c_str(), jointsWhereLinkIsChild.front()->ParentName().size());
const auto parentEntry = createdLinks.find(parentName);
if (parentEntry == createdLinks.end())
{
Expand All @@ -262,7 +261,11 @@ namespace ROS2
}
if (!parentEntry->second.IsSuccess())
{
AZ_Trace("CreatePrefabFromUrdfOrSdf", "Link %s has parent %s which has failed to create\n", linkName.c_str(), parentName.c_str());
AZ_Trace(
"CreatePrefabFromUrdfOrSdf",
"Link %s has parent %s which has failed to create\n",
linkName.c_str(),
parentName.c_str());
continue;
}
AZ_Trace(
Expand All @@ -286,15 +289,25 @@ namespace ROS2
auto parentLinkIter = createdLinks.find(parentLinkName);
if (parentLinkIter == createdLinks.end())
{
AZ_Warning("CreatePrefabFromUrdfOrSdf", false, "Joint %s has no parent link %s. Cannot create", azJointName.c_str(), parentLinkName.c_str());
AZ_Warning(
"CreatePrefabFromUrdfOrSdf",
false,
"Joint %s has no parent link %s. Cannot create",
azJointName.c_str(),
parentLinkName.c_str());
return true;
}
auto leadEntity = parentLinkIter->second;

auto childLinkIter = createdLinks.find(childLinkName);
if (childLinkIter == createdLinks.end())
{
AZ_Warning("CreatePrefabFromUrdfOrSdf", false, "Joint %s has no child link %s. Cannot create", azJointName.c_str(), childLinkName.c_str());
AZ_Warning(
"CreatePrefabFromUrdfOrSdf",
false,
"Joint %s has no child link %s. Cannot create",
azJointName.c_str(),
childLinkName.c_str());
return true;
}
auto childEntity = childLinkIter->second;
Expand All @@ -306,7 +319,6 @@ namespace ROS2
parentLinkName.c_str(),
childLinkName.c_str());


AZ::Entity* childEntityPtr = AzToolsFramework::GetEntityById(childEntity.GetValue());
if (childEntityPtr)
{
Expand Down Expand Up @@ -381,7 +393,8 @@ namespace ROS2
AzToolsFramework::Prefab::PrefabSystemScriptingBus::BroadcastResult(
prefabTemplateId,
&AzToolsFramework::Prefab::PrefabSystemScriptingBus::Events::CreatePrefabTemplate,
createdEntities, relativePath.String());
createdEntities,
relativePath.String());

if (prefabTemplateId == AzToolsFramework::Prefab::InvalidTemplateId)
{
Expand Down Expand Up @@ -432,8 +445,7 @@ namespace ROS2
}
else
{
result = AZ::Failure(AZStd::string::format("Could not save the newly created prefab to '%s'",
m_prefabPath.c_str()));
result = AZ::Failure(AZStd::string::format("Could not save the newly created prefab to '%s'", m_prefabPath.c_str()));
}

// End undo batch labeled "Robot Importer prefab creation"
Expand All @@ -446,7 +458,8 @@ namespace ROS2
return result;
}

AzToolsFramework::Prefab::PrefabEntityResult URDFPrefabMaker::AddEntitiesForLink(const sdf::Link* link, AZ::EntityId parentEntityId, AZStd::vector<AZ::EntityId>& createdEntities)
AzToolsFramework::Prefab::PrefabEntityResult URDFPrefabMaker::AddEntitiesForLink(
const sdf::Link* link, AZ::EntityId parentEntityId, AZStd::vector<AZ::EntityId>& createdEntities)
{
if (!link)
{
Expand Down Expand Up @@ -491,6 +504,7 @@ namespace ROS2
if (modelContainingLink != nullptr)
{
m_collidersMaker.AddColliders(*modelContainingLink, link, entityId);
m_sensorsMaker.AddSensors(*modelContainingLink, link, entityId);
}
return AZ::Success(entityId);
}
Expand Down Expand Up @@ -524,8 +538,7 @@ namespace ROS2
return;
}

if (auto entity_ = AzToolsFramework::GetEntityById(rootEntityId);
entity_ != nullptr)
if (auto entity_ = AzToolsFramework::GetEntityById(rootEntityId); entity_ != nullptr)
{
auto* transformInterface_ = entity_->FindComponent<AzToolsFramework::Components::TransformComponent>();

Expand Down
Loading

0 comments on commit 5ce38c9

Please sign in to comment.