将一系列值映射到另一个值
- 2025-04-16 08:57:00
- admin 原创
- 19
问题描述:
我正在寻找如何在 Python 中将一个范围值转换为另一个范围值的方法。我正在做一个硬件项目,正在从一个可以返回一系列值的传感器读取数据,然后使用这些数据来驱动一个需要不同范围值的执行器。
例如,假设传感器返回的值在 1 到 512 之间,而执行器由 5 到 10 之间的值驱动。我想要一个函数,可以传递一个值和两个范围,并返回映射到第二个范围的值。如果有一个这样的函数,translate
可以像这样使用:
sensor_value = 256
actuator_value = translate(sensor_value, 1, 512, 5, 10)
在这个例子中,我期望输出actuator_value
是,7.5
因为sensor_value
位于可能的输入范围的中间。
解决方案 1:
使用 scipy.interpolate.interp1d
您还可以使用scipy.interpolate
包来进行此类转换(如果您不介意依赖 SciPy):
>>> from scipy.interpolate import interp1d
>>> m = interp1d([1,512],[5,10])
>>> m(256)
array(7.4951076320939336)
或者将其从 0 阶 scipy 数组转换回普通浮点数:
>>> float(m(256))
7.4951076320939336
您还可以轻松地在一个命令中执行多个转换:
>>> m([100,200,300])
array([ 5.96868885, 6.94716243, 7.92563601])
另外,您还可以从一个范围到另一个范围进行非统一映射,例如,如果要将 [1,128] 映射到 [1,10],将 [128,256] 映射到 [10,90],将 [256,512] 映射到 [90,100],您可以这样做:
>>> m = interp1d([1,128,256,512],[1,10,90,100])
>>> float(m(400))
95.625
interp1d
创建分段线性插值对象(可以像函数一样调用)。
使用 numpy.interp
正如~unutbu所指出的,numpy.interp
这也是一种选择(依赖性较少):
>>> from numpy import interp
>>> interp(256,[1,512],[5,10])
7.4951076320939336
解决方案 2:
一种解决方案是:
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)
您可以使用代数来使其更高效,但牺牲可读性。
解决方案 3:
这实际上是创建闭包的一个很好的例子,即编写一个返回函数的函数。由于你可能有很多这样的值,因此为每个值计算并重新计算这些值的范围和因子几乎没有意义,更不用说一直传递这些最小/最大限制了。
相反,尝试这样做:
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
现在您可以将处理器编写为:
# 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]
解决方案 4:
我在 python 中寻找同样的东西,将角度 0-300 度映射到原始 dynamixel 值 0-1023,或 1023-0,具体取决于执行器方向。
我最终选择了非常简单的方式。
变量:
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)
解决方案 5:
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
解决方案 6:
简单地图范围函数:
def mapRange(value, inMin, inMax, outMin, outMax):
return outMin + (((value - inMin) / (inMax - inMin)) * (outMax - outMin))
解决方案 7:
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]
发现于https://rosettacode.org/wiki/Map_range#Python_
不将转换后的值限制在范围内
a
或b
(它进行推断)也适用于
from_lower > from_upper
或to_lower > to_upper
解决方案 8:
所有现有答案均遵循 CC BY-SA 许可。这是我写的一个;在法律允许的范围内,我放弃此代码的所有版权或相关及邻接权利。(知识共享 CC0 公共领域贡献)。
def remap(number, from_min, from_max, to_min, to_max):
number_s = number - from_min
from_max_s = from_max - from_min
to_max_s = to_max - to_min
return ((number_s / from_max_s) * to_max_s) + to_min
解决方案 9:
v
是值。将a-b
范围映射到c-d
范围
def map_range(v, a, b, c, d):
return (v-a) / (b-a) * (d-c) + c
用法
a = map_range(4, 0, 10, 0, 1)
print(a) # -> 0.4
解决方案 10:
您可以使用 lambda 函数
translate = lambda a, b, c, d, e: (a - b) * (e - d) / (c - b) + d
sensor_value = 256
translate(sensor_value, 1, 512, 5, 10)
>> 7.495107632093934
扫码咨询,免费领取项目管理大礼包!