Je cherche des idées sur la façon de traduire des valeurs d'une plage en une autre en Python. Je travaille sur un projet matériel et je lis les données d'un capteur qui peut renvoyer une plage de valeurs, j'utilise ensuite ces données pour piloter un actionneur qui nécessite une plage de valeurs différente.
Par exemple, disons que le capteur renvoie des valeurs comprises entre 1 et 512, et que l'actionneur est piloté par des valeurs comprises entre 5 et 10. Je voudrais une fonction permettant de transmettre une valeur et les deux plages et de récupérer la valeur mappé à la deuxième plage. Si une telle fonction était nommée translate
, elle pourrait être utilisée comme ceci:
sensor_value = 256
actuator_value = translate(sensor_value, 1, 512, 5, 10)
Dans cet exemple, je m'attendrais à ce que la sortie actuator_value
être 7.5
depuis le sensor_value
est au milieu de la plage d'entrée possible.
Une solution serait:
def translate(value, leftMin, leftMax, rightMin, rightMax):
# Figure out how 'wide' each range is
leftSpan = leftMax - leftMin
rightSpan = rightMax - rightMin
# Convert the left range into a 0-1 range (float)
valueScaled = float(value - leftMin) / float(leftSpan)
# Convert the 0-1 range into a value in the right range.
return rightMin + (valueScaled * rightSpan)
Vous pourriez éventuellement utiliser l'algèbre pour la rendre plus efficace, au détriment de la lisibilité.
Vous pouvez aussi utiliser scipy.interpolate
package pour effectuer de telles conversions (si cela ne vous dérange pas la dépendance à SciPy):
>>> from scipy.interpolate import interp1d
>>> m = interp1d([1,512],[5,10])
>>> m(256)
array(7.4951076320939336)
ou pour le reconvertir en flottant normal à partir d'un tableau scipy de rang 0:
>>> float(m(256))
7.4951076320939336
Vous pouvez également effectuer plusieurs conversions en une seule commande facilement:
>>> m([100,200,300])
array([ 5.96868885, 6.94716243, 7.92563601])
En prime, vous pouvez effectuer des mappages non uniformes d'une plage à une autre, par exemple si vous souhaitez mapper [1,128] à [1,10], [128,256] à [10,90] et [256,512] à [90,100 ] vous pouvez le faire comme ceci:
>>> m = interp1d([1,128,256,512],[1,10,90,100])
>>> float(m(400))
95.625
interp1d
crée des objets d'interpolation linéaire par morceaux (qui peuvent être appelés comme des fonctions).
Comme indiqué par ~ unutb, numpy.interp
est également une option (avec moins de dépendances):
>>> from numpy import interp
>>> interp(256,[1,512],[5,10])
7.4951076320939336
Ce serait en fait un bon cas pour créer une fermeture, c'est-à-dire écrire une fonction qui renvoie une fonction. Étant donné que vous avez probablement un grand nombre de ces valeurs, il y a peu de valeur à calculer et recalculer ces plages de valeurs et facteurs pour chaque valeur, ni d'ailleurs, à passer ces limites min/max tout le temps.
Essayez plutôt ceci:
def make_interpolater(left_min, left_max, right_min, right_max):
# Figure out how 'wide' each range is
leftSpan = left_max - left_min
rightSpan = right_max - right_min
# Compute the scale factor between left and right values
scaleFactor = float(rightSpan) / float(leftSpan)
# create interpolation function using pre-calculated scaleFactor
def interp_fn(value):
return right_min + (value-left_min)*scaleFactor
return interp_fn
Vous pouvez maintenant écrire votre processeur comme:
# create function for doing interpolation of the desired
# ranges
scaler = make_interpolater(1, 512, 5, 10)
# receive list of raw values from sensor, assign to data_list
# now convert to scaled values using map
scaled_data = map(scaler, data_list)
# or a list comprehension, if you prefer
scaled_data = [scaler(x) for x in data_list]
def translate(sensor_val, in_from, in_to, out_from, out_to):
out_range = out_to - out_from
in_range = in_to - in_from
in_val = sensor_val - in_from
val=(float(in_val)/in_range)*out_range
out_val = out_from+val
return out_val
Je cherchais la même chose dans python pour mapper les angles 0-300deg aux valeurs de dynamixel brutes 0-1023 ou 1023-0 selon les orientations de l'actionneur.
J'ai fini par être très simple.
x:input value;
a,b:input range
c,d:output range
y:return value
def mapFromTo(x,a,b,c,d):
y=(x-a)/(b-a)*(d-c)+c
return y
dyn111.goal_position=mapFromTo(pos111,0,300,0,1024)
def maprange(a, b, s):
(a1, a2), (b1, b2) = a, b
return b1 + ((s - a1) * (b2 - b1) / (a2 - a1))
a = [from_lower, from_upper]
b = [to_lower, to_upper]
trouvé à https://rosettacode.org/wiki/Map_range#Python_
a
ou b
(elle extrapole)from_lower > from_upper
ou to_lower > to_upper