hysteresis_sinesweep_plot.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright (c) 2008, Willow Garage, Inc.
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions are met:
00008 #
00009 #     * Redistributions of source code must retain the above copyright
00010 #       notice, this list of conditions and the following disclaimer.
00011 #     * Redistributions in binary form must reproduce the above copyright
00012 #       notice, this list of conditions and the following disclaimer in the
00013 #       documentation and/or other materials provided with the distribution.
00014 #     * Neither the name of the Willow Garage, Inc. nor the names of its
00015 #       contributors may be used to endorse or promote products derived from
00016 #       this software without specific prior written permission.
00017 #
00018 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00019 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00022 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00023 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00024 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00025 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00026 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00027 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00028 # POSSIBILITY OF SUCH DAMAGE.
00029 
00030 ##\author Kevin Watts
00031 
00032 PKG = "qualification"
00033 import roslib; roslib.load_manifest(PKG)
00034 
00035 import numpy
00036 
00037 import rospy
00038 
00039 from pr2_self_test_msgs.srv import TestResult, TestResultRequest
00040 from qualification.analysis.hysteresis_analysis import *
00041 
00042 from joint_qualification_controllers.msg import HysteresisData
00043 from std_msgs.msg import Bool
00044 
00045 import traceback
00046 
00047 class AnalysisApp:
00048   def __init__(self):
00049     self.data_sent = False
00050     self._motors_halted = True
00051     rospy.init_node("test_plotter")
00052     self.data_topic = rospy.Subscriber("/test_data", HysteresisData, self._on_data)
00053     self.motors_topic = rospy.Subscriber("pr2_etherCAT/motors_halted", Bool, self.on_motor_cb)
00054     self.result_service = rospy.ServiceProxy('/test_result', TestResult)
00055     self.data = None
00056     
00057   def _on_data(self, msg):
00058     self.data = msg
00059 
00060   def has_data(self):
00061     return self.data is not None
00062     
00063   def on_motor_cb(self, msg):
00064     self._motors_halted = msg.data
00065 
00066   def test_failed_service_call(self, except_str = ''):
00067     rospy.logerr(except_str)
00068     r = TestResultRequest()
00069     r.html_result = except_str
00070     r.text_summary = 'Caught exception, automated test failure.'
00071     r.plots = []
00072     r.result = TestResultRequest.RESULT_FAIL
00073     self.send_results(r)
00074 
00075   def send_results(self, test_result):
00076     if not self.data_sent:
00077       rospy.wait_for_service('test_result')
00078       self.result_service.call(test_result)
00079       self.data_sent = True
00080 
00081   def hysteresis_plot(self):
00082     try:
00083       r = TestResultRequest()
00084       r.html_result = ''
00085       r.text_summary = 'No data.'
00086       r.plots = []
00087       r.result = TestResultRequest.RESULT_FAIL
00088 
00089       image_title = self.data.joint_name + "_hysteresis"
00090 
00091       params = HysteresisParameters()
00092       params.joint_name = self.data.joint_name
00093       params.pos_effort = self.data.arg_value[1]
00094       params.neg_effort = self.data.arg_value[0]
00095       params.range_max  = self.data.arg_value[3]
00096       params.range_min  = self.data.arg_value[2]
00097       params.tolerance  = self.data.arg_value[7]
00098       params.sd_max     = self.data.arg_value[8]
00099       params.slope      = self.data.arg_value[9]
00100       params.timeout    = self.data.arg_value[5]
00101       params.velocity   = self.data.arg_value[4]
00102       params.max_effort = self.data.arg_value[6]
00103 
00104       params.p_gain     = self.data.arg_value[10]
00105       params.i_gain     = self.data.arg_value[11]
00106       params.d_gain     = self.data.arg_value[12]
00107       params.i_clamp    = self.data.arg_value[13]
00108 
00109       r.params = params.get_test_params()
00110 
00111       if self._motors_halted:
00112         r.result = TestResultRequest.RESULT_FAIL
00113         r.text_summary = "Motors halted. Check runstop. Hysteresis results are invalid."
00114         r.html_result = "<p>Motors halted during test. Check estop and verify that power is on.</p>\n"
00115         self.send_results(r)
00116 
00117       # Check the num points, timeout
00118       if len(self.data.position_up) < 20 or len(self.data.position_down) < 20:
00119         r.result = TestResultRequest.RESULT_FAIL
00120         r.text_summary = "Not enough data points, bad encoder or bad gains."
00121         
00122         error_msg = "<p>Not enough data points, hysteresis controller may have malfunctioned. Check diagnostics.</p>"
00123         error_msg += "<p>Up data points: %d, down points: %d</p>\n" % (len(self.data.position_up), len(self.data.position_down))
00124         error_msg += "<p>The encoder may be missing ticks, check encoder.<br>\n"
00125         error_msg += "Make sure mechanism is free to move and not trapped or constrained<br>\n"
00126         error_msg += "Make sure motors are not in safety lockout (check diagnostics)<br>\n"
00127         error_msg += "Check controller gains if problem persists on components of same type.</p>\n"
00128         error_msg += "<p>Test status: <b>FAIL</b>.</p>"
00129         error_msg += '<p align=center><b>Test Parameters</b></p>\n'
00130         
00131         r.html_result = error_msg
00132         self.send_results(r)
00133         return
00134 
00135       if self.data.arg_value[5] == 0:
00136         r.result = TestResultRequest.RESULT_FAIL
00137         r.text_summary = 'Hysteresis controller timed out. Check diagnostics.'
00138         r.html_result = '<p>Hysteresis controller timed out. Check diagnostics. Controller gains may be bad, or motors may be in safety lockout. Did the device pass the visualizer? Is it calibrated?</p><p>Test status: <b>FAIL</b>.</p>'
00139         r.html_result += '<p align=center><b>Test Parameters</b></p>\n'
00140         self.send_results(r)
00141         return
00142 
00143       # Compute the encoder travel
00144       min_encoder = min(min(numpy.array(self.data.position_up)), min(numpy.array(self.data.position_down)))
00145       max_encoder = max(max(numpy.array(self.data.position_up)), max(numpy.array(self.data.position_down)))
00146 
00147       if abs(max_encoder - min_encoder) < 0.005:
00148         r.result = TestResultRequest.RESULT_FAIL
00149         r.text_summary = 'Mechanism didn\'t move. Check diagnostics.'
00150         r.html_result = "<p>No travel of mechanism, hysteresis did not complete. Check controller gains and encoder.</p><p>Test status: <b>FAIL</b>.</p>"
00151         r.html_result += '<p align=center><b>Test Parameters</b></p>\n'
00152         self.send_results(r)
00153         return
00154       
00155       pos_data = HysteresisDirectionData(self.data.position_up, self.data.effort_up, self.data.velocity_up)
00156       neg_data = HysteresisDirectionData(self.data.position_down, self.data.effort_down, self.data.velocity_down)
00157       hyst_data = HysteresisTestData(pos_data, neg_data)
00158       
00159       # Make sure we have at least some points in both directions
00160       if pos_data.position.size < 20 or neg_data.position.size < 20:
00161         r.result = TestResultRequest.RESULT_FAIL
00162         r.text_summary = 'Incomplete data set. Check diagnostics.'
00163         r.html_result = "<p>Not enough data in one or more directions.</p><p>Test Status: <b>FAIL</b>.</p><p>Test status: <b>FAIL</b>.</p>"
00164         r.html_result += '<p align=center><b>Test Parameters</b></p>\n'
00165         self.send_results(r)
00166         return
00167 
00168       # Process HTML result
00169       range_result = range_analysis(params, hyst_data)
00170  
00171       if params.slope == 0:
00172         effort_result = effort_analysis(params, hyst_data)
00173       else:
00174         effort_result = regression_analysis(params, hyst_data)
00175  
00176       vel_result = velocity_analysis(params, hyst_data)
00177       vel_html = vel_result.html
00178 
00179 
00180       effort_plot = plot_effort(params, hyst_data)
00181       vel_plot = plot_velocity(params, hyst_data)
00182 
00183       # render positive effort and velocity to a CSV "plot"
00184       positive_csv = Plot()
00185       positive_csv.image_format = "csv"
00186       positive_csv.title = params.joint_name + "_hysteresis_p"
00187       csv = "Position,Effort,Velocity\n"
00188       p = hyst_data.positive
00189       for i in range(len(p.position)):
00190          csv = csv + "%f,%f,%f\n"%(p.position[i], p.effort[i], p.velocity[i])
00191       positive_csv.image = csv
00192 
00193       # render positive effort and velocity to a CSV "plot"
00194       negative_csv = Plot()
00195       negative_csv.image_format = "csv"
00196       negative_csv.title = params.joint_name + "_hysteresis_n"
00197       csv = "Position,Effort,Velocity\n"
00198       p = hyst_data.negative
00199       for i in range(len(p.position)):
00200          csv = csv + "%f,%f,%f\n"%(p.position[i], p.effort[i], p.velocity[i])
00201       negative_csv.image = csv
00202 
00203       r.plots = [effort_plot, vel_plot, positive_csv, negative_csv]
00204 
00205       html = '<img src=\"IMG_PATH/%s.png\", width = 640, height = 480/>' % effort_plot.title
00206       html += '<p><b>Legend:</b> Red - Average Effort. Yellow - Error Bars. Green - Expected Effort.</p>\n'
00207       html += '<p align=center><b>Range Data</b></p>' + range_result.html + '<hr size="2">\n'
00208       html += '<p align=center><b>Effort Data</b></p>' + effort_result.html + '<hr size="2">\n'
00209 
00210       html += '<p align=center><b>Velocity Data</b></p>' 
00211       html += '<img src=\"IMG_PATH/%s.png\", width = 640, height = 480/>' % vel_plot.title
00212 
00213       html += vel_html + '<hr size="2">\n'
00214       html += '<p align=center><b>Test Parameters</b></p>\n'
00215 
00216 
00217       r.html_result = html
00218 
00219       r.values.extend(effort_result.values)
00220       r.values.extend(range_result.values)
00221       r.values.extend(vel_result.values)
00222      
00223       if range_result.result and effort_result.result:
00224         r.text_summary = 'Hysteresis result: OK'
00225         r.result = TestResultRequest.RESULT_PASS
00226       elif not range_result.result:
00227         r.text_summary = 'Hysteresis range failed. Joint did not complete range of motion.'
00228         r.result = TestResultRequest.RESULT_FAIL
00229       else:
00230         r.text_summary = 'Hysteresis result: Questionable. ' + range_result.summary + ' ' + effort_result.summary
00231         r.result = TestResultRequest.RESULT_HUMAN_REQUIRED
00232 
00233       self.send_results(r)
00234     except Exception, e:
00235       rospy.logerr('hysteresis_plot caught exception, returning test failure.')
00236       rospy.logerr(traceback.format_exc())
00237       self.test_failed_service_call(traceback.format_exc())
00238 
00239       
00240 if __name__ == "__main__":
00241   try:
00242     app = AnalysisApp()
00243     my_rate = rospy.Rate(5)
00244     while not rospy.is_shutdown() and not app.has_data():
00245       my_rate.sleep()
00246       
00247     if not rospy.is_shutdown():
00248       app.hysteresis_plot()
00249     rospy.spin()
00250   except KeyboardInterrupt:
00251     pass
00252   except Exception, e:
00253     traceback.print_exc()
00254     


qualification
Author(s): Kevin Watts (watts@willowgarage.com), Josh Faust (jfaust@willowgarage.com)
autogenerated on Sat Dec 28 2013 17:57:34