moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
motion_planning_frame_objects.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2012, Willow Garage, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of Willow Garage nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: Ioan Sucan, Mario Prats */
37
42
43#include <interactive_markers/tools.hpp>
44
45#include <rviz_common/display_context.hpp>
46#include <rviz_common/frame_manager_iface.hpp>
47#include <rviz_common/window_manager_interface.hpp>
48
49#include <tf2_eigen/tf2_eigen.hpp>
50#include <geometric_shapes/shape_operations.h>
51
52#include <QMessageBox>
53#include <QInputDialog>
54#include <QFileDialog>
55
56#include "ui_motion_planning_rviz_plugin_frame.h"
57
58namespace
59{
60QString subframePosesToQstring(const moveit::core::FixedTransformsMap& subframes)
61{
62 QString status_text = "\nIt has the subframes '";
63 for (const auto& subframe : subframes)
64 {
65 status_text += QString::fromStdString(subframe.first) + "', '";
66 }
67 status_text.chop(3);
68 status_text += ".";
69 return status_text;
70}
71} // namespace
72
73namespace moveit_rviz_plugin
74{
75
76void MotionPlanningFrame::shapesComboBoxChanged(const QString& /*text*/)
77{
78 switch (ui_->shapes_combo_box->currentData().toInt()) // fetch shape ID from current combobox item
79 {
80 case shapes::BOX:
81 ui_->shape_size_x_spin_box->setEnabled(true);
82 ui_->shape_size_y_spin_box->setEnabled(true);
83 ui_->shape_size_z_spin_box->setEnabled(true);
84 break;
85 case shapes::SPHERE:
86 ui_->shape_size_x_spin_box->setEnabled(true);
87 ui_->shape_size_y_spin_box->setEnabled(false);
88 ui_->shape_size_z_spin_box->setEnabled(false);
89 break;
90 case shapes::CYLINDER:
91 case shapes::CONE:
92 ui_->shape_size_x_spin_box->setEnabled(true);
93 ui_->shape_size_y_spin_box->setEnabled(false);
94 ui_->shape_size_z_spin_box->setEnabled(true);
95 break;
96 case shapes::MESH:
97 ui_->shape_size_x_spin_box->setEnabled(false);
98 ui_->shape_size_y_spin_box->setEnabled(false);
99 ui_->shape_size_z_spin_box->setEnabled(false);
100 break;
101 default:
102 break;
103 }
104}
105
106void MotionPlanningFrame::setLocalSceneEdited(bool dirty)
107{
108 ui_->publish_current_scene_button->setEnabled(dirty);
109}
110
111bool MotionPlanningFrame::isLocalSceneDirty() const
112{
113 return ui_->publish_current_scene_button->isEnabled();
114}
115
116void MotionPlanningFrame::publishScene()
117{
118 const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
119 if (ps)
120 {
121 moveit_msgs::msg::PlanningScene msg;
122 ps->getPlanningSceneMsg(msg);
123 planning_scene_publisher_->publish(msg);
124 setLocalSceneEdited(false);
125 }
126}
127
128void MotionPlanningFrame::publishSceneIfNeeded()
129{
130 if (isLocalSceneDirty() &&
131 QMessageBox::question(this, "Update PlanningScene",
132 "You have local changes to your planning scene.\n"
133 "Publish them to the move_group node?",
134 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes)
135 publishScene();
136}
137
138void MotionPlanningFrame::clearScene()
139{
140 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
141 if (ps)
142 {
143 ps->getWorldNonConst()->clearObjects();
144 ps->getCurrentStateNonConst().clearAttachedBodies();
145 moveit_msgs::msg::PlanningScene msg;
146 ps->getPlanningSceneMsg(msg);
147 planning_scene_publisher_->publish(msg);
148 setLocalSceneEdited(false);
149 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
150 planning_display_->queueRenderSceneGeometry();
151 }
152}
153
154void MotionPlanningFrame::sceneScaleChanged(int value)
155{
156 const double scaling_factor = static_cast<double>(value) / 100.0; // The GUI slider gives percent values
157 if (scaled_object_)
158 {
159 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
160 if (ps)
161 {
162 if (ps->getWorld()->hasObject(scaled_object_->id_))
163 {
164 ps->getWorldNonConst()->removeObject(scaled_object_->id_);
165 for (std::size_t i = 0; i < scaled_object_->shapes_.size(); ++i)
166 {
167 shapes::Shape* s = scaled_object_->shapes_[i]->clone();
168 s->scale(scaling_factor);
169
170 Eigen::Isometry3d scaled_shape_pose = scaled_object_->shape_poses_[i];
171 scaled_shape_pose.translation() *= scaling_factor;
172
173 ps->getWorldNonConst()->addToObject(scaled_object_->id_, scaled_object_->pose_, shapes::ShapeConstPtr(s),
174 scaled_shape_pose);
175 }
176 moveit::core::FixedTransformsMap scaled_subframes = scaled_object_->subframe_poses_;
177 for (auto& subframe_pair : scaled_subframes)
178 subframe_pair.second.translation() *= scaling_factor;
179
180 ps->getWorldNonConst()->setSubframesOfObject(scaled_object_->id_, scaled_subframes);
181 setLocalSceneEdited();
182 scene_marker_->processMessage(createObjectMarkerMsg(ps->getWorld()->getObject(scaled_object_->id_)));
183 planning_display_->queueRenderSceneGeometry();
184 }
185 else
186 {
187 scaled_object_.reset();
188 }
189 }
190 else
191 {
192 scaled_object_.reset();
193 }
194 }
195}
196
197void MotionPlanningFrame::sceneScaleStartChange()
198{
199 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
200 if (sel.empty())
201 return;
202 if (planning_display_->getPlanningSceneMonitor() && sel[0]->checkState() == Qt::Unchecked)
203 {
204 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
205 if (ps)
206 {
207 scaled_object_ = ps->getWorld()->getObject(sel[0]->text().toStdString());
208 scaled_object_subframes_ = scaled_object_->subframe_poses_;
209 scaled_object_shape_poses_ = scaled_object_->shape_poses_;
210 }
211 }
212}
213
214void MotionPlanningFrame::sceneScaleEndChange()
215{
216 scaled_object_.reset();
217 ui_->scene_scale->setSliderPosition(100);
218}
219
220void MotionPlanningFrame::removeSceneObject()
221{
222 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
223 if (sel.empty())
224 return;
225 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
226 if (ps)
227 {
228 for (int i = 0; i < sel.count(); ++i)
229 {
230 if (sel[i]->checkState() == Qt::Unchecked)
231 {
232 ps->getWorldNonConst()->removeObject(sel[i]->text().toStdString());
233 }
234 else
235 {
236 ps->getCurrentStateNonConst().clearAttachedBody(sel[i]->text().toStdString());
237 }
238 }
239 scene_marker_.reset();
240 setLocalSceneEdited();
241 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
242 planning_display_->queueRenderSceneGeometry();
243 }
244}
245
246static QString decideStatusText(const collision_detection::CollisionEnv::ObjectConstPtr& obj)
247{
248 QString status_text = "'" + QString::fromStdString(obj->id_) + "' is a collision object with ";
249 if (obj->shapes_.empty())
250 {
251 status_text += "no geometry";
252 }
253 else
254 {
255 std::vector<QString> shape_names;
256 for (const shapes::ShapeConstPtr& shape : obj->shapes_)
257 shape_names.push_back(QString::fromStdString(shapes::shapeStringName(shape.get())));
258 if (shape_names.size() == 1)
259 {
260 status_text += "one " + shape_names[0];
261 }
262 else
263 {
264 status_text += QString::fromStdString(std::to_string(shape_names.size())) + " shapes:";
265 for (const QString& shape_name : shape_names)
266 status_text += " " + shape_name;
267 }
268 status_text += ".";
269 }
270 if (!obj->subframe_poses_.empty())
271 {
272 status_text += subframePosesToQstring(obj->subframe_poses_);
273 }
274 return status_text;
275}
276
277static QString decideStatusText(const moveit::core::AttachedBody* attached_body)
278{
279 QString status_text = "'" + QString::fromStdString(attached_body->getName()) + "' is attached to '" +
280 QString::fromStdString(attached_body->getAttachedLinkName()) + "'.";
281 if (!attached_body->getSubframes().empty())
282 {
283 status_text += subframePosesToQstring(attached_body->getSubframes());
284 }
285 return status_text;
286}
287
288void MotionPlanningFrame::selectedCollisionObjectChanged()
289{
290 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
291 if (sel.empty())
292 {
293 bool old_state = ui_->object_x->blockSignals(true);
294 ui_->object_x->setValue(0.0);
295 ui_->object_x->blockSignals(old_state);
296
297 old_state = ui_->object_y->blockSignals(true);
298 ui_->object_y->setValue(0.0);
299 ui_->object_y->blockSignals(old_state);
300
301 old_state = ui_->object_z->blockSignals(true);
302 ui_->object_z->setValue(0.0);
303 ui_->object_z->blockSignals(old_state);
304
305 old_state = ui_->object_rx->blockSignals(true);
306 ui_->object_rx->setValue(0.0);
307 ui_->object_rx->blockSignals(old_state);
308
309 old_state = ui_->object_ry->blockSignals(true);
310 ui_->object_ry->setValue(0.0);
311 ui_->object_ry->blockSignals(old_state);
312
313 old_state = ui_->object_rz->blockSignals(true);
314 ui_->object_rz->setValue(0.0);
315 ui_->object_rz->blockSignals(old_state);
316
317 ui_->object_status->setText("");
318 scene_marker_.reset();
319 ui_->pose_scale_group_box->setEnabled(false);
320 }
321 else if (planning_display_->getPlanningSceneMonitor())
322 {
323 // if this is a CollisionWorld element
324 if (sel[0]->checkState() == Qt::Unchecked)
325 {
326 ui_->pose_scale_group_box->setEnabled(true);
327 bool update_scene_marker = false;
328 Eigen::Isometry3d obj_pose;
329 {
330 const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
332 ps->getWorld()->getObject(sel[0]->text().toStdString());
333 if (obj)
334 {
335 ui_->object_status->setText(decideStatusText(obj));
336
337 if (obj->shapes_.size() == 1)
338 {
339 obj_pose = obj->pose_; // valid isometry by contract
340 Eigen::Vector3d xyz = obj_pose.linear().eulerAngles(0, 1, 2);
341 update_scene_marker = true; // do the marker update outside locked scope to avoid deadlock
342
343 bool old_state = ui_->object_x->blockSignals(true);
344 ui_->object_x->setValue(obj_pose.translation()[0]);
345 ui_->object_x->blockSignals(old_state);
346
347 old_state = ui_->object_y->blockSignals(true);
348 ui_->object_y->setValue(obj_pose.translation()[1]);
349 ui_->object_y->blockSignals(old_state);
350
351 old_state = ui_->object_z->blockSignals(true);
352 ui_->object_z->setValue(obj_pose.translation()[2]);
353 ui_->object_z->blockSignals(old_state);
354
355 old_state = ui_->object_rx->blockSignals(true);
356 ui_->object_rx->setValue(xyz[0]);
357 ui_->object_rx->blockSignals(old_state);
358
359 old_state = ui_->object_ry->blockSignals(true);
360 ui_->object_ry->setValue(xyz[1]);
361 ui_->object_ry->blockSignals(old_state);
362
363 old_state = ui_->object_rz->blockSignals(true);
364 ui_->object_rz->setValue(xyz[2]);
365 ui_->object_rz->blockSignals(old_state);
366 }
367 }
368 else
369 {
370 ui_->object_status->setText("ERROR: '" + sel[0]->text() + "' should be a collision object but it is not");
371 }
372 }
373 if (update_scene_marker && ui_->tabWidget->tabText(ui_->tabWidget->currentIndex()).toStdString() == TAB_OBJECTS)
374 {
375 createSceneInteractiveMarker();
376 }
377 }
378 else
379 {
380 ui_->pose_scale_group_box->setEnabled(false);
381 // if it is an attached object
382 scene_marker_.reset();
383 const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
384 const moveit::core::AttachedBody* attached_body =
385 ps->getCurrentState().getAttachedBody(sel[0]->text().toStdString());
386 if (attached_body)
387 {
388 ui_->object_status->setText(decideStatusText(attached_body));
389 }
390 else
391 {
392 ui_->object_status->setText("ERROR: '" + sel[0]->text() + "' should be an attached object but it is not");
393 }
394 }
395 }
396}
397
398void MotionPlanningFrame::objectPoseValueChanged(double /* value */)
399{
400 updateCollisionObjectPose(true);
401}
402
403void MotionPlanningFrame::updateCollisionObjectPose(bool update_marker_position)
404{
405 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
406 if (sel.empty())
407 return;
408 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
409 if (ps)
410 {
411 collision_detection::CollisionEnv::ObjectConstPtr obj = ps->getWorld()->getObject(sel[0]->text().toStdString());
412 if (obj)
413 {
414 Eigen::Isometry3d p;
415 p.translation()[0] = ui_->object_x->value();
416 p.translation()[1] = ui_->object_y->value();
417 p.translation()[2] = ui_->object_z->value();
418
419 p = Eigen::Translation3d(p.translation()) *
420 (Eigen::AngleAxisd(ui_->object_rx->value(), Eigen::Vector3d::UnitX()) *
421 Eigen::AngleAxisd(ui_->object_ry->value(), Eigen::Vector3d::UnitY()) *
422 Eigen::AngleAxisd(ui_->object_rz->value(), Eigen::Vector3d::UnitZ()));
423
424 ps->getWorldNonConst()->setObjectPose(obj->id_, p);
425 planning_display_->queueRenderSceneGeometry();
426 setLocalSceneEdited();
427
428 // Update the interactive marker pose to match the manually introduced one
429 if (update_marker_position && scene_marker_)
430 {
431 // p is isometry by construction
432 Eigen::Quaterniond eq(p.linear());
433 scene_marker_->setPose(Ogre::Vector3(ui_->object_x->value(), ui_->object_y->value(), ui_->object_z->value()),
434 Ogre::Quaternion(eq.w(), eq.x(), eq.y(), eq.z()), "");
435 }
436 }
437 }
438}
439
440void MotionPlanningFrame::collisionObjectChanged(QListWidgetItem* item)
441{
442 if (item->type() < static_cast<int>(known_collision_objects_.size()) && planning_display_->getPlanningSceneMonitor())
443 {
444 // if we have a name change
445 if (known_collision_objects_[item->type()].first != item->text().toStdString())
446 {
447 renameCollisionObject(item);
448 }
449 else
450 {
451 bool checked = item->checkState() == Qt::Checked;
452 if (known_collision_objects_[item->type()].second != checked)
453 attachDetachCollisionObject(item);
454 }
455 }
456}
457
458/* Receives feedback from the interactive marker and updates the shape pose in the world accordingly */
459void MotionPlanningFrame::imProcessFeedback(visualization_msgs::msg::InteractiveMarkerFeedback& feedback)
460{
461 if (!planning_display_->getPlanningSceneRO()->knowsFrameTransform(feedback.header.frame_id))
462 {
463 RCLCPP_ERROR_STREAM(logger_,
464 "Frame `" << feedback.header.frame_id << "` unknown doesn't exists in the planning scene");
465 }
466 Eigen::Isometry3d fixed_frame_t_scene_marker;
467 tf2::fromMsg(feedback.pose, fixed_frame_t_scene_marker);
468 Eigen::Isometry3d model_frame_t_scene_marker =
469 planning_display_->getPlanningSceneRO()->getFrameTransform(feedback.header.frame_id) * fixed_frame_t_scene_marker;
470
471 bool old_state = ui_->object_x->blockSignals(true);
472 ui_->object_x->setValue(model_frame_t_scene_marker.translation().x());
473 ui_->object_x->blockSignals(old_state);
474
475 old_state = ui_->object_y->blockSignals(true);
476 ui_->object_y->setValue(model_frame_t_scene_marker.translation().y());
477 ui_->object_y->blockSignals(old_state);
478
479 old_state = ui_->object_z->blockSignals(true);
480 ui_->object_z->setValue(model_frame_t_scene_marker.translation().z());
481 ui_->object_z->blockSignals(old_state);
482
483 Eigen::Vector3d xyz = model_frame_t_scene_marker.linear().eulerAngles(0, 1, 2);
484
485 old_state = ui_->object_rx->blockSignals(true);
486 ui_->object_rx->setValue(xyz[0]);
487 ui_->object_rx->blockSignals(old_state);
488
489 old_state = ui_->object_ry->blockSignals(true);
490 ui_->object_ry->setValue(xyz[1]);
491 ui_->object_ry->blockSignals(old_state);
492
493 old_state = ui_->object_rz->blockSignals(true);
494 ui_->object_rz->setValue(xyz[2]);
495 ui_->object_rz->blockSignals(old_state);
496
497 updateCollisionObjectPose(false);
498}
499
500void MotionPlanningFrame::copySelectedCollisionObject()
501{
502 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
503 if (sel.empty())
504 return;
505
506 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
507 if (!ps)
508 return;
509
510 for (const QListWidgetItem* item : sel)
511 {
512 std::string name = item->text().toStdString();
513 collision_detection::CollisionEnv::ObjectConstPtr obj = ps->getWorld()->getObject(name);
514 if (!obj)
515 continue;
516
517 // find a name for the copy
518 name.insert(0, "Copy of ");
519 if (ps->getWorld()->hasObject(name))
520 {
521 name += " ";
522 unsigned int n = 1;
523 while (ps->getWorld()->hasObject(name + std::to_string(n)))
524 n++;
525 name += std::to_string(n);
526 }
527 ps->getWorldNonConst()->addToObject(name, obj->shapes_, obj->shape_poses_);
528 RCLCPP_DEBUG(logger_, "Copied collision object to '%s'", name.c_str());
529 }
530 setLocalSceneEdited();
531 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
532}
533
534void MotionPlanningFrame::computeSaveSceneButtonClicked()
535{
537 {
538 moveit_msgs::msg::PlanningScene msg;
539 planning_display_->getPlanningSceneRO()->getPlanningSceneMsg(msg);
540 try
541 {
542 planning_scene_storage_->removePlanningScene(msg.name);
543 planning_scene_storage_->addPlanningScene(msg);
544 }
545 catch (std::exception& ex)
546 {
547 RCLCPP_ERROR(logger_, "%s", ex.what());
548 }
549
550 planning_display_->addMainLoopJob([this] { populatePlanningSceneTreeView(); });
551 }
552}
553
554void MotionPlanningFrame::computeSaveQueryButtonClicked(const std::string& scene, const std::string& query_name)
555{
556 moveit_msgs::msg::MotionPlanRequest mreq;
559 {
560 try
561 {
562 if (!query_name.empty())
563 planning_scene_storage_->removePlanningQuery(scene, query_name);
564 planning_scene_storage_->addPlanningQuery(mreq, scene, query_name);
565 }
566 catch (std::exception& ex)
567 {
568 RCLCPP_ERROR(logger_, "%s", ex.what());
569 }
570
571 planning_display_->addMainLoopJob([this] { populatePlanningSceneTreeView(); });
572 }
573}
574
575void MotionPlanningFrame::computeDeleteSceneButtonClicked()
576{
578 {
579 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
580 if (!sel.empty())
581 {
582 QTreeWidgetItem* s = sel.front();
583 if (s->type() == ITEM_TYPE_SCENE)
584 {
585 std::string scene = s->text(0).toStdString();
586 try
587 {
588 planning_scene_storage_->removePlanningScene(scene);
589 }
590 catch (std::exception& ex)
591 {
592 RCLCPP_ERROR(logger_, "%s", ex.what());
593 }
594 }
595 else
596 {
597 // if we selected a query name, then we overwrite that query
598 std::string scene = s->parent()->text(0).toStdString();
599 try
600 {
601 planning_scene_storage_->removePlanningScene(scene);
602 }
603 catch (std::exception& ex)
604 {
605 RCLCPP_ERROR(logger_, "%s", ex.what());
606 }
607 }
608 planning_display_->addMainLoopJob([this] { populatePlanningSceneTreeView(); });
609 }
610 }
611}
612
613void MotionPlanningFrame::computeDeleteQueryButtonClicked()
614{
616 {
617 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
618 if (!sel.empty())
619 {
620 QTreeWidgetItem* s = sel.front();
621 if (s->type() == ITEM_TYPE_QUERY)
622 {
623 std::string scene = s->parent()->text(0).toStdString();
624 std::string query_name = s->text(0).toStdString();
625 try
626 {
627 planning_scene_storage_->removePlanningQuery(scene, query_name);
628 }
629 catch (std::exception& ex)
630 {
631 RCLCPP_ERROR(logger_, "%s", ex.what());
632 }
633 planning_display_->addMainLoopJob([this, s] { computeDeleteQueryButtonClickedHelper(s); });
634 }
635 }
636 }
637}
638
639void MotionPlanningFrame::computeDeleteQueryButtonClickedHelper(QTreeWidgetItem* s)
640{
641 ui_->planning_scene_tree->setUpdatesEnabled(false);
642 s->parent()->removeChild(s);
643 ui_->planning_scene_tree->setUpdatesEnabled(true);
644}
645
646void MotionPlanningFrame::checkPlanningSceneTreeEnabledButtons()
647{
648 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
649 if (sel.empty())
650 {
651 ui_->load_scene_button->setEnabled(false);
652 ui_->load_query_button->setEnabled(false);
653 ui_->save_query_button->setEnabled(false);
654 ui_->delete_scene_button->setEnabled(false);
655 }
656 else
657 {
658 ui_->save_query_button->setEnabled(true);
659
660 QTreeWidgetItem* s = sel.front();
661
662 // if the item is a PlanningScene
663 if (s->type() == ITEM_TYPE_SCENE)
664 {
665 ui_->load_scene_button->setEnabled(true);
666 ui_->load_query_button->setEnabled(false);
667 ui_->delete_scene_button->setEnabled(true);
668 ui_->delete_query_button->setEnabled(false);
669 ui_->save_query_button->setEnabled(true);
670 }
671 else
672 {
673 // if the item is a query
674 ui_->load_scene_button->setEnabled(false);
675 ui_->load_query_button->setEnabled(true);
676 ui_->delete_scene_button->setEnabled(false);
677 ui_->delete_query_button->setEnabled(true);
678 }
679 }
680}
681
682void MotionPlanningFrame::computeLoadSceneButtonClicked()
683{
685 {
686 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
687 if (!sel.empty())
688 {
689 QTreeWidgetItem* s = sel.front();
690 if (s->type() == ITEM_TYPE_SCENE)
691 {
692 std::string scene = s->text(0).toStdString();
693 RCLCPP_DEBUG(logger_, "Attempting to load scene '%s'", scene.c_str());
694
696 bool got_ps = false;
697 try
698 {
699 got_ps = planning_scene_storage_->getPlanningScene(scene_m, scene);
700 }
701 catch (std::exception& ex)
702 {
703 RCLCPP_ERROR(logger_, "%s", ex.what());
704 }
705
706 if (got_ps)
707 {
708 RCLCPP_INFO(logger_, "Loaded scene '%s'", scene.c_str());
709 if (planning_display_->getPlanningSceneMonitor())
710 {
711 if (scene_m->robot_model_name != planning_display_->getRobotModel()->getName())
712 {
713 RCLCPP_INFO(logger_,
714 "Scene '%s' was saved for robot '%s' but we are using robot '%s'. Using scene geometry only",
715 scene.c_str(), scene_m->robot_model_name.c_str(),
716 planning_display_->getRobotModel()->getName().c_str());
717 planning_scene_world_publisher_->publish(scene_m->world);
718 // publish the parts that are not in the world
719 moveit_msgs::msg::PlanningScene diff;
720 diff.is_diff = true;
721 diff.name = scene_m->name;
722 planning_scene_publisher_->publish(diff);
723 }
724 else
725 {
726 planning_scene_publisher_->publish(static_cast<const moveit_msgs::msg::PlanningScene&>(*scene_m));
727 }
728 }
729 else
730 {
731 planning_scene_publisher_->publish(static_cast<const moveit_msgs::msg::PlanningScene&>(*scene_m));
732 }
733 }
734 else
735 {
736 RCLCPP_WARN(logger_, "Failed to load scene '%s'. Has the message format changed since the scene was saved?",
737 scene.c_str());
738 }
739 }
740 }
741 }
742}
743
744void MotionPlanningFrame::computeLoadQueryButtonClicked()
745{
747 {
748 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
749 if (!sel.empty())
750 {
751 QTreeWidgetItem* s = sel.front();
752 if (s->type() == ITEM_TYPE_QUERY)
753 {
754 std::string scene = s->parent()->text(0).toStdString();
755 std::string query_name = s->text(0).toStdString();
756
758 bool got_q = false;
759 try
760 {
761 got_q = planning_scene_storage_->getPlanningQuery(mp, scene, query_name);
762 }
763 catch (std::exception& ex)
764 {
765 RCLCPP_ERROR(logger_, "%s", ex.what());
766 }
767
768 if (got_q)
769 {
770 moveit::core::RobotStatePtr start_state(
771 new moveit::core::RobotState(*planning_display_->getQueryStartState()));
772 moveit::core::robotStateMsgToRobotState(planning_display_->getPlanningSceneRO()->getTransforms(),
773 mp->start_state, *start_state);
774 planning_display_->setQueryStartState(*start_state);
775
776 auto goal_state = std::make_shared<moveit::core::RobotState>(*planning_display_->getQueryGoalState());
777 for (const moveit_msgs::msg::Constraints& goal_constraint : mp->goal_constraints)
778 {
779 if (!goal_constraint.joint_constraints.empty())
780 {
781 std::map<std::string, double> vals;
782 for (const moveit_msgs::msg::JointConstraint& joint_constraint : goal_constraint.joint_constraints)
783 vals[joint_constraint.joint_name] = joint_constraint.position;
784 goal_state->setVariablePositions(vals);
785 break;
786 }
787 }
788 planning_display_->setQueryGoalState(*goal_state);
789 }
790 else
791 {
792 RCLCPP_ERROR(logger_,
793 "Failed to load planning query '%s'. Has the message format changed since the query was saved?",
794 query_name.c_str());
795 }
796 }
797 }
798 }
799}
800
801visualization_msgs::msg::InteractiveMarker
802MotionPlanningFrame::createObjectMarkerMsg(const collision_detection::CollisionEnv::ObjectConstPtr& obj)
803{
804 Eigen::Vector3d center;
805 double scale;
806 shapes::computeShapeBoundingSphere(obj->shapes_[0].get(), center, scale);
807 geometry_msgs::msg::PoseStamped shape_pose = tf2::toMsg(tf2::Stamped<Eigen::Isometry3d>(
808 obj->pose_, std::chrono::system_clock::now(), planning_display_->getRobotModel()->getModelFrame()));
809 // TODO(felixvd): Consider where to place the object marker.
810 // obj->pose*obj->shape_poses_[0] is backwards compatible, sits on the visible part of
811 // the object, and is more difficult to implement now.
812 // obj->pose is easier to implement and makes more sense.
813 scale = (scale + center.cwiseAbs().maxCoeff()) * 2.0 * 1.2; // add padding of 20% size
814
815 // create an interactive marker msg for the given shape
816 visualization_msgs::msg::InteractiveMarker imarker =
817 robot_interaction::make6DOFMarker("marker_scene_object", shape_pose, scale);
818 imarker.description = obj->id_;
819 interactive_markers::autoComplete(imarker);
820 return imarker;
821}
822
823void MotionPlanningFrame::createSceneInteractiveMarker()
824{
825 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
826 if (sel.empty())
827 return;
828
829 const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
830 if (!ps)
831 return;
832
834 ps->getWorld()->getObject(sel[0]->text().toStdString());
835 if (obj && obj->shapes_.size() == 1)
836 {
837 scene_marker_ = std::make_shared<rviz_default_plugins::displays::InteractiveMarker>(
838 planning_display_->getSceneNode(), context_);
839 scene_marker_->processMessage(createObjectMarkerMsg(obj));
840 scene_marker_->setShowAxes(false);
841
842 // Connect signals
843 connect(scene_marker_.get(), SIGNAL(userFeedback(visualization_msgs::msg::InteractiveMarkerFeedback&)), this,
844 SLOT(imProcessFeedback(visualization_msgs::msg::InteractiveMarkerFeedback&)));
845 }
846 else
847 {
848 scene_marker_.reset();
849 }
850}
851
852void MotionPlanningFrame::renameCollisionObject(QListWidgetItem* item)
853{
854 long unsigned int version = known_collision_objects_version_;
855 if (item->text().isEmpty())
856 {
857 QMessageBox::warning(this, "Invalid object name", "Cannot set an empty object name.");
858 if (version == known_collision_objects_version_)
859 item->setText(QString::fromStdString(known_collision_objects_[item->type()].first));
860 return;
861 }
862
863 std::string item_text = item->text().toStdString();
864 bool already_exists = planning_display_->getPlanningSceneRO()->getWorld()->hasObject(item_text);
865 if (!already_exists)
866 already_exists = planning_display_->getPlanningSceneRO()->getCurrentState().hasAttachedBody(item_text);
867 if (already_exists)
868 {
869 QMessageBox::warning(this, "Duplicate object name",
870 QString("The name '")
871 .append(item->text())
872 .append("' already exists. Not renaming object ")
873 .append((known_collision_objects_[item->type()].first.c_str())));
874 if (version == known_collision_objects_version_)
875 item->setText(QString::fromStdString(known_collision_objects_[item->type()].first));
876 return;
877 }
878
879 if (item->checkState() == Qt::Unchecked)
880 {
881 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
883 ps->getWorld()->getObject(known_collision_objects_[item->type()].first);
884 if (obj)
885 {
886 known_collision_objects_[item->type()].first = item_text;
887 ps->getWorldNonConst()->removeObject(obj->id_);
888 ps->getWorldNonConst()->addToObject(known_collision_objects_[item->type()].first, obj->pose_, obj->shapes_,
889 obj->shape_poses_);
890 ps->getWorldNonConst()->setSubframesOfObject(obj->id_, obj->subframe_poses_);
891 if (scene_marker_)
892 {
893 scene_marker_.reset();
894 planning_display_->addMainLoopJob([this] { createSceneInteractiveMarker(); });
895 }
896 }
897 }
898 else
899 {
900 // rename attached body
901 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
902 moveit::core::RobotState& cs = ps->getCurrentStateNonConst();
903 const moveit::core::AttachedBody* ab = cs.getAttachedBody(known_collision_objects_[item->type()].first);
904 if (ab)
905 {
906 known_collision_objects_[item->type()].first = item_text;
907 auto new_ab = std::make_unique<moveit::core::AttachedBody>(
908 ab->getAttachedLink(), known_collision_objects_[item->type()].first, ab->getPose(), ab->getShapes(),
909 ab->getShapePoses(), ab->getTouchLinks(), ab->getDetachPosture(), ab->getSubframes());
910 cs.clearAttachedBody(ab->getName());
911 cs.attachBody(std::move(new_ab));
912 }
913 }
914 setLocalSceneEdited();
915}
916
917void MotionPlanningFrame::attachDetachCollisionObject(QListWidgetItem* item)
918{
919 long unsigned int version = known_collision_objects_version_;
920 bool checked = item->checkState() == Qt::Checked;
921 std::pair<std::string, bool> data = known_collision_objects_[item->type()];
922 moveit_msgs::msg::AttachedCollisionObject aco;
923
924 if (checked) // we need to attach a known collision object
925 {
926 QStringList links;
927 const std::vector<std::string>& links_std = planning_display_->getRobotModel()->getLinkModelNames();
928 for (const std::string& link : links_std)
929 links.append(QString::fromStdString(link));
930 bool ok = false;
931 QString response =
932 QInputDialog::getItem(this, tr("Select Link Name"), tr("Choose the link to attach to:"), links, 0, false, &ok);
933 if (!ok)
934 {
935 if (version == known_collision_objects_version_)
936 item->setCheckState(Qt::Unchecked);
937 return;
938 }
939 aco.link_name = response.toStdString();
940 aco.object.id = data.first;
941 aco.object.operation = moveit_msgs::msg::CollisionObject::ADD;
942 }
943 else // we need to detach an attached object
944 {
945 const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
946 const moveit::core::AttachedBody* attached_body = ps->getCurrentState().getAttachedBody(data.first);
947 if (attached_body)
948 {
949 aco.link_name = attached_body->getAttachedLinkName();
950 aco.object.id = attached_body->getName();
951 aco.object.operation = moveit_msgs::msg::CollisionObject::REMOVE;
952 }
953 }
954
955 moveit::core::RobotState rs(planning_display_->getRobotModel());
956 {
957 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
958 // we loop through the list in case updates were received since the start of the function
959 for (std::pair<std::string, bool>& known_collision_object : known_collision_objects_)
960 {
961 if (known_collision_object.first == data.first)
962 {
963 known_collision_object.second = checked;
964 break;
965 }
966 }
967 ps->processAttachedCollisionObjectMsg(aco);
968 rs = ps->getCurrentState();
969 }
970
971 selectedCollisionObjectChanged();
972 setLocalSceneEdited();
973 planning_display_->updateQueryStates(rs);
974 planning_display_->queueRenderSceneGeometry();
975}
976
977void MotionPlanningFrame::populateCollisionObjectsList()
978{
979 ui_->collision_objects_list->setUpdatesEnabled(false);
980 bool old_state = ui_->collision_objects_list->blockSignals(true);
981 bool octomap_in_scene = false;
982
983 {
984 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
985 std::set<std::string> to_select;
986 for (QListWidgetItem* item : sel)
987 to_select.insert(item->text().toStdString());
988 ui_->collision_objects_list->clear();
989 known_collision_objects_.clear();
990 known_collision_objects_version_++;
991
992 planning_scene_monitor::LockedPlanningSceneRO ps = planning_display_->getPlanningSceneRO();
993 if (ps)
994 {
995 const std::vector<std::string>& collision_object_names = ps->getWorld()->getObjectIds();
996 for (std::size_t i = 0; i < collision_object_names.size(); ++i)
997 {
998 if (collision_object_names[i] == planning_scene::PlanningScene::OCTOMAP_NS)
999 {
1000 octomap_in_scene = true;
1001 continue;
1002 }
1003
1004 QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(collision_object_names[i]),
1005 ui_->collision_objects_list, static_cast<int>(i));
1006 item->setFlags(item->flags() | Qt::ItemIsEditable);
1007 item->setToolTip(item->text());
1008 item->setCheckState(Qt::Unchecked);
1009 if (to_select.find(collision_object_names[i]) != to_select.end())
1010 item->setSelected(true);
1011 ui_->collision_objects_list->addItem(item);
1012 known_collision_objects_.push_back(std::make_pair(collision_object_names[i], false));
1013 }
1014
1015 const moveit::core::RobotState& cs = ps->getCurrentState();
1016 std::vector<const moveit::core::AttachedBody*> attached_bodies;
1017 cs.getAttachedBodies(attached_bodies);
1018 for (std::size_t i = 0; i < attached_bodies.size(); ++i)
1019 {
1020 QListWidgetItem* item =
1021 new QListWidgetItem(QString::fromStdString(attached_bodies[i]->getName()), ui_->collision_objects_list,
1022 static_cast<int>(i + collision_object_names.size()));
1023 item->setFlags(item->flags() | Qt::ItemIsEditable);
1024 item->setToolTip(item->text());
1025 item->setCheckState(Qt::Checked);
1026 if (to_select.find(attached_bodies[i]->getName()) != to_select.end())
1027 item->setSelected(true);
1028 ui_->collision_objects_list->addItem(item);
1029 known_collision_objects_.push_back(std::make_pair(attached_bodies[i]->getName(), true));
1030 }
1031 }
1032 }
1033
1034 ui_->clear_octomap_button->setEnabled(octomap_in_scene);
1035 ui_->collision_objects_list->blockSignals(old_state);
1036 ui_->collision_objects_list->setUpdatesEnabled(true);
1037 selectedCollisionObjectChanged();
1038}
1039
1040void MotionPlanningFrame::exportGeometryAsTextButtonClicked()
1041{
1042 QString path =
1043 QFileDialog::getSaveFileName(this, tr("Export Scene Geometry"), tr(""), tr("Scene Geometry (*.scene)"));
1044 if (!path.isEmpty())
1045 {
1046 planning_display_->addBackgroundJob([this, path = path.toStdString()] { computeExportGeometryAsText(path); },
1047 "export as text");
1048 }
1049}
1050
1051void MotionPlanningFrame::computeExportGeometryAsText(const std::string& path)
1052{
1053 planning_scene_monitor::LockedPlanningSceneRO ps = planning_display_->getPlanningSceneRO();
1054 if (ps)
1055 {
1056 std::string p = (path.length() < 7 || path.substr(path.length() - 6) != ".scene") ? path + ".scene" : path;
1057 std::ofstream fout(p.c_str());
1058 if (fout.good())
1059 {
1060 ps->saveGeometryToStream(fout);
1061 fout.close();
1062 RCLCPP_INFO(logger_, "Saved current scene geometry to '%s'", p.c_str());
1063 }
1064 else
1065 {
1066 RCLCPP_WARN(logger_, "Unable to save current scene geometry to '%s'", p.c_str());
1067 }
1068 }
1069}
1070
1071void MotionPlanningFrame::computeImportGeometryFromText(const std::string& path)
1072{
1073 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
1074 if (ps)
1075 {
1076 std::ifstream fin(path.c_str());
1077 if (ps->loadGeometryFromStream(fin))
1078 {
1079 RCLCPP_INFO(logger_, "Loaded scene geometry from '%s'", path.c_str());
1080 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
1081 planning_display_->queueRenderSceneGeometry();
1082 setLocalSceneEdited();
1083 }
1084 else
1085 {
1086 QMessageBox::warning(nullptr, "Loading scene geometry",
1087 "Failed to load scene geometry.\n"
1088 "See console output for more details.");
1089 }
1090 }
1091}
1092
1093void MotionPlanningFrame::importGeometryFromTextButtonClicked()
1094{
1095 QString path =
1096 QFileDialog::getOpenFileName(this, tr("Import Scene Geometry"), tr(""), tr("Scene Geometry (*.scene)"));
1097 if (!path.isEmpty())
1098 {
1099 planning_display_->addBackgroundJob([this, path = path.toStdString()] { computeImportGeometryFromText(path); },
1100 "import from text");
1101 }
1102}
1103} // namespace moveit_rviz_plugin
World::ObjectConstPtr ObjectConstPtr
const Eigen::Isometry3d & getPose() const
Get the pose of the attached body relative to the parent link.
const LinkModel * getAttachedLink() const
Get the model of the link this body is attached to.
const moveit::core::FixedTransformsMap & getSubframes() const
Get subframes of this object (relative to the object pose). The returned transforms are guaranteed to...
const std::set< std::string > & getTouchLinks() const
Get the links that the attached body is allowed to touch.
const EigenSTL::vector_Isometry3d & getShapePoses() const
Get the shape poses (the transforms to the shapes of this body, relative to the pose)....
const std::string & getName() const
Get the name of the attached body.
const std::string & getAttachedLinkName() const
Get the name of the link this body is attached to.
const std::vector< shapes::ShapeConstPtr > & getShapes() const
Get the shapes that make up this attached body.
const trajectory_msgs::msg::JointTrajectory & getDetachPosture() const
Return the posture that is necessary for the object to be released, (if any). This is useful for exam...
void attachBody(std::unique_ptr< AttachedBody > attached_body)
Add an attached body to this state.
void getAttachedBodies(std::vector< const AttachedBody * > &attached_bodies) const
Get all bodies attached to the model corresponding to this state.
bool clearAttachedBody(const std::string &id)
Remove the attached body named id. Return false if the object was not found (and thus not removed)....
const AttachedBody * getAttachedBody(const std::string &name) const
Get the attached body named name. Return nullptr if not found.
moveit_warehouse::PlanningSceneStoragePtr planning_scene_storage_
std::shared_ptr< rviz_default_plugins::displays::InteractiveMarker > scene_marker_
void constructPlanningRequest(moveit_msgs::msg::MotionPlanRequest &mreq)
static const std::string OCTOMAP_NS
std::map< std::string, Eigen::Isometry3d, std::less< std::string >, Eigen::aligned_allocator< std::pair< const std::string, Eigen::Isometry3d > > > FixedTransformsMap
Map frame names to the transformation matrix that can transform objects from the frame name to the pl...
bool robotStateMsgToRobotState(const Transforms &tf, const moveit_msgs::msg::RobotState &robot_state, RobotState &state, bool copy_attached_bodies=true)
Convert a robot state msg (with accompanying extra transforms) to a MoveIt robot state.
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::PlanningScene >::ConstPtr PlanningSceneWithMetadata
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::MotionPlanRequest >::ConstPtr MotionPlanRequestWithMetadata
std::string append(const std::string &left, const std::string &right)
visualization_msgs::msg::InteractiveMarker make6DOFMarker(const std::string &name, const geometry_msgs::msg::PoseStamped &stamped, double scale, bool orientation_fixed=false)
version
Definition setup.py:8
name
Definition setup.py:7