Skip to content

Commit

Permalink
Odometry sensor (#261)
Browse files Browse the repository at this point in the history
* Odometry sensor
* Updated documentation, added review changes

Co-authored-by: Adam Dabrowski <adam.dabrowski@robotec.ai>
Signed-off-by: Piotr Jaroszek <piotr.jaroszek@robotec.ai>
  • Loading branch information
pijaro and adamdbrw committed Jan 4, 2023
1 parent 739db1c commit 30968ea
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Gems/ROS2/Code/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ ly_add_target(
Gem::StartingPointInput
)

target_depends_on_ros2_packages(ROS2.Static rclcpp builtin_interfaces std_msgs sensor_msgs urdfdom tf2_ros ackermann_msgs gazebo_msgs control_toolbox)
target_depends_on_ros2_packages(ROS2.Static rclcpp builtin_interfaces std_msgs sensor_msgs nav_msgs urdfdom tf2_ros ackermann_msgs gazebo_msgs control_toolbox)

ly_add_target(
NAME ROS2.API HEADERONLY
Expand Down
97 changes: 97 additions & 0 deletions Gems/ROS2/Code/Source/Odometry/ROS2OdometrySensorComponent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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 "ROS2OdometrySensorComponent.h"
#include "ROS2/Frame/ROS2FrameComponent.h"
#include "ROS2/ROS2Bus.h"
#include "ROS2/Utilities/ROS2Conversions.h"
#include "ROS2/Utilities/ROS2Names.h"
#include <AzFramework/Physics/RigidBodyBus.h>

namespace ROS2
{
namespace Internal
{
const char* kOdometryMsgType = "nav_msgs::msg::Odometry";
}

void ROS2OdometrySensorComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<ROS2OdometrySensorComponent, ROS2SensorComponent>()->Version(1);

if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<ROS2OdometrySensorComponent>("ROS2 Odometry Sensor", "Odometry sensor component")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "ROS2")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"));
}
}
}

ROS2OdometrySensorComponent::ROS2OdometrySensorComponent()
{
TopicConfiguration tc;
const AZStd::string type = Internal::kOdometryMsgType;
tc.m_type = type;
tc.m_topic = "odom";
m_sensorConfiguration.m_frequency = 10;
m_sensorConfiguration.m_publishersConfigurations.insert(AZStd::make_pair(type, tc));
}

void ROS2OdometrySensorComponent::FrequencyTick()
{
auto* ros2Frame = Utils::GetGameOrEditorComponent<ROS2FrameComponent>(GetEntity());
auto transform = ros2Frame->GetFrameTransform();

AZ::Vector3 linearVelocity;
Physics::RigidBodyRequestBus::EventResult(linearVelocity, m_entity->GetId(), &Physics::RigidBodyRequests::GetLinearVelocity);

linearVelocity = transform.GetInverse().TransformVector(linearVelocity);
AZ::Vector3 angularVelocity;
Physics::RigidBodyRequestBus::EventResult(angularVelocity, m_entity->GetId(), &Physics::RigidBodyRequests::GetAngularVelocity);
angularVelocity = transform.GetInverse().TransformVector(angularVelocity);
m_odometryMsg.twist.twist.linear = ROS2Conversions::ToROS2Vector3(linearVelocity);
m_odometryMsg.twist.twist.angular = ROS2Conversions::ToROS2Vector3(angularVelocity);

auto translation = transform.GetTranslation();
m_odometryMsg.pose.pose.position.x = translation.GetX();
m_odometryMsg.pose.pose.position.y = translation.GetY();
m_odometryMsg.pose.pose.position.z = translation.GetZ();

m_odometryPublisher->publish(m_odometryMsg);
}

void ROS2OdometrySensorComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC_CE("PhysicsRigidBodyService"));
}

void ROS2OdometrySensorComponent::Activate()
{
// "odom" is globally fixed frame for all robots, no matter the namespace
m_odometryMsg.header.frame_id = ROS2Names::GetNamespacedName(GetNamespace(), "odom").c_str();
m_odometryMsg.child_frame_id = GetFrameID().c_str();

ROS2SensorComponent::Activate();
auto ros2Node = ROS2Interface::Get()->GetNode();
AZ_Assert(m_sensorConfiguration.m_publishersConfigurations.size() == 1, "Invalid configuration of publishers for Odometry sensor");

const auto publisherConfig = m_sensorConfiguration.m_publishersConfigurations[Internal::kOdometryMsgType];
const auto fullTopic = ROS2Names::GetNamespacedName(GetNamespace(), publisherConfig.m_topic);
m_odometryPublisher = ros2Node->create_publisher<nav_msgs::msg::Odometry>(fullTopic.data(), publisherConfig.GetQoS());
}

void ROS2OdometrySensorComponent::Deactivate()
{
ROS2SensorComponent::Deactivate();
m_odometryPublisher.reset();
}
} // namespace ROS2
39 changes: 39 additions & 0 deletions Gems/ROS2/Code/Source/Odometry/ROS2OdometrySensorComponent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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 "Sensor/ROS2SensorComponent.h"
#include <AzCore/Math/Transform.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <nav_msgs/msg/odometry.hpp>
#include <rclcpp/publisher.hpp>

namespace ROS2
{
//! Odometry sensor Component.
//! It constructs and publishes an odometry message, which contains information about vehicle velocity and position in space.
//! This is a ground truth "sensor", which can be helpful for development and machine learning.
//! @see <a href="https://index.ros.org/p/nav_msgs/"> nav_msgs package. </a>
class ROS2OdometrySensorComponent : public ROS2SensorComponent
{
public:
AZ_COMPONENT(ROS2OdometrySensorComponent, "{61387448-63AA-4563-AF87-60C72B05B863}", ROS2SensorComponent);
ROS2OdometrySensorComponent();
~ROS2OdometrySensorComponent() = default;
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void Reflect(AZ::ReflectContext* context);
void Activate() override;
void Deactivate() override;

private:
void FrequencyTick() override;

std::shared_ptr<rclcpp::Publisher<nav_msgs::msg::Odometry>> m_odometryPublisher;
nav_msgs::msg::Odometry m_odometryMsg;
};
} // namespace ROS2
2 changes: 2 additions & 0 deletions Gems/ROS2/Code/Source/ROS2ModuleInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Imu/ROS2ImuSensorComponent.h"
#include "Lidar/ROS2LidarSensorComponent.h"
#include "ROS2/Frame/ROS2FrameComponent.h"
#include "Odometry/ROS2OdometrySensorComponent.h"
#include "ROS2/Manipulator/MotorizedJoint.h"
#include "ROS2SystemComponent.h"
#include "RobotControl/Controllers/AckermannController/AckermannControlComponent.h"
Expand Down Expand Up @@ -47,6 +48,7 @@ namespace ROS2
ROS2ImuSensorComponent::CreateDescriptor(),
ROS2GNSSSensorComponent::CreateDescriptor(),
ROS2LidarSensorComponent::CreateDescriptor(),
ROS2OdometrySensorComponent::CreateDescriptor(),
ROS2FrameComponent::CreateDescriptor(),
ROS2RobotControlComponent::CreateDescriptor(),
ROS2CameraSensorComponent::CreateDescriptor(),
Expand Down
2 changes: 2 additions & 0 deletions Gems/ROS2/Code/ros2_files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ set(FILES
Source/GNSS/GNSSFormatConversions.h
Source/Imu/ROS2ImuSensorComponent.cpp
Source/Imu/ROS2ImuSensorComponent.h
Source/Odometry/ROS2OdometrySensorComponent.cpp
Source/Odometry/ROS2OdometrySensorComponent.h
Source/RobotImporter/ROS2RobotImporterSystemComponent.cpp
Source/RobotImporter/ROS2RobotImporterSystemComponent.h
Source/RobotImporter/Utils/RobotImporterUtils.h
Expand Down
2 changes: 1 addition & 1 deletion Gems/ROS2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ If a `desktop` installation of ROS 2 distro was selected, everything else should
Use this helpful command to install:

```
sudo apt install ros-${ROS_DISTRO}-ackermann-msgs ros-${ROS_DISTRO}-control-toolbox ros-${ROS_DISTRO}-gazebo-msgs
sudo apt install ros-${ROS_DISTRO}-ackermann-msgs ros-${ROS_DISTRO}-control-toolbox ros-${ROS_DISTRO}-nav-msgs ros-${ROS_DISTRO}-gazebo-msgs
```

## Features
Expand Down
13 changes: 9 additions & 4 deletions Gems/ROS2/docs/guides/ros2-gem.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ You can browse Doxygen-generated documentation on [Gem's GitHub page](https://ro
- ROS2FrameComponent
- ROS2SensorComponent
- __Sensors__
- ROS2CameraComponent
- ROS2GNSSComponent
- ROS2IMUComponent
- ROS2LidarComponent
- ROS2CameraSensorComponent
- ROS2GNSSSensorComponent
- ROS2IMUSensorComponent
- ROS2LidarSensorComponent
- ROS2OdometrySensorComponent
- __Robot control__
- ROS2RobotControlComponent
- AckermannControlComponent
Expand Down Expand Up @@ -101,6 +102,10 @@ it to ROS 2 domain.
If you intend to add your own sensor, it might be useful to look at how sensors already provided within the Gem are
implemented.

Sensors can be fall into one of two categories:
- sensors which replicate real devices to some degree of realism.
- ground truth "sensors", which can be useful for development and machine learning.

### Robot Control

The Gem comes with `ROS2RobotControlComponent`, which you can use to move your robot through:
Expand Down
4 changes: 2 additions & 2 deletions Gems/ROS2/docs/guides/static/diagrams/diagram_ros2_gem.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 65 additions & 5 deletions Gems/ROS2/docs/guides/static/diagrams/diagram_ros2_gem_yEd.graphml
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ Validate*()<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:Model
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="105.64550781250028" width="238.48111979166663" x="-238.66731770833326" y="396.89978027343705"/>
<y:Geometry height="105.64550781250028" width="238.48111979166663" x="91.546875" y="472.6347656250004"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="238.48111979166663" x="0.0" xml:space="preserve" y="0.0">Robot Importer</y:NodeLabel>
Expand All @@ -314,7 +314,7 @@ Validate*()<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:Model
<node id="n3::n0">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.predefinedProcess">
<y:Geometry height="40.0" width="177.0" x="-219.14388020833326" y="442.410034179688"/>
<y:Geometry height="40.0" width="177.0" x="111.0703125" y="518.1450195312514"/>
<y:Fill color="#00FFFF" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="144.3203125" x="16.33984375" xml:space="preserve" y="11.015625">Robot Importer Classes<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
Expand Down Expand Up @@ -526,14 +526,14 @@ GetQoS()<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelPar
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="107.4609375" width="238.48111979166669" x="-238.66731770833331" y="274.43884277343705"/>
<y:Geometry height="107.4609375" width="240.01106770833331" x="-238.66731770833331" y="274.43884277343705"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="238.48111979166669" x="0.0" xml:space="preserve" y="0.0">IMU</y:NodeLabel>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="240.01106770833331" x="0.0" xml:space="preserve" y="0.0">IMU</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="2" rightF="1.5299479166666288" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
Expand Down Expand Up @@ -741,6 +741,52 @@ GetQoS()<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelPar
</node>
</graph>
</node>
<node id="n12" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="107.4609375" width="245.75895182291674" x="-244.41520182291674" y="398.5357666015615"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="245.75895182291674" x="0.0" xml:space="preserve" y="0.0">Odometry</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="74.74462890625" x="-12.372314453125" xml:space="preserve" y="0.0">Folder 13</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n12:">
<node id="n12::n0">
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="56.0" width="215.75895182291674" x="-229.41520182291674" y="434.9967041015615"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#B7C9E3" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="208.052734375" x="3.8531087239583712" xml:space="preserve" y="4.0">ROS2OdometrySensorComponent</y:NodeLabel>
<y:NodeLabel alignment="left" autoSizePolicy="content" configuration="com.yworks.entityRelationship.label.attributes" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="top" visible="true" width="15.443359375" x="2.0" xml:space="preserve" y="29.96875">...<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParameter><y:ErdAttributesNodeLabelModelParameter/></y:ModelParameter></y:NodeLabel>
<y:StyleProperties>
<y:Property class="java.lang.Boolean" name="y.view.ShadowNodePainter.SHADOW_PAINTING" value="true"/>
</y:StyleProperties>
</y:GenericNode>
</data>
</node>
</graph>
</node>
<edge id="n1::e0" source="n1::n2" target="n1::n1">
<data key="d10">
<y:PolyLineEdge>
Expand Down Expand Up @@ -964,6 +1010,20 @@ GetQoS()<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelPar
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n5::n0" target="n12::n0">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="16.79817708333337" y="44.351318359374844"/>
<y:Point x="16.79817708333337" y="462.9967041015615"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="white_delta" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources/>
Expand Down

0 comments on commit 30968ea

Please sign in to comment.