robot_poses_widget.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: Dave Coleman */
36 
37 // SA
38 #include "robot_poses_widget.h"
39 #include "header_widget.h"
40 #include <moveit_msgs/JointLimits.h>
41 // Qt
42 #include <QApplication>
43 #include <QComboBox>
44 #include <QDoubleValidator>
45 #include <QFontMetrics>
46 #include <QFormLayout>
47 #include <QLabel>
48 #include <QLineEdit>
49 #include <QMessageBox>
50 #include <QPushButton>
51 #include <QScrollArea>
52 #include <QSlider>
53 #include <QStackedWidget>
54 #include <QTableWidget>
55 
57 #include <moveit_msgs/DisplayRobotState.h>
59 
60 namespace moveit_setup_assistant
61 {
62 // ******************************************************************************************
63 // Outer User Interface for MoveIt Configuration Assistant
64 // ******************************************************************************************
65 RobotPosesWidget::RobotPosesWidget(QWidget* parent, const MoveItConfigDataPtr& config_data)
66  : SetupScreenWidget(parent), config_data_(config_data)
67 {
68  // Set pointer to null so later we can tell if we need to delete it
69  joint_list_layout_ = nullptr;
70 
71  // Basic widget container
72  QVBoxLayout* layout = new QVBoxLayout();
73 
74  // Top Header Area ------------------------------------------------
75 
76  HeaderWidget* header =
77  new HeaderWidget("Define Robot Poses",
78  "Create poses for the robot. Poses are defined as sets of joint values for "
79  "particular planning groups. This is useful for things like <i>home position</i>. "
80  "The <i>first</i> listed pose will be the robot's initial pose in simulation.",
81  this);
82  layout->addWidget(header);
83 
84  // Create contents screens ---------------------------------------
85  pose_list_widget_ = createContentsWidget();
86  pose_edit_widget_ = createEditWidget();
87 
88  // Create stacked layout -----------------------------------------
89  stacked_widget_ = new QStackedWidget(this);
90  stacked_widget_->addWidget(pose_list_widget_); // screen index 0
91  stacked_widget_->addWidget(pose_edit_widget_); // screen index 1
92  layout->addWidget(stacked_widget_);
93 
94  // Finish Layout --------------------------------------------------
95  this->setLayout(layout);
96 
97  // Create joint publisher -----------------------------------------
98  ros::NodeHandle nh;
99 
100  // Create scene publisher for later use
101  pub_robot_state_ = nh.advertise<moveit_msgs::DisplayRobotState>(MOVEIT_ROBOT_STATE, 1);
102 
103  // Set the planning scene
104  config_data_->getPlanningScene()->setName("MoveIt Planning Scene");
105 
106  // Collision Detection initializtion -------------------------------
107 
108  // Setup the request
109  request.contacts = true;
110  request.max_contacts = 1;
111  request.max_contacts_per_pair = 1;
112  request.verbose = false;
113 }
114 
115 // ******************************************************************************************
116 // Create the main content widget
117 // ******************************************************************************************
118 QWidget* RobotPosesWidget::createContentsWidget()
119 {
120  // Main widget
121  QWidget* content_widget = new QWidget(this);
122 
123  // Basic widget container
124  QVBoxLayout* layout = new QVBoxLayout(this);
125 
126  // Table ------------ ------------------------------------------------
127 
128  data_table_ = new QTableWidget(this);
129  data_table_->setColumnCount(2);
130  data_table_->setSortingEnabled(true);
131  data_table_->setSelectionBehavior(QAbstractItemView::SelectRows);
132  connect(data_table_, &QTableWidget::cellDoubleClicked, this, &RobotPosesWidget::editDoubleClicked);
133  connect(data_table_, &QTableWidget::currentCellChanged, this, &RobotPosesWidget::previewClicked);
134  layout->addWidget(data_table_);
135 
136  // Set header labels
137  QStringList header_list;
138  header_list.append("Pose Name");
139  header_list.append("Group Name");
140  data_table_->setHorizontalHeaderLabels(header_list);
141 
142  // Bottom Buttons --------------------------------------------------
143 
144  QHBoxLayout* controls_layout = new QHBoxLayout();
145 
146  // Set Default Button
147  QPushButton* btn_default = new QPushButton("&Show Default Pose", this);
148  btn_default->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
149  btn_default->setMaximumWidth(300);
150  connect(btn_default, &QPushButton::clicked, this, &RobotPosesWidget::showDefaultPose);
151  controls_layout->addWidget(btn_default);
152  controls_layout->setAlignment(btn_default, Qt::AlignLeft);
153 
154  // Set play button
155  QPushButton* btn_play = new QPushButton("&MoveIt", this);
156  btn_play->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
157  btn_play->setMaximumWidth(300);
158  connect(btn_play, &QPushButton::clicked, this, &RobotPosesWidget::playPoses);
159  controls_layout->addWidget(btn_play);
160  controls_layout->setAlignment(btn_play, Qt::AlignLeft);
161 
162  // Spacer
163  controls_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
164 
165  // Edit Selected Button
166  btn_edit_ = new QPushButton("&Edit Selected", this);
167  btn_edit_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
168  btn_edit_->setMaximumWidth(300);
169  btn_edit_->hide(); // show once we know if there are existing poses
170  connect(btn_edit_, &QPushButton::clicked, this, &RobotPosesWidget::editSelected);
171  controls_layout->addWidget(btn_edit_);
172  controls_layout->setAlignment(btn_edit_, Qt::AlignRight);
173 
174  // Delete
175  btn_delete_ = new QPushButton("&Delete Selected", this);
176  connect(btn_delete_, &QPushButton::clicked, this, &RobotPosesWidget::deleteSelected);
177  controls_layout->addWidget(btn_delete_);
178  controls_layout->setAlignment(btn_delete_, Qt::AlignRight);
179 
180  // Add Group Button
181  QPushButton* btn_add = new QPushButton("&Add Pose", this);
182  btn_add->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
183  btn_add->setMaximumWidth(300);
184  connect(btn_add, &QPushButton::clicked, this, &RobotPosesWidget::showNewScreen);
185  controls_layout->addWidget(btn_add);
186  controls_layout->setAlignment(btn_add, Qt::AlignRight);
187 
188  // Add layout
189  layout->addLayout(controls_layout);
190 
191  // Set layout -----------------------------------------------------
192  content_widget->setLayout(layout);
193 
194  return content_widget;
195 }
196 
197 // ******************************************************************************************
198 // Create the edit widget
199 // ******************************************************************************************
200 QWidget* RobotPosesWidget::createEditWidget()
201 {
202  // Main widget
203  QWidget* edit_widget = new QWidget(this);
204  // Layout
205  QVBoxLayout* layout = new QVBoxLayout();
206 
207  // 2 columns -------------------------------------------------------
208 
209  QHBoxLayout* columns_layout = new QHBoxLayout();
210  QVBoxLayout* column1 = new QVBoxLayout();
211  column2_ = new QVBoxLayout();
212 
213  // Column 1 --------------------------------------------------------
214 
215  // Simple form -------------------------------------------
216  QFormLayout* form_layout = new QFormLayout();
217  // form_layout->setContentsMargins( 0, 15, 0, 15 );
218  form_layout->setRowWrapPolicy(QFormLayout::WrapAllRows);
219 
220  // Name input
221  pose_name_field_ = new QLineEdit(this);
222  // pose_name_field_->setMaximumWidth( 300 );
223  form_layout->addRow("Pose Name:", pose_name_field_);
224 
225  // Group name input
226  group_name_field_ = new QComboBox(this);
227  group_name_field_->setEditable(false);
228  // Connect the signal for changes to the drop down box
229  connect(group_name_field_, &QComboBox::currentTextChanged, this, &RobotPosesWidget::loadJointSliders);
230  // group_name_field_->setMaximumWidth( 300 );
231  form_layout->addRow("Planning Group:", group_name_field_);
232 
233  // Indicator that robot is in collision or not
234  collision_warning_ = new QLabel("<font color='red'><b>Robot in Collision State</b></font>", this);
235  collision_warning_->setTextFormat(Qt::RichText);
236  collision_warning_->hide(); // show later
237  form_layout->addRow(" ", collision_warning_);
238 
239  column1->addLayout(form_layout);
240  columns_layout->addLayout(column1);
241 
242  // Column 2 --------------------------------------------------------
243 
244  // Box to hold joint sliders
245  joint_list_widget_ = new QWidget(this);
246 
247  // Create scroll area
248  scroll_area_ = new QScrollArea(this);
249  scroll_area_->setWidget(joint_list_widget_);
250  scroll_area_->setWidgetResizable(true);
251 
252  column2_->addWidget(scroll_area_);
253 
254  columns_layout->addLayout(column2_);
255 
256  // Set columns in main layout
257  layout->addLayout(columns_layout);
258 
259  // Bottom Buttons --------------------------------------------------
260 
261  QHBoxLayout* controls_layout = new QHBoxLayout();
262  controls_layout->setContentsMargins(0, 25, 0, 15);
263 
264  // Spacer
265  controls_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
266 
267  // Save
268  btn_save_ = new QPushButton("&Save", this);
269  btn_save_->setMaximumWidth(200);
270  connect(btn_save_, &QPushButton::clicked, this, &RobotPosesWidget::doneEditing);
271  controls_layout->addWidget(btn_save_);
272  controls_layout->setAlignment(btn_save_, Qt::AlignRight);
273 
274  // Cancel
275  btn_cancel_ = new QPushButton("&Cancel", this);
276  btn_cancel_->setMaximumWidth(200);
277  connect(btn_cancel_, &QPushButton::clicked, this, &RobotPosesWidget::cancelEditing);
278  controls_layout->addWidget(btn_cancel_);
279  controls_layout->setAlignment(btn_cancel_, Qt::AlignRight);
280 
281  // Add layout
282  layout->addLayout(controls_layout);
283 
284  // Set layout -----------------------------------------------------
285  edit_widget->setLayout(layout);
286 
287  return edit_widget;
288 }
289 
290 // ******************************************************************************************
291 // Show edit screen for creating a new pose
292 // ******************************************************************************************
293 void RobotPosesWidget::showNewScreen()
294 {
295  // Switch to screen - do this before clearEditText()
296  stacked_widget_->setCurrentIndex(1);
297 
298  // Remember that this is a new pose
299  current_edit_pose_ = nullptr;
300 
301  // Manually send the load joint sliders signal
302  if (!group_name_field_->currentText().isEmpty())
303  loadJointSliders(group_name_field_->currentText());
304 
305  // Clear previous data
306  pose_name_field_->setText("");
307 
308  // Announce that this widget is in modal mode
309  Q_EMIT isModal(true);
310 }
311 
312 // ******************************************************************************************
313 // Edit whatever element is selected
314 // ******************************************************************************************
315 void RobotPosesWidget::editDoubleClicked(int /*row*/, int /*column*/)
316 {
317  // We'll just base the edit on the selection highlight
318  editSelected();
319 }
320 
321 // ******************************************************************************************
322 // Preview whatever element is selected
323 // ******************************************************************************************
324 void RobotPosesWidget::previewClicked(int row, int /*column*/, int /*previous_row*/, int /*previous_column*/)
325 {
326  QTableWidgetItem* name = data_table_->item(row, 0);
327  QTableWidgetItem* group = data_table_->item(row, 1);
328 
329  // nullptr check before dereferencing
330  if (name && group)
331  {
332  // Find the selected in datastructure
333  srdf::Model::GroupState* pose = findPoseByName(name->text().toStdString(), group->text().toStdString());
334 
335  showPose(pose);
336  }
337 }
338 
339 // ******************************************************************************************
340 // Show the robot in the current pose
341 // ******************************************************************************************
342 void RobotPosesWidget::showPose(srdf::Model::GroupState* pose)
343 {
344  // Set the joints based on the SRDF pose
345  moveit::core::RobotState& robot_state = config_data_->getPlanningScene()->getCurrentStateNonConst();
346  for (std::map<std::string, std::vector<double> >::const_iterator value_it = pose->joint_values_.begin();
347  value_it != pose->joint_values_.end(); ++value_it)
348  {
349  robot_state.setJointPositions(value_it->first, value_it->second);
350  }
351 
352  // Update the joints
353  publishJoints();
354 
355  // Unhighlight all links
356  Q_EMIT unhighlightAll();
357 
358  // Highlight group
359  Q_EMIT highlightGroup(pose->group_);
360 }
361 
362 // ******************************************************************************************
363 // Show the robot in its default joint positions
364 // ******************************************************************************************
365 void RobotPosesWidget::showDefaultPose()
366 {
367  moveit::core::RobotState& robot_state = config_data_->getPlanningScene()->getCurrentStateNonConst();
368  robot_state.setToDefaultValues();
369 
370  // Update the joints
371  publishJoints();
372 
373  // Unhighlight all links
374  Q_EMIT unhighlightAll();
375 }
376 
377 // ******************************************************************************************
378 // Play through the poses
379 // ******************************************************************************************
380 void RobotPosesWidget::playPoses()
381 {
382  // Loop through each pose and play them
383  for (std::vector<srdf::Model::GroupState>::iterator pose_it = config_data_->srdf_->group_states_.begin();
384  pose_it != config_data_->srdf_->group_states_.end(); ++pose_it)
385  {
386  ROS_INFO_STREAM("Showing pose " << pose_it->name_);
387  showPose(&(*pose_it));
388  ros::Duration(0.05).sleep();
389  QApplication::processEvents();
390  ros::Duration(0.45).sleep();
391  }
392 }
393 
394 // ******************************************************************************************
395 // Edit whatever element is selected
396 // ******************************************************************************************
397 void RobotPosesWidget::editSelected()
398 {
399  const auto& ranges = data_table_->selectedRanges();
400  if (ranges.empty())
401  return;
402  edit(ranges[0].bottomRow());
403 }
404 
405 // ******************************************************************************************
406 // Edit pose
407 // ******************************************************************************************
408 void RobotPosesWidget::edit(int row)
409 {
410  const std::string& name = data_table_->item(row, 0)->text().toStdString();
411  const std::string& group = data_table_->item(row, 1)->text().toStdString();
412 
413  // Find the selected in datastruture
414  srdf::Model::GroupState* pose = findPoseByName(name, group);
415  current_edit_pose_ = pose;
416 
417  // Set pose name
418  pose_name_field_->setText(pose->name_.c_str());
419 
420  // Set group:
421  int index = group_name_field_->findText(pose->group_.c_str());
422  if (index == -1)
423  {
424  QMessageBox::critical(this, "Error Loading", "Unable to find group name in drop down box");
425  return;
426  }
427  group_name_field_->setCurrentIndex(index);
428 
429  showPose(pose);
430 
431  // Switch to screen - do this before setCurrentIndex
432  stacked_widget_->setCurrentIndex(1);
433 
434  // Announce that this widget is in modal mode
435  Q_EMIT isModal(true);
436 
437  // Manually send the load joint sliders signal
438  loadJointSliders(QString(pose->group_.c_str()));
439 }
440 
441 // ******************************************************************************************
442 // Populate the combo dropdown box with avail group names
443 // ******************************************************************************************
444 void RobotPosesWidget::loadGroupsComboBox()
445 {
446  // Remove all old groups
447  group_name_field_->clear();
448 
449  // Add all group names to combo box
450  for (srdf::Model::Group& group : config_data_->srdf_->groups_)
451  {
452  group_name_field_->addItem(group.name_.c_str());
453  }
454 }
455 
456 // ******************************************************************************************
457 // Load the joint sliders based on group selected from combo box
458 // ******************************************************************************************
459 void RobotPosesWidget::loadJointSliders(const QString& selected)
460 {
461  // Ignore this event if the combo box is empty. This occurs when clearing the combo box and reloading with the
462  // newest groups. Also ignore if we are not on the edit screen
463  if (!group_name_field_->count() || selected.isEmpty() || stacked_widget_->currentIndex() == 0)
464  return;
465 
466  // Get group name from input
467  const std::string group_name = selected.toStdString();
468 
469  // Check that joint model exist
470  if (!config_data_->getRobotModel()->hasJointModelGroup(group_name))
471  {
472  QMessageBox::critical(this, "Error Loading",
473  QString("Unable to find joint model group for group: ")
474  .append(group_name.c_str())
475  .append(" Are you sure this group has associated joints/links?"));
476  return;
477  }
478 
479  // Delete old sliders from joint_list_layout_ if this has been loaded before
480  if (joint_list_layout_)
481  {
482  delete joint_list_layout_;
483  qDeleteAll(joint_list_widget_->children());
484  }
485 
486  // Create layout again
487  joint_list_layout_ = new QVBoxLayout();
488  joint_list_widget_->setLayout(joint_list_layout_);
489  joint_list_widget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
490 
491  // Get list of associated joints
492  const moveit::core::JointModelGroup* joint_model_group =
493  config_data_->getRobotModel()->getJointModelGroup(group_name);
494  const auto& robot_state = config_data_->getPlanningScene()->getCurrentState();
495 
496  // Iterate through the joints
497  for (const moveit::core::JointModel* joint_model : joint_model_group->getJointModels())
498  {
499  if (joint_model->getVariableCount() != 1 || // only consider 1-variable joints
500  joint_model->isPassive() || // ignore passive
501  joint_model->getMimic()) // and mimic joints
502  continue;
503 
504  double init_value = robot_state.getVariablePosition(joint_model->getVariableNames()[0]);
505 
506  // For each joint in group add slider
507  SliderWidget* sw = new SliderWidget(this, joint_model, init_value);
508  joint_list_layout_->addWidget(sw);
509 
510  // Connect value change event
511  connect(sw, &SliderWidget::jointValueChanged, this, &RobotPosesWidget::updateRobotModel);
512  }
513 
514  // Update the robot model in Rviz with newly selected joint values
515  publishJoints();
516 
517  // Highlight the planning group
518  Q_EMIT unhighlightAll();
519  Q_EMIT highlightGroup(group_name);
520 }
521 
522 // ******************************************************************************************
523 // Find the associated data by name
524 // ******************************************************************************************
525 srdf::Model::GroupState* RobotPosesWidget::findPoseByName(const std::string& name, const std::string& group)
526 {
527  // Find the group state we are editing based on the pose name
528  srdf::Model::GroupState* searched_state = nullptr; // used for holding our search results
529 
530  for (srdf::Model::GroupState& state : config_data_->srdf_->group_states_)
531  {
532  if (state.name_ == name && state.group_ == group) // match
533  {
534  searched_state = &state;
535  break;
536  }
537  }
538 
539  return searched_state;
540 }
541 
542 // ******************************************************************************************
543 // Delete currently editing item
544 // ******************************************************************************************
545 void RobotPosesWidget::deleteSelected()
546 {
547  const auto& ranges = data_table_->selectedRanges();
548  if (ranges.empty())
549  return;
550  int row = ranges[0].bottomRow();
551 
552  const std::string& name = data_table_->item(row, 0)->text().toStdString();
553  const std::string& group = data_table_->item(row, 1)->text().toStdString();
554 
555  // Confirm user wants to delete group
556  if (QMessageBox::question(this, "Confirm Pose Deletion",
557  QString("Are you sure you want to delete the pose '").append(name.c_str()).append("'?"),
558  QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
559  {
560  return;
561  }
562 
563  // Delete pose from vector
564  for (std::vector<srdf::Model::GroupState>::iterator pose_it = config_data_->srdf_->group_states_.begin();
565  pose_it != config_data_->srdf_->group_states_.end(); ++pose_it)
566  {
567  // check if this is the group we want to delete
568  if (pose_it->name_ == name && pose_it->group_ == group) // match
569  {
570  config_data_->srdf_->group_states_.erase(pose_it);
571  break;
572  }
573  }
574 
575  // Reload main screen table
576  loadDataTable();
577  config_data_->changes |= MoveItConfigData::POSES;
578 }
579 
580 // ******************************************************************************************
581 // Save editing changes
582 // ******************************************************************************************
583 void RobotPosesWidget::doneEditing()
584 {
585  // Get a reference to the supplied strings
586  const std::string& name = pose_name_field_->text().toStdString();
587  const std::string& group = group_name_field_->currentText().toStdString();
588 
589  // Used for editing existing groups
590  srdf::Model::GroupState* searched_data = nullptr;
591 
592  // Check that name field is not empty
593  if (name.empty())
594  {
595  QMessageBox::warning(this, "Error Saving", "A name must be given for the pose!");
596  pose_name_field_->setFocus();
597  return;
598  }
599  // Check that a group was selected
600  if (group.empty())
601  {
602  QMessageBox::warning(this, "Error Saving", "A planning group must be chosen!");
603  group_name_field_->setFocus();
604  return;
605  }
606 
607  // If creating a new pose, check if the (name, group) pair already exists
608  if (!current_edit_pose_)
609  {
610  searched_data = findPoseByName(name, group);
611  if (searched_data != current_edit_pose_)
612  {
613  if (QMessageBox::warning(this, "Warning Saving", "A pose already exists with that name! Overwrite?",
614  QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)
615  return;
616  }
617  }
618  else
619  searched_data = current_edit_pose_; // overwrite edited pose
620 
621  config_data_->changes |= MoveItConfigData::POSES;
622 
623  // Save the new pose name or create the new pose ----------------------------
624  bool is_new = false;
625 
626  if (searched_data == nullptr) // create new
627  {
628  is_new = true;
629  searched_data = new srdf::Model::GroupState();
630  }
631 
632  // Copy name data ----------------------------------------------------
633  searched_data->name_ = name;
634  searched_data->group_ = group;
635 
636  // Copy joint positions ----------------------------------------
637 
638  // Clear the old values
639  searched_data->joint_values_.clear();
640 
641  const moveit::core::JointModelGroup* joint_model_group = config_data_->getRobotModel()->getJointModelGroup(group);
642  const auto& robot_state = config_data_->getPlanningScene()->getCurrentState();
643 
644  // Iterate through the current group's joints and add them to SRDF
645  for (const moveit::core::JointModel* jm : joint_model_group->getJointModels())
646  {
647  // Check that this joint only represents 1 variable.
648  if (jm->getVariableCount() == 1 && !jm->isPassive() && !jm->getMimic())
649  {
650  // Create vector for new joint values
651  std::vector<double> joint_values(jm->getVariableCount());
652  const double* const first_variable = robot_state.getVariablePositions() + jm->getFirstVariableIndex();
653  std::copy(first_variable, first_variable + joint_values.size(), joint_values.begin());
654 
655  // Add joint vector to SRDF
656  searched_data->joint_values_[jm->getName()] = std::move(joint_values);
657  }
658  }
659 
660  // Insert new poses into group state vector --------------------------
661  if (is_new)
662  {
663  config_data_->srdf_->group_states_.push_back(*searched_data);
664  delete searched_data;
665  }
666 
667  // Finish up ------------------------------------------------------
668 
669  // Reload main screen table
670  loadDataTable();
671 
672  // Switch to screen
673  stacked_widget_->setCurrentIndex(0);
674 
675  // Announce that this widget is done with modal mode
676  Q_EMIT isModal(false);
677 }
678 
679 // ******************************************************************************************
680 // Cancel changes
681 // ******************************************************************************************
682 void RobotPosesWidget::cancelEditing()
683 {
684  // Switch to screen
685  stacked_widget_->setCurrentIndex(0);
686 
687  // Announce that this widget is done with modal mode
688  Q_EMIT isModal(false);
689 }
690 
691 // ******************************************************************************************
692 // Load the robot poses into the table
693 // ******************************************************************************************
694 void RobotPosesWidget::loadDataTable()
695 {
696  // Disable Table
697  data_table_->setUpdatesEnabled(false); // prevent table from updating until we are completely done
698  data_table_->setDisabled(true); // make sure we disable it so that the cellChanged event is not called
699  data_table_->clearContents();
700 
701  // Set size of datatable
702  data_table_->setRowCount(config_data_->srdf_->group_states_.size());
703 
704  // Loop through every pose
705  int row = 0;
706  for (const auto& group_state : config_data_->srdf_->group_states_)
707  {
708  // Create row elements
709  QTableWidgetItem* data_name = new QTableWidgetItem(group_state.name_.c_str());
710  data_name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
711  QTableWidgetItem* group_name = new QTableWidgetItem(group_state.group_.c_str());
712  group_name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
713 
714  // Add to table
715  data_table_->setItem(row, 0, data_name);
716  data_table_->setItem(row, 1, group_name);
717 
718  // Increment counter
719  ++row;
720  }
721 
722  // Reenable
723  data_table_->setUpdatesEnabled(true); // prevent table from updating until we are completely done
724  data_table_->setDisabled(false); // make sure we disable it so that the cellChanged event is not called
725 
726  // Resize header
727  data_table_->resizeColumnToContents(0);
728  data_table_->resizeColumnToContents(1);
729 
730  // Show edit button if applicable
731  if (!config_data_->srdf_->group_states_.empty())
732  btn_edit_->show();
733 }
734 
735 // ******************************************************************************************
736 // Called when setup assistant navigation switches to this screen
737 // ******************************************************************************************
738 void RobotPosesWidget::focusGiven()
739 {
740  // Show the current poses screen
741  stacked_widget_->setCurrentIndex(0);
742 
743  // Load the data to the tree
744  loadDataTable();
745 
746  // Load the avail groups to the combo box
747  loadGroupsComboBox();
748 }
749 
750 // ******************************************************************************************
751 // Call when one of the sliders has its value changed
752 // ******************************************************************************************
753 void RobotPosesWidget::updateRobotModel(const std::string& name, double value)
754 {
755  moveit::core::RobotState& robot_state = config_data_->getPlanningScene()->getCurrentStateNonConst();
756  robot_state.setVariablePosition(name, value);
757 
758  // Update the robot model/rviz
759  publishJoints();
760 }
761 
762 // ******************************************************************************************
763 // Publish the current RobotState to Rviz
764 // ******************************************************************************************
765 void RobotPosesWidget::publishJoints()
766 {
767  // Update link + collision transforms
768  auto& robot_state = config_data_->getPlanningScene()->getCurrentStateNonConst();
769  robot_state.update();
770  // Create a planning scene message
771  moveit_msgs::DisplayRobotState msg;
772  moveit::core::robotStateToRobotStateMsg(robot_state, msg.state);
773 
774  // Publish!
775  pub_robot_state_.publish(msg);
776 
777  // Decide if current state is in collision
779  config_data_->getPlanningScene()->checkSelfCollision(request, result, robot_state,
780  config_data_->allowed_collision_matrix_);
781  collision_warning_->setHidden(result.contacts.empty());
782 }
783 
784 // ******************************************************************************************
785 // ******************************************************************************************
786 // Slider Widget
787 // ******************************************************************************************
788 // ******************************************************************************************
789 
790 // ******************************************************************************************
791 // Simple widget for adjusting joints of a robot
792 // ******************************************************************************************
793 SliderWidget::SliderWidget(QWidget* parent, const moveit::core::JointModel* joint_model, double init_value)
794  : QWidget(parent), joint_model_(joint_model)
795 {
796  // Create layouts
797  QVBoxLayout* layout = new QVBoxLayout();
798  QHBoxLayout* row2 = new QHBoxLayout();
799 
800  // Row 1
801  joint_label_ = new QLabel(joint_model_->getName().c_str(), this);
802  joint_label_->setContentsMargins(0, 0, 0, 0);
803  layout->addWidget(joint_label_);
804 
805  // Row 2 -------------------------------------------------------------
806  joint_slider_ = new QSlider(Qt::Horizontal, this);
807  joint_slider_->setTickPosition(QSlider::TicksBelow);
808  joint_slider_->setSingleStep(10);
809  joint_slider_->setPageStep(500);
810  joint_slider_->setTickInterval(1000);
811  joint_slider_->setContentsMargins(0, 0, 0, 0);
812  row2->addWidget(joint_slider_);
813 
814  QFontMetrics m{ QFont() };
815  // Joint Value Box ------------------------------------------------
816  joint_value_ = new QLineEdit(this);
817  joint_value_->setMaximumWidth(m.boundingRect("0000.00000").width());
818  joint_value_->setContentsMargins(0, 0, 0, 0);
819  connect(joint_value_, &QLineEdit::editingFinished, this, &SliderWidget::changeJointSlider);
820  row2->addWidget(joint_value_);
821 
822  // Joint Limits ----------------------------------------------------
823  const std::vector<moveit_msgs::JointLimits>& limits = joint_model_->getVariableBoundsMsg();
824  if (limits.empty())
825  {
826  QMessageBox::critical(this, "Error Loading", "An internal error has occured while loading the joints");
827  return;
828  }
829 
830  // Only use the first limit, because there is only 1 variable (as checked earlier)
831  moveit_msgs::JointLimits joint_limit = limits[0];
832  max_position_ = joint_limit.max_position;
833  min_position_ = joint_limit.min_position;
834 
835  // Set the slider limits
836  joint_slider_->setMaximum(max_position_ * 10000);
837  joint_slider_->setMinimum(min_position_ * 10000);
838 
839  // Connect slider to joint value box
840  connect(joint_slider_, &QSlider::valueChanged, this, &SliderWidget::changeJointValue);
841 
842  // Initial joint values -------------------------------------------
843  int value = init_value * 10000; // scale double to integer for slider use
844  joint_slider_->setSliderPosition(value); // set slider value
845  changeJointValue(value); // show in textbox
846 
847  // Finish GUI ----------------------------------------
848  layout->addLayout(row2);
849 
850  this->setContentsMargins(0, 0, 0, 0);
851  // this->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
852  this->setGeometry(QRect(110, 80, 120, 80));
853  this->setLayout(layout);
854 
855  // Declare std::string as metatype so we can use it in a signal
856  qRegisterMetaType<std::string>("std::string");
857 }
858 
859 // ******************************************************************************************
860 // Deconstructor
861 // ******************************************************************************************
862 SliderWidget::~SliderWidget() = default;
863 
864 // ******************************************************************************************
865 // Called when the joint value slider is changed
866 // ******************************************************************************************
867 void SliderWidget::changeJointValue(int value)
868 {
869  // Get joint value
870  const double double_value = double(value) / 10000;
871 
872  // Set textbox
873  joint_value_->setText(QString("%1").arg(double_value, 0, 'f', 4));
874 
875  // Send event to parent widget
876  Q_EMIT jointValueChanged(joint_model_->getName(), double_value);
877 }
878 
879 // ******************************************************************************************
880 // Called when the joint value box is changed
881 // ******************************************************************************************
883 {
884  // Get joint value
885  double value = joint_value_->text().toDouble();
886 
887  if (min_position_ > value || value > max_position_)
888  {
890  joint_value_->setText(QString("%1").arg(value, 0, 'f', 4));
891  }
892 
893  // We assume it converts to double because of the validator
894  joint_slider_->setSliderPosition(value * 10000);
895 
896  // Send event to parent widget
897  Q_EMIT jointValueChanged(joint_model_->getName(), value);
898 }
899 
900 } // namespace moveit_setup_assistant
moveit_setup_assistant::SliderWidget::changeJointSlider
void changeJointSlider()
Called when the joint value box is changed.
Definition: robot_poses_widget.cpp:914
moveit::core::RobotState::setJointPositions
void setJointPositions(const std::string &joint_name, const double *position)
moveit_setup_assistant::SliderWidget::joint_value_
QLineEdit * joint_value_
Definition: robot_poses_widget.h:242
moveit::core::JointModel::getName
const std::string & getName() const
moveit_setup_assistant::MOVEIT_ROBOT_STATE
static const std::string MOVEIT_ROBOT_STATE
Definition: moveit_config_data.h:87
moveit::core::RobotState
ros::NodeHandle::advertise
Publisher advertise(AdvertiseOptions &ops)
moveit_setup_assistant::SliderWidget::min_position_
double min_position_
Definition: robot_poses_widget.h:275
moveit_setup_assistant::SliderWidget::joint_slider_
QSlider * joint_slider_
Definition: robot_poses_widget.h:241
moveit::core::RobotState::update
void update(bool force=false)
srdf::Model::GroupState::group_
std::string group_
moveit::core::RobotState::setVariablePosition
void setVariablePosition(const std::string &variable, double value)
collision_detection::CollisionResult
moveit_setup_assistant::SliderWidget::max_position_
double max_position_
Definition: robot_poses_widget.h:274
moveit_setup_assistant::SliderWidget::jointValueChanged
void jointValueChanged(const std::string &name, double value)
Indicate joint name and value when slider widget changed.
moveit::core::RobotState::getVariablePosition
double getVariablePosition(const std::string &variable) const
moveit::core::RobotState::setToDefaultValues
void setToDefaultValues()
value
float value
SetupScreenWidget
Definition: setup_screen_widget.h:44
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
name
name
moveit::core::JointModelGroup::getJointModels
const std::vector< const JointModel * > & getJointModels() const
robot_poses_widget.h
srdf::Model::GroupState::joint_values_
std::map< std::string, std::vector< double > > joint_values_
header_widget.h
moveit_setup_assistant
Definition: compute_default_collisions.h:46
srdf::Model::GroupState
collision_detection::CollisionResult::contacts
ContactMap contacts
moveit_setup_assistant::SliderWidget::changeJointValue
void changeJointValue(int value)
Called when the joint value slider is changed.
Definition: robot_poses_widget.cpp:899
moveit::core::RobotState::getVariablePositions
double * getVariablePositions()
moveit_setup_assistant::SliderWidget
Definition: robot_poses_widget.h:214
m
m
planning_scene.h
append
ROSCPP_DECL std::string append(const std::string &left, const std::string &right)
moveit::core::JointModelGroup
index
unsigned int index
moveit_setup_assistant::SliderWidget::~SliderWidget
~SliderWidget() override
conversions.h
moveit_setup_assistant::SliderWidget::joint_model_
const moveit::core::JointModel * joint_model_
Definition: robot_poses_widget.h:271
ros::Duration::sleep
bool sleep() const
srdf::Model::GroupState::name_
std::string name_
srdf::Model::Group
header
const std::string header
ros::Duration
moveit::core::robotStateToRobotStateMsg
void robotStateToRobotStateMsg(const RobotState &state, moveit_msgs::RobotState &robot_state, bool copy_attached_bodies=true)
moveit_setup_assistant::RobotPosesWidget::RobotPosesWidget
RobotPosesWidget(QWidget *parent, const MoveItConfigDataPtr &config_data)
Definition: robot_poses_widget.cpp:97
moveit::core::JointModel
ros::NodeHandle
group
group


moveit_setup_assistant
Author(s): Dave Coleman
autogenerated on Sat May 3 2025 02:28:04