6#include <SimoxUtility/algorithm/get_map_keys_values.h>
7#include <SimoxUtility/algorithm/string/string_tools.h>
8#include <SimoxUtility/math/pose/interpolate.h>
22#include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h>
30 template <
class... Args>
32 TransformHelper::_lookupTransform(
33 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
36 const std::vector<std::string> tfChain =
37 _buildTransformChain(localizationCoreSegment, query);
40 return {.transform = {.header = query.header},
42 .errorMessage =
"Cannot create tf lookup chain '" + query.header.parentFrame +
43 " -> " + query.header.frame +
"' for robot `" +
44 query.header.agent +
"`."};
47 const std::vector<Eigen::Isometry3f> transforms = _obtainTransforms(
48 localizationCoreSegment, tfChain, query.header.agent, query.header.timestamp);
50 const std::optional<armem::Time> sanitizedTimestamp =
51 _obtainTimestamp(localizationCoreSegment, query.header.timestamp);
53 if (not sanitizedTimestamp.has_value())
55 return {.transform = {.header = query.header},
57 .errorMessage =
"Error: Issue with timestamp"};
61 auto header = query.header;
67 header.timestamp = sanitizedTimestamp.value();
69 if (transforms.empty())
72 return {.transform = {.header = query.header},
74 .errorMessage =
"Error in TF lookup: '" + query.header.parentFrame +
" -> " +
76 "'. No memory data in time range. Reference time " +
77 sanitizedTimestamp.value().toTimeString()};
80 const Eigen::Isometry3f
transform = std::accumulate(transforms.begin(),
82 Eigen::Isometry3f::Identity(),
87 return {.transform = {.header = header, .transform =
transform},
91 template <
class... Args>
93 TransformHelper::_lookupTransformChain(
94 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
97 const std::vector<std::string> tfChain =
98 _buildTransformChain(localizationCoreSegment, query);
102 return {.header = query.header,
103 .transforms = std::vector<Eigen::Isometry3f>{},
105 .errorMessage =
"Cannot create tf lookup chain '" + query.header.parentFrame +
106 " -> " + query.header.frame +
"' for robot `" +
107 query.header.agent +
"`."};
110 const std::vector<Eigen::Isometry3f> transforms = _obtainTransforms(
111 localizationCoreSegment, tfChain, query.header.agent, query.header.timestamp);
112 if (transforms.empty())
115 return {.header = query.header,
118 .errorMessage =
"Error in TF lookup: '" + query.header.parentFrame +
" -> " +
120 "'. No memory data in time range. Reference time " +
121 query.header.timestamp.toTimeString()};
127 return {.header = query.header,
128 .transforms = transforms,
132 template <
class... Args>
133 std::vector<std::string>
134 TransformHelper::_buildTransformChain(
135 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
138 ARMARX_DEBUG <<
"Building transform chain for robot `" << query.header.agent <<
"`.";
140 std::vector<std::string> chain;
142 const auto& agentProviderSegment =
145 const std::vector<std::string> tfs = agentProviderSegment.getEntityNames();
148 std::map<std::string, std::string> tfLookup;
150 for (
const std::string& tf : tfs)
152 const auto frames = simox::alg::split(tf,
",");
155 tfLookup[frames.front()] = frames.back();
158 std::string currentFrame = query.header.parentFrame;
159 chain.push_back(currentFrame);
160 while (tfLookup.count(currentFrame) > 0 and currentFrame != query.header.frame)
162 currentFrame = tfLookup.at(currentFrame);
163 chain.push_back(currentFrame);
168 if (chain.empty() or chain.back() != query.header.frame)
171 << query.header.parentFrame <<
" -> " << query.header.frame
172 <<
"' for robot `" + query.header.agent +
"`.";
176 std::vector<std::string> frameChain;
177 for (
size_t i = 0; i < (chain.size() - 1); i++)
179 frameChain.push_back(chain.at(i) +
"," + chain.at(i + 1));
185 template <
class... Args>
186 std::optional<armarx::core::time::DateTime>
187 TransformHelper::_obtainTimestamp(
188 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
193 std::optional<int64_t> timeSinceEpochUs = std::nullopt;
196 [&timeSinceEpochUs, &
timestamp](
const auto& entity)
198 auto snapshot = entity.findLatestSnapshotBeforeOrAt(
timestamp);
200 if (snapshot ==
nullptr)
205 if (not snapshot->hasInstance(0))
210 const armem::wm::EntityInstance& item = snapshot->getInstance(0);
211 const auto tf = _convertEntityToTransform(item);
213 const auto& dataTs = tf.header.timestamp;
216 std::max(timeSinceEpochUs.value_or(0), dataTs.toMicroSecondsSinceEpoch());
219 if (not timeSinceEpochUs.has_value())
225 timeSinceEpochUs = std::min(timeSinceEpochUs.value(),
timestamp.toMicroSecondsSinceEpoch());
227 return armarx::core::time::DateTime(
231 template <
class... Args>
232 std::vector<Eigen::Isometry3f>
233 TransformHelper::_obtainTransforms(
234 const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment,
235 const std::vector<std::string>& tfChain,
236 const std::string& agent,
242 ARMARX_DEBUG <<
"Entities: " << agentProviderSegment.getEntityNames();
246 std::vector<Eigen::Isometry3f> transforms;
247 transforms.reserve(tfChain.size());
248 std::transform(tfChain.begin(),
250 std::back_inserter(transforms),
251 [&](
const std::string& entityName) {
252 return _obtainTransform(entityName, agentProviderSegment, timestamp);
256 catch (
const armem::error::MissingEntry& missingEntryError)
260 catch (const ::armarx::exceptions::local::ExpressionException& ex)
264 catch (const ::armarx::LocalException& ex)
276 template <
class... Args>
278 TransformHelper::_obtainTransform(
279 const std::string& entityName,
280 const armem::base::ProviderSegmentBase<Args...>& agentProviderSegment,
284 const auto& entity = agentProviderSegment.
getEntity(entityName);
295 std::vector<::armarx::armem::robot_state::localization::Transform> transforms;
297 auto snapshot = entity.findLatestSnapshotBeforeOrAt(
timestamp);
306 <<
"Snapshot at time " << snapshot->time() <<
" for entity '" << entityName
307 <<
"' has no instances";
308 transforms.push_back(_convertEntityToTransform(snapshot->getInstance(0)));
311 if (transforms.size() > 1)
314 return transforms.front().transform;
317 const auto p = _interpolateTransform(transforms,
timestamp);
323 if (transforms.empty())
327 throw armem::error::MissingEntry(
"foo",
"bar",
"foo2",
"bar2", 0);
332 return transforms.front().transform;
335 ::armarx::armem::robot_state::localization::Transform
336 TransformHelper::_convertEntityToTransform(
const armem::wm::EntityInstance& item)
338 arondto::Transform aronTransform;
339 aronTransform.fromAron(item.
data());
341 ::armarx::armem::robot_state::localization::Transform
transform;
349 const std::vector<::armarx::armem::robot_state::localization::Transform>& transforms,
355 const auto it = std::upper_bound(transforms.begin(), transforms.end(),
timestamp, comp);
360 const auto poseNextIt = std::find_if(transforms.begin(), transforms.end(), timestampBeyond);
368 TransformHelper::_interpolateTransform(
369 const std::vector<::armarx::armem::robot_state::localization::Transform>& queue,
377 <<
"The queue has to contain at least two items to perform a lookup";
380 <<
"Q front " << queue.front().header.timestamp <<
" "
381 <<
"Q back " << queue.back().header.timestamp <<
" "
387 <<
"Cannot perform lookup into the future!";
391 <<
"Cannot perform lookup. Timestamp too old";
400 const auto posePreIt = poseNextIt - 1;
406 (
timestamp - posePreIt->header.timestamp).toMicroSecondsDouble() /
407 (poseNextIt->header.timestamp - posePreIt->header.timestamp).toMicroSecondsDouble();
411 return simox::math::interpolatePose(
412 posePreIt->transform, poseNextIt->transform,
static_cast<float>(t));
419 return _lookupTransform(localizationCoreSegment,
query);
426 return _lookupTransform(localizationCoreSegment,
query);
433 return _lookupTransformChain(localizationCoreSegment,
query);
441 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_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...