NavigateToNamedLocation.cpp
Go to the documentation of this file.
2
3#include <map>
4#include <optional>
5#include <set>
6#include <sstream>
7#include <string>
8#include <vector>
9
10#include <SimoxUtility/algorithm/string/string_tools.h>
11
17
27
32#include <armarx/navigation/skills/aron/NavigateToLocation.aron.generated.h>
34
35#include <range/v3/action.hpp>
36#include <range/v3/algorithm/contains.hpp>
37#include <range/v3/range/conversion.hpp>
38#include <range/v3/view/concat.hpp>
39#include <range/v3/view/filter.hpp>
40#include <range/v3/view/map.hpp>
41#include <range/v3/view/transform.hpp>
42
44{
45
46 armarx::skills::SkillDescription
48 {
49 ParamType defaultParameters;
50
51 defaultParameters.useObjectLocations = true;
52
53 defaultParameters.navigateToLocation.fromAron(
55 defaultParameters.navigateToLocation.location = "<Resolved according to 'locationName'>";
56
57 std::stringstream description;
58 description << "Resolve a named location and navigate to it."
59 << "\n\n`locationName`: The location's human-readable name."
60 << "\n\n`minMatchRatio`: Minimum match ratio required to accept a match "
61 "between a location's name and `locationName`."
62 << "\n\nThe parameters `navigateToLocation` are passed to the sub skill "
63 "`NavigateToLocation`, except for `navigateToLocation.location`, which is "
64 "set to the location that `locationName` is resolved to.";
65
68 .description = description.str(),
69 .rootProfileDefaults = defaultParameters.toAron(),
71 .parametersType = Params::ToAronType(),
72 };
73 }
74
79
81 const Services& services) :
82 Base(GetSkillDescription()), services(services), properties(properties)
83 {
84 ARMARX_CHECK_NOT_NULL(services.locationReader);
85 ARMARX_CHECK_NOT_NULL(services.objectClassReader);
86 }
87
89 NavigateToNamedLocation::main(const Base::SpecializedMainInput& in)
90 {
92
93 struct ResolvedLocation
94 {
95 std::string name;
96 std::string provider;
97 };
98
99 // Try to match requested location name to an object. If there is a (perfect) match, we check whether there is a
100 // location associated with the object (`inFrontOf`).
101 const auto resolvedLocationFromObjectFn = [&]() -> std::optional<ResolvedLocation>
102 {
103 // query object poses from memory
104 const auto objectPoseStorage = services.objectPoseClient.getObjectPoseStorage();
105 const auto objectPosesIce = objectPoseStorage->getObjectPoses();
106
107 const std::map<armarx::ObjectID, armem::clazz::ObjectClass> objectClasses =
109
110 objpose::ObjectPoseSeq objectPoses;
111 objpose::fromIce(objectPosesIce, objectPoses);
112
113 // search for matching objects (helper lambda)
114 const auto isMatchingObject = [&](const objpose::ObjectPose& pose) -> bool
115 {
116 // make sure that the classes exist.
117 if(not ranges::contains(ranges::views::keys(objectClasses),
118 pose.objectID.getClassID(),
119 [](const armarx::ObjectID& oid)
120 { return oid.getClassID(); }))
121 {
122 ARMARX_VERBOSE << "Requested to match object but there is no match";
123 return false;
124 }
125
126 const auto& objectClass = objectClasses.at(pose.objectID.getClassID());
127 const auto& recognizedNames = objectClass.names.recognized;
128
129 // perform case-insensitive comparison
130 return ranges::contains(recognizedNames,
131 simox::alg::to_lower(in.parameters.locationName),
132 simox::alg::to_lower);
133 };
134
135 // find all matching objects
136 const std::vector<objpose::ObjectPose> matchingObjects =
137 objectPoses | ranges::views::filter(isMatchingObject) | ranges::to<std::vector>();
138
139 ARMARX_INFO << "Found " << matchingObjects.size() << " objects matching location name '"
140 << in.parameters.locationName << "'.";
141
142 const auto locations = services.locationReader->locations();
143
144 const auto getMatchingLocation = [&](const objpose::ObjectPose& pose)
145 -> std::vector<armarx::navigation::core::Location>
146 {
147 const std::string locationName = pose.objectID.getClassID().str() + "/inFrontOf";
148 const std::string locationNameWithInstance =
149 pose.objectID.getClassID().str() + "/inFrontOf:" + pose.objectID.instanceName();
150
151 const auto matchingLocations =
152 core::util::findMatchingLocations(locations, locationName, std::nullopt);
153 ARMARX_INFO << "Found " << matchingLocations.size() << " locations matching name "
154 << QUOTED(locationName) << ".";
155
156 const auto matchingLocationsWithInstance =
158 locationNameWithInstance,
159 std::nullopt);
160 ARMARX_INFO << "Found " << matchingLocationsWithInstance.size()
161 << " locations matching name " << QUOTED(locationNameWithInstance)
162 << ".";
163
164 const auto appendLocationInstanceName =
165 [&pose](core::Location loc) -> core::Location
166 {
167 loc.name += ":" + pose.objectID.instanceName();
168 return loc;
169 };
170
171 // For those matching locations on class level, append the instance name
172 const auto matchingLocationsWithAppendedInstance =
173 matchingLocations | ranges::views::transform(appendLocationInstanceName) |
174 ranges::to<std::vector>();
175
176 return ranges::views::concat(matchingLocationsWithAppendedInstance,
177 matchingLocationsWithInstance) |
178 ranges::to<std::vector>();
179 };
180
181 // obtain all locations associated with matching objects
182 std::vector<armarx::navigation::core::Location> associatedLocations;
183 for (const auto& matchingObject : matchingObjects)
184 {
185 const auto locationsForObject = getMatchingLocation(matchingObject);
186 for (const core::Location& loc : locationsForObject)
187 {
188 ARMARX_INFO << "Found associated location " << QUOTED(loc.name)
189 << " for matching object " << QUOTED(matchingObject.objectID.str())
190 << ".";
191 associatedLocations.push_back(loc);
192 }
193 }
194
195 ARMARX_INFO << "Found total of " << associatedLocations.size()
196 << " associated locations for matching objects.";
197
198 for (const auto& loc : associatedLocations)
199 {
200 ARMARX_INFO << "- " << loc.name;
201 }
202
203 if (associatedLocations.empty())
204 {
205 return std::nullopt;
206 }
207
208 if (associatedLocations.size() > 1)
209 {
210 ARMARX_WARNING << "Multiple associated locations found for matching objects. Using "
211 "the first one.";
212 }
213
214 const auto& firstLocation = *associatedLocations.begin();
215
216 return ResolvedLocation{.name = firstLocation.name, .provider = firstLocation.provider};
217 };
218
219
220 // Try to match requested location name to a named location in the location graph.
221 const auto resolvedLocationFromKnownLocationsFn = [&]() -> std::optional<ResolvedLocation>
222 {
223 std::stringstream log;
224 auto matchResult =
225 services.locationReader->resolveLocationName(in.parameters.locationName,
226 in.parameters.minMatchRatio,
227 &log);
228
229 ARMARX_INFO << log.str();
230 ARMARX_CHECK(matchResult.resolved.has_value() xor matchResult.errorMessage.has_value());
231
232 if (matchResult.errorMessage.has_value())
233 {
234 ARMARX_INFO << "Failed to resolve location name '" << in.parameters.locationName
235 << "'. Reason: " << matchResult.errorMessage.value();
236 return std::nullopt;
237 }
238
239 ARMARX_CHECK(matchResult.resolved.has_value());
240 return ResolvedLocation{.name = matchResult.resolved->locationId.entityName,
241 .provider =
242 matchResult.resolved->locationId.providerSegmentName};
243 };
244
245 // Decide which resolution to use
246 std::optional<ResolvedLocation> resolvedLocation;
247
248 if (in.parameters.useObjectLocations)
249 {
250 const auto resolvedLocationFromObject = resolvedLocationFromObjectFn();
251 if (resolvedLocationFromObject.has_value())
252 {
253 resolvedLocation = resolvedLocationFromObject;
254 ARMARX_INFO << "Resolved location name '" << in.parameters.locationName
255 << "' to location associated with matching object.";
256 }
257 }
258
259 if (not resolvedLocation.has_value())
260 {
261 const auto resolvedLocationFromKnownLocations = resolvedLocationFromKnownLocationsFn();
262
263 if (resolvedLocationFromKnownLocations.has_value())
264 {
265 resolvedLocation = resolvedLocationFromKnownLocations;
266 ARMARX_INFO << "Resolved location name '" << in.parameters.locationName
267 << "' to known location: "
268 << QUOTED(resolvedLocation->provider + "::" + resolvedLocation->name)
269 << ".";
270 }
271 }
272
273 if (not resolvedLocation.has_value())
274 {
275 ARMARX_ERROR << "Could not resolve location name '" << in.parameters.locationName
276 << "' to any location.";
277 return MakeFailedResult();
278 }
279
280 ARMARX_IMPORTANT << "Navigating to location "
281 << QUOTED(resolvedLocation->provider + "::" + resolvedLocation->name)
282 << ".";
283
285
286 // Navigate to location
287 {
288 using ParamsT = arondto::NavigateToLocationParams;
289
290 // pedantic check
291 ARMARX_CHECK(resolvedLocation.has_value());
292
293 auto update = callSubskill<ParamsT>(properties.subSkillIDs.navigateToLocation,
294 [&resolvedLocation, &in](ParamsT& parameters) noexcept
295 {
296 // override the parameters completely
297 parameters = in.parameters.navigateToLocation;
298
299 parameters.location = resolvedLocation->name;
300 parameters.locationProvider =
301 resolvedLocation->provider;
302 });
304 {
305 return MakeFailedResult();
306 }
307 }
308
310
311 return MakeSucceededResult();
312 }
313
314} // namespace armarx::navigation::skills
#define QUOTED(x)
A known object ID of the form "Dataset/ClassName" or "Dataset/ClassName/InstanceName".
Definition ObjectID.h:11
std::map< armarx::ObjectID, armem::clazz::ObjectClass > getAllObjectClasses()
static Duration Hours(std::int64_t hours)
Constructs a duration in hours.
Definition Duration.cpp:120
std::vector< core::Location > locations()
Definition Reader.cpp:53
static armarx::skills::SkillDescription DefaultSkillDescription()
::armarx::skills::SimpleSpecializedSkill< Params > Base
NavigateToNamedLocation(const Properties &properties, const Services &services)
const ObjectPoseStorageInterfacePrx & getObjectPoseStorage() const
Get the object pose storage's proxy.
std::optional< TerminatedSkillStatusUpdate > callSubskill(const SkillID &skillId)
Call a subskill with the given ID and its default parameters.
Definition Skill.cpp:119
static MainResult MakeSucceededResult(aron::data::DictPtr data=nullptr)
Definition Skill.cpp:413
armarx::aron::data::DictPtr parameters
Definition Skill.h:369
SkillDescription description
Definition Skill.h:372
void throwIfSkillShouldTerminate(const std::string &abortedMessage="") const
Definition Skill.cpp:389
static MainResult MakeFailedResult(aron::data::DictPtr data=nullptr)
Definition Skill.cpp:422
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
bool update(mongocxx::collection &coll, const nlohmann::json &query, const nlohmann::json &update)
Definition mongodb.cpp:68
std::vector< core::Location > findMatchingLocations(const std::vector< core::Location > &locations, const std::string &locationName, const std::optional< std::string > &provider)
Definition location.cpp:46
const armarx::skills::SkillID NavigateToNamedLocation
Definition skill_ids.cpp:66
std::vector< ObjectPose > ObjectPoseSeq
void fromIce(const Box &box, simox::OrientedBox< float > &oobb)
bool skillExecutionFailed(const std::optional< armarx::skills::TerminatedSkillStatusUpdate > &update)
This file offers overloads of toIce() and fromIce() functions for STL container types.
This file is part of ArmarX.
This file is part of ArmarX.
armarx::navigation::memory::client::graph::Reader * locationReader
An object pose as stored by the ObjectPoseStorage.
Definition ObjectPose.h:34
A result struct for th main method of a skill.
Definition Skill.h:62
#define ARMARX_TRACE
Definition trace.h:77