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 +
75 " -> " + query.header.frame +
76 "'. No memory data in time range. Reference time " + sanitizedTimestamp.value().toTimeString()};
79 const Eigen::Isometry3f
transform = std::accumulate(
84 return {.transform = {.header = header, .transform =
transform},
88 template <
class... Args>
90 TransformHelper::_lookupTransformChain(
92 const TransformQuery& query)
94 const std::vector<std::string> tfChain =
99 return {.header = query.header,
100 .transforms = std::vector<Eigen::Isometry3f>{},
102 .errorMessage =
"Cannot create tf lookup chain '" + query.header.parentFrame +
103 " -> " + query.header.frame +
"' for robot `" +
104 query.header.agent +
"`."};
107 const std::vector<Eigen::Isometry3f> transforms = _obtainTransforms(
109 if (transforms.empty())
114 .header = query.header,
117 .errorMessage =
"Error in TF lookup: '" + query.header.parentFrame +
" -> " +
118 query.header.frame +
"'. No memory data in time range. Reference time " + query.header.timestamp.toTimeString()
125 return {.header = query.header,
126 .transforms = transforms,
130 template <
class... Args>
131 std::vector<std::string>
132 TransformHelper::_buildTransformChain(
134 const TransformQuery& query)
136 ARMARX_DEBUG <<
"Building transform chain for robot `" << query.header.agent <<
"`.";
138 std::vector<std::string> chain;
140 const auto& agentProviderSegment =
143 const std::vector<std::string> tfs = agentProviderSegment.getEntityNames();
146 std::map<std::string, std::string> tfLookup;
148 for (
const std::string& tf : tfs)
153 tfLookup[frames.front()] = frames.back();
156 std::string currentFrame = query.header.parentFrame;
157 chain.push_back(currentFrame);
158 while (tfLookup.count(currentFrame) > 0 and currentFrame != query.header.frame)
160 currentFrame = tfLookup.at(currentFrame);
161 chain.push_back(currentFrame);
166 if (chain.empty() or chain.back() != query.header.frame)
169 << query.header.parentFrame <<
" -> " << query.header.frame
170 <<
"' for robot `" + query.header.agent +
"`.";
174 std::vector<std::string> frameChain;
175 for (
size_t i = 0; i < (chain.size() - 1); i++)
177 frameChain.push_back(chain.at(i) +
"," + chain.at(i + 1));
183 template <
class... Args>
184 std::optional<armarx::core::time::DateTime>
185 TransformHelper::_obtainTimestamp(
191 std::optional<int64_t> timeSinceEpochUs = std::nullopt;
194 [&timeSinceEpochUs, ×tamp](
const auto& entity)
196 auto snapshot = entity.findLatestSnapshotBeforeOrAt(timestamp);
198 if (snapshot ==
nullptr)
203 if (not snapshot->hasInstance(0))
209 const auto tf = _convertEntityToTransform(item);
214 std::max(timeSinceEpochUs.value_or(0), dataTs.toMicroSecondsSinceEpoch());
217 if (not timeSinceEpochUs.has_value())
229 template <
class... Args>
230 std::vector<Eigen::Isometry3f>
231 TransformHelper::_obtainTransforms(
233 const std::vector<std::string>& tfChain,
234 const std::string& agent,
240 ARMARX_DEBUG <<
"Entities: " << agentProviderSegment.getEntityNames();
244 std::vector<Eigen::Isometry3f> transforms;
245 transforms.reserve(tfChain.size());
248 std::back_inserter(transforms),
249 [&](
const std::string& entityName) {
250 return _obtainTransform(entityName, agentProviderSegment, timestamp);
254 catch (
const armem::error::MissingEntry& missingEntryError)
258 catch (const ::armarx::exceptions::local::ExpressionException& ex)
262 catch (const ::armarx::LocalException& ex)
274 template <
class... Args>
276 TransformHelper::_obtainTransform(
277 const std::string& entityName,
278 const armem::base::ProviderSegmentBase<Args...>& agentProviderSegment,
282 const auto& entity = agentProviderSegment.getEntity(entityName);
293 std::vector<::armarx::armem::robot_state::localization::Transform> transforms;
295 auto snapshot = entity.findLatestSnapshotBeforeOrAt(timestamp);
296 ARMARX_CHECK(snapshot) <<
"No snapshot found before or at time " << timestamp;
297 transforms.push_back(_convertEntityToTransform(snapshot->getInstance(0)));
300 if (transforms.size() > 1)
303 return transforms.front().transform;
306 const auto p = _interpolateTransform(transforms, timestamp);
312 if (transforms.empty())
316 throw armem::error::MissingEntry(
"foo",
"bar",
"foo2",
"bar2", 0);
321 return transforms.front().transform;
327 arondto::Transform aronTransform;
328 aronTransform.fromAron(item.data());
341 {
return transform.header.timestamp < timestamp; };
343 const auto it = std::upper_bound(transforms.begin(), transforms.end(), timestamp, comp);
346 {
return transform.header.timestamp > timestamp; };
348 const auto poseNextIt = std::find_if(transforms.begin(), transforms.end(), timestampBeyond);
356 TransformHelper::_interpolateTransform(
357 const std::vector<::armarx::armem::robot_state::localization::Transform>& queue,
365 <<
"The queue has to contain at least two items to perform a lookup";
368 <<
"Q front " << queue.front().header.timestamp <<
" "
369 <<
"Q back " << queue.back().header.timestamp <<
" "
370 <<
"query timestamp " << timestamp;
375 <<
"Cannot perform lookup into the future!";
378 ARMARX_CHECK(queue.front().header.timestamp < timestamp)
379 <<
"Cannot perform lookup. Timestamp too old";
388 const auto posePreIt = poseNextIt - 1;
394 (timestamp - posePreIt->header.timestamp).toMicroSecondsDouble() /
395 (poseNextIt->header.timestamp - posePreIt->header.timestamp).toMicroSecondsDouble();
399 return simox::math::interpolatePose(
400 posePreIt->transform, poseNextIt->transform,
static_cast<float>(t));