0

I'm trying to perform Non-Linear Least-Squares Fitting LMFIT for Trilateration purposes:

  • LMFIT
  • beacons includes beacon position x,y,z
  • Parameters() include Xinit location to estimate.
  • tag_distances[i] include measurements (distance from tag to beacon[i])

I get this error:

raise TypeError('Improper input: N=%s must not exceed M=%s' % (n, m)) TypeError: Improper input: N=3 must not exceed M=1

import numpy as np
from math import pow
from lmfit import minimize, Parameters

beacons=np.array([[0.,0.,5.],
              [0.,15.,5.],
              [15.,0.,5.],
              [15.,15.,5.]])

tag=np.array([0,15,0])  
tag_distances=np.arange(float(beacons.shape[0]))

xinit=np.array([5.,5.,2.])

def distance (p1, p2):
    s=0
    for i in range (0,3):
    s+=(p1[i]-p2[i])**2
    return s**.5

for i in range (0,beacons.shape[0]): 
  tag_distances[i]=distance(beacons[i,:],tag)
tag_distances= tag_distances.reshape(beacons.shape[0], 1)

def residual(params, measures , beacons, eps_data):
    x=np.array([ params['x'].value,
               params['y'].value,
               params['z'].value])
    s=0
    for i in range (0,beacons.shape[0]): 
       s+=(measures[i,0]-distance(beacons[i,:],x))**2
 return s 

params = Parameters()
params.add('x', value=xinit[0])
params.add('y', value=xinit[1])
params.add('z', value=xinit[2])

eps_data=np.array([1.0,1.0,1.0,1.0])

out = minimize(residual, params, args=(tag_distances, beacons, eps_data))
user
  • 5,370
  • 8
  • 47
  • 75
Guido
  • 634
  • 1
  • 8
  • 19

1 Answers1

1

When fitting a function to data with any least-squares algorithm (linear or non-linear), you need at least as many data points (m) on your curve as you have parameters in your model (n). If you have more parameters than points, your algorithm will not converge to a single solution (in fact there will be infinitely many equally good solutions).

The error TypeError('Improper input: N=%s must not exceed M=%s' % (n, m)) is raised by scipy because as your code is currently implemented, it has that n is larger than m.

lmfit's minimize requires that your function residual return an array of values and as of now, it only returns a single float. You can edit residual in the following away so that it returns an array of residuals:

def residual(params, measures , beacons, eps_data):
    x=np.array([ params['x'].value,
                 params['y'].value,
                 params['z'].value])
    s= np.empty(beacons.shape[0])   # Make s an np.array beforehand
    for i in range (0,beacons.shape[0]): 
        s[i] =(measures[i,0]-distance(beacons[i,:],x))**2 # Store each residual
    return s 

out = minimize(residual, params, args=(tag_distances, beacons, eps_data))
# Print results
print out.params['x'].value
print out.params['y'].value
print out.params['z'].value

Now your script will work in that it will return the parameters x,y,z which minimize the residuals returned from residual.

However, with the residual function you have, the parameters x,y,z which minimize this residual function will always be equal to tag. Thus this is not a very useful minimization procedure. I imagine you meant to write a different residual function that actually depends on eps_data.

Mark Hannel
  • 767
  • 5
  • 12
  • Can you provide an examble on how to build this array of result? If params are x,y,z then how should I build the array of values between measuremens and the theory? I'm sure your answer is the correct but I don't know how to implement it. – Guido Nov 11 '16 at 07:21