Go to the documentation of this file.00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 #include <tf2_ros/static_transform_broadcaster.h>
00031 #include <Eigen/Geometry>
00032 
00033 #include <boost/program_options.hpp>
00034 namespace po=boost::program_options;
00035 
00036 static void usage (const char* prog_name, const po::options_description &opts, bool desc=false) {
00037   if (desc) {
00038     std::cout << "A command line utility for manually defining a (static) transform" << std::endl;
00039     std::cout << "from parent_frame_id to child_frame_id." << std::endl;
00040   }
00041   std::cout << std::endl;
00042   std::cout << "Usage: static_transform_publisher [options] x y z  <rotation> parent_frame_id child_frame_id" << std::endl;
00043   std::cout << opts << std::endl;
00044 }
00045 
00046 static void parse_arguments(int argc, char **argv,
00047                             geometry_msgs::TransformStamped &msg) {
00048   std::string mode;
00049   po::options_description options_description("allowed options");
00050   options_description.add_options()
00051       ("help,h", "show this help message")
00052       ("mode,m", po::value<std::string>(&mode))
00053       ;
00054 
00055   po::variables_map variables_map;
00056   std::vector<std::string> args;
00057   std::vector<std::string>::const_iterator arg;
00058   try {
00059     po::parsed_options parsed =
00060         po::command_line_parser(argc, argv)
00061         .options(options_description)
00062         .allow_unregistered()
00063         .run();
00064 
00065     po::store(parsed, variables_map);
00066     po::notify(variables_map);
00067     args = po::collect_unrecognized(parsed.options, po::include_positional);
00068     arg = args.begin();
00069 
00070     if (variables_map.count("help")) {
00071       usage(argv[0], options_description, true);
00072       exit (EXIT_SUCCESS);
00073     }
00074     const size_t numArgs = 3 + 2;
00075 
00076     bool bQuatMode = (mode == "wxyz" || mode == "xyzw");
00077     if (mode == "")
00078     {
00079       if (args.size() == numArgs+4) {
00080         bQuatMode = true; 
00081         mode = "xyzw";
00082       } else if (args.size() == numArgs+3) {
00083         mode = "zyx";
00084       } else {
00085         throw po::error("invalid number of positional arguments");
00086       }
00087     }
00088 
00089     
00090     msg.transform.translation.x = boost::lexical_cast<double>(*arg); ++arg;
00091     msg.transform.translation.y = boost::lexical_cast<double>(*arg); ++arg;
00092     msg.transform.translation.z = boost::lexical_cast<double>(*arg); ++arg;
00093 
00094     
00095     Eigen::Quaterniond q;
00096     if (bQuatMode) { 
00097       if (args.size() != numArgs+4)
00098         throw po::error("quaternion mode requires " +
00099                         boost::lexical_cast<std::string>(numArgs+4) +
00100                         " positional arguments");
00101 
00102       const std::string eigen_order("xyzw");
00103       double data[4];
00104       for (size_t i=0; i<4; ++i) {
00105         size_t idx = eigen_order.find(mode[i]);
00106         data[idx] = boost::lexical_cast<double>(*arg); ++arg;
00107       }
00108       q = Eigen::Quaterniond(data);
00109 
00110     } else { 
00111       if (args.size() != numArgs+3)
00112         throw po::error("Euler angles require " +
00113                         boost::lexical_cast<std::string>(numArgs+3) +
00114                         " positional arguments");
00115       if (mode.size() != 3)
00116         throw po::error("mode specification for Euler angles requires a string from 3 chars (xyz)");
00117 
00118       const std::string axes_order("xyz");
00119       size_t axes_idxs[3];
00120       double angles[3];
00121 
00122       for (size_t i=0; i<3; ++i) {
00123         size_t idx = axes_order.find(mode[i]);
00124         if (idx == std::string::npos)
00125           throw po::error("invalid axis specification for Euler angles: " +
00126                           boost::lexical_cast<std::string>(mode[i]));
00127         axes_idxs[i] = idx;
00128         angles[i] = boost::lexical_cast<double>(*arg); ++arg;
00129       }
00130       q = Eigen::AngleAxisd(angles[0], Eigen::Vector3d::Unit(axes_idxs[0])) *
00131           Eigen::AngleAxisd(angles[1], Eigen::Vector3d::Unit(axes_idxs[1])) *
00132           Eigen::AngleAxisd(angles[2], Eigen::Vector3d::Unit(axes_idxs[2]));
00133     }
00134     
00135     q.normalize();
00136     msg.transform.rotation.x = q.x();
00137     msg.transform.rotation.y = q.y();
00138     msg.transform.rotation.z = q.z();
00139     msg.transform.rotation.w = q.w();
00140 
00141     
00142     msg.header.frame_id = *arg++;
00143     msg.child_frame_id = *arg++;
00144   } catch (const po::error  &e) {
00145     ROS_FATAL_STREAM(e.what());
00146     usage(argv[0], options_description);
00147     exit (EXIT_FAILURE);
00148   } catch (const boost::bad_lexical_cast &e) {
00149     ROS_FATAL_STREAM("failed to parse numerical value: " << *arg);
00150     usage(argv[0], options_description);
00151     exit (EXIT_FAILURE);
00152   }
00153 }
00154 
00155 int main(int argc, char ** argv)
00156 {
00157   
00158   ros::init(argc, argv, "static_transform_publisher", ros::init_options::AnonymousName);
00159 
00160   geometry_msgs::TransformStamped msg;
00161   parse_arguments(argc, argv, msg);
00162 
00163   if (msg.header.frame_id.empty() || msg.child_frame_id.empty())
00164   {
00165     ROS_FATAL("target or source frame is empty");
00166     exit(1);
00167   }
00168   if (msg.header.frame_id == msg.child_frame_id)
00169   {
00170     ROS_FATAL("target and source frame are the same (%s, %s) this cannot work",
00171               msg.child_frame_id.c_str(), msg.header.frame_id.c_str());
00172     exit(1);
00173   }
00174 
00175   tf2_ros::StaticTransformBroadcaster broadcaster;
00176   broadcaster.sendTransform(msg);
00177 
00178   ROS_INFO("Spinning until killed, publishing %s to %s",
00179            msg.header.frame_id.c_str(), msg.child_frame_id.c_str());
00180   ros::spin();
00181 
00182   return 0;
00183 }