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(
34 const TransformQuery& query)
36 const std::vector<std::string> tfChain =
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(
50 const std::optional<armem::Time> sanitizedTimestamp =
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(),
87 return {.transform = {.header = header, .transform =
transform},
91 template <
class... Args>
93 TransformHelper::_lookupTransformChain(
95 const TransformQuery& query)
97 const std::vector<std::string> tfChain =
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(
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(
136 const TransformQuery& query)
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)
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(
193 std::optional<int64_t> timeSinceEpochUs = std::nullopt;
196 [&timeSinceEpochUs, ×tamp](
const auto& entity)
198 auto snapshot = entity.findLatestSnapshotBeforeOrAt(timestamp);
200 if (snapshot ==
nullptr)
205 if (not snapshot->hasInstance(0))
211 const auto tf = _convertEntityToTransform(item);
216 std::max(timeSinceEpochUs.value_or(0), dataTs.toMicroSecondsSinceEpoch());
219 if (not timeSinceEpochUs.has_value())
231 template <
class... Args>
232 std::vector<Eigen::Isometry3f>
233 TransformHelper::_obtainTransforms(
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());
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);
298 ARMARX_CHECK(snapshot) <<
"No snapshot found before or at time " << timestamp;
299 transforms.push_back(_convertEntityToTransform(snapshot->getInstance(0)));
302 if (transforms.size() > 1)
305 return transforms.front().transform;
308 const auto p = _interpolateTransform(transforms, timestamp);
314 if (transforms.empty())
318 throw armem::error::MissingEntry(
"foo",
"bar",
"foo2",
"bar2", 0);
323 return transforms.front().transform;
329 arondto::Transform aronTransform;
330 aronTransform.fromAron(item.data());
340 const std::vector<::armarx::armem::robot_state::localization::Transform>& transforms,
344 {
return transform.header.timestamp < timestamp; };
346 const auto it = std::upper_bound(transforms.begin(), transforms.end(), timestamp, comp);
349 {
return transform.header.timestamp > timestamp; };
351 const auto poseNextIt = std::find_if(transforms.begin(), transforms.end(), timestampBeyond);
359 TransformHelper::_interpolateTransform(
360 const std::vector<::armarx::armem::robot_state::localization::Transform>& queue,
368 <<
"The queue has to contain at least two items to perform a lookup";
371 <<
"Q front " << queue.front().header.timestamp <<
" "
372 <<
"Q back " << queue.back().header.timestamp <<
" "
373 <<
"query timestamp " << timestamp;
378 <<
"Cannot perform lookup into the future!";
381 ARMARX_CHECK(queue.front().header.timestamp < timestamp)
382 <<
"Cannot perform lookup. Timestamp too old";
391 const auto posePreIt = poseNextIt - 1;
397 (timestamp - posePreIt->header.timestamp).toMicroSecondsDouble() /
398 (poseNextIt->header.timestamp - posePreIt->header.timestamp).toMicroSecondsDouble();
402 return simox::math::interpolatePose(
403 posePreIt->transform, poseNextIt->transform,
static_cast<float>(t));