diff --git a/source/RobotAPI/libraries/armem_robot_mapping/CMakeLists.txt b/source/RobotAPI/libraries/armem_robot_mapping/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..4e54b03872870436f527d2f1be8bac72ae09ad7e --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_mapping/CMakeLists.txt @@ -0,0 +1,35 @@ +set(LIB_NAME armem_robot_mapping) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +armarx_add_library( + LIBS + # ArmarX + ArmarXCore + # This package + RobotAPICore + RobotAPI::libraries::armem + # TODO(fabian.reister): remove this dependency! + RobotAPI::armem_robot_localization + # System / External + Eigen3::Eigen + HEADERS + ./aron_conversions.h + ./MappingDataWriter.h + ./MappingDataReader.h + SOURCES + ./aron_conversions.cpp + ./MappingDataWriter.cpp +) + + +armarx_enable_aron_file_generation_for_target( + TARGET_NAME + "${LIB_NAME}" + ARON_FILES + aron/LaserScan.xml +) + + +add_library(RobotAPI::armem_robot_mapping ALIAS armem_robot_mapping) diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.cpp b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h new file mode 100644 index 0000000000000000000000000000000000000000..37668f211ea4df74284a1434c6eed4ab1ef3f4fb --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h @@ -0,0 +1,105 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects:: + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <RobotAPI/libraries/armem/client/Reader.h> + +#include "MemoryConnector.h" + +namespace armarx::armem +{ + + + + /** + * @defgroup Component-ExampleClient ExampleClient + * @ingroup RobotAPI-Components + * A description of the component ExampleClient. + * + * @class ExampleClient + * @ingroup Component-ExampleClient + * @brief Brief description of class ExampleClient. + * + * Detailed description of class ExampleClient. + */ + class MappingDataReader : + virtual public armarx::MemoryConnector + { + public: + MappingDataReader(ManagedIceObject& component); + + ~MappingDataReader() override; + + void connect() override; + + + struct Query + { + std::string agentName; + + TimeRange timeRange; + + // if empty, all sensors will be queried + std::vector<std::string> sensorList; + + }; + + struct Result + { + + }; + + Result queryData(const Query& query) const override; + + + void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def) override; + + const std::string& getPropertyPrefix() const override + { + return propertyPrefix; + } + + private: + std::vector<std::string> buildTransformChain(const armem::Memory& memory, + const TransformQuery& query) const; + + std::vector<Eigen::Affine3f> obtainTransforms(const armem::Memory& memory, + const std::vector<std::string>& tfChain, + const std::string& agent, const std::int64_t& timestamp) const; + + Eigen::Affine3f obtainTransform(const std::string& entityName, const armem::ProviderSegment& agentProviderSegment, int64_t timestamp) const; + + + armem::client::Reader memoryReader; + + // Properties + struct Properties + { + std::string memoryName = "RobotState"; + std::string localizationMemoryName = "Localization"; + } properties; + + + const std::string propertyPrefix = "mem.localization.read."; + }; +} // namespace armarx diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.cpp b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..37f85182fd7339d013a6611d34157bc1d2c2f3dc --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.cpp @@ -0,0 +1,90 @@ +#include "MappingDataWriter.h" + +#include "RobotAPI/libraries/armem_robot_mapping/aron_conversions.h" +#include <RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.aron.generated.h> + + +namespace armarx::armem { + +MappingDataWriter::MappingDataWriter(ManagedIceObject &component) + : MemoryConnector(component) {} + +MappingDataWriter::~MappingDataWriter() = default; + +void MappingDataWriter::registerPropertyDefinitions( + armarx::PropertyDefinitionsPtr &def) { + ARMARX_DEBUG << "TransformWriter: registerPropertyDefinitions"; + + MemoryConnector::registerPropertyDefinitions(def); + + const std::string prefix = getPropertyPrefix(); + + def->optional(properties.mappingMemoryName, prefix + "MappingMemoryName", + "Name of the mapping memory core segment to use."); + + def->optional(properties.memoryName, prefix + "MemoryName"); +} + + void MappingDataWriter::connect() + { + // Wait for the memory to become available and add it as dependency. + ARMARX_IMPORTANT << "MappingDataWriter: Waiting for memory '" << properties.memoryName + << "' ..."; + auto result = useMemory(properties.memoryName); + if (not result.success) + { + ARMARX_ERROR << result.errorMessage; + return; + } + + ARMARX_IMPORTANT << "TransformWriter: Connected to memory '" << properties.memoryName; + + memoryWriter.setWritingMemory(result.proxy); + } + + +bool MappingDataWriter::storeSensorData(const LaserScan &laserScan, + const std::string &frame, + const std::string &agentName, + const std::int64_t ×tamp) { + std::lock_guard g{memoryWriterMutex}; + + const auto result = + memoryWriter.addSegment(properties.mappingMemoryName, agentName); + + if (not result.success) { + ARMARX_ERROR << result.errorMessage; + + // TODO(fabian.reister): message + return false; + } + + const auto iceTimestamp = IceUtil::Time::microSeconds(timestamp); + + const auto providerId = armem::MemoryID(result.segmentID); + const auto entityID = + providerId.withEntityName(frame).withTimestamp(iceTimestamp); + + armem::EntityUpdate update; + update.entityID = entityID; + update.timeCreated = armem::Time::now(); + + aron::LaserScanStamped aronSensorData; + toAron(laserScan, timestamp, frame, agentName, aronSensorData); + + update.instancesData = {aronSensorData.toAron()}; + update.timeCreated = iceTimestamp; + + ARMARX_DEBUG << "Committing " << update << " at time " << iceTimestamp; + armem::EntityUpdateResult updateResult = memoryWriter.commit(update); + + ARMARX_DEBUG << updateResult; + + if (not updateResult.success) { + ARMARX_ERROR << updateResult.errorMessage; + } + + return updateResult.success; +} + +} // namespace armarx::armem \ No newline at end of file diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..46a67a4b2cf930535842589dd75ba3074d12c08e --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h @@ -0,0 +1,88 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects:: + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <RobotAPI/interface/units/LaserScannerUnit.h> +#include <mutex> + +#include <RobotAPI/libraries/armem/client/Writer.h> + +// TODO(fabian.reister): move MemoryConnector to armem lib +#include "RobotAPI/libraries/armem_robot_localization/MemoryConnector.h" + + +namespace armarx::armem +{ + + /** + * @defgroup Component-ExampleClient ExampleClient + * @ingroup RobotAPI-Components + * A description of the component ExampleClient. + * + * @class ExampleClient + * @ingroup Component-ExampleClient + * @brief Brief description of class ExampleClient. + * + * Detailed description of class ExampleClient. + */ + class MappingDataWriter : + virtual public armarx::MemoryConnector + { + public: + MappingDataWriter(ManagedIceObject& component); + ~MappingDataWriter(); + + void connect(); + + // MappingDataWriterInterface + /// to be called in Component::onConnectComponent + // void connect() override; + + /// to be called in Component::addPropertyDefinitions + void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def) /*override*/; + + bool storeSensorData(const LaserScan& laserScan, const std::string& frame, const std::string& agentName, const std::int64_t& timestamp); + + const std::string& getPropertyPrefix() const override + { + return propertyPrefix; + } + + private: + armem::client::Writer memoryWriter; + + // Properties + struct Properties + { + std::string memoryName = "RobotState"; + std::string mappingMemoryName = "Mapping"; + } properties; + + std::mutex memoryWriterMutex; + + + const std::string propertyPrefix = "mem.mapping.write."; + }; + + +} // namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.xml b/source/RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.xml new file mode 100644 index 0000000000000000000000000000000000000000..140e2aab83c6d8368ea39d1c2a031bce31f0ccd4 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.xml @@ -0,0 +1,74 @@ +<!--Some fancy comment --> +<?xml version="1.0" encoding="UTF-8" ?> +<AronTypeDefinition> + <CodeIncludes> + </CodeIncludes> + <AronIncludes> + </AronIncludes> + + <GenerateTypes> + + <Object name='armarx::aron::LaserScanStep'> + <ObjectChild key='distance'> + <float /> + </ObjectChild> + <ObjectChild key='angle'> + <float /> + </ObjectChild> + <!--ObjectChild key='relativeTime'> + <float /> + </ObjectChild--> + <!--ObjectChild key='intensity'> + <float /> + </ObjectChild--> + </Object> + + <Object name='armarx::aron::LaserScannerInfo'> + <ObjectChild key='device'> + <string /> + </ObjectChild> + <ObjectChild key='frame'> + <string /> + </ObjectChild> + <ObjectChild key='minAngle'> + <string /> + </ObjectChild> + <ObjectChild key='maxAngle'> + <string /> + </ObjectChild> + <ObjectChild key='stepSize'> + <string /> + </ObjectChild> + </Object> + + <Object name="armarx::aron::SensorHeader"> + <ObjectChild key="agent"> + <string/> + </ObjectChild> + <ObjectChild key="frame"> + <string/> + </ObjectChild> + <ObjectChild key='timestamp'> + <Time /> + </ObjectChild> + </Object> + + <Object name="armarx::aron::LaserScan"> + <ObjectChild key='scan'> + <List> + <armarx::aron::LaserScanStep /> + </List> + </ObjectChild> + </Object> + + <Object name='armarx::aron::LaserScanStamped'> + <ObjectChild key="header"> + <armarx::aron::SensorHeader /> + </ObjectChild> + <ObjectChild key='data'> + <armarx::aron::LaserScan /> + </ObjectChild> + </Object> + + </GenerateTypes> +</AronTypeDefinition> \ No newline at end of file diff --git a/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.cpp b/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ea1b429888fee00631e71bc998127722e192352 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.cpp @@ -0,0 +1,117 @@ +#include "aron_conversions.h" + +#include <algorithm> +#include <iterator> + +#include <RobotAPI/interface/units/LaserScannerUnit.h> +#include <RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.aron.generated.h> + +namespace armarx { + +/************ fromAron ************/ + + +LaserScanStep fromAron(const aron::LaserScanStep &aronLaserScanStep) { + LaserScanStep laserScanStep; + + laserScanStep.angle = aronLaserScanStep.angle; + laserScanStep.distance = aronLaserScanStep.distance; + + return laserScanStep; +} + +template <typename T> auto fromAron(const std::vector<T> &v) { + std::vector<decltype(fromAron(T()))> r; + r.reserve(v.size()); + + std::transform(v.begin(), v.end(), std::back_inserter(r), + [](const T &t) { return fromAron(t); }); + + return r; +} + +auto fromAron(const aron::LaserScan &aronLaserScan) { + return fromAron(aronLaserScan.scan); +} + +SensorHeader fromAron(const aron::SensorHeader &aronSensorHeader) { + return {.agent = aronSensorHeader.agent, + .frame = aronSensorHeader.frame, + .timestamp = aronSensorHeader.timestamp}; +} + +void fromAron(const aron::LaserScanStamped &aronLaserScan, + LaserScanStamped &laserScan) { + laserScan.header = fromAron(aronLaserScan.header); + laserScan.data = fromAron(aronLaserScan.data); +} + +void fromAron(const aron::LaserScanStamped &aronLaserScan, LaserScan &laserScan, + std::int64_t ×tamp, std::string &frame, + std::string &agentName) { + const auto header = fromAron(aronLaserScan.header); + + laserScan = fromAron(aronLaserScan.data); + + timestamp = header.timestamp; + frame = header.frame; + agentName = header.agent; +} + + +/************ toAron ************/ + + +template <typename T> auto toAron(const std::vector<T> &v) { + std::vector<decltype(toAron(T()))> r; + r.reserve(v.size()); + + std::transform(v.begin(), v.end(), std::back_inserter(r), + [](const T &t) { return toAron(t); }); + + return r; +} + +aron::LaserScanStep toAron(const LaserScanStep &laserScanStep) { + aron::LaserScanStep aronLaserScan; + + aronLaserScan.angle = laserScanStep.angle; + aronLaserScan.distance = laserScanStep.distance; + + return aronLaserScan; +} + +auto toAron(const LaserScan &laserScan, aron::LaserScan &aronLaserScan) { + aronLaserScan.scan = toAron(laserScan); +} + +aron::SensorHeader toAron(const SensorHeader &sensorHeader) { + aron::SensorHeader aronSensorHeader; + + aronSensorHeader.agent = sensorHeader.agent; + aronSensorHeader.frame = sensorHeader.frame; + aronSensorHeader.timestamp = sensorHeader.timestamp; + + return aronSensorHeader; +} + +void toAron(const LaserScanStamped &laserScanStamped, + aron::LaserScanStamped &aronLaserScanStamped) { + aronLaserScanStamped.header = toAron(laserScanStamped.header); + toAron(laserScanStamped.data, aronLaserScanStamped.data); +} + +void toAron(const LaserScan &laserScan, + const std::int64_t ×tamp, + const std::string &frame, + const std::string &agentName, + aron::LaserScanStamped &aronLaserScanStamped) { + const SensorHeader header{ + .agent = agentName, .frame = frame, .timestamp = timestamp}; + + const LaserScanStamped laserScanStamped{.header = header, .data = laserScan}; + + toAron(laserScanStamped, aronLaserScanStamped); +} + +} // namespace armarx \ No newline at end of file diff --git a/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.h b/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.h new file mode 100644 index 0000000000000000000000000000000000000000..235dd79c5f0d3316e8d4f2d7578438cd85793bdc --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_mapping/aron_conversions.h @@ -0,0 +1,42 @@ +#pragma once + +#include <RobotAPI/interface/units/LaserScannerUnit.h> + +namespace armarx { + +namespace aron { +struct LaserScanStamped; +} // namespace aron + + +struct SensorHeader +{ + std::string agent; + std::string frame; + std::int64_t timestamp; +}; + +struct LaserScanStamped +{ + SensorHeader header; + LaserScan data; +}; + + +void fromAron( + const aron::LaserScanStamped &aronLaserScan, + LaserScan &laserScan, + std::int64_t ×tamp, + std::string &frame, + std::string &agentName); + +void fromAron(const aron::LaserScanStamped& aronLaserScan, LaserScanStamped& laserScan); + +void toAron( + const LaserScan &laserScan, + const std::int64_t ×tamp, + const std::string &frame, + const std::string& agentName, + aron::LaserScanStamped &aronLaserScan); + +} // namespace armarx \ No newline at end of file