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 }