31 #include <Eigen/Geometry> 33 #include <boost/program_options.hpp> 34 namespace po=boost::program_options;
36 static void usage (
const char* prog_name,
const po::options_description &opts,
bool desc=
false) {
38 std::cout <<
"A command line utility for manually defining a (static) transform" << std::endl;
39 std::cout <<
"from parent_frame_id to child_frame_id." << std::endl;
41 std::cout << std::endl;
42 std::cout <<
"Usage: static_transform_publisher [options] x y z <rotation> parent_frame_id child_frame_id" << std::endl;
43 std::cout << opts << std::endl;
47 geometry_msgs::TransformStamped &msg) {
49 po::options_description options_description(
"allowed options");
50 options_description.add_options()
51 (
"help,h",
"show this help message")
52 (
"mode,m", po::value<std::string>(&mode))
55 po::variables_map variables_map;
56 std::vector<std::string>
args;
57 std::vector<std::string>::const_iterator arg;
59 po::parsed_options parsed =
60 po::command_line_parser(argc, argv)
61 .options(options_description)
65 po::store(parsed, variables_map);
66 po::notify(variables_map);
67 args = po::collect_unrecognized(parsed.options, po::include_positional);
70 if (variables_map.count(
"help")) {
71 usage(argv[0], options_description,
true);
74 const size_t numArgs = 3 + 2;
76 bool bQuatMode = (mode ==
"wxyz" || mode ==
"xyzw");
79 if (args.size() == numArgs+4) {
82 }
else if (args.size() == numArgs+3) {
85 throw po::error(
"invalid number of positional arguments");
90 msg.transform.translation.x = boost::lexical_cast<
double>(*arg); ++arg;
91 msg.transform.translation.y = boost::lexical_cast<
double>(*arg); ++arg;
92 msg.transform.translation.z = boost::lexical_cast<
double>(*arg); ++arg;
97 if (args.size() != numArgs+4)
98 throw po::error(
"quaternion mode requires " +
99 boost::lexical_cast<std::string>(numArgs+4) +
100 " positional arguments");
102 const std::string eigen_order(
"xyzw");
104 for (
size_t i=0; i<4; ++i) {
105 size_t idx = eigen_order.find(mode[i]);
106 data[idx] = boost::lexical_cast<
double>(*arg); ++arg;
108 q = Eigen::Quaterniond(data);
111 if (args.size() != numArgs+3)
112 throw po::error(
"Euler angles require " +
113 boost::lexical_cast<std::string>(numArgs+3) +
114 " positional arguments");
115 if (mode.size() != 3)
116 throw po::error(
"mode specification for Euler angles requires a string from 3 chars (xyz)");
118 const std::string axes_order(
"xyz");
122 for (
size_t i=0; i<3; ++i) {
123 size_t idx = axes_order.find(mode[i]);
124 if (idx == std::string::npos)
125 throw po::error(
"invalid axis specification for Euler angles: " +
126 boost::lexical_cast<std::string>(mode[i]));
128 angles[i] = boost::lexical_cast<
double>(*arg); ++arg;
130 q = Eigen::AngleAxisd(angles[0], Eigen::Vector3d::Unit(axes_idxs[0])) *
131 Eigen::AngleAxisd(angles[1], Eigen::Vector3d::Unit(axes_idxs[1])) *
132 Eigen::AngleAxisd(angles[2], Eigen::Vector3d::Unit(axes_idxs[2]));
136 msg.transform.rotation.x = q.x();
137 msg.transform.rotation.y = q.y();
138 msg.transform.rotation.z = q.z();
139 msg.transform.rotation.w = q.w();
142 msg.header.frame_id = *arg++;
143 msg.child_frame_id = *arg++;
144 }
catch (
const po::error &e) {
146 usage(argv[0], options_description);
148 }
catch (
const boost::bad_lexical_cast &e) {
150 usage(argv[0], options_description);
155 int main(
int argc,
char ** argv)
160 geometry_msgs::TransformStamped msg;
163 if (msg.header.frame_id.empty() || msg.child_frame_id.empty())
165 ROS_FATAL(
"target or source frame is empty");
168 if (msg.header.frame_id == msg.child_frame_id)
170 ROS_FATAL(
"target and source frame are the same (%s, %s) this cannot work",
171 msg.child_frame_id.c_str(), msg.header.frame_id.c_str());
178 ROS_INFO(
"Spinning until killed, publishing %s to %s",
179 msg.header.frame_id.c_str(), msg.child_frame_id.c_str());
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
ROSCPP_DECL void spin(Spinner &spinner)
#define ROS_FATAL_STREAM(args)