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
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)
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
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
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))
00231         pl.ylabel("FT_z (Force applied to tactile sensor, fz)")
00232
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))
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
00256         print "rtax_array =", self.rtax_array
00258         print "vdigi_array =", self.vdigi_array
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()