00001
00002
00003
00004
00005
00006
00007
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
00019
00020
00021
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
00034
00035 self.contact_area_percent = contact_area_percent
00036
00037
00038 self.r1 = 47.0
00039
00040
00041 self.vtot = 5.0
00042
00043
00044 self.rtax_max = 50.0
00045
00046
00047 self.fz_min = 0.0
00048
00049
00050 self.fz_max = 45.0
00051
00052
00053 self.adc_bits = 10
00054
00055
00056 self.taxel_area = 0.04 * 0.04
00057
00058
00059
00060
00061 self.pressure_max = self.fz_max/(0.4 * self.taxel_area)
00062
00063
00064
00065
00066
00067 self.r_min_percent_of_r_no_contact = 0.001
00068
00069
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)
00075 self.adc_range = pow(2.0, self.adc_bits)
00076 self.volts_per_adc_unit = self.vtot/self.adc_range
00077 self.contact_area = self.taxel_area * (self.contact_area_percent/100.0)
00078 self.no_contact_area = self.taxel_area - self.contact_area
00079 self.pressure_array = pl.array([f/self.contact_area for f in self.fz_array])
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
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156 e_nylon = 3.0 * 10**9
00157 e_spandex = 25.0 * 10**6
00158
00159
00160 elastic_modulus = (0.69 * e_nylon) + (0.31 * e_spandex)
00161
00162
00163
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
00211
00212
00213
00214
00215
00216 r_contact = self.pressure2resistance_2(p)
00217
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
00269
00270 tm.plot_pressure_z_adc()
00271
00272 pl.legend(loc="upper left", prop={'size':8})
00273 pl.show()