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{
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{
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(); });
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 {
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_)));
184 }
185 else
186 scaled_object_.reset();
187 }
188 else
189 scaled_object_.reset();
190 }
191}
192
193void MotionPlanningFrame::sceneScaleStartChange()
194{
195 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
196 if (sel.empty())
197 return;
198 if (planning_display_->getPlanningSceneMonitor() && sel[0]->checkState() == Qt::Unchecked)
199 {
201 if (ps)
202 {
203 scaled_object_ = ps->getWorld()->getObject(sel[0]->text().toStdString());
204 scaled_object_subframes_ = scaled_object_->subframe_poses_;
205 scaled_object_shape_poses_ = scaled_object_->shape_poses_;
206 }
207 }
208}
209
210void MotionPlanningFrame::sceneScaleEndChange()
211{
212 scaled_object_.reset();
213 ui_->scene_scale->setSliderPosition(100);
214}
215
216void MotionPlanningFrame::removeSceneObject()
217{
218 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
219 if (sel.empty())
220 return;
222 if (ps)
223 {
224 for (int i = 0; i < sel.count(); ++i)
225 {
226 if (sel[i]->checkState() == Qt::Unchecked)
227 {
228 ps->getWorldNonConst()->removeObject(sel[i]->text().toStdString());
229 }
230 else
231 {
232 ps->getCurrentStateNonConst().clearAttachedBody(sel[i]->text().toStdString());
233 }
234 }
235 scene_marker_.reset();
236 setLocalSceneEdited();
237 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
239 }
240}
241
242static QString decideStatusText(const collision_detection::CollisionEnv::ObjectConstPtr& obj)
243{
244 QString status_text = "'" + QString::fromStdString(obj->id_) + "' is a collision object with ";
245 if (obj->shapes_.empty())
246 {
247 status_text += "no geometry";
248 }
249 else
250 {
251 std::vector<QString> shape_names;
252 for (const shapes::ShapeConstPtr& shape : obj->shapes_)
253 shape_names.push_back(QString::fromStdString(shapes::shapeStringName(shape.get())));
254 if (shape_names.size() == 1)
255 {
256 status_text += "one " + shape_names[0];
257 }
258 else
259 {
260 status_text += QString::fromStdString(std::to_string(shape_names.size())) + " shapes:";
261 for (const QString& shape_name : shape_names)
262 status_text += " " + shape_name;
263 }
264 status_text += ".";
265 }
266 if (!obj->subframe_poses_.empty())
267 {
268 status_text += subframePosesToQstring(obj->subframe_poses_);
269 }
270 return status_text;
271}
272
273static QString decideStatusText(const moveit::core::AttachedBody* attached_body)
274{
275 QString status_text = "'" + QString::fromStdString(attached_body->getName()) + "' is attached to '" +
276 QString::fromStdString(attached_body->getAttachedLinkName()) + "'.";
277 if (!attached_body->getSubframes().empty())
278 {
279 status_text += subframePosesToQstring(attached_body->getSubframes());
280 }
281 return status_text;
282}
283
284void MotionPlanningFrame::selectedCollisionObjectChanged()
285{
286 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
287 if (sel.empty())
288 {
289 bool old_state = ui_->object_x->blockSignals(true);
290 ui_->object_x->setValue(0.0);
291 ui_->object_x->blockSignals(old_state);
292
293 old_state = ui_->object_y->blockSignals(true);
294 ui_->object_y->setValue(0.0);
295 ui_->object_y->blockSignals(old_state);
296
297 old_state = ui_->object_z->blockSignals(true);
298 ui_->object_z->setValue(0.0);
299 ui_->object_z->blockSignals(old_state);
300
301 old_state = ui_->object_rx->blockSignals(true);
302 ui_->object_rx->setValue(0.0);
303 ui_->object_rx->blockSignals(old_state);
304
305 old_state = ui_->object_ry->blockSignals(true);
306 ui_->object_ry->setValue(0.0);
307 ui_->object_ry->blockSignals(old_state);
308
309 old_state = ui_->object_rz->blockSignals(true);
310 ui_->object_rz->setValue(0.0);
311 ui_->object_rz->blockSignals(old_state);
312
313 ui_->object_status->setText("");
314 scene_marker_.reset();
315 ui_->pose_scale_group_box->setEnabled(false);
316 }
318 {
319 // if this is a CollisionWorld element
320 if (sel[0]->checkState() == Qt::Unchecked)
321 {
322 ui_->pose_scale_group_box->setEnabled(true);
323 bool update_scene_marker = false;
324 Eigen::Isometry3d obj_pose;
325 {
328 ps->getWorld()->getObject(sel[0]->text().toStdString());
329 if (obj)
330 {
331 ui_->object_status->setText(decideStatusText(obj));
332
333 if (obj->shapes_.size() == 1)
334 {
335 obj_pose = obj->pose_; // valid isometry by contract
336 Eigen::Vector3d xyz = obj_pose.linear().eulerAngles(0, 1, 2);
337 update_scene_marker = true; // do the marker update outside locked scope to avoid deadlock
338
339 bool old_state = ui_->object_x->blockSignals(true);
340 ui_->object_x->setValue(obj_pose.translation()[0]);
341 ui_->object_x->blockSignals(old_state);
342
343 old_state = ui_->object_y->blockSignals(true);
344 ui_->object_y->setValue(obj_pose.translation()[1]);
345 ui_->object_y->blockSignals(old_state);
346
347 old_state = ui_->object_z->blockSignals(true);
348 ui_->object_z->setValue(obj_pose.translation()[2]);
349 ui_->object_z->blockSignals(old_state);
350
351 old_state = ui_->object_rx->blockSignals(true);
352 ui_->object_rx->setValue(xyz[0]);
353 ui_->object_rx->blockSignals(old_state);
354
355 old_state = ui_->object_ry->blockSignals(true);
356 ui_->object_ry->setValue(xyz[1]);
357 ui_->object_ry->blockSignals(old_state);
358
359 old_state = ui_->object_rz->blockSignals(true);
360 ui_->object_rz->setValue(xyz[2]);
361 ui_->object_rz->blockSignals(old_state);
362 }
363 }
364 else
365 ui_->object_status->setText("ERROR: '" + sel[0]->text() + "' should be a collision object but it is not");
366 }
367 if (update_scene_marker && ui_->tabWidget->tabText(ui_->tabWidget->currentIndex()).toStdString() == TAB_OBJECTS)
368 {
369 createSceneInteractiveMarker();
370 }
371 }
372 else
373 {
374 ui_->pose_scale_group_box->setEnabled(false);
375 // if it is an attached object
376 scene_marker_.reset();
378 const moveit::core::AttachedBody* attached_body =
379 ps->getCurrentState().getAttachedBody(sel[0]->text().toStdString());
380 if (attached_body)
381 {
382 ui_->object_status->setText(decideStatusText(attached_body));
383 }
384 else
385 {
386 ui_->object_status->setText("ERROR: '" + sel[0]->text() + "' should be an attached object but it is not");
387 }
388 }
389 }
390}
391
392void MotionPlanningFrame::objectPoseValueChanged(double /* value */)
393{
394 updateCollisionObjectPose(true);
395}
396
397void MotionPlanningFrame::updateCollisionObjectPose(bool update_marker_position)
398{
399 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
400 if (sel.empty())
401 return;
403 if (ps)
404 {
405 collision_detection::CollisionEnv::ObjectConstPtr obj = ps->getWorld()->getObject(sel[0]->text().toStdString());
406 if (obj)
407 {
408 Eigen::Isometry3d p;
409 p.translation()[0] = ui_->object_x->value();
410 p.translation()[1] = ui_->object_y->value();
411 p.translation()[2] = ui_->object_z->value();
412
413 p = Eigen::Translation3d(p.translation()) *
414 (Eigen::AngleAxisd(ui_->object_rx->value(), Eigen::Vector3d::UnitX()) *
415 Eigen::AngleAxisd(ui_->object_ry->value(), Eigen::Vector3d::UnitY()) *
416 Eigen::AngleAxisd(ui_->object_rz->value(), Eigen::Vector3d::UnitZ()));
417
418 ps->getWorldNonConst()->setObjectPose(obj->id_, p);
420 setLocalSceneEdited();
421
422 // Update the interactive marker pose to match the manually introduced one
423 if (update_marker_position && scene_marker_)
424 {
425 // p is isometry by construction
426 Eigen::Quaterniond eq(p.linear());
427 scene_marker_->setPose(Ogre::Vector3(ui_->object_x->value(), ui_->object_y->value(), ui_->object_z->value()),
428 Ogre::Quaternion(eq.w(), eq.x(), eq.y(), eq.z()), "");
429 }
430 }
431 }
432}
433
434void MotionPlanningFrame::collisionObjectChanged(QListWidgetItem* item)
435{
436 if (item->type() < static_cast<int>(known_collision_objects_.size()) && planning_display_->getPlanningSceneMonitor())
437 {
438 // if we have a name change
439 if (known_collision_objects_[item->type()].first != item->text().toStdString())
440 {
441 renameCollisionObject(item);
442 }
443 else
444 {
445 bool checked = item->checkState() == Qt::Checked;
446 if (known_collision_objects_[item->type()].second != checked)
447 attachDetachCollisionObject(item);
448 }
449 }
450}
451
452/* Receives feedback from the interactive marker and updates the shape pose in the world accordingly */
453void MotionPlanningFrame::imProcessFeedback(visualization_msgs::msg::InteractiveMarkerFeedback& feedback)
454{
455 if (!planning_display_->getPlanningSceneRO()->knowsFrameTransform(feedback.header.frame_id))
456 {
457 RCLCPP_ERROR_STREAM(logger_,
458 "Frame `" << feedback.header.frame_id << "` unknown doesn't exists in the planning scene");
459 }
460 Eigen::Isometry3d fixed_frame_t_scene_marker;
461 tf2::fromMsg(feedback.pose, fixed_frame_t_scene_marker);
462 Eigen::Isometry3d model_frame_t_scene_marker =
463 planning_display_->getPlanningSceneRO()->getFrameTransform(feedback.header.frame_id) * fixed_frame_t_scene_marker;
464
465 bool old_state = ui_->object_x->blockSignals(true);
466 ui_->object_x->setValue(model_frame_t_scene_marker.translation().x());
467 ui_->object_x->blockSignals(old_state);
468
469 old_state = ui_->object_y->blockSignals(true);
470 ui_->object_y->setValue(model_frame_t_scene_marker.translation().y());
471 ui_->object_y->blockSignals(old_state);
472
473 old_state = ui_->object_z->blockSignals(true);
474 ui_->object_z->setValue(model_frame_t_scene_marker.translation().z());
475 ui_->object_z->blockSignals(old_state);
476
477 Eigen::Vector3d xyz = model_frame_t_scene_marker.linear().eulerAngles(0, 1, 2);
478
479 old_state = ui_->object_rx->blockSignals(true);
480 ui_->object_rx->setValue(xyz[0]);
481 ui_->object_rx->blockSignals(old_state);
482
483 old_state = ui_->object_ry->blockSignals(true);
484 ui_->object_ry->setValue(xyz[1]);
485 ui_->object_ry->blockSignals(old_state);
486
487 old_state = ui_->object_rz->blockSignals(true);
488 ui_->object_rz->setValue(xyz[2]);
489 ui_->object_rz->blockSignals(old_state);
490
491 updateCollisionObjectPose(false);
492}
493
494void MotionPlanningFrame::copySelectedCollisionObject()
495{
496 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
497 if (sel.empty())
498 return;
499
501 if (!ps)
502 return;
503
504 for (const QListWidgetItem* item : sel)
505 {
506 std::string name = item->text().toStdString();
507 collision_detection::CollisionEnv::ObjectConstPtr obj = ps->getWorld()->getObject(name);
508 if (!obj)
509 continue;
510
511 // find a name for the copy
512 name.insert(0, "Copy of ");
513 if (ps->getWorld()->hasObject(name))
514 {
515 name += " ";
516 unsigned int n = 1;
517 while (ps->getWorld()->hasObject(name + std::to_string(n)))
518 n++;
519 name += std::to_string(n);
520 }
521 ps->getWorldNonConst()->addToObject(name, obj->shapes_, obj->shape_poses_);
522 RCLCPP_DEBUG(logger_, "Copied collision object to '%s'", name.c_str());
523 }
524 setLocalSceneEdited();
525 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
526}
527
528void MotionPlanningFrame::computeSaveSceneButtonClicked()
529{
531 {
532 moveit_msgs::msg::PlanningScene msg;
533 planning_display_->getPlanningSceneRO()->getPlanningSceneMsg(msg);
534 try
535 {
536 planning_scene_storage_->removePlanningScene(msg.name);
537 planning_scene_storage_->addPlanningScene(msg);
538 }
539 catch (std::exception& ex)
540 {
541 RCLCPP_ERROR(logger_, "%s", ex.what());
542 }
543
544 planning_display_->addMainLoopJob([this] { populatePlanningSceneTreeView(); });
545 }
546}
547
548void MotionPlanningFrame::computeSaveQueryButtonClicked(const std::string& scene, const std::string& query_name)
549{
550 moveit_msgs::msg::MotionPlanRequest mreq;
553 {
554 try
555 {
556 if (!query_name.empty())
557 planning_scene_storage_->removePlanningQuery(scene, query_name);
558 planning_scene_storage_->addPlanningQuery(mreq, scene, query_name);
559 }
560 catch (std::exception& ex)
561 {
562 RCLCPP_ERROR(logger_, "%s", ex.what());
563 }
564
565 planning_display_->addMainLoopJob([this] { populatePlanningSceneTreeView(); });
566 }
567}
568
569void MotionPlanningFrame::computeDeleteSceneButtonClicked()
570{
572 {
573 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
574 if (!sel.empty())
575 {
576 QTreeWidgetItem* s = sel.front();
577 if (s->type() == ITEM_TYPE_SCENE)
578 {
579 std::string scene = s->text(0).toStdString();
580 try
581 {
582 planning_scene_storage_->removePlanningScene(scene);
583 }
584 catch (std::exception& ex)
585 {
586 RCLCPP_ERROR(logger_, "%s", ex.what());
587 }
588 }
589 else
590 {
591 // if we selected a query name, then we overwrite that query
592 std::string scene = s->parent()->text(0).toStdString();
593 try
594 {
595 planning_scene_storage_->removePlanningScene(scene);
596 }
597 catch (std::exception& ex)
598 {
599 RCLCPP_ERROR(logger_, "%s", ex.what());
600 }
601 }
602 planning_display_->addMainLoopJob([this] { populatePlanningSceneTreeView(); });
603 }
604 }
605}
606
607void MotionPlanningFrame::computeDeleteQueryButtonClicked()
608{
610 {
611 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
612 if (!sel.empty())
613 {
614 QTreeWidgetItem* s = sel.front();
615 if (s->type() == ITEM_TYPE_QUERY)
616 {
617 std::string scene = s->parent()->text(0).toStdString();
618 std::string query_name = s->text(0).toStdString();
619 try
620 {
621 planning_scene_storage_->removePlanningQuery(scene, query_name);
622 }
623 catch (std::exception& ex)
624 {
625 RCLCPP_ERROR(logger_, "%s", ex.what());
626 }
627 planning_display_->addMainLoopJob([this, s] { computeDeleteQueryButtonClickedHelper(s); });
628 }
629 }
630 }
631}
632
633void MotionPlanningFrame::computeDeleteQueryButtonClickedHelper(QTreeWidgetItem* s)
634{
635 ui_->planning_scene_tree->setUpdatesEnabled(false);
636 s->parent()->removeChild(s);
637 ui_->planning_scene_tree->setUpdatesEnabled(true);
638}
639
640void MotionPlanningFrame::checkPlanningSceneTreeEnabledButtons()
641{
642 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
643 if (sel.empty())
644 {
645 ui_->load_scene_button->setEnabled(false);
646 ui_->load_query_button->setEnabled(false);
647 ui_->save_query_button->setEnabled(false);
648 ui_->delete_scene_button->setEnabled(false);
649 }
650 else
651 {
652 ui_->save_query_button->setEnabled(true);
653
654 QTreeWidgetItem* s = sel.front();
655
656 // if the item is a PlanningScene
657 if (s->type() == ITEM_TYPE_SCENE)
658 {
659 ui_->load_scene_button->setEnabled(true);
660 ui_->load_query_button->setEnabled(false);
661 ui_->delete_scene_button->setEnabled(true);
662 ui_->delete_query_button->setEnabled(false);
663 ui_->save_query_button->setEnabled(true);
664 }
665 else
666 {
667 // if the item is a query
668 ui_->load_scene_button->setEnabled(false);
669 ui_->load_query_button->setEnabled(true);
670 ui_->delete_scene_button->setEnabled(false);
671 ui_->delete_query_button->setEnabled(true);
672 }
673 }
674}
675
676void MotionPlanningFrame::computeLoadSceneButtonClicked()
677{
679 {
680 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
681 if (!sel.empty())
682 {
683 QTreeWidgetItem* s = sel.front();
684 if (s->type() == ITEM_TYPE_SCENE)
685 {
686 std::string scene = s->text(0).toStdString();
687 RCLCPP_DEBUG(logger_, "Attempting to load scene '%s'", scene.c_str());
688
690 bool got_ps = false;
691 try
692 {
693 got_ps = planning_scene_storage_->getPlanningScene(scene_m, scene);
694 }
695 catch (std::exception& ex)
696 {
697 RCLCPP_ERROR(logger_, "%s", ex.what());
698 }
699
700 if (got_ps)
701 {
702 RCLCPP_INFO(logger_, "Loaded scene '%s'", scene.c_str());
704 {
705 if (scene_m->robot_model_name != planning_display_->getRobotModel()->getName())
706 {
707 RCLCPP_INFO(logger_,
708 "Scene '%s' was saved for robot '%s' but we are using robot '%s'. Using scene geometry only",
709 scene.c_str(), scene_m->robot_model_name.c_str(),
710 planning_display_->getRobotModel()->getName().c_str());
711 planning_scene_world_publisher_->publish(scene_m->world);
712 // publish the parts that are not in the world
713 moveit_msgs::msg::PlanningScene diff;
714 diff.is_diff = true;
715 diff.name = scene_m->name;
716 planning_scene_publisher_->publish(diff);
717 }
718 else
719 planning_scene_publisher_->publish(static_cast<const moveit_msgs::msg::PlanningScene&>(*scene_m));
720 }
721 else
722 planning_scene_publisher_->publish(static_cast<const moveit_msgs::msg::PlanningScene&>(*scene_m));
723 }
724 else
725 {
726 RCLCPP_WARN(logger_, "Failed to load scene '%s'. Has the message format changed since the scene was saved?",
727 scene.c_str());
728 }
729 }
730 }
731 }
732}
733
734void MotionPlanningFrame::computeLoadQueryButtonClicked()
735{
737 {
738 QList<QTreeWidgetItem*> sel = ui_->planning_scene_tree->selectedItems();
739 if (!sel.empty())
740 {
741 QTreeWidgetItem* s = sel.front();
742 if (s->type() == ITEM_TYPE_QUERY)
743 {
744 std::string scene = s->parent()->text(0).toStdString();
745 std::string query_name = s->text(0).toStdString();
746
748 bool got_q = false;
749 try
750 {
751 got_q = planning_scene_storage_->getPlanningQuery(mp, scene, query_name);
752 }
753 catch (std::exception& ex)
754 {
755 RCLCPP_ERROR(logger_, "%s", ex.what());
756 }
757
758 if (got_q)
759 {
760 moveit::core::RobotStatePtr start_state(
763 mp->start_state, *start_state);
765
766 auto goal_state = std::make_shared<moveit::core::RobotState>(*planning_display_->getQueryGoalState());
767 for (const moveit_msgs::msg::Constraints& goal_constraint : mp->goal_constraints)
768 {
769 if (!goal_constraint.joint_constraints.empty())
770 {
771 std::map<std::string, double> vals;
772 for (const moveit_msgs::msg::JointConstraint& joint_constraint : goal_constraint.joint_constraints)
773 vals[joint_constraint.joint_name] = joint_constraint.position;
774 goal_state->setVariablePositions(vals);
775 break;
776 }
777 }
779 }
780 else
781 {
782 RCLCPP_ERROR(logger_,
783 "Failed to load planning query '%s'. Has the message format changed since the query was saved?",
784 query_name.c_str());
785 }
786 }
787 }
788 }
789}
790
791visualization_msgs::msg::InteractiveMarker
792MotionPlanningFrame::createObjectMarkerMsg(const collision_detection::CollisionEnv::ObjectConstPtr& obj)
793{
794 Eigen::Vector3d center;
795 double scale;
796 shapes::computeShapeBoundingSphere(obj->shapes_[0].get(), center, scale);
797 geometry_msgs::msg::PoseStamped shape_pose = tf2::toMsg(tf2::Stamped<Eigen::Isometry3d>(
798 obj->pose_, std::chrono::system_clock::now(), planning_display_->getRobotModel()->getModelFrame()));
799 // TODO(felixvd): Consider where to place the object marker.
800 // obj->pose*obj->shape_poses_[0] is backwards compatible, sits on the visible part of
801 // the object, and is more difficult to implement now.
802 // obj->pose is easier to implement and makes more sense.
803 scale = (scale + center.cwiseAbs().maxCoeff()) * 2.0 * 1.2; // add padding of 20% size
804
805 // create an interactive marker msg for the given shape
806 visualization_msgs::msg::InteractiveMarker imarker =
807 robot_interaction::make6DOFMarker("marker_scene_object", shape_pose, scale);
808 imarker.description = obj->id_;
809 interactive_markers::autoComplete(imarker);
810 return imarker;
811}
812
813void MotionPlanningFrame::createSceneInteractiveMarker()
814{
815 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
816 if (sel.empty())
817 return;
818
820 if (!ps)
821 return;
822
824 ps->getWorld()->getObject(sel[0]->text().toStdString());
825 if (obj && obj->shapes_.size() == 1)
826 {
827 scene_marker_ = std::make_shared<rviz_default_plugins::displays::InteractiveMarker>(
828 planning_display_->getSceneNode(), context_);
829 scene_marker_->processMessage(createObjectMarkerMsg(obj));
830 scene_marker_->setShowAxes(false);
831
832 // Connect signals
833 connect(scene_marker_.get(), SIGNAL(userFeedback(visualization_msgs::msg::InteractiveMarkerFeedback&)), this,
834 SLOT(imProcessFeedback(visualization_msgs::msg::InteractiveMarkerFeedback&)));
835 }
836 else
837 {
838 scene_marker_.reset();
839 }
840}
841
842void MotionPlanningFrame::renameCollisionObject(QListWidgetItem* item)
843{
844 long unsigned int version = known_collision_objects_version_;
845 if (item->text().isEmpty())
846 {
847 QMessageBox::warning(this, "Invalid object name", "Cannot set an empty object name.");
848 if (version == known_collision_objects_version_)
849 item->setText(QString::fromStdString(known_collision_objects_[item->type()].first));
850 return;
851 }
852
853 std::string item_text = item->text().toStdString();
854 bool already_exists = planning_display_->getPlanningSceneRO()->getWorld()->hasObject(item_text);
855 if (!already_exists)
856 already_exists = planning_display_->getPlanningSceneRO()->getCurrentState().hasAttachedBody(item_text);
857 if (already_exists)
858 {
859 QMessageBox::warning(this, "Duplicate object name",
860 QString("The name '")
861 .append(item->text())
862 .append("' already exists. Not renaming object ")
863 .append((known_collision_objects_[item->type()].first.c_str())));
864 if (version == known_collision_objects_version_)
865 item->setText(QString::fromStdString(known_collision_objects_[item->type()].first));
866 return;
867 }
868
869 if (item->checkState() == Qt::Unchecked)
870 {
873 ps->getWorld()->getObject(known_collision_objects_[item->type()].first);
874 if (obj)
875 {
876 known_collision_objects_[item->type()].first = item_text;
877 ps->getWorldNonConst()->removeObject(obj->id_);
878 ps->getWorldNonConst()->addToObject(known_collision_objects_[item->type()].first, obj->pose_, obj->shapes_,
879 obj->shape_poses_);
880 ps->getWorldNonConst()->setSubframesOfObject(obj->id_, obj->subframe_poses_);
881 if (scene_marker_)
882 {
883 scene_marker_.reset();
884 planning_display_->addMainLoopJob([this] { createSceneInteractiveMarker(); });
885 }
886 }
887 }
888 else
889 {
890 // rename attached body
892 moveit::core::RobotState& cs = ps->getCurrentStateNonConst();
893 const moveit::core::AttachedBody* ab = cs.getAttachedBody(known_collision_objects_[item->type()].first);
894 if (ab)
895 {
896 known_collision_objects_[item->type()].first = item_text;
897 auto new_ab = std::make_unique<moveit::core::AttachedBody>(
898 ab->getAttachedLink(), known_collision_objects_[item->type()].first, ab->getPose(), ab->getShapes(),
899 ab->getShapePoses(), ab->getTouchLinks(), ab->getDetachPosture(), ab->getSubframes());
900 cs.clearAttachedBody(ab->getName());
901 cs.attachBody(std::move(new_ab));
902 }
903 }
904 setLocalSceneEdited();
905}
906
907void MotionPlanningFrame::attachDetachCollisionObject(QListWidgetItem* item)
908{
909 long unsigned int version = known_collision_objects_version_;
910 bool checked = item->checkState() == Qt::Checked;
911 std::pair<std::string, bool> data = known_collision_objects_[item->type()];
912 moveit_msgs::msg::AttachedCollisionObject aco;
913
914 if (checked) // we need to attach a known collision object
915 {
916 QStringList links;
917 const std::vector<std::string>& links_std = planning_display_->getRobotModel()->getLinkModelNames();
918 for (const std::string& link : links_std)
919 links.append(QString::fromStdString(link));
920 bool ok = false;
921 QString response =
922 QInputDialog::getItem(this, tr("Select Link Name"), tr("Choose the link to attach to:"), links, 0, false, &ok);
923 if (!ok)
924 {
925 if (version == known_collision_objects_version_)
926 item->setCheckState(Qt::Unchecked);
927 return;
928 }
929 aco.link_name = response.toStdString();
930 aco.object.id = data.first;
931 aco.object.operation = moveit_msgs::msg::CollisionObject::ADD;
932 }
933 else // we need to detach an attached object
934 {
936 const moveit::core::AttachedBody* attached_body = ps->getCurrentState().getAttachedBody(data.first);
937 if (attached_body)
938 {
939 aco.link_name = attached_body->getAttachedLinkName();
940 aco.object.id = attached_body->getName();
941 aco.object.operation = moveit_msgs::msg::CollisionObject::REMOVE;
942 }
943 }
944
946 {
948 // we loop through the list in case updates were received since the start of the function
949 for (std::pair<std::string, bool>& known_collision_object : known_collision_objects_)
950 {
951 if (known_collision_object.first == data.first)
952 {
953 known_collision_object.second = checked;
954 break;
955 }
956 }
957 ps->processAttachedCollisionObjectMsg(aco);
958 rs = ps->getCurrentState();
959 }
960
961 selectedCollisionObjectChanged();
962 setLocalSceneEdited();
965}
966
967void MotionPlanningFrame::populateCollisionObjectsList()
968{
969 ui_->collision_objects_list->setUpdatesEnabled(false);
970 bool old_state = ui_->collision_objects_list->blockSignals(true);
971 bool octomap_in_scene = false;
972
973 {
974 QList<QListWidgetItem*> sel = ui_->collision_objects_list->selectedItems();
975 std::set<std::string> to_select;
976 for (QListWidgetItem* item : sel)
977 to_select.insert(item->text().toStdString());
978 ui_->collision_objects_list->clear();
979 known_collision_objects_.clear();
980 known_collision_objects_version_++;
981
983 if (ps)
984 {
985 const std::vector<std::string>& collision_object_names = ps->getWorld()->getObjectIds();
986 for (std::size_t i = 0; i < collision_object_names.size(); ++i)
987 {
988 if (collision_object_names[i] == planning_scene::PlanningScene::OCTOMAP_NS)
989 {
990 octomap_in_scene = true;
991 continue;
992 }
993
994 QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(collision_object_names[i]),
995 ui_->collision_objects_list, static_cast<int>(i));
996 item->setFlags(item->flags() | Qt::ItemIsEditable);
997 item->setToolTip(item->text());
998 item->setCheckState(Qt::Unchecked);
999 if (to_select.find(collision_object_names[i]) != to_select.end())
1000 item->setSelected(true);
1001 ui_->collision_objects_list->addItem(item);
1002 known_collision_objects_.push_back(std::make_pair(collision_object_names[i], false));
1003 }
1004
1005 const moveit::core::RobotState& cs = ps->getCurrentState();
1006 std::vector<const moveit::core::AttachedBody*> attached_bodies;
1007 cs.getAttachedBodies(attached_bodies);
1008 for (std::size_t i = 0; i < attached_bodies.size(); ++i)
1009 {
1010 QListWidgetItem* item =
1011 new QListWidgetItem(QString::fromStdString(attached_bodies[i]->getName()), ui_->collision_objects_list,
1012 static_cast<int>(i + collision_object_names.size()));
1013 item->setFlags(item->flags() | Qt::ItemIsEditable);
1014 item->setToolTip(item->text());
1015 item->setCheckState(Qt::Checked);
1016 if (to_select.find(attached_bodies[i]->getName()) != to_select.end())
1017 item->setSelected(true);
1018 ui_->collision_objects_list->addItem(item);
1019 known_collision_objects_.push_back(std::make_pair(attached_bodies[i]->getName(), true));
1020 }
1021 }
1022 }
1023
1024 ui_->clear_octomap_button->setEnabled(octomap_in_scene);
1025 ui_->collision_objects_list->blockSignals(old_state);
1026 ui_->collision_objects_list->setUpdatesEnabled(true);
1027 selectedCollisionObjectChanged();
1028}
1029
1030void MotionPlanningFrame::exportGeometryAsTextButtonClicked()
1031{
1032 QString path =
1033 QFileDialog::getSaveFileName(this, tr("Export Scene Geometry"), tr(""), tr("Scene Geometry (*.scene)"));
1034 if (!path.isEmpty())
1035 {
1036 planning_display_->addBackgroundJob([this, path = path.toStdString()] { computeExportGeometryAsText(path); },
1037 "export as text");
1038 }
1039}
1040
1041void MotionPlanningFrame::computeExportGeometryAsText(const std::string& path)
1042{
1044 if (ps)
1045 {
1046 std::string p = (path.length() < 7 || path.substr(path.length() - 6) != ".scene") ? path + ".scene" : path;
1047 std::ofstream fout(p.c_str());
1048 if (fout.good())
1049 {
1050 ps->saveGeometryToStream(fout);
1051 fout.close();
1052 RCLCPP_INFO(logger_, "Saved current scene geometry to '%s'", p.c_str());
1053 }
1054 else
1055 RCLCPP_WARN(logger_, "Unable to save current scene geometry to '%s'", p.c_str());
1056 }
1057}
1058
1059void MotionPlanningFrame::computeImportGeometryFromText(const std::string& path)
1060{
1062 if (ps)
1063 {
1064 std::ifstream fin(path.c_str());
1065 if (ps->loadGeometryFromStream(fin))
1066 {
1067 RCLCPP_INFO(logger_, "Loaded scene geometry from '%s'", path.c_str());
1068 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
1070 setLocalSceneEdited();
1071 }
1072 else
1073 {
1074 QMessageBox::warning(nullptr, "Loading scene geometry",
1075 "Failed to load scene geometry.\n"
1076 "See console output for more details.");
1077 }
1078 }
1079}
1080
1081void MotionPlanningFrame::importGeometryFromTextButtonClicked()
1082{
1083 QString path =
1084 QFileDialog::getOpenFileName(this, tr("Import Scene Geometry"), tr(""), tr("Scene Geometry (*.scene)"));
1085 if (!path.isEmpty())
1086 {
1087 planning_display_->addBackgroundJob([this, path = path.toStdString()] { computeImportGeometryFromText(path); },
1088 "import from text");
1089 }
1090}
1091} // namespace moveit_rviz_plugin
World::ObjectConstPtr ObjectConstPtr
Object defining bodies that can be attached to robot links.
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...
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition robot_state.h:90
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.
void setQueryGoalState(const moveit::core::RobotState &goal)
void setQueryStartState(const moveit::core::RobotState &start)
void updateQueryStates(const moveit::core::RobotState &current_state)
moveit::core::RobotStateConstPtr getQueryStartState() const
moveit::core::RobotStateConstPtr getQueryGoalState() const
moveit_warehouse::PlanningSceneStoragePtr planning_scene_storage_
std::shared_ptr< rviz_default_plugins::displays::InteractiveMarker > scene_marker_
void constructPlanningRequest(moveit_msgs::msg::MotionPlanRequest &mreq)
planning_scene_monitor::LockedPlanningSceneRO getPlanningSceneRO() const
get read-only access to planning scene
planning_scene_monitor::LockedPlanningSceneRW getPlanningSceneRW()
get write access to planning scene
void addMainLoopJob(const std::function< void()> &job)
queue the execution of this function for the next time the main update() loop gets called
const moveit::core::RobotModelConstPtr & getRobotModel() const
void addBackgroundJob(const std::function< void()> &job, const std::string &name)
const planning_scene_monitor::PlanningSceneMonitorPtr & getPlanningSceneMonitor()
static const std::string OCTOMAP_NS
This is a convenience class for obtaining access to an instance of a locked PlanningScene.
This is a convenience class for obtaining access to an instance of a locked PlanningScene.
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...
Definition transforms.h:53
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
Definition world.h:49