7#include <SimoxUtility/algorithm/get_map_keys_values.h>
8#include <SimoxUtility/algorithm/string/string_tools.h>
9#include <SimoxUtility/math/pose/interpolate.h>
23#include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h>
31 template <
class... Args>
33 TransformHelper::_lookupTransform(
34 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
37 const std::vector<std::string> tfChain =
38 _buildTransformChain(localizationCoreSegment, query);
41 return {.transform = {.header = query.header},
43 .errorMessage =
"Cannot create tf lookup chain '" + query.header.parentFrame +
44 " -> " + query.header.frame +
"' for robot `" +
45 query.header.agent +
"`."};
48 const std::vector<Eigen::Isometry3f> transforms = _obtainTransforms(
49 localizationCoreSegment, tfChain, query.header.agent, query.header.timestamp);
51 const std::optional<armem::Time> sanitizedTimestamp =
52 _obtainTimestamp(localizationCoreSegment, query.header.timestamp);
54 if (not sanitizedTimestamp.has_value())
56 return {.transform = {.header = query.header},
58 .errorMessage =
"Error: Issue with timestamp"};
62 auto header = query.header;
68 header.timestamp = sanitizedTimestamp.value();
70 if (transforms.empty())
73 return {.transform = {.header = query.header},
75 .errorMessage =
"Error in TF lookup: '" + query.header.parentFrame +
" -> " +
77 "'. No memory data in time range. Reference time " +
78 sanitizedTimestamp.value().toTimeString()};
81 const Eigen::Isometry3f
transform = std::accumulate(transforms.begin(),
83 Eigen::Isometry3f::Identity(),
88 return {.transform = {.header = header, .transform =
transform},
92 template <
class... Args>
94 TransformHelper::_lookupTransformChain(
95 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
98 const std::vector<std::string> tfChain =
99 _buildTransformChain(localizationCoreSegment, query);
103 return {.header = query.header,
104 .transforms = std::vector<Eigen::Isometry3f>{},
106 .errorMessage =
"Cannot create tf lookup chain '" + query.header.parentFrame +
107 " -> " + query.header.frame +
"' for robot `" +
108 query.header.agent +
"`."};
111 const std::vector<Eigen::Isometry3f> transforms = _obtainTransforms(
112 localizationCoreSegment, tfChain, query.header.agent, query.header.timestamp);
113 if (transforms.empty())
116 return {.header = query.header,
119 .errorMessage =
"Error in TF lookup: '" + query.header.parentFrame +
" -> " +
121 "'. No memory data in time range. Reference time " +
122 query.header.timestamp.toTimeString()};
128 return {.header = query.header,
129 .transforms = transforms,
133 template <
class... Args>
134 std::vector<std::string>
135 TransformHelper::_buildTransformChain(
136 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
139 ARMARX_DEBUG <<
"Building transform chain for robot `" << query.header.agent <<
"`.";
141 std::vector<std::string> chain;
143 const auto& agentProviderSegment =
146 const std::vector<std::string> tfs = agentProviderSegment.getEntityNames();
149 std::map<std::string, std::string> tfLookup;
151 for (
const std::string& tf : tfs)
153 const auto frames = simox::alg::split(tf,
",");
156 tfLookup[frames.front()] = frames.back();
159 std::string currentFrame = query.header.parentFrame;
160 chain.push_back(currentFrame);
166 std::set<std::string> visited{currentFrame};
167 while (tfLookup.count(currentFrame) > 0 and currentFrame != query.header.frame)
169 currentFrame = tfLookup.at(currentFrame);
171 if (not visited.insert(currentFrame).second)
174 << query.header.agent <<
"` at frame '" << currentFrame
175 <<
"'. Cannot create tf lookup chain '" << query.header.parentFrame
176 <<
" -> " << query.header.frame <<
"'.";
180 chain.push_back(currentFrame);
185 if (chain.empty() or chain.back() != query.header.frame)
188 << query.header.parentFrame <<
" -> " << query.header.frame
189 <<
"' for robot `" + query.header.agent +
"`.";
193 std::vector<std::string> frameChain;
194 for (
size_t i = 0; i < (chain.size() - 1); i++)
196 frameChain.push_back(chain.at(i) +
"," + chain.at(i + 1));
202 template <
class... Args>
203 std::optional<armarx::core::time::DateTime>
204 TransformHelper::_obtainTimestamp(
205 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
210 std::optional<int64_t> timeSinceEpochUs = std::nullopt;
213 [&timeSinceEpochUs, &
timestamp](
const auto& entity)
215 auto snapshot = entity.findLatestSnapshotBeforeOrAt(
timestamp);
217 if (snapshot ==
nullptr)
222 if (not snapshot->hasInstance(0))
227 const armem::wm::EntityInstance& item = snapshot->getInstance(0);
228 const auto tf = _convertEntityToTransform(item);
230 const auto& dataTs = tf.header.timestamp;
233 std::max(timeSinceEpochUs.value_or(0), dataTs.toMicroSecondsSinceEpoch());
236 if (not timeSinceEpochUs.has_value())
242 timeSinceEpochUs = std::min(timeSinceEpochUs.value(),
timestamp.toMicroSecondsSinceEpoch());
244 return armarx::core::time::DateTime(
248 template <
class... Args>
249 std::vector<Eigen::Isometry3f>
250 TransformHelper::_obtainTransforms(
251 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
252 const std::vector<std::string>& tfChain,
253 const std::string& agent,
259 ARMARX_DEBUG <<
"Entities: " << agentProviderSegment.getEntityNames();
263 std::vector<Eigen::Isometry3f> transforms;
264 transforms.reserve(tfChain.size());
265 std::transform(tfChain.begin(),
267 std::back_inserter(transforms),
268 [&](
const std::string& entityName) {
269 return _obtainTransform(entityName, agentProviderSegment, timestamp);
273 catch (
const armem::error::MissingEntry& missingEntryError)
277 catch (const ::armarx::exceptions::local::ExpressionException& ex)
281 catch (const ::armarx::LocalException& ex)
293 template <
class... Args>
295 TransformHelper::_obtainTransform(
296 const std::string& entityName,
297 const armem::base::ProviderSegmentBase<Args...>& agentProviderSegment,
301 const auto& entity = agentProviderSegment.
getEntity(entityName);
312 std::vector<::armarx::armem::robot_state::localization::Transform> transforms;
314 auto snapshot = entity.findLatestSnapshotBeforeOrAt(
timestamp);
323 <<
"Snapshot at time " << snapshot->time() <<
" for entity '" << entityName
324 <<
"' has no instances";
325 transforms.push_back(_convertEntityToTransform(snapshot->getInstance(0)));
328 if (transforms.size() > 1)
331 return transforms.front().transform;
334 const auto p = _interpolateTransform(transforms,
timestamp);
340 if (transforms.empty())
344 throw armem::error::MissingEntry(
"foo",
"bar",
"foo2",
"bar2", 0);
349 return transforms.front().transform;
352 ::armarx::armem::robot_state::localization::Transform
353 TransformHelper::_convertEntityToTransform(
const armem::wm::EntityInstance& item)
355 arondto::Transform aronTransform;
356 aronTransform.fromAron(item.
data());
358 ::armarx::armem::robot_state::localization::Transform
transform;
366 const std::vector<::armarx::armem::robot_state::localization::Transform>& transforms,
372 const auto it = std::upper_bound(transforms.begin(), transforms.end(),
timestamp, comp);
377 const auto poseNextIt = std::find_if(transforms.begin(), transforms.end(), timestampBeyond);
385 TransformHelper::_interpolateTransform(
386 const std::vector<::armarx::armem::robot_state::localization::Transform>& queue,
394 <<
"The queue has to contain at least two items to perform a lookup";
397 <<
"Q front " << queue.front().header.timestamp <<
" "
398 <<
"Q back " << queue.back().header.timestamp <<
" "
404 <<
"Cannot perform lookup into the future!";
408 <<
"Cannot perform lookup. Timestamp too old";
417 const auto posePreIt = poseNextIt - 1;
423 (
timestamp - posePreIt->header.timestamp).toMicroSecondsDouble() /
424 (poseNextIt->header.timestamp - posePreIt->header.timestamp).toMicroSecondsDouble();
428 return simox::math::interpolatePose(
429 posePreIt->transform, poseNextIt->transform,
static_cast<float>(t));
436 return _lookupTransform(localizationCoreSegment,
query);
443 return _lookupTransform(localizationCoreSegment,
query);
450 return _lookupTransformChain(localizationCoreSegment,
query);
458 return _lookupTransformChain(localizationCoreSegment,
query);
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
const DataT & data() const
EntityT & getEntity(const std::string &name)
Client-side working memory core segment.
static Duration MicroSeconds(std::int64_t microSeconds)
Constructs a duration in microseconds.
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_INFO
The normal logging level.
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
const std::string localizationCoreSegment
auto findFirstElementAfter(const std::vector<::armarx::armem::robot_state::localization::Transform > &transforms, const armem::Time ×tamp)
void fromAron(const arondto::Transform &dto, Transform &bo)
armarx::core::time::DateTime Time
std::string GetHandledExceptionString()
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT > >
Convenience function (with less typing) to transform a container of type InputT into the same contain...