moveit_config_data.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2012, Willow Garage, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the Willow Garage nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 /* Author: Dave Coleman */
00036 
00037 // ******************************************************************************************
00038 /* DEVELOPER NOTES
00039 
00040    This class is shared with all widgets and contains the common configuration data
00041    needed for generating each robot's MoveIt configuration package. All SRDF data is
00042    contained in a subclass of this class - srdf_writer.cpp. This class also contains
00043    the functions for writing out the configuration files. Maybe it would have been best to
00044    keep the writing out functions in configuration_files_widget.cpp, but I am not sure.
00045 */
00046 // ******************************************************************************************
00047 
00048 #include <moveit/setup_assistant/tools/moveit_config_data.h>
00049 // Reading/Writing Files
00050 #include <iostream> // For writing yaml and launch files
00051 #include <fstream>
00052 #include <yaml-cpp/yaml.h> // outputing yaml config files
00053 #include <boost/filesystem.hpp>  // for creating folders/files
00054 #include <boost/algorithm/string.hpp> // for string find and replace in templates
00055 // ROS
00056 #include <ros/console.h>
00057 #include <ros/package.h> // for getting file path for loading images
00058 
00059 namespace moveit_setup_assistant
00060 {
00061 
00062 // File system
00063 namespace fs = boost::filesystem;
00064 
00065 // ******************************************************************************************
00066 // Constructor
00067 // ******************************************************************************************
00068 MoveItConfigData::MoveItConfigData() :
00069   config_pkg_generated_timestamp_(0)
00070 {
00071   // Create an instance of SRDF writer and URDF model for all widgets to share
00072   srdf_.reset( new SRDFWriter() );
00073   urdf_model_.reset( new urdf::Model() );
00074 
00075   // Not in debug mode
00076   debug_ = false;
00077 
00078   // Get MoveIt Setup Assistant package path
00079   setup_assistant_path_ = ros::package::getPath("moveit_setup_assistant");
00080   if( setup_assistant_path_.empty() )
00081   {
00082     setup_assistant_path_ = ".";
00083   }
00084 }
00085 
00086 // ******************************************************************************************
00087 // Destructor
00088 // ******************************************************************************************
00089 MoveItConfigData::~MoveItConfigData()
00090 {
00091 }
00092 
00093 // ******************************************************************************************
00094 // Provide a kinematic model. Load a new one if necessary
00095 // ******************************************************************************************
00096 robot_model::RobotModelConstPtr MoveItConfigData::getRobotModel()
00097 {
00098   if( !kin_model_ )
00099   {
00100     // Initialize with a URDF Model Interface and a SRDF Model
00101     kin_model_.reset( new robot_model::RobotModel( urdf_model_, srdf_->srdf_model_ ) );
00102     kin_model_const_ = kin_model_;
00103   }
00104 
00105   return kin_model_const_;
00106 }
00107 
00108 // ******************************************************************************************
00109 // Update the Kinematic Model with latest SRDF modifications
00110 // ******************************************************************************************
00111 void MoveItConfigData::updateRobotModel()
00112 {
00113   ROS_INFO( "Updating kinematic model");
00114 
00115   // Tell SRDF Writer to create new SRDF Model, use original URDF model
00116   srdf_->updateSRDFModel( *urdf_model_ );
00117 
00118   // Create new kin model
00119   kin_model_.reset( new robot_model::RobotModel( urdf_model_, srdf_->srdf_model_ ) );
00120   kin_model_const_ = kin_model_;
00121 
00122   // Reset the planning scene
00123   planning_scene_.reset();
00124 }
00125 
00126 // ******************************************************************************************
00127 // Provide a shared planning scene
00128 // ******************************************************************************************
00129 planning_scene::PlanningScenePtr MoveItConfigData::getPlanningScene()
00130 {
00131   if( !planning_scene_ )
00132   {
00133     // make sure kinematic model exists
00134     getRobotModel();
00135 
00136     // Allocate an empty planning scene
00137     planning_scene_.reset(new planning_scene::PlanningScene(kin_model_));
00138   }
00139   return planning_scene_;
00140 }
00141 
00142 // ******************************************************************************************
00143 // Load the allowed collision matrix from the SRDF's list of link pairs
00144 // ******************************************************************************************
00145 void MoveItConfigData::loadAllowedCollisionMatrix()
00146 {
00147   // Clear the allowed collision matrix
00148   allowed_collision_matrix_.clear();
00149 
00150   // Update the allowed collision matrix, in case there has been a change
00151   for( std::vector<srdf::Model::DisabledCollision>::const_iterator pair_it = srdf_->disabled_collisions_.begin();
00152        pair_it != srdf_->disabled_collisions_.end(); ++pair_it )
00153   {
00154     allowed_collision_matrix_.setEntry( pair_it->link1_, pair_it->link2_, true );
00155   }
00156 }
00157 
00158 // ******************************************************************************************
00159 // Output MoveIt Setup Assistant hidden settings file
00160 // ******************************************************************************************
00161 bool MoveItConfigData::outputSetupAssistantFile( const std::string& file_path )
00162 {
00163   YAML::Emitter emitter;
00164   emitter << YAML::BeginMap;
00165 
00166   // Output every available planner ---------------------------------------------------
00167   emitter << YAML::Key << "moveit_setup_assistant_config";
00168 
00169   emitter << YAML::Value << YAML::BeginMap;
00170 
00171   // URDF Path Location
00172   emitter << YAML::Key << "URDF";
00173   emitter << YAML::Value << YAML::BeginMap;
00174   emitter << YAML::Key << "package" << YAML::Value << urdf_pkg_name_;
00175   emitter << YAML::Key << "relative_path" << YAML::Value << urdf_pkg_relative_path_;
00176   emitter << YAML::EndMap;
00177 
00179   emitter << YAML::Key << "SRDF";
00180   emitter << YAML::Value << YAML::BeginMap;
00181   emitter << YAML::Key << "relative_path" << YAML::Value << srdf_pkg_relative_path_;
00182   emitter << YAML::EndMap;
00183 
00185   emitter << YAML::Key << "CONFIG";
00186   emitter << YAML::Value << YAML::BeginMap;
00187   emitter << YAML::Key << "generated_timestamp" << YAML::Value << std::time(NULL); // TODO: is this cross-platform?
00188   emitter << YAML::EndMap;
00189 
00190   emitter << YAML::EndMap;
00191 
00192   std::ofstream output_stream( file_path.c_str(), std::ios_base::trunc );
00193   if( !output_stream.good() )
00194   {
00195     ROS_ERROR_STREAM( "Unable to open file for writing " << file_path );
00196     return false;
00197   }
00198 
00199   output_stream << emitter.c_str();
00200   output_stream.close();
00201 
00202   return true; // file created successfully
00203 }
00204 
00205 // ******************************************************************************************
00206 // Output OMPL Planning config files
00207 // ******************************************************************************************
00208 bool MoveItConfigData::outputOMPLPlanningYAML( const std::string& file_path )
00209 {
00210   YAML::Emitter emitter;
00211   emitter << YAML::BeginMap;
00212 
00213   // Output every available planner ---------------------------------------------------
00214   emitter << YAML::Key << "planner_configs";
00215 
00216   emitter << YAML::Value << YAML::BeginMap;
00217 
00218   // Add Planners
00219   static const std::string planners[] =
00220     {"SBL", "EST", "LBKPIECE", "BKPIECE", "KPIECE", "RRT", "RRTConnect", "RRTstar", "TRRT", "PRM", "PRMstar" };
00221 
00222   std::vector<std::string> pconfigs;
00223   for (std::size_t i = 0 ; i < sizeof(planners) / sizeof(std::string) ; ++i)
00224   {
00225     std::string defaultconfig = planners[i] + "kConfigDefault";
00226     emitter << YAML::Key << defaultconfig;
00227     emitter << YAML::Value << YAML::BeginMap;
00228     emitter << YAML::Key << "type" << YAML::Value << "geometric::" + planners[i];
00229     emitter << YAML::EndMap;
00230     pconfigs.push_back(defaultconfig);
00231   }
00232 
00233   // End of every avail planner
00234   emitter << YAML::EndMap;
00235 
00236   // Output every group and the planners it can use ----------------------------------
00237   for( std::vector<srdf::Model::Group>::iterator group_it = srdf_->groups_.begin();
00238        group_it != srdf_->groups_.end();  ++group_it )
00239   {
00240     emitter << YAML::Key << group_it->name_;
00241     emitter << YAML::Value << YAML::BeginMap;
00242     // Output associated planners
00243     emitter << YAML::Key << "planner_configs";
00244     emitter << YAML::Value << YAML::BeginSeq;
00245     for (std::size_t i = 0 ; i < pconfigs.size() ; ++i)
00246       emitter << pconfigs[i];
00247     emitter << YAML::EndSeq;
00248 
00249     // Output projection_evaluator
00250     std::string projection_joints = decideProjectionJoints( group_it->name_ );
00251     if( !projection_joints.empty() )
00252     {
00253       emitter << YAML::Key << "projection_evaluator";
00254       emitter << YAML::Value << projection_joints;
00255       emitter << YAML::Key << "longest_valid_segment_fraction";
00256       emitter << YAML::Value << "0.05";
00257     }
00258 
00259     emitter << YAML::EndMap;
00260   }
00261 
00262   emitter << YAML::EndMap;
00263 
00264   std::ofstream output_stream( file_path.c_str(), std::ios_base::trunc );
00265   if( !output_stream.good() )
00266   {
00267     ROS_ERROR_STREAM( "Unable to open file for writing " << file_path );
00268     return false;
00269   }
00270 
00271   output_stream << emitter.c_str();
00272   output_stream.close();
00273 
00274   return true; // file created successfully
00275 }
00276 
00277 // ******************************************************************************************
00278 // Output kinematic config files
00279 // ******************************************************************************************
00280 bool MoveItConfigData::outputKinematicsYAML( const std::string& file_path )
00281 {
00282   YAML::Emitter emitter;
00283   emitter << YAML::BeginMap;
00284 
00285   // Output every group and the kinematic solver it can use ----------------------------------
00286   for( std::vector<srdf::Model::Group>::iterator group_it = srdf_->groups_.begin();
00287        group_it != srdf_->groups_.end();  ++group_it )
00288   {
00289     // Only save kinematic data if the solver is not "None"
00290     if( group_meta_data_[ group_it->name_ ].kinematics_solver_.empty() ||
00291         group_meta_data_[ group_it->name_ ].kinematics_solver_ == "None" )
00292       continue;
00293 
00294     emitter << YAML::Key << group_it->name_;
00295     emitter << YAML::Value << YAML::BeginMap;
00296 
00297     // Kinematic Solver
00298     emitter << YAML::Key << "kinematics_solver";
00299     emitter << YAML::Value << group_meta_data_[ group_it->name_ ].kinematics_solver_;
00300 
00301     // Search Resolution
00302     emitter << YAML::Key << "kinematics_solver_search_resolution";
00303     emitter << YAML::Value << group_meta_data_[ group_it->name_ ].kinematics_solver_search_resolution_;
00304 
00305     // Solver Timeout
00306     emitter << YAML::Key << "kinematics_solver_timeout";
00307     emitter << YAML::Value << group_meta_data_[ group_it->name_ ].kinematics_solver_timeout_;
00308 
00309     // Solver Attempts
00310     emitter << YAML::Key << "kinematics_solver_attempts";
00311     emitter << YAML::Value << group_meta_data_[ group_it->name_ ].kinematics_solver_attempts_;
00312 
00313     emitter << YAML::EndMap;
00314   }
00315 
00316   emitter << YAML::EndMap;
00317 
00318   std::ofstream output_stream( file_path.c_str(), std::ios_base::trunc );
00319   if( !output_stream.good() )
00320   {
00321     ROS_ERROR_STREAM( "Unable to open file for writing " << file_path );
00322     return false;
00323   }
00324 
00325   output_stream << emitter.c_str();
00326   output_stream.close();
00327 
00328   return true; // file created successfully
00329 }
00330 
00331 // ******************************************************************************************
00332 // Output joint limits config files
00333 // ******************************************************************************************
00334 bool MoveItConfigData::outputJointLimitsYAML( const std::string& file_path )
00335 {
00336   YAML::Emitter emitter;
00337   emitter << YAML::BeginMap;
00338 
00339   emitter << YAML::Key << "joint_limits";
00340   emitter << YAML::Value << YAML::BeginMap;
00341 
00342   // Union all the joints in groups
00343   std::set<const robot_model::JointModel*> joints;
00344 
00345   // Loop through groups
00346   for (std::vector<srdf::Model::Group>::iterator group_it = srdf_->groups_.begin();
00347        group_it != srdf_->groups_.end();  ++group_it)
00348   {
00349     // Get list of associated joints
00350     const robot_model::JointModelGroup *joint_model_group =
00351       getRobotModel()->getJointModelGroup( group_it->name_ );
00352 
00353     std::vector<const robot_model::JointModel*> joint_models = joint_model_group->getJointModels();
00354 
00355     // Iterate through the joints
00356     for (std::vector<const robot_model::JointModel*>::const_iterator joint_it = joint_models.begin();
00357          joint_it != joint_models.end(); ++joint_it)
00358     {
00359       // Check that this joint only represents 1 variable.
00360       if ((*joint_it)->getVariableCount() == 1)
00361         joints.insert(*joint_it);
00362     }
00363   }
00364 
00365   // Add joints to yaml file, if no more than 1 dof
00366   for ( std::set<const robot_model::JointModel*>::iterator joint_it = joints.begin() ; joint_it != joints.end() ; ++joint_it )
00367   {
00368     emitter << YAML::Key << (*joint_it)->getName();
00369     emitter << YAML::Value << YAML::BeginMap;
00370 
00371     double vel = (*joint_it)->getMaximumVelocity();
00372 
00373     // Output property
00374     emitter << YAML::Key << "has_velocity_limits";
00375     if (vel > std::numeric_limits<double>::epsilon())
00376       emitter << YAML::Value << "true";
00377     else
00378       emitter << YAML::Value << "false";
00379 
00380     // Output property
00381     emitter << YAML::Key << "max_velocity";
00382     emitter << YAML::Value << (*joint_it)->getMaximumVelocity();
00383 
00384     // Output property
00385     emitter << YAML::Key << "has_acceleration_limits";
00386     emitter << YAML::Value << "true";
00387 
00388     // Output property
00389     emitter << YAML::Key << "max_acceleration";
00390     emitter << YAML::Value << (*joint_it)->getMaximumVelocity() / 5.0;
00391 
00392     emitter << YAML::EndMap;
00393   }
00394 
00395   emitter << YAML::EndMap;
00396 
00397   std::ofstream output_stream( file_path.c_str(), std::ios_base::trunc );
00398   if( !output_stream.good() )
00399   {
00400     ROS_ERROR_STREAM( "Unable to open file for writing " << file_path );
00401     return false;
00402   }
00403 
00404   output_stream << emitter.c_str();
00405   output_stream.close();
00406 
00407   return true; // file created successfully
00408 }
00409 
00410 // ******************************************************************************************
00411 // Decide the best two joints to be used for the projection evaluator
00412 // ******************************************************************************************
00413 std::string MoveItConfigData::decideProjectionJoints(std::string planning_group)
00414 {
00415   std::string joint_pair = "";
00416 
00417   // Retrieve pointer to the shared kinematic model
00418   const robot_model::RobotModelConstPtr &model = getRobotModel();
00419 
00420   // Error check
00421   if( !model->hasJointModelGroup( planning_group ) )
00422     return joint_pair;
00423 
00424   // Get the joint model group
00425   const robot_model::JointModelGroup* group = model->getJointModelGroup(planning_group);
00426 
00427   // get vector of joint names
00428   const std::vector<std::string> joints = group->getJointModelNames();
00429 
00430   if( joints.size() >= 2 )
00431   {
00432     // Check that the first two joints have only 1 variable
00433     if( group->getJointModel( joints[0] )->getVariableCount() == 1 &&
00434         group->getJointModel( joints[1] )->getVariableCount() == 1)
00435     {
00436       // Just choose the first two joints.
00437       joint_pair = "joints("+joints[0] + "," + joints[1]+")";
00438     }
00439   }
00440 
00441   return joint_pair;
00442 }
00443 
00444 // ******************************************************************************************
00445 // Input kinematics.yaml file
00446 // ******************************************************************************************
00447 bool MoveItConfigData::inputKinematicsYAML( const std::string& file_path )
00448 {
00449   // Load file
00450   std::ifstream input_stream( file_path.c_str() );
00451   if( !input_stream.good() )
00452   {
00453     ROS_ERROR_STREAM( "Unable to open file for reading " << file_path );
00454     return false;
00455   }
00456 
00457   // Begin parsing
00458   try {
00459     YAML::Parser parser(input_stream);
00460     YAML::Node doc;
00461     parser.GetNextDocument(doc);
00462 
00463     // Loop through all groups
00464     for( YAML::Iterator group_it = doc.begin(); group_it != doc.end(); ++group_it )
00465     {
00466       std::string group_name;
00467       group_it.first() >> group_name;
00468 
00469       // Create new meta data
00470       GroupMetaData new_meta_data;
00471 
00472       // kinematics_solver
00473       if( const YAML::Node *prop_name = group_it.second().FindValue( "kinematics_solver" ) )
00474       {
00475         *prop_name >> new_meta_data.kinematics_solver_;
00476       }
00477 
00478       // kinematics_solver_search_resolution
00479       if( const YAML::Node *prop_name = group_it.second().FindValue( "kinematics_solver_search_resolution" ) )
00480       {
00481         *prop_name >> new_meta_data.kinematics_solver_search_resolution_;
00482       }
00483       else
00484       {
00485         new_meta_data.kinematics_solver_attempts_ = DEFAULT_KIN_SOLVER_SEARCH_RESOLUTION_;
00486       }
00487 
00488       // kinematics_solver_timeout
00489       if( const YAML::Node *prop_name = group_it.second().FindValue( "kinematics_solver_timeout" ) )
00490       {
00491         *prop_name >> new_meta_data.kinematics_solver_timeout_;
00492       }
00493       else
00494       {
00495         new_meta_data.kinematics_solver_attempts_ = DEFAULT_KIN_SOLVER_TIMEOUT_;
00496       }
00497 
00498       // kinematics_solver_attempts
00499       if( const YAML::Node *prop_name = group_it.second().FindValue( "kinematics_solver_attempts" ) )
00500       {
00501         *prop_name >> new_meta_data.kinematics_solver_attempts_;
00502       }
00503       else
00504       {
00505         new_meta_data.kinematics_solver_attempts_ = DEFAULT_KIN_SOLVER_ATTEMPTS_;
00506       }
00507 
00508       // Assign meta data to vector
00509       group_meta_data_[ group_name ] = new_meta_data;
00510     }
00511 
00512   }
00513   catch(YAML::ParserException& e)  // Catch errors
00514   {
00515     ROS_ERROR_STREAM( e.what() );
00516     return false;
00517   }
00518 
00519   return true; // file created successfully
00520 }
00521 
00522 // ******************************************************************************************
00523 // Input .setup_assistant file - contains data used for the MoveIt Setup Assistant
00524 // ******************************************************************************************
00525 bool MoveItConfigData::inputSetupAssistantYAML( const std::string& file_path )
00526 {
00527   // Load file
00528   std::ifstream input_stream( file_path.c_str() );
00529   if( !input_stream.good() )
00530   {
00531     ROS_ERROR_STREAM( "Unable to open file for reading " << file_path );
00532     return false;
00533   }
00534 
00535   // Begin parsing
00536   try {
00537     YAML::Parser parser(input_stream);
00538     YAML::Node doc;
00539     parser.GetNextDocument(doc);
00540 
00541     // Get title node
00542     if( const YAML::Node *title_node =doc.FindValue( "moveit_setup_assistant_config" ) )
00543     {
00544       // URDF Properties
00545       if( const YAML::Node *urdf_node = title_node->FindValue( "URDF" ) )
00546       {
00547         // Load first property
00548         if( const YAML::Node *package_node = urdf_node->FindValue( "package" ) )
00549         {
00550           *package_node >> urdf_pkg_name_;
00551         }
00552         else
00553         {
00554           return false; // if we do not find this value we cannot continue
00555         }
00556 
00557         // Load second property
00558         if( const YAML::Node *relative_node = urdf_node->FindValue( "relative_path" ) )
00559         {
00560           *relative_node >> urdf_pkg_relative_path_;
00561         }
00562         else
00563         {
00564           return false; // if we do not find this value we cannot continue
00565         }
00566       }
00567       // SRDF Properties
00568       if( const YAML::Node *srdf_node = title_node->FindValue( "SRDF" ) )
00569       {
00570         // Load first property
00571         if( const YAML::Node *relative_node = srdf_node->FindValue( "relative_path" ) )
00572         {
00573           *relative_node >> srdf_pkg_relative_path_;
00574         }
00575         else
00576         {
00577           return false; // if we do not find this value we cannot continue
00578         }
00579       }
00580       // Package generation time
00581       if( const YAML::Node *config_node = title_node->FindValue( "CONFIG" ) )
00582       {
00583         // Load first property
00584         if( const YAML::Node *timestamp_node = config_node->FindValue( "generated_timestamp" ) )
00585         {
00586           *timestamp_node >> config_pkg_generated_timestamp_;
00587         }
00588         else
00589         {
00590           // if we do not find this value it is fine, not required
00591         }
00592       }
00593       return true;
00594     }
00595   }
00596   catch(YAML::ParserException& e)  // Catch errors
00597   {
00598     ROS_ERROR_STREAM( e.what() );
00599   }
00600 
00601   return false; // if it gets to this point an error has occured
00602 }
00603 
00604 // ******************************************************************************************
00605 // Helper Function for joining a file path and a file name, or two file paths, etc, in a cross-platform way
00606 // ******************************************************************************************
00607 std::string MoveItConfigData::appendPaths( const std::string &path1, const std::string &path2 )
00608 {
00609   fs::path result = path1;
00610   result /= path2;
00611   return result.make_preferred().native().c_str();
00612 }
00613 
00614 srdf::Model::Group* MoveItConfigData::findGroupByName( const std::string &name )
00615 {
00616   // Find the group we are editing based on the goup name string
00617   srdf::Model::Group *searched_group = NULL; // used for holding our search results
00618 
00619   for( std::vector<srdf::Model::Group>::iterator group_it = srdf_->groups_.begin();
00620        group_it != srdf_->groups_.end(); ++group_it )
00621   {
00622     if( group_it->name_ == name ) // string match
00623     {
00624       searched_group = &(*group_it);  // convert to pointer from iterator
00625       break; // we are done searching
00626     }
00627   }
00628 
00629   // Check if subgroup was found
00630   if( searched_group == NULL ) // not found
00631     ROS_FATAL_STREAM("An internal error has occured while searching for groups. Group '" << name << "' was not found in the SRDF.");
00632 
00633   return searched_group;
00634 }
00635 
00636 
00637 
00638 } // namespace


moveit_setup_assistant
Author(s): Dave Coleman
autogenerated on Mon Oct 6 2014 02:32:27