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 
00031 
00032 
00033 
00034 
00035 #include "power_monitor.h"
00036 
00037 using namespace std;
00038 using namespace power_monitor;
00039 
00040 PowerMonitor::PowerMonitor() : master_state_(-1),
00041                                battery_update_timeout_(120)
00042 {
00043     ros::NodeHandle node;
00044     ros::NodeHandle pnh("~");
00045 
00046     static const string battery_server_topic = "battery/server2";
00047     static const string power_board_node     = "power_board";
00048     string estimator_type_str                = "advanced";
00049     double freq                              = 0.1;
00050 
00051     pnh.getParam("estimation_method",    estimator_type_str);
00052     pnh.getParam("frequency",            freq);
00053     pnh.getParam("battery_update_timeout", battery_update_timeout_);
00054 
00055     ros::Duration(2).sleep();
00056 
00057     
00058     addEstimator(new FuelGaugePowerStateEstimator());
00059     addEstimator(new AdvancedPowerStateEstimator());
00060 
00061     
00062     dynamic_reconfigure::Server<PowerMonitorConfig>::CallbackType config_callback = boost::bind(&PowerMonitor::configCallback, this, _1, _2);
00063     config_server_.setCallback(config_callback);
00064 
00065     
00066     if (estimator_types_.size() == 0)
00067     {
00068         ROS_FATAL("No power state estimators defined. Shutting down");
00069         exit(1);
00070     }
00071     map<string, PowerStateEstimator::Type>::const_iterator i = estimator_types_.find(estimator_type_str);
00072     if (i == estimator_types_.end())
00073     {
00074         
00075         string first_estimator_type_str = estimator_types_.begin()->first;
00076         ROS_ERROR("Unknown power state estimator type: %s. Defaulting to %s", estimator_type_str.c_str(), first_estimator_type_str.c_str());
00077         setActiveEstimator(estimator_types_.begin()->second);
00078     }
00079     else
00080         setActiveEstimator(i->second);
00081 
00082     power_state_pub_       = node.advertise<pr2_msgs::PowerState>("power_state", 5, true);
00083     power_state_pub_timer_ = node.createTimer(ros::Duration(1.0 / freq), &PowerMonitor::onPublishTimer, this);
00084     battery_server_sub_    = node.subscribe(battery_server_topic, 10, &PowerMonitor::batteryServerUpdate, this);
00085     power_node_sub_        = node.subscribe(power_board_node + "/state", 10, &PowerMonitor::powerNodeUpdate, this);
00086 }
00087 
00088 void PowerMonitor::addEstimator(PowerStateEstimator* est)
00089 {
00090     estimator_types_[est->getMethodName()] = est->getMethodType();
00091     estimators_[est->getMethodType()] = boost::shared_ptr<PowerStateEstimator>(est);
00092 }
00093 
00094 void PowerMonitor::configCallback(PowerMonitorConfig& config, uint32_t level)
00095 {
00096     setActiveEstimator((PowerStateEstimator::Type) config.power_state_estimator_);
00097 }
00098 
00099 bool PowerMonitor::setActiveEstimator(PowerStateEstimator::Type estimator_type)
00100 {
00101     map<PowerStateEstimator::Type, boost::shared_ptr<PowerStateEstimator> >::const_iterator i = estimators_.find(estimator_type);
00102     if (i == estimators_.end())
00103         return false;
00104     if (active_estimator_ == i->second)
00105         return true;
00106 
00107     if (active_estimator_ == boost::shared_ptr<PowerStateEstimator>())
00108         ROS_INFO("Power state estimator set to %s", i->second->getMethodName().c_str());
00109     else
00110         ROS_INFO("Power state estimator changed from %s to %s", active_estimator_->getMethodName().c_str(), i->second->getMethodName().c_str());
00111 
00112     active_estimator_ = i->second;
00113 
00114     return true;
00115 }
00116 
00117 void PowerMonitor::batteryServerUpdate(const boost::shared_ptr<const pr2_msgs::BatteryServer2>& battery_server)
00118 {
00119     boost::mutex::scoped_lock lock(update_mutex_);
00120 
00121     ROS_DEBUG("Received battery message: voltage=%.2f", battery_server->battery[0].battery_register[0x9] / 1000.0f);
00122 
00123     battery_servers_[battery_server->id] = battery_server;
00124 }
00125 
00126 void PowerMonitor::powerNodeUpdate(const boost::shared_ptr<const pr2_msgs::PowerBoardState>& power_board_state)
00127 {
00128     boost::mutex::scoped_lock lock(update_mutex_);
00129 
00130     ROS_DEBUG("Received power board state message: %s", masterStateToString(power_board_state->master_state).c_str());
00131 
00132     master_state_ = power_board_state->master_state;
00133 
00134     
00135     if (master_state_ == pr2_msgs::PowerBoardState::MASTER_SHUTDOWN)
00136     {
00137       ROS_WARN("Power board reports imminant shutdown");
00138       publishPowerState();
00139     }
00140 }
00141 
00142 string PowerMonitor::masterStateToString(int8_t master_state) const
00143 {
00144     switch (master_state)
00145     {
00146         case pr2_msgs::PowerBoardState::MASTER_NOPOWER:  return "No Power";
00147         case pr2_msgs::PowerBoardState::MASTER_STANDBY:  return "Standby";
00148         case pr2_msgs::PowerBoardState::MASTER_ON:       return "On";
00149         case pr2_msgs::PowerBoardState::MASTER_OFF:      return "Off";
00150         case pr2_msgs::PowerBoardState::MASTER_SHUTDOWN: return "Shutdown";
00151         default:                                         return "Unknown";
00152     }
00153 }
00154 
00155 PowerObservation PowerMonitor::extractObservation()
00156 {
00157     boost::mutex::scoped_lock lock(update_mutex_);
00158 
00159     vector<BatteryObservation> batteries;
00160     for (map<int, boost::shared_ptr<const pr2_msgs::BatteryServer2> >::iterator i = battery_servers_.begin(); i != battery_servers_.end(); i++)
00161     {
00162         const pr2_msgs::BatteryServer2* bs = i->second.get();
00163 
00164         ros::Time stamp = bs->header.stamp;
00165 
00166         for (unsigned int j = 0; j < bs->battery.size(); j++)
00167         {
00168             const pr2_msgs::BatteryState2& b = bs->battery[j];
00169 
00170             bool         ac_present = b.power_present;
00171             float        voltage    = b.battery_register[0x9] / 1000.0f;
00172             float        current    = b.battery_register[0xA] / 1000.0f;
00173             unsigned int rsc        = b.battery_register[0x0D];
00174             float        rem_cap    = b.battery_register[0x0F] / 1000.0f;
00175             unsigned int tte_min    = b.battery_register[0x12];
00176             unsigned int ttf_min    = b.battery_register[0x13];
00177 
00178             ros::Duration tte;
00179             if (tte_min > 0)
00180                 tte = ros::Duration(tte_min * 60, 0);
00181             else
00182                 tte = ros::Duration(-1, 0);
00183 
00184             ros::Duration ttf;
00185             if (ttf_min > 0)
00186                 ttf = ros::Duration(ttf_min * 60, 0);
00187             else
00188                 ttf = ros::Duration(-1, 0);
00189 
00190             if (voltage == 0.0)
00191                 continue;
00192 
00193             batteries.push_back(BatteryObservation(stamp, ac_present, voltage, current, rsc, rem_cap, tte, ttf));
00194 
00195             ROS_DEBUG("Battery %d.%d: %6.2f V %6.2f A %6.2f W (soc: %d, cap: %6.2f, tte: %dm, ttf: %dm)", bs->id, j + 1, voltage, current, current * voltage, rsc, rem_cap, tte_min, ttf_min);
00196         }
00197     }
00198 
00199     return PowerObservation(ros::Time::now(), master_state_, batteries);
00200 }
00201 
00202 ros::Time PowerMonitor::getLastBatteryUpdate() const
00203 {
00204   ros::Time rv;
00205   for (map<int, boost::shared_ptr<const pr2_msgs::BatteryServer2> >::const_iterator i = battery_servers_.begin(); i != battery_servers_.end(); i++)
00206   {
00207     const pr2_msgs::BatteryServer2* bs = i->second.get();
00208     if (bs->header.stamp > rv)
00209       rv = bs->header.stamp;
00210   }
00211 
00212   return rv;
00213 }
00214 
00215 void PowerMonitor::onPublishTimer(const ros::TimerEvent& e)
00216 {
00217   
00218   if (battery_update_timeout_ > 0 && ((ros::Time::now() - getLastBatteryUpdate()).toSec() > battery_update_timeout_))
00219   {
00220     ROS_WARN_THROTTLE(60, "Power monitor not publishing estimate, batteries have not recently updated. Check diagnostics.");
00221     return;
00222   }
00223 
00224   publishPowerState();
00225 }
00226 
00227 void PowerMonitor::publishPowerState()
00228 {
00229     boost::mutex::scoped_lock lock(publish_mutex_);
00230 
00231     
00232     PowerObservation obs = extractObservation();
00233     if (obs.getBatteries().size() == 0)
00234     {
00235         ROS_DEBUG("Nothing observed");
00236         return;
00237     }
00238 
00239     ROS_DEBUG("Power: %6.1f W. Min voltage: %6.2f V", obs.getTotalPower(), obs.getMinVoltage());
00240 
00241     
00242     for (map<PowerStateEstimator::Type, boost::shared_ptr<PowerStateEstimator> >::const_iterator i = estimators_.begin(); i != estimators_.end(); i++)
00243         i->second.get()->recordObservation(obs);
00244 
00245     
00246     ros::Time t = ros::Time::now();
00247     if (active_estimator_->canEstimate(t))
00248     {
00249         PowerStateEstimate estimate = active_estimator_->estimate(t);
00250         ROS_DEBUG("Remaining: %.0f min (%d%%)", estimate.time_remaining.toSec() / 60, estimate.relative_capacity);
00251 
00252         
00253         pr2_msgs::PowerState ps;
00254         ps.header.stamp      = ros::Time::now();
00255         ps.AC_present        = obs.getAcCount();
00256         ps.power_consumption = obs.getTotalPower();
00257         ps.prediction_method = active_estimator_->getMethodName();
00258         ps.relative_capacity = (int8_t) estimate.relative_capacity;
00259         ps.time_remaining    = estimate.time_remaining;
00260         power_state_pub_.publish(ps);
00261     }
00262 }
00263 
00264 int main(int argc, char** argv)
00265 {
00266     ros::init(argc, argv, "power_monitor");
00267     PowerMonitor monitor;
00268     ros::spin();
00269     return 0;
00270 }