tactile_sensor_model.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 
00003 # Charlie Kemp's initial attempt to model the force -> digital signal
00004 # curves for a single taxel. 
00005 #
00006 # + First version written on June 4, 2012. 
00007 # + Cleaned up, documented, and made minor edits June 5, 2012
00008 
00009 import matplotlib.pylab as pl
00010 
00011 def logistic(t):
00012     return(1.0/(1.0 + pl.exp(-t)))
00013 
00014 def norm_logistic(t):
00015     return(2.0 * (logistic(t)-0.5))
00016 
00017 def abbot_curve(t):
00018     # total hack in attempt to make curve that looks like the Abbot's
00019     # Curve in the paper haven't visualized it, yet...
00020     #
00021     # assume t in in range [0, 1]
00022     tmp = logistic((t-0.5) * 12.0)
00023     return(tmp)
00024 
00025 class TaxelModel:
00026     '''Attempts to model the digital signal that results from a normal
00027     force applied to a taxel. It assumes that the force is uniformly
00028     distributed over an area. The contact area is specified as a
00029     percentage of the taxel area.'''
00030     def __init__(self, contact_area_percent=50.0):
00031 
00032         ######################################
00033         # begin: parameters to be specified
00034 
00035         self.contact_area_percent = contact_area_percent
00036 
00037         # resistor that is in series with the taxel (Ohms)
00038         self.r1 = 47.0 
00039 
00040         # total voltage across the taxel and r1, which are in serise (Volts)
00041         self.vtot = 5.0 
00042 
00043         # the maximum resistance of the taxel when no pressure is applied (Ohms)
00044         self.rtax_max = 50.0 
00045 
00046         # the minimum force that will be applied to the taxel (Newtons)
00047         self.fz_min = 0.0 
00048 
00049         # the maximum force that will be applied to the taxel (Newtons)
00050         self.fz_max = 45.0 
00051 
00052         # the number of bits for the analog to digital conversion 
00053         self.adc_bits = 10 
00054 
00055         # the pressure sensitive area of the taxel (meters^2)
00056         self.taxel_area = 0.04 * 0.04 
00057 
00058         # pressure that results in minimum resistance after which
00059         # further pressure does not result in a reduction in the
00060         # signal, since the sensor is saturated (Pascals = N/m^2)
00061         self.pressure_max = self.fz_max/(0.4 * self.taxel_area) 
00062 
00063         # hack to specify the minimum resistance of the taxel, which
00064         # is associated with the maximum pressure. for now, it's
00065         # specified as a percentage of the maximum resistance, which
00066         # is associated with 0 applied pressure (no contact)
00067         self.r_min_percent_of_r_no_contact = 0.001 #
00068 
00069         # end
00070         ######################################
00071 
00072         self.r_no_contact = self.taxel_area * self.rtax_max 
00073         self.r_min = self.r_no_contact * (self.r_min_percent_of_r_no_contact/100.0)
00074         self.fz_array = pl.arange(self.fz_min, self.fz_max, 0.001) # N
00075         self.adc_range = pow(2.0, self.adc_bits)
00076         self.volts_per_adc_unit = self.vtot/self.adc_range # V 
00077         self.contact_area = self.taxel_area * (self.contact_area_percent/100.0) # m^2
00078         self.no_contact_area = self.taxel_area - self.contact_area # m^2
00079         self.pressure_array = pl.array([f/self.contact_area for f in self.fz_array]) # Pascals = N/m^2
00080         self.rtax_array = pl.array([self.rtax(f) for f in self.pressure_array])
00081         self.vdigi_array = pl.array([self.output_voltage(r) for r in self.rtax_array])
00082         self.vdigi_max = self.output_voltage(self.rtax_max)
00083         self.adc_bias = self.vdigi_max/self.volts_per_adc_unit
00084         self.adc_array = self.vdigi_array/self.volts_per_adc_unit
00085         self.adc_plot = self.adc_bias - self.adc_array
00086 
00087 
00088     def output_voltage(self, r): 
00089         '''given the resistance for the entire taxel, this returns the
00090         voltage across the taxel, which is what the analog to digital
00091         converter reads'''
00092         return( (self.vtot/(self.r1 + r)) * r )
00093 
00094 
00095     def pressure2resistance(self, p):
00096         '''given an applied pressure, returns the resistivity of the
00097         contacted region of the taxel. this uses a simple linear
00098         model, where: 
00099              0 Pascals   -> r_no_contact
00100              pressure max -> r_min Ohms
00101         '''
00102         r = ((self.r_no_contact-self.r_min) * ((self.pressure_max - p)/self.pressure_max)) + self.r_min
00103         if r < self.r_min: 
00104             print "r<r_min = %f<%f" % (r, self.r_min)
00105             r = self.r_min
00106         elif r > self.r_no_contact:
00107             r = self.r_no_contact
00108             print "r>r_no_contact"
00109         return(r)
00110 
00111 
00112     def pressure2resistance_2(self, p):
00113         '''given an applied pressure, returns the resistivity of the
00114         contacted region of the taxel. this uses a logistic model,
00115         where:
00116              0 Pascals   -> r_no_contact
00117              pressure max -> r_min Ohms
00118         '''
00119         p = self.pressure_max * norm_logistic(6.0 * (p/self.pressure_max))
00120         norm_pressure = (self.pressure_max - p)/self.pressure_max
00121         r = ((self.r_no_contact - self.r_min) * norm_pressure) + self.r_min
00122         if r < self.r_min: 
00123             print "r<r_min = %f<%f" % (r, self.r_min)
00124             r = self.r_min
00125         elif r > self.r_no_contact:
00126             r = self.r_no_contact
00127             print "r>r_no_contact"
00128         return(r)
00129 
00130 
00131     def pressure2resistance_3(self, p):
00132         '''given an applied pressure, returns the resistivity of the
00133         contacted region of the taxel. this was a quick attempt to use
00134         a model similar to "The Working Principle of Resistive Tactile
00135         Sensor Cells" by Karsten Weiss and Heinz Worn. It doesn't
00136         work, yet?
00137         '''
00138 
00139         r_surface_resistance = self.r_min
00140         
00141         # wikipedia: 
00142         # young's modulud (elastic_modulus) of nylon is 2-4 GPa = 2-4 x 10^9 Pa
00143         # 
00144         # according to 
00145         # "Spandex Fiber Reinforced Shape Memory Polymer Composites and their Mechanical Properties"
00146         # Journal       Advanced Materials Research (Volume 410)
00147         # Volume        Processing and Fabrication of Advanced Materials
00148         # Online since  November, 2011
00149         # Authors       Jian Sun, Yan Yi Xu, Yi Jin Chen, Yan Ju Liu, Jin Song Leng
00150         #
00151         # the elastic modulus of spandex is 25 MPa = 25 x 10^6 Pa
00152         #
00153         # according to the material data sheet for EEONTEX LR-SL-PA-10E5
00154         # it is 69% nylon + 31% spandex
00155         
00156         e_nylon = 3.0 * 10**9
00157         e_spandex = 25.0 * 10**6
00158         # this is a very poor model and should be improved with simple
00159         # parallel fibers and constant displacement model or something
00160         elastic_modulus = (0.69 * e_nylon) + (0.31 * e_spandex)
00161 
00162         # uses hacked qualitative Abbot's curve, which I have not even
00163         # checked by visualizing
00164         r = (1.0/abbot_curve(1000.0 * (p/elastic_modulus))) * (10.0*r_surface_resistance)
00165 
00166         if r < self.r_min: 
00167             print "r<r_min = %f<%f" % (r, self.r_min)
00168             r = self.r_min
00169         elif r > self.r_no_contact:
00170             print "r>r_no_contact = %f<%f" % (r, self.r_no_contact)
00171             r = self.r_no_contact
00172         return(r)
00173 
00174 
00175     def rtax(self, p): 
00176         '''given the pressure uniformly applied across the contact
00177         area this returns the resistance for the entire taxel. 
00178 
00179         it essentially models the taxel as parallel resistors, where
00180         resistors in the contact area have a resistance dependent on
00181         the applied pressure, and resistors in the non-contact area
00182         have the maximum resistance. i started with a discrete model,
00183         and then made a continuous approximation, which appears to
00184         correspond with using two volumes in parallel with different
00185         resistivities.
00186         
00187         based on wikipedia, it looks like i've been using something
00188         called resistivity (rho). so, this model can use the equation
00189         r = rho*(length/area). if we assume length is constant, then
00190         this leads to r_contact = rho_contact/area_contact and the
00191         same for not_contact. assuming that they are in parallel. then
00192         R_total = 1/((area_contact/rho_contact) +
00193         (area_non_contact/rho_non_contact)). if we assume length
00194         changes, then we can make rho_contact = resistivity_contact *
00195         length_contact.
00196 
00197         this should be more carefully investigated, but
00198         it seems right...
00199 
00200         the biggest unknown is the function that converts pressure to
00201         resistivity. there are currently three models of this function
00202         in this code. fitting a parametric model to the data would be
00203         a good next step. probably use an optimizer like Nelder-Mead
00204         that only requires function evaluations and use a cost
00205         function that compares the fz->adc mapping to empirically
00206         collected data.
00207 
00208         '''
00209 
00210         # the function that converts pressure to resistivity appears
00211         # to be the big unknown for this model. based on the data, it
00212         # seems like it needs to be a non-linear function. so far,
00213         # i've had the best success with a logistic model
00214 
00215         #r_contact = self.pressure2resistance(p)
00216         r_contact = self.pressure2resistance_2(p) # best performance, so far
00217         #r_contact = self.pressure2resistance_3(p)
00218 
00219         r = 1.0/((self.contact_area/r_contact) + 
00220                  (self.no_contact_area/self.r_no_contact))
00221 
00222         return(r)
00223 
00224 
00225     def plot_fz_adc(self):
00226         '''plot the curve relating applied normal force to the analog
00227         to digital converter output. this corresponds with the
00228         empirically generated scatter plots from a real taxel'''
00229         pl.plot(self.adc_plot, self.fz_array, label="contact area = {0:.0f}%".format(self.contact_area_percent))
00230         pl.xlabel("ADC bias - ADC (adc_bias - adc)")
00231         pl.ylabel("FT_z (Force applied to tactile sensor, fz)")
00232 
00233     def plot_pressure_z_adc(self):
00234         '''plot the curve relating applied normal force to the analog
00235         to digital converter output. this corresponds with the
00236         empirically generated scatter plots from a real taxel'''
00237         pl.plot(self.adc_plot, self.fz_array / self.contact_area_percent, label="contact area = {0:.0f}%".format(self.contact_area_percent))
00238         pl.xlabel("ADC bias - ADC (adc_bias - adc)")
00239         pl.ylabel("pressure_z (Force applied to tactile sensor, pz)")
00240 
00241 
00242     def plot_rtax_vout(self):
00243         '''plot the curve relating the total taxel resistance and the
00244         voltage across the taxel, which corresponds to the voltage
00245         converted to a digital signal'''
00246         pl.plot(self.vdigi_array, self.rtax_array, label="contact area = {0:.0f}%".format(self.contact_area_percent))
00247         pl.xlabel("Volts at digitizer (vdigi) proportional to ADC")
00248         pl.ylabel("Resistance of tactile sensor (rtax)")
00249 
00250 
00251     def debug(self):
00252         '''print out many of the key member variables of the TaxelModel object'''
00253         print "fz_array", self.fz_array
00254         print "pressure_array =", self.pressure_array
00255         print "adc_range", self.adc_range
00256         print "rtax_array =", self.rtax_array
00257         print "volts_per_adc_unit =", self.volts_per_adc_unit
00258         print "vdigi_array =", self.vdigi_array
00259         print "adc_bias =", self.adc_bias
00260         print "adc_array =", self.adc_array
00261         print "adc_plot =", self.adc_plot
00262 
00263 
00264 
00265 pl.title("Single Taxel Model")
00266 for area_percent in [100.0, 80.0, 60.0, 40.0, 30.0, 20.0, 10.0, 5.0, 2.0, 1.0, 0.1, 0.001]:
00267     tm = TaxelModel(area_percent)
00268     #tm.plot_rtax_vout()
00269     #tm.plot_fz_adc()
00270     tm.plot_pressure_z_adc()
00271     #tm.debug()
00272 pl.legend(loc="upper left", prop={'size':8})
00273 pl.show()


hrl_fabric_based_tactile_sensor
Author(s): Advait Jain, Advisor: Prof. Charles C. Kemp. Healthcare Robotics Lab, Georgia Tech
autogenerated on Wed Nov 27 2013 12:02:33