battery_profiler.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 import rospy
00003 import roslib
00004 import sys
00005 import rospy
00006 import rosbag
00007 import numpy
00008 import scipy.optimize
00009 import rospkg
00010 import socket
00011 
00012 from matplotlib import pyplot
00013 from scipy.optimize import curve_fit
00014 from diagnostic_msgs.msg import DiagnosticArray, DiagnosticStatus, KeyValue
00015 from std_msgs.msg import Header
00016 from rospy.numpy_msg import numpy_msg
00017 
00018 """
00019 This program is a diagnostic tool for interpreting offline voltage data from the Segbot fleet.
00020 In particular, the program reads in a .bag file, and extracts voltage data published on the /diagnostics topic.
00021 
00022 Currently, this
00023             plots the voltage data as well as the fitted model.
00024             writes the model to file for time-remaining estimation (handled elsewhere)
00025 
00026 Possible improvements:
00027     Have ways of merging multple runs into one model, or an existing model and an additional run
00028     Change from half hours to hours, without breaking fitting function.
00029         (note this would also requires changes to the battery diagnostics node)
00030 
00031 Author: Maxwell J Svetlik
00032 """
00033 
00034 sliding_window = 1
00035 half_hour_conversion = 1800
00036 eol_voltage = 10.75
00037 
00038 
00039 def model_func(t, A, K, C):
00040     return -A * numpy.exp(K * t) + C
00041 
00042 
00043 def fit_exp_nonlinear(t, y):
00044     opt_parms, parm_cov = scipy.optimize.curve_fit(model_func, t, y, maxfev=1000)
00045     A, K, C = opt_parms
00046     return A, K, C
00047 
00048 
00049 class battery_profiler:
00050 
00051     def __init__(self):
00052         print 'STARTING BATTERY PROFILER'
00053         self.voltage_values = []
00054         self.time_values = []
00055         self.time_base = -1
00056         self.rospack = rospkg.RosPack()
00057         self.model_filename = self.rospack.get_path('segbot_sensors') + "/config/battery_profile"
00058 
00059     def mySmooth(self, data, window):
00060         smoothed = numpy.zeros((len(data), 1))
00061         for d in range(len(data)):
00062             smoothed[d] = numpy.mean(data[max(0, d-window):d])
00063         return smoothed
00064 
00065     """
00066     returns the time remaining based on the current voltage value, and the estimated time to reach
00067     the end of life voltage (eol_voltage). Note that this voltage may need to be changed based on the specific
00068     battery type. For instance, on a segbot_v3, this value should be around 71.5
00069     """
00070     def get_time_estimate(self, A, K, C, voltage):
00071         max_life = numpy.log((eol_voltage - C) / -A) / K
00072         if voltage > C:
00073                 A = -A
00074                 return (numpy.log((voltage - C) / -A) / K + max_life)
00075         cur_life = numpy.log((voltage - C) / -A) / K
00076         return max_life - cur_life
00077 
00078     def writeModelToFile(self, A, K, C):
00079         print "Opening the file at {}...".format(self.model_filename)
00080         target = open(self.model_filename, 'w')
00081         target.write(str(A)+'\n')
00082         target.write(str(K)+'\n')
00083         target.write(str(C)+'\n')
00084         target.close()
00085         print "Model written to configuration file successfully."
00086 
00087     def listener(self):
00088         try:
00089             bag = rosbag.Bag(sys.argv[1])
00090         except IOError:
00091             print 'Could not open bag file at ' + sys.argv[1]
00092             sys.exit(-1)
00093         topic, msg_arr, t = bag.read_messages(topics=['/diagnostics']).next()
00094         self.time_base = msg_arr.header.stamp.to_sec()
00095         print 'Base time: {}\n'.format(self.time_base)
00096 
00097         for topic, msg_arr, t in bag.read_messages(topics=['/diagnostics']):
00098             for msg in msg_arr.status:
00099                 if msg.name == 'voltage':
00100                     self.voltage_values.append(float(msg.values[0].value))
00101                     """
00102                     Collect relative times, and convert into half-hours
00103                     Note that this is necessary for the exp-fit function; minutes are too large, hours are too small
00104                     in terms of values that the fit function can handle.
00105                     """
00106                     self.time_values.append(float((msg_arr.header.stamp.to_sec() -
00107                                                    self.time_base) / half_hour_conversion))
00108         bag.close()
00109 
00110         pyplot.plot(self.time_values, self.mySmooth(self.voltage_values, sliding_window), 'b', label="Voltage Data")
00111         title = 'Segbot Voltage over Time on Active Use: {}'.format(socket.gethostname())
00112         pyplot.legend(loc=4)
00113         pyplot.title(title)
00114         pyplot.xlabel('Time ( 30-min Increments)')
00115         pyplot.ylabel('Voltage')
00116         pyplot.legend(loc='upper right')
00117 
00118         total_time = self.time_values[len(self.time_values) - 1] - self.time_values[0]
00119         print 'EXPERIMENT DATA'
00120         print 'Total run time: {}'.format(total_time)
00121         print 'Starting voltage: {}'.format(self.voltage_values[0])
00122         print 'Ending voltage: {}'.format(self.voltage_values[len(self.voltage_values) - 1])
00123 
00124         A, K, C = fit_exp_nonlinear(numpy.asarray(self.time_values), numpy.asarray(self.voltage_values))
00125         fit_y = model_func(numpy.asarray(self.time_values), A, K, C)
00126         print 'Model: -{} * exp({}x) + {}\n'.format(A, K, C)
00127         self.writeModelToFile(A, K, C)
00128 
00129         pyplot.plot(numpy.asarray(self.time_values), fit_y, "r--", label="Exponential Model")
00130         pyplot.show()
00131 
00132 if __name__ == '__main__':
00133     if len(sys.argv) < 2:
00134         print 'Missing argument: path to bagfile. Exiting.'
00135         sys.exit(-1)
00136     battery_profiler = battery_profiler()
00137     rospy.init_node('battery_profiler', anonymous=True)
00138     battery_profiler.listener()


segbot_sensors
Author(s): Piyush Khandelwal
autogenerated on Thu Jun 6 2019 21:37:13