简体   繁体   English

改进近指数数据的指数曲线拟合

[英]Improve an exponential curve fit of nearly exponential data

I have recorded some data about the color of an LED that varies with the 8bit signal sent to the LED driver, the signal can vary between 0 and 255.我已经记录了一些关于 LED 颜色的数据,这些数据随发送到 LED 驱动器的 8 位信号而变化,该信号可以在 0 到 255 之间变化。

Exponential curve fitting seems to work very well to represent the LED's behavior.指数曲线拟合似乎可以很好地代表 LED 的行为。 I have had good results with the following formula:我用下面的公式得到了很好的结果:

x * signal ** ex 
y * signal ** ey 
z * signal ** ez 

In Python, I use the following function:在Python中,我用的是下面的function:

from scipy.optimize import curve_fit

def fit_func_xae(x, a, e):
    # Curve fitting function
    return a * x**e

# X, Y, Z are real colorimetric values that are measured by a physical instrument

(aX, eX), cov = curve_fit(fit_func3xa, signal, X)
(aY, eY), cov = curve_fit(fit_func3xa, signal, Y)
(aZ, eZ), cov = curve_fit(fit_func3xa, signal, Z)

Note:笔记:

In colorimetry, we represent the color of the LED in the CIE XYZ color space, which is a linear space that works in a similar way as a linear RGB color space.在色度学中,我们在 CIE XYZ 颜色空间中表示 LED 的颜色,这是一个线性空间,其工作方式与线性 RGB 颜色空间类似。 Even if it is an aproximation, you can think of XYZ as a synonym of (linear) RGB.即使是近似值,您也可以将 XYZ 视为(线性)RGB 的同义词。 So a color can be represented as a triplet of linear values X, Y, Z.因此,颜色可以表示为线性值 X、Y、Z 的三元组。

here is the data behind the curves.这是曲线背后的数据。 for each 8bit parameter sent to the LED driver, there are 3 measures对于发送到 LED 驱动器的每个 8 位参数,有 3 个测量值

Signal 
[  3.   3.   3.   5.   5.   5.   7.   7.   7.  10.  10.  10.  15.  15.
  15.  20.  20.  20.  30.  30.  30.  40.  40.  40.  80.  80.  80. 160.
 160. 160. 240. 240. 240. 255. 255. 255.]

X, Y, Z
[[9.93295448e-05 8.88955748e-04 6.34978556e-04]
 [9.66399391e-05 8.86031926e-04 6.24680520e-04]
 [1.06108685e-04 8.99010175e-04 6.41577838e-04]
 [1.96407653e-04 1.70210146e-03 1.27178991e-03]
 [1.84965943e-04 1.67927596e-03 1.24985475e-03]
 [1.83770476e-04 1.67905297e-03 1.24855580e-03]
 [3.28537613e-04 2.75382195e-03 2.14639821e-03]
 [3.17804246e-04 2.74152647e-03 2.11730825e-03]
 [3.19167905e-04 2.74977632e-03 2.11142769e-03]
 [5.43770342e-04 4.09314433e-03 3.33793380e-03]
 [5.02493149e-04 4.04392581e-03 3.24784452e-03]
 [5.00712102e-04 4.03456071e-03 3.26803716e-03]
 [1.48001671e-03 1.09367632e-02 9.59283037e-03]
 [1.52082180e-03 1.09920985e-02 9.63624777e-03]
 [1.50153844e-03 1.09623592e-02 9.61724422e-03]
 [3.66206564e-03 2.74730946e-02 2.51982924e-02]
 [3.64074861e-03 2.74283157e-02 2.52187294e-02]
 [3.68719991e-03 2.75033778e-02 2.51691331e-02]
 [1.50905917e-02 1.06056566e-01 1.06534373e-01]
 [1.51370269e-02 1.06091182e-01 1.06790424e-01]
 [1.51654172e-02 1.06109863e-01 1.06943957e-01]
 [3.42912601e-02 2.30854413e-01 2.43427207e-01]
 [3.42217124e-02 2.30565972e-01 2.43529454e-01]
 [3.41486993e-02 2.30807320e-01 2.43591644e-01]
 [1.95905112e-01 1.27409867e+00 1.37490536e+00]
 [1.94923951e-01 1.26934278e+00 1.37751808e+00]
 [1.95242984e-01 1.26805844e+00 1.37565458e+00]
 [1.07931878e+00 6.97822521e+00 7.49602715e+00]
 [1.08944832e+00 7.03128378e+00 7.54296884e+00]
 [1.07994964e+00 6.96864302e+00 7.44011991e+00]
 [2.95296087e+00 1.90746191e+01 1.99164655e+01]
 [2.94254973e+00 1.89524517e+01 1.98158118e+01]
 [2.95753358e+00 1.90200667e+01 1.98885050e+01]
 [3.44049055e+00 2.21221159e+01 2.29667049e+01]
 [3.43817829e+00 2.21225393e+01 2.29363833e+01]
 [3.43077583e+00 2.21158929e+01 2.29399652e+01]]

_ _

The Problem:问题:

Here's a scatter plot of some of my LED's XYZ values, together with a plot of the exponential curve fitting obtained with the code above:这是我的一些 LED 的 XYZ 值的散点 plot,以及通过上述代码获得的指数曲线拟合的 plot:

使用公式 a*pwm**e 拟合 LED 的 XYZ 值的曲线

It all seems good... until you zoom a bit:一切似乎都很好......直到你放大一点:

信号在 80

信号在 30

On this zoom we can also see that the curve is fitted on multiple measurements:在此缩放中,我们还可以看到曲线适用于多个测量值:

信号为 10,具有可见的多项措施

At high values, Z values (blue dots) are always higher than Y values (green dots).在高值时,Z 值(蓝点)总是高于 Y 值(绿点)。 But at low values, Y values are higher than Z values.但在低值时,Y 值高于 Z 值。

The meaning of this is that the LED changes in color depending on the PWM applied, for some reason (maybe because the temperature rises when more power is applied).这意味着 LED 的颜色会根据所应用的 PWM 发生变化,出于某种原因(可能是因为当施加更多功率时温度会升高)。

This behavior cannot be represented mathematically by the formula that I have used for the curve fit, however, the curve fit is so good for high values that I am searching for a way to improve it in a simple and elegant way.这种行为不能用我用于曲线拟合的公式在数学上表示,但是,曲线拟合对于高值非常好,以至于我正在寻找一种以简单而优雅的方式改进它的方法。

Do you have any idea how this could be done?你知道如何做到这一点吗? I have tried unsuccessfully to add more parameters, for example I tried to use:我尝试添加更多参数但未成功,例如我尝试使用:

x1 * signal ** ex + x2 * signal ** fx

instead of:代替:

x * signal ** ex

but that causes scipy to overflow.但这会导致 scipy 溢出。

My idea was that by adding two such elements I could still have a funtion that equals 0 when signal = 0, but that increases faster at low values than a simple exponential.我的想法是,通过添加两个这样的元素,当信号 = 0 时,我仍然可以得到一个等于 0 的函数,但在低值时它比简单的指数增长得更快。

The data shows two steps in the log-log plot so I used an approach already used here .数据在 log-log plot 中显示了两个步骤,因此我使用了此处已使用的方法。

Code is as follows:代码如下:

import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
signal = np.array( [
    3.0,   3.0,   3.0,
    5.0,   5.0,   5.0,
    7.0,   7.0,   7.0,
    10.0,  10., 10.,
    15.0,  15., 15.,
    20.0,  20.,  20.,
    30.0,  30.,  30.,
    40.0,  40.,  40.,
    80.0,  80., 80.,
    160.0, 160., 160.,
    240.0, 240., 240.,
    255.0, 255., 255.
] )

data = np.array( [
    [9.93295448e-05, 8.88955748e-04, 6.34978556e-04],
    [9.66399391e-05, 8.86031926e-04, 6.24680520e-04],
    [1.06108685e-04, 8.99010175e-04, 6.41577838e-04],
    [1.96407653e-04, 1.70210146e-03, 1.27178991e-03],
    [1.84965943e-04, 1.67927596e-03, 1.24985475e-03],
    [1.83770476e-04, 1.67905297e-03, 1.24855580e-03],
    [3.28537613e-04, 2.75382195e-03, 2.14639821e-03],
    [3.17804246e-04, 2.74152647e-03, 2.11730825e-03],
    [3.19167905e-04, 2.74977632e-03, 2.11142769e-03],
    [5.43770342e-04, 4.09314433e-03, 3.33793380e-03],
    [5.02493149e-04, 4.04392581e-03, 3.24784452e-03],
    [5.00712102e-04, 4.03456071e-03, 3.26803716e-03],
    [1.48001671e-03, 1.09367632e-02, 9.59283037e-03],
    [1.52082180e-03, 1.09920985e-02, 9.63624777e-03],
    [1.50153844e-03, 1.09623592e-02, 9.61724422e-03],
    [3.66206564e-03, 2.74730946e-02, 2.51982924e-02],
    [3.64074861e-03, 2.74283157e-02, 2.52187294e-02],
    [3.68719991e-03, 2.75033778e-02, 2.51691331e-02],
    [1.50905917e-02, 1.06056566e-01, 1.06534373e-01],
    [1.51370269e-02, 1.06091182e-01, 1.06790424e-01],
    [1.51654172e-02, 1.06109863e-01, 1.06943957e-01],
    [3.42912601e-02, 2.30854413e-01, 2.43427207e-01],
    [3.42217124e-02, 2.30565972e-01, 2.43529454e-01],
    [3.41486993e-02, 2.30807320e-01, 2.43591644e-01],
    [1.95905112e-01, 1.27409867e+00, 1.37490536e+00],
    [1.94923951e-01, 1.26934278e+00, 1.37751808e+00],
    [1.95242984e-01, 1.26805844e+00, 1.37565458e+00],
    [1.07931878e+00, 6.97822521e+00, 7.49602715e+00],
    [1.08944832e+00, 7.03128378e+00, 7.54296884e+00],
    [1.07994964e+00, 6.96864302e+00, 7.44011991e+00],
    [2.95296087e+00, 1.90746191e+01, 1.99164655e+01],
    [2.94254973e+00, 1.89524517e+01, 1.98158118e+01],
    [2.95753358e+00, 1.90200667e+01, 1.98885050e+01],
    [3.44049055e+00, 2.21221159e+01, 2.29667049e+01],
    [3.43817829e+00, 2.21225393e+01, 2.29363833e+01],
    [3.43077583e+00, 2.21158929e+01, 2.29399652e+01]
] )

def determine_start_parameters(  x , y, edge=9 ):
    logx = np.log( x )
    logy = np.log( y )
    xx = logx[ :edge ]
    yy = logy[ :edge ]
    (ar1, br1), _ = curve_fit( lambda x, slope, off: slope * x + off, xx , yy )
    xx = logx[ edge : -edge ]
    yy = logy[ edge : -edge]
    (ar2, br2), _ = curve_fit( lambda x, slope, off: slope * x + off, xx , yy )
    xx = logx[ -edge : ]
    yy = logy[ -edge : ]
    (ar3, br3), _ = curve_fit( lambda x, slope, off: slope * x + off, xx , yy )
    cross1r = ( br2 - br1 ) / ( ar1 - ar2 )
    mr = ar1 * cross1r + br1
    cross2r = ( br3 - br2 ) / ( ar2 - ar3 )
    xx0r = [ mr, ar1, ar2 , ar3, cross1r, cross2r, 1 ]
    return xx0r

def func(
    x, b,
    m1, m2, m3,
    a1, a2,
    p
):
    """
    continuous approxiation for a two-step function
    used to fit the log-log data
    p is a sharpness parameter for the transition
    """
    out = b - np.log(
    ( 1 + np.exp( -m1 * ( x - a1 ) )**abs( p ) )
    ) / p + np.log(
    ( 1 + np.exp( m2 * ( x - a1 ) )**abs( p ) )
    ) / p - np.log(
    ( 1 + np.exp( m3 * ( x - a2 ) )**abs( p ) )
    ) / abs( p )
    return out

def expfunc(
    x, b,
    m1, m2, m3,
    a1, a2,
    p
):
    """
    remapping to the original data
    """
    xi = np.log( x )
    eta = func(
    xi, b,
    m1, m2, m3,
    a1, a2,
    p)
    return np.exp(eta)

def expfunc2(
    x, b,
    m1, m2, m3,
    a1, a2,
    p
):
    """
    simplified remapping
    """
    aa1 = np.exp( a1 )
    aa2 = np.exp( a2 )
    return (
        np.exp( b ) * (
        ( 1 + ( x / aa1 )**( m2 * p ) ) / 
        ( 1 + ( x / aa2 )**( m3 * p ) ) /
        ( 1 + ( aa1 / x )**( m1 * p ) )
        )**( 1 / p )
    )

logsig = np.log( signal )
logred =  np.log( data[:,0] )
loggreen =  np.log( data[:,1] )
logblue =  np.log( data[:,2] )

### getting initial parameters
### red

xx0r = determine_start_parameters( signal, data[ :, 0 ] )
xx0g = determine_start_parameters( signal, data[ :, 1 ] )
xx0b = determine_start_parameters( signal, data[ :, 2 ] )
print( xx0r )
print( xx0g )
print( xx0b )

xl = np.linspace( 1, 6, 150 )
tl = np.linspace( 1, 260, 150 )


solred = curve_fit( func, logsig, logred, p0=xx0r )[0]
solgreen = curve_fit( func, logsig, loggreen, p0=xx0g )[0]
solblue = curve_fit( func, logsig, logblue, p0=xx0b )[0]

print( solred )
print( solgreen )
print( solblue )

fig = plt.figure()
ax = fig.add_subplot( 2, 1, 1 )
bx = fig.add_subplot( 2, 1, 2 )

ax.scatter(  np.log( signal ), np.log( data[:,0] ),  color = 'r' )
ax.scatter(  np.log( signal ), np.log( data[:,1] ),  color = 'g' )
ax.scatter(  np.log( signal ), np.log( data[:,2] ),  color = 'b' )

ax.plot( xl, func( xl, *solred ),  color = 'r' )
ax.plot( xl, func( xl, *solgreen ),  color = 'g' )
ax.plot( xl, func( xl, *solblue ),  color = 'b' )

bx.scatter( signal, data[:,0],  color = 'r' )
bx.scatter( signal, data[:,1],  color = 'g' )
bx.scatter( signal, data[:,2],  color = 'b' )
bx.plot( tl, expfunc2( tl, *solred), color = 'r'  )
bx.plot( tl, expfunc2( tl, *solgreen), color = 'g'  )
bx.plot( tl, expfunc2( tl, *solblue), color = 'b'  )


plt.show()

Which results in结果是

适合对数对数

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM