Component.cpp
Go to the documentation of this file.
1/**
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package navigation::ArmarXObjects::navigation_skill_provider
17 * @author Fabian Reister ( fabian dot reister at kit dot edu )
18 * @date 2023
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23
24#include "Component.h"
25
26#include <experimental/memory>
27#include <mutex>
28#include <string>
29
34
36
38
54
56{
58 {
59 addPlugin(virtualRobotReaderPlugin);
60 addPlugin(costmapReaderPlugin);
61 addPlugin(graphReaderPlugin);
62 addPlugin(roomsReaderPlugin);
63 addPlugin(objectClassReaderPlugin);
64 }
65
66 const std::string Component::defaultName = skills::constants::NavigationSkillProviderName;
67
68 namespace
69 {
70 void
71 defineProximityFieldParams(const armarx::PropertyDefinitionsPtr& def,
72 const std::string& prefix,
74 {
75 def->optional(params.mode, prefix + ".mode", "The mode of the proximity field.")
77 .map("DirectionDependent",
79 .map("DirectionIndependent",
81 def->optional(params.reduceVelocity,
82 prefix + ".reduceVelocity",
83 "Whether to first reduce the velocity when coming closer to an obstacle "
84 "before stopping.");
85 def->optional(params.safetyDistance,
86 prefix + ".minDistance",
87 "The minimum distance, below which the robot will stop.");
88 def->optional(
89 params.influenceDistance,
90 prefix + ".maxDistance",
91 "The maximum distance, above which the robot will move with the maximum velocity.");
92 def->optional(
93 params.k,
94 prefix + ".k",
95 "A parameter to adjust how the velocity is reduced when coming closer to "
96 "an obstacle. A higher value means the robot will reduce it's speed later.")
97 .setMin(1);
98 def->optional(
99 params.lambda,
100 prefix + ".lambda",
101 "A parameter to adjust how the velocity is reduced when coming closer to an "
102 "obstacle. A higher value means the robot will reduce it's speed earlier.")
103 .setMin(1);
104 }
105 } // namespace
106
109 {
112
113 def->component(navigatorPrx, "navigator");
114
115 def->required(properties.robotName, "RobotName", "Default robot name.");
116
117 auto optionalSubSkillID =
118 [&def](armarx::skills::SkillID& skillID, const std::string& nameBase)
119 {
120 ARMARX_CHECK(skillID.providerId.has_value());
121 def->optional(skillID.providerId->providerName, nameBase + ".providerName");
122 def->optional(skillID.skillName, nameBase + ".skillName");
123 };
124
125 def->required(properties.safetyGuardParams.robotRadius,
126 "p.navigateToLocation.safetyGuardParams.robotRadius",
127 "The robot radius used for distance calculation to the platform.");
128 def->optional(properties.safetyGuardParams.enableHumans,
129 "p.navigateToLocation.safetyGuardParams.enableHumans",
130 "Whether to consider humans for the safety guard.");
131 def->optional(properties.safetyGuardParams.enableLaserScanners,
132 "p.navigateToLocation.safetyGuardParams.enableLaserscanners",
133 "Whether to consider laser scanners for the safety guard.");
134 defineProximityFieldParams(def,
135 "p.navigateToLocation.safetyGuardParams.humanProximityField",
136 properties.safetyGuardParams.humanProximityField);
137 defineProximityFieldParams(
138 def,
139 "p.navigateToLocation.safetyGuardParams.laserScannerProximityField",
140 properties.safetyGuardParams.laserScannerProximityField);
141 def->optional(properties.safetyGuardParams.ignoreAttachedObjects,
142 "p.navigateToLocation.safetyGuardParams.ignoreAttachedObjects",
143 "Whether to ignore laser scanner features lying inside attached objects.");
144 def->optional(properties.safetyGuardParams.enableLaserScannerFiltering,
145 "p.navigateToLocation.safetyGuardParams.enableLaserScannerFiltering",
146 "Whether to filter out small laser scanner features close to the robot.");
147 def->optional(properties.safetyGuardParams.laserScannerFilteringThreshold,
148 "p.navigateToLocation.safetyGuardParams.laserScannerFilteringThreshold",
149 "Features with less or equal number of points are filtered out.");
150 def->optional(properties.safetyGuardParams.laserScannerMaxFilteringDistance,
151 "p.navigateToLocation.safetyGuardParams.laserScannerMaxFilteringDistance",
152 "Only features where all points are within this distance of the robot are "
153 "filtered out [mm].");
154
155
156 def->required(properties.generalConfig.maxVel.linear,
157 "p.default.generalConfig.maxVelocity.linear",
158 "The default maximum linear velocity.");
159 def->required(properties.generalConfig.maxVel.angular,
160 "p.default.generalConfig.maxVelocity.angular",
161 "The default maximum angular velocity.");
162
163 def->optional(
164 properties.generalConfig.enableRampingStart,
165 "p.default.generalConfig.enableRampingStart",
166 "The default value for whether ramping is enabled at the start of the trajectory.");
167 def->optional(
168 properties.generalConfig.enableRampingEnd,
169 "p.default.generalConfig.enableRampingEnd",
170 "The default value for whether ramping is enabled at the end of the trajectory.");
171 def->optional(
172 properties.generalConfig.enableRampingCorners,
173 "p.default.generalConfig.enableRampingCorners",
174 "The default value for whether ramping is enabled in corners of the trajectory.");
175 def->optional(properties.generalConfig.rampLength,
176 "p.default.generalConfig.rampLength",
177 "The default value for the distance over which ramping is applied [mm].");
178 def->optional(properties.generalConfig.cornerVelocity,
179 "p.default.generalConfig.cornerVelocity",
180 "The default value for the velocity in a corner [mm/s].");
181 def->optional(properties.generalConfig.boundaryVelocity,
182 "p.default.generalConfig.boundaryVelocity",
183 "The default value for the velocity at the ends [mm/s].");
184 def->optional(
185 properties.generalConfig.cornerLimit,
186 "p.default.generalConfig.cornerLimit",
187 "The default value for the angle above which a bend is considered a corner [deg].");
188
189 // Keep the default that the skills have been shipping (150 mm). `core::GeneralConfig`'s
190 // own default stays 100 mm, so the legacy-config path and the statecharts are unaffected.
191 properties.generalConfig.inCollisionDistanceThresholdForRecovery = 150.0F;
192 def->optional(properties.generalConfig.inCollisionDistanceThresholdForRecovery,
193 "p.default.generalConfig.inCollisionDistanceThresholdForRecovery",
194 "The default maximum distance [mm] to search for a collision-free recovery "
195 "position when the robot starts in collision. Skills can override this per "
196 "call via the parameter of the same name.");
197
198 // The fleet-wide default. Deliberately set here rather than on `core::GeneralConfig`
199 // itself: that struct's default stays `Ramping`, so the legacy-config compatibility
200 // path, the statecharts and the simulated human are unaffected by this choice.
201 //
202 // A skill can override it per call; the navigator degrades to ramping when TOPP-RA is
203 // unavailable or the path is too short to parametrize.
204 properties.generalConfig.parametrization = core::TrajectoryParametrization::Toppra;
205 def->optional(properties.generalConfig.parametrization,
206 "p.default.generalConfig.trajectoryParametrization",
207 "How the velocities along the planned path are assigned by default.")
211
212
213 optionalSubSkillID(properties.navigateToNamedLocation.subSkillIDs.navigateToLocation,
214 "p.navigateToNamedLocation.subSkillIDs.navigateToLocation");
215 optionalSubSkillID(properties.navigateToChargingStation.subSkillIDs.navigateToNamedLocation,
216 "p.navigateToChargingStation.subSkillIDs.navigateToNamedLocation");
217
218 return def;
219 }
220
221 void
223 {
224 // Topics and properties defined above are automagically registered.
225
226 // Keep debug observer data until calling `sendDebugObserverBatch()`.
227 // (Requies the armarx::DebugObserverComponentPluginUser.)
228 // setDebugObserverBatchModeEnabled(true);
229 }
230
231 void
233 {
234 // Do things after connecting to topics and components.
235
236 iceNavigatorFactory.emplace(navigatorPrx);
237
238 skills::NavigatingSkillHelper::Services helperSrv{.iceNavigatorFactory =
239 iceNavigatorFactory.value(),
240 .memoryNameSystem = memoryNameSystem()};
242 .safetyGuardParamsMutex = std::experimental::make_observer(&safetyGuardParamsMutex),
243 .safetyGuardParams = std::experimental::make_observer(&properties.safetyGuardParams),
244 .defaultGeneralConfig = properties.generalConfig};
245
246 {
247 addSkillFactory<skills::NavigateTo>(helperProperties, helperSrv);
248 }
249
250 {
251 addSkillFactory<skills::NavigateToAlternatives>(helperProperties, helperSrv);
252 }
253
254 {
255 addSkillFactory<skills::NavigateToLocation>(helperProperties, helperSrv);
256 }
257
258 {
260 .locationReader = &graphReaderPlugin->get(),
261 .objectPoseClient = ObjectPoseClientPluginUser::getClient(),
262 .objectClassReader = &objectClassReaderPlugin->get(),
263 };
264 addSkillFactory<skills::NavigateToNamedLocation>(properties.navigateToNamedLocation,
265 services);
266 }
267
268 {
270 properties.navigateToChargingStation);
271 }
272
273 {
274 addSkillFactory<skills::MoveXMeters>(helperProperties, helperSrv);
275 }
276
277 {
278 addSkillFactory<skills::RotateXDegrees>(helperProperties, helperSrv);
279 }
280
281 {
283 .robotName = this->properties.robotName,
284 };
286 .robotReader = virtualRobotReaderPlugin->get(),
287 };
289 helperProperties, helperSrv, properties, srv);
290 }
291
292 {
293 ARMARX_CHECK_NOT_NULL(virtualRobotReaderPlugin);
294 ARMARX_CHECK_NOT_NULL(virtualRobotReaderPlugin);
295 ARMARX_CHECK_NOT_NULL(roomsReaderPlugin);
296
297 skills::GuideHumanToRoom::Services srv{.virtualRobotReader =
298 virtualRobotReaderPlugin->get(),
299 .costmapReader = costmapReaderPlugin->get(),
300 .roomsReader = roomsReaderPlugin->get(),
301 .arviz = arviz};
302
303 addSkillFactory<skills::GuideHumanToRoom>(helperProperties, helperSrv, srv);
304 }
305
306 {
307 skills::RotateTowards::Properties props{.robotName = properties.robotName};
308 skills::RotateTowards::Services srv{.robotReader = virtualRobotReaderPlugin->get()};
309 addSkillFactory<skills::RotateTowards>(helperProperties, helperSrv, props, srv);
310 }
311
312 {
314 skills::RotateTowardsLocation::Services srv{.locationReader = graphReaderPlugin->get()};
316 }
317
318
319 /* (Requies the armarx::DebugObserverComponentPluginUser.)
320 // Use the debug observer to log data over time.
321 // The data can be viewed in the ObserverView and the LivePlotter.
322 // (Before starting any threads, we don't need to lock mutexes.)
323 {
324 setDebugObserverDatafield("numBoxes", properties.numBoxes);
325 setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
326 sendDebugObserverBatch();
327 }
328 */
329
330 /* (Requires the armarx::ArVizComponentPluginUser.)
331 // Draw boxes in ArViz.
332 // (Before starting any threads, we don't need to lock mutexes.)
333 drawBoxes(properties, arviz);
334 */
335
336 // Setup the remote GUI.
337 {
340 }
341 }
342
343 void
347
348 void
352
353 std::string
355 {
356 return Component::defaultName;
357 }
358
359 std::string
361 {
362 return Component::defaultName;
363 }
364
365 void
367 {
368 using namespace armarx::RemoteGui::Client;
369
370 VBoxLayout root;
371
372 // thread-safe access to params
373 const auto safetyGuardParams = [this]()
374 {
375 std::lock_guard g{safetyGuardParamsMutex};
376 return properties.safetyGuardParams;
377 }();
378
379 const auto setSpinBox = [](armarx::RemoteGui::Client::FloatSpinBox& w,
380 float min,
381 float max,
382 int decimals,
383 float step)
384 {
385 w.setRange(min, max);
386 w.setDecimals(decimals);
387 w.setSteps(static_cast<int>((max - min) / step));
388 };
389
390 {
391 GridLayout grid;
392 int row = 0;
393 grid.add(Label("Enable Humans"), {.row = row, .column = 0})
394 .add(tab.enableHumans, {.row = row++, .column = 1});
395 grid.add(Label("Enable Laserscanner"), {.row = row, .column = 0})
396 .add(tab.enableLaserScanners, {.row = row++, .column = 1});
397 root.addChild(grid);
398
399 tab.enableHumans.setValue(safetyGuardParams.enableHumans);
400 tab.enableLaserScanners.setValue(safetyGuardParams.enableLaserScanners);
401 }
402 root.addChild(VSpacer());
403
404 const auto setupProximityFieldGui =
405 [&root, &setSpinBox](RemoteGuiTab::ProximityField& field,
407 const std::string& label)
408 {
409 GridLayout grid;
410 int row = 0;
411 grid.add(Label("Reduce speed"), {.row = row, .column = 0})
412 .add(field.reduceSpeed, {.row = row++, .column = 1});
413 grid.add(Label("Min distance"), {.row = row, .column = 0})
414 .add(field.minDistance, {.row = row++, .column = 1});
415 grid.add(armarx::RemoteGui::Client::VSpacer(), {.row = row++, .column = 0});
416 grid.add(Label("Max distance"), {.row = row, .column = 0})
417 .add(field.maxDistance, {.row = row++, .column = 1});
418 grid.add(Label("k"), {.row = row, .column = 0})
419 .add(field.k, {.row = row++, .column = 1});
420 grid.add(Label("lambda"), {.row = row, .column = 0})
421 .add(field.lambda, {.row = row++, .column = 1});
422
423 GroupBox box({grid});
424 box.setLabel(label);
425 root.addChild(box);
426
427 field.reduceSpeed.setValue(values.reduceVelocity);
428 field.minDistance.setValue(values.safetyDistance);
429 setSpinBox(field.minDistance, 0, 5000, 1, 1);
430 field.maxDistance.setValue(values.influenceDistance);
431 setSpinBox(field.maxDistance, 0, 10000, 1, 1);
432 field.k.setValue(values.k);
433 setSpinBox(field.k, 1, 20, 2, 0.1);
434 field.lambda.setValue(values.lambda);
435 setSpinBox(field.lambda, 1, 20, 2, 0.1);
436 };
437
438 setupProximityFieldGui(tab.humanProximityField,
439 safetyGuardParams.humanProximityField,
440 "Human proximity field");
441 root.addChild(VSpacer());
442 setupProximityFieldGui(tab.laserScannerProximityField,
443 safetyGuardParams.laserScannerProximityField,
444 "LaserScanner proximity field");
445
446 RemoteGui_createTab(getName(), root, &tab);
447 }
448
449 void
451 {
452 std::lock_guard g{safetyGuardParamsMutex};
453
454 auto& safetyGuardParams = properties.safetyGuardParams;
455
456 safetyGuardParams.enableHumans = tab.enableHumans.getValue();
457 safetyGuardParams.enableLaserScanners = tab.enableLaserScanners.getValue();
458
459 const auto readProximityField = [&](const RemoteGuiTab::ProximityField& field,
461 {
462 values.reduceVelocity = field.reduceSpeed.getValue();
463 values.safetyDistance = field.minDistance.getValue();
464 values.influenceDistance = field.maxDistance.getValue();
465 values.k = field.k.getValue();
466 values.lambda = field.lambda.getValue();
467 };
468
469 readProximityField(tab.humanProximityField, safetyGuardParams.humanProximityField);
470 readProximityField(tab.laserScannerProximityField,
471 safetyGuardParams.laserScannerProximityField);
472 }
473
474 /* (Requires the armarx::ArVizComponentPluginUser.)
475 void
476 Component::drawBoxes(const Component::Properties& p, viz::Client& arviz)
477 {
478 // Draw something in ArViz (requires the armarx::ArVizComponentPluginUser.
479 // See the ArVizExample in RobotAPI for more examples.
480
481 viz::Layer layer = arviz.layer(p.boxLayerName);
482 for (int i = 0; i < p.numBoxes; ++i)
483 {
484 layer.add(viz::Box("box_" + std::to_string(i))
485 .position(Eigen::Vector3f(i * 100, 0, 0))
486 .size(20).color(simox::Color::blue()));
487 }
488 arviz.commit(layer);
489 }
490 */
491
492
494
495} // namespace armarx::navigation::components::navigation_skill_provider
int Label(int n[], int size, int *curLabel, MiscLib::Vector< std::pair< int, size_t > > *labels)
Definition Bitmap.cpp:801
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:88
PluginT * addPlugin(const std::string prefix="", ParamsT &&... params)
std::string getName() const
Retrieve name of object.
objpose::ObjectPoseClient getClient() const
skills::SkillBlueprint * addSkillFactory(const skills::SkillDescription &desc, const skills::LambdaSkill::FunctionType &f)
void RemoteGui_update() override
After calling RemoteGui_startRunningTask, this function is called periodically in a separate thread.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
static std::string GetDefaultName()
Get the component's default name.
void createRemoteGuiTab()
This function should be called once in onConnect() or when you need to re-create the Remote GUI tab.
std::optional< ProviderID > providerId
Definition SkillID.h:40
std::string skillName
Definition SkillID.h:41
#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...
@ Ramping
Ramp down at the start, the goal and every corner. The stack's historical behaviour.
@ Toppra
Time-optimal under per-motor torque and command-ramp limits.
const std::string NavigationSkillProviderName
Definition constants.h:28
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
std::vector< T > min(const std::vector< T > &v1, const std::vector< T > &v2)
observer_ptr< _Tp > make_observer(_Tp *__p) noexcept
void RemoteGui_createTab(std::string const &name, RemoteGui::Client::Widget const &rootWidget, RemoteGui::Client::Tab *tab)
void addChild(Widget const &child)
Definition Widgets.cpp:95
void setRange(float min, float max)
Definition Widgets.cpp:330
GridLayout & add(Widget const &child, Pos pos, Span span=Span{1, 1})
Definition Widgets.cpp:438
void setLabel(std::string const &text)
Definition Widgets.cpp:420