servo_state_publisher.cpp
Go to the documentation of this file.
00001 
00006 /*
00007  * Copyright 2015 Vladimir Ermakov
00008  *
00009  * This file is part of the mavros package and subject to the license terms
00010  * in the top-level LICENSE file of the mavros repository.
00011  * https://github.com/mavlink/mavros/tree/master/LICENSE.md
00012  */
00013 
00014 #include <ros/ros.h>
00015 #include <ros/console.h>
00016 
00017 #include <urdf/model.h>
00018 #include <mavros_msgs/RCOut.h>
00019 #include <sensor_msgs/JointState.h>
00020 
00021 class ServoDescription {
00022 public:
00023         std::string joint_name;
00024         float joint_lower;
00025         float joint_upper;
00026 
00027         size_t rc_channel;
00028 
00029         uint16_t rc_min;
00030         uint16_t rc_max;
00031         uint16_t rc_trim;
00032         uint16_t rc_dz;
00033         bool rc_rev;
00034 
00035         ServoDescription() :
00036                 joint_name{},
00037                 joint_lower(-M_PI/4),
00038                 joint_upper(M_PI/4),
00039                 rc_channel(0),
00040                 rc_min(1000),
00041                 rc_max(2000),
00042                 rc_trim(1500),
00043                 rc_dz(0),
00044                 rc_rev(false)
00045         { };
00046 
00047         ServoDescription(std::string joint_name_, double lower_, double upper_,
00048                         int channel_,
00049                         int min_, int max_, int trim_, int dz_,
00050                         bool rev_) :
00051                 joint_name(joint_name_),
00052                 joint_lower(lower_),
00053                 joint_upper(upper_),
00054                 rc_channel(channel_),
00055                 rc_min(min_),
00056                 rc_max(max_),
00057                 rc_trim(trim_),
00058                 rc_dz(dz_),
00059                 rc_rev(rev_)
00060         { };
00061 
00066         inline float normalize(uint16_t pwm) {
00067                 // 1) fix bounds
00068                 pwm = std::max(pwm, rc_min);
00069                 pwm = std::min(pwm, rc_max);
00070 
00071                 // 2) scale around mid point
00072                 float chan;
00073                 if (pwm > (rc_trim + rc_dz)) {
00074                         chan = (pwm - rc_trim - rc_dz) / (float)(rc_max - rc_trim - rc_dz);
00075                 }
00076                 else if (pwm < (rc_trim - rc_dz)) {
00077                         chan = (pwm - rc_trim + rc_dz) / (float)(rc_trim - rc_min - rc_dz);
00078                 }
00079                 else {
00080                         chan = 0.0;
00081                 }
00082 
00083                 if (rc_rev)
00084                         chan *= -1;
00085 
00086                 if (!std::isfinite(chan)) {
00087                         ROS_DEBUG("SSP: not finite result in RC%zu channel normalization!", rc_channel);
00088                         chan = 0.0;
00089                 }
00090 
00091                 return chan;
00092         }
00093 
00094         float calculate_position(uint16_t pwm) {
00095                 float channel = normalize(pwm);
00096 
00097                 // not sure should i differently map -1..0 and 0..1
00098                 // for now there arduino map() (explicit)
00099                 float position = (channel + 1.0) * (joint_upper - joint_lower) / (1.0 + 1.0) + joint_lower;
00100 
00101                 return position;
00102         }
00103 };
00104 
00105 class ServoStatePublisher {
00106 public:
00107         ServoStatePublisher() :
00108                 nh()
00109         {
00110                 ros::NodeHandle priv_nh("~");
00111 
00112                 XmlRpc::XmlRpcValue param_dict;
00113                 priv_nh.getParam("", param_dict);
00114 
00115                 ROS_ASSERT(param_dict.getType() == XmlRpc::XmlRpcValue::TypeStruct);
00116 
00117                 urdf::Model model;
00118                 model.initParam("robot_description");
00119                 ROS_INFO("SSP: URDF robot: %s", model.getName().c_str());
00120 
00121                 for (auto &pair : param_dict) {
00122                         ROS_DEBUG("SSP: Loading joint: %s", pair.first.c_str());
00123 
00124                         // inefficient, but easier to program
00125                         ros::NodeHandle pnh(priv_nh, pair.first);
00126 
00127                         bool rc_rev;
00128                         int rc_channel, rc_min, rc_max, rc_trim, rc_dz;
00129 
00130                         if (!pnh.getParam("rc_channel", rc_channel)) {
00131                                 ROS_ERROR("SSP: '%s' should provice rc_channel", pair.first.c_str());
00132                                 continue;
00133                         }
00134 
00135                         pnh.param("rc_min", rc_min, 1000);
00136                         pnh.param("rc_max", rc_max, 2000);
00137                         if (!pnh.getParam("rc_trim", rc_trim)) {
00138                                 rc_trim = rc_min + (rc_max - rc_min) / 2;
00139                         }
00140 
00141                         pnh.param("rc_dz", rc_dz, 0);
00142                         pnh.param("rc_rev", rc_rev, false);
00143 
00144                         auto joint = model.getJoint(pair.first);
00145                         if (!joint) {
00146                                 ROS_ERROR("SSP: URDF: there no joint '%s'", pair.first.c_str());
00147                                 continue;
00148                         }
00149                         if (!joint->limits) {
00150                                 ROS_ERROR("SSP: URDF: joint '%s' should provide <limit>", pair.first.c_str());
00151                                 continue;
00152                         }
00153 
00154                         double lower = joint->limits->lower;
00155                         double upper = joint->limits->upper;
00156 
00157                         servos.emplace_back(pair.first, lower, upper, rc_channel, rc_min, rc_max, rc_trim, rc_dz, rc_rev);
00158                         ROS_INFO("SSP: joint '%s' (RC%d) loaded", pair.first.c_str(), rc_channel);
00159                 }
00160 
00161                 rc_out_sub = nh.subscribe("rc_out", 10, &ServoStatePublisher::rc_out_cb, this);
00162                 joint_states_pub = nh.advertise<sensor_msgs::JointState>("joint_states", 10);
00163         }
00164 
00165         void spin() {
00166                 if (servos.empty()) {
00167                         ROS_WARN("SSP: there nothing to do, exiting");
00168                         return;
00169                 }
00170 
00171                 ROS_INFO("SSP: Initialization done. %zu joints served", servos.size());
00172                 ros::spin();
00173         }
00174 
00175 private:
00176         ros::NodeHandle nh;
00177         ros::Subscriber rc_out_sub;
00178         ros::Publisher joint_states_pub;
00179 
00180         std::list<ServoDescription> servos;
00181 
00182         void rc_out_cb(const mavros_msgs::RCOut::ConstPtr &msg) {
00183                 if (msg->channels.empty())
00184                         return;         // nothing to do
00185 
00186                 auto states = boost::make_shared<sensor_msgs::JointState>();
00187                 states->header.stamp = msg->header.stamp;
00188 
00189                 for (auto &desc : servos) {
00190                         if (!(desc.rc_channel != 0 && desc.rc_channel <= msg->channels.size()))
00191                                 continue;       // prevent crash on servos not in that message
00192 
00193                         uint16_t pwm = msg->channels[desc.rc_channel - 1];
00194                         if (pwm == 0 || pwm == UINT16_MAX)
00195                                 continue;       // exclude unset channels
00196 
00197                         states->name.emplace_back(desc.joint_name);
00198                         states->position.emplace_back(desc.calculate_position(pwm));
00199                 }
00200 
00201                 joint_states_pub.publish(states);
00202         }
00203 };
00204 
00205 int main(int argc, char *argv[])
00206 {
00207         ros::init(argc, argv, "servo_state_publisher");
00208 
00209         ServoStatePublisher state_publisher;
00210         state_publisher.spin();
00211         return 0;
00212 }
00213 


mavros_extras
Author(s): Vladimir Ermakov
autogenerated on Thu Feb 9 2017 04:00:23