Author Topic: pow(a,b)  (Read 7054 times)

0 Members and 1 Guest are viewing this topic.

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9889
  • Country: us
Re: pow(a,b)
« Reply #25 on: March 06, 2021, 04:36:30 pm »
I think I would look at a polynomial curve fit and graph the actual and interpolated data.  I don't know anything about Labview but Octave (or MATLAB) can do this with a single statement to any degree required.

The link covers single polynomial fitting and spline fitting:

https://octave.org/doc/v4.4.1/Polynomial-Interpolation.html

If the computation is to be done on the uC, all I would need to store in memory are the coefficients.

Clearly, sending the raw data to the PC and using more powerful math tools will be a lot easier.
« Last Edit: March 06, 2021, 04:39:29 pm by rstofer »
 
The following users thanked this post: DiTBho

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #26 on: March 06, 2021, 05:32:55 pm »
Just to consider alternatives, which sensor would you use to (contactless) measure a distance in the range of 60cm .. 130cm?
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14448
  • Country: fr
Re: pow(a,b)
« Reply #27 on: March 06, 2021, 05:52:18 pm »
Just to consider alternatives, which sensor would you use to (contactless) measure a distance in the range of 60cm .. 130cm?

What kind of sensor are you considering in this thread? I may have missed it, but all I saw was "analog sensor". Just needs a bit of details so we don't suggest the same.

For this range, ultrasonic sensors may be the cheapest and easiest approach? 5mm accuracy should be doable.
Otherwise, you may consider time-of-flight sensors such as this: https://www.st.com/en/imaging-and-photonics-solutions/vl53l0x.html (not sure you'd manage 5mm accuracy with this though...)

 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #28 on: March 06, 2021, 06:19:01 pm »
What kind of sensor are you considering in this thread?

A sensor that without any contact can measure a distance in the range of 60cm .. 130cm.
I am currently using a pure analogical optical reflective IR sensor.

I really like the vl53l0x sensor. It's embedded, it's powerful, and it's very small. I think I will use one of these for other projects, thanks :D
« Last Edit: March 11, 2021, 07:58:33 am by DiTBho »
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14448
  • Country: fr
Re: pow(a,b)
« Reply #29 on: March 06, 2021, 06:24:17 pm »
I really like the vl53l0x sensor. It's embedded, it's powerful, and it's very small. I think I will use one of these for other projects,

Those are nice and reasonably cheap, but I'm not too sure they would yield the kind of accuracy you're looking for. Something to prototype though.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4030
  • Country: nz
Re: pow(a,b)
« Reply #30 on: March 07, 2021, 12:35:13 am »
I'm sure you must be overthinking all of this. It must be able to be linearised or something over the values in the possible range. And especially only talking about 8 bit precision anyway.

How can you be sure if you haven't seen anything?

Experience.

Quote
I wrote the above showed piece of code to acquire data and send them over the serial, so I sampled data within the range {15..1600}mm and put these samples inside a Labview's table, and when I click on the button it suggested a linearization model made with three pow(a,b), as I described above.

It means, if you want to linerize these

Quote
...
2.75   015 cm
2.55   020 cm
2.00   030 cm
1.55   040 cm
1.25   050 cm
1.15   060 cm
0.90   070 cm
0.80   080 cm
0.75   090 cm
0.65   100 cm
0.60   110 cm
0.55   120 cm
0.50   130 cm
0.44   140 cm
0.45   150 cm
...

You need to implement this: h = K1*pow1(a1,b1) + K2*pow2(a2,b2) + K3*pow3(a3,b3) + q

What are the labels for the above columns? What is the input? What is the output? How does this relate to the formula? What on Earth are pow1, pow2, pow3?

Clearly your data are not very accurate given the reversal of 0.44 and 0.45.

Quote
You have to optimize a1,b1,a2,b2,a3,b3,q, but you will always have to deal with fpow, fmul, fadd, fand fcmp

If you are dealing with fpow at runtime on the microcontroller then this is not anything I know of as linearisation.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: pow(a,b)
« Reply #31 on: March 07, 2021, 08:20:35 am »
So we have just moved from this

h = K1*pow1(a1,b1) + K2*pow2(a2,b2) + K3*pow3(a3,b3) + q

to this

h = K1*pow1(a1,b1) + K2*pow2(a2,b2) + q
Do you have values for K1 & K2, and the ranges and precision required for a1, b1, a2 and b2? I would like to play around with them...
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #32 on: March 07, 2021, 10:58:25 am »
What are the labels for the above columns? What is the input? What is the output?

The sensor outputs a voltage value for each distance between the sensor and the ground I try to measure. The measuring plane moves vertically, and I have a meter tape to take the measures. So I can report { Voltage, distance } in a table.
« Last Edit: March 11, 2021, 07:34:32 am by DiTBho »
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline ledtester

  • Super Contributor
  • ***
  • Posts: 3035
  • Country: us
Re: pow(a,b)
« Reply #33 on: March 07, 2021, 01:34:19 pm »
Have you considered an optical encoder -- either linear or rotary?
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4030
  • Country: nz
Re: pow(a,b)
« Reply #34 on: March 08, 2021, 12:56:16 am »
Do you have values for K1 & K2, and the ranges and precision required for a1, b1, a2 and b2? I would like to play around with them...

Abandoned for now since Labview keeps suggesting irrational numbers for those constants. I tried several times and always got the same result, an this seems to force me to use floating point in double precision, which is really too much for this project, and it's not worth it.

I am going to play with a quadratic approach. It seems more appropriate for my little CPU. Probably the National Instruments curve fitting assistant is made for a PC, where you have floating point in double precision for free as implemented in hardware and with large available resources (lot of ram, high clock frequency, etc).

None of this makes any sense.

Coefficients spat out by a program can be implemented by floating point or fixed point of whatever precision makes sense for the application. "Irrational" or "double precision" is irrelevant. 16 bit fixed point should certainly be more than enough, and can be efficiently handled by any microcontroller ever made.

Programs such as Labview of Octave can save you from having to do the actual arithmetic yourself, but they are not a substitute for understanding the math.
 
The following users thanked this post: hamster_nz

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: pow(a,b)
« Reply #35 on: March 08, 2021, 01:27:16 am »
Do you have values for K1 & K2, and the ranges and precision required for a1, b1, a2 and b2? I would like to play around with them...

Abandoned for now since Labview keeps suggesting irrational numbers for those constants. I tried several times and always got the same result, an this seems to force me to use floating point in double precision, which is really too much for this project, and it's not worth it.

I am going to play with a quadratic approach. It seems more appropriate for my little CPU. Probably the National Instruments curve fitting assistant is made for a PC, where you have floating point in double precision for free as implemented in hardware and with large available resources (lot of ram, high clock frequency, etc).

None of this makes any sense.

Coefficients spat out by a program can be implemented by floating point or fixed point of whatever precision makes sense for the application. "Irrational" or "double precision" is irrelevant. 16 bit fixed point should certainly be more than enough, and can be efficiently handled by any microcontroller ever made.

Programs such as Labview of Octave can save you from having to do the actual arithmetic yourself, but they are not a substitute for understanding the math.

Fully agree.

However, should somebody find themselves in the situation where they do need to calculate fixed-point exponentials please ping me. It isn't that that hard to write a log(x) function using only a lookup table and division, and an exp(x) function using just a table and multiplication.

As pow(a,b) = exp(b*log(a)) this is perfectly achievable in fixed point once the bounds for 'a' and 'b' have been established.

Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4030
  • Country: nz
Re: pow(a,b)
« Reply #36 on: March 08, 2021, 03:32:52 am »
However, should somebody find themselves in the situation where they do need to calculate fixed-point exponentials please ping me. It isn't that that hard to write a log(x) function using only a lookup table and division, and an exp(x) function using just a table and multiplication.

The same lookup table in both cases, of exp(2**n), for appropriate +ve and -ve n?
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4030
  • Country: nz
Re: pow(a,b)
« Reply #37 on: March 08, 2021, 03:44:36 am »
As pow(a,b) = exp(b*log(a)) this is perfectly achievable in fixed point once the bounds for 'a' and 'b' have been established.

... also as pow(a, b) = x**(logx(a)*b) for any base x, it's not necessarily best to use e for that base. You can use 10. Or ... if you use 2 as the base then maybe you don't need any table at all (it's just shifts).
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #38 on: March 08, 2021, 09:23:19 am »
as pow(a, b) = x**(logx(a)*b) for any base x, it's not necessarily best to use e for that base. You can use 10. Or ... if you use 2 as the base then maybe you don't need any table at all (it's just shifts).

This is a great contribute, and it's probably what I was looking for when I opened this topic! Thanks!
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #39 on: March 08, 2021, 09:35:23 am »
For a similar sensor, I was able to manually synthesize an approximation in this terms

distance = (1 / (k1 * Volt + q1))
k1=0.0002391473        (irrational, truncated)
q1=-0.0100251467      (irrational, truncated)

No pow(a,b) required, just a division, a multiplication, and a sum  :D
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #40 on: March 08, 2021, 09:58:00 am »
Just a proof, but for that similar IR-reflective sensor, this is what I can simplify.

distance = k1 * pow(volt,b1) +q
k1 = 10650.080        (irrational, truncated)
b1 = -0.935             (irrational, truncated)
q = - 10.000

No more three pow, just one.
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4030
  • Country: nz
Re: pow(a,b)
« Reply #41 on: March 08, 2021, 10:03:14 am »
For a similar sensor, I was able to manually synthesize an approximation in this terms

distance = (1 / (k1 * Volt + q1))
k1=0.0002391473        (irrational, truncated)
q1=-0.0100251467      (irrational, truncated)

No pow(a,b) required, just a division, a multiplication, and a sum  :D

We're finally starting to get somewhere.

Or try 10000 / (2.391473 * Volt - 100.25)

Or maybe a low order polynomial on the top line also.
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #42 on: March 08, 2021, 11:20:31 am »
We're finally starting to get somewhere.

Thanks! Just, don't assume THIS as working model, it's just an example of what I'd like to achieve for the sensor I am using, which is a bit different and it has a different measuring range.

I don't know if it's possible, and that's the reason why I am evaluating other solutions and sensors
I mean, if I apply this approach to my sensor, I get an error of +/-70mm for distances > 1000mm  :o :o :o

« Last Edit: March 08, 2021, 12:11:27 pm by DiTBho »
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #43 on: March 08, 2021, 12:47:32 pm »
Code: [Select]
    H{ "success", failure" | [ measured=get_value(value), expected ] } :->
             (abs(expected - measured) <= tolerance)

    H[data, tolerance = 20.00] // 20mm

Code: [Select]
data{ volt, expected } =
{
    { 2.75,  150.00 }, // volt, mm
    { 2.55,  200.00 },
    { 2.00,  300.00 },
    { 1.55,  400.00 },
    { 1.25,  500.00 },
    { 1.15,  600.00 },
    { 0.90,  700.00 },
    { 0.80,  800.00 },
    { 0.75,  900.00 },
    { 0.65, 1000.00 },
    { 0.60, 1100.00 },
    { 0.55, 1200.00 },
    { 0.50, 1300.00 },
    { 0.44, 1400.00 },
    { 0.45, 1500.00 },
};

Code: [Select]
get_value(value) :->
    k1 = 106500.800
    a1 = 1023.000 / 5.000 // 10bit ADC, 5V range
    b1 = -0.935
    q1 = -100.000
    return k1 * pow(a1 * value, b1) + q1

Code: [Select]
volt=2.750000 failure
       expected distance=150.000000
       measured distance=185.680132
volt=2.550000 success
       expected distance=200.000000
       measured distance=206.578036
volt=2.000000 success
       expected distance=300.000000
       measured distance=284.762786
volt=1.550000 success
       expected distance=400.000000
       measured distance=388.310394
volt=1.250000 success
       expected distance=500.000000
       measured distance=497.097488
volt=1.150000 failure
       expected distance=600.000000
       measured distance=545.510968
volt=0.900000 success
       expected distance=700.000000
       measured distance=711.781893
volt=0.800000 success
       expected distance=800.000000
       measured distance=806.289542
volt=0.750000 failure
       expected distance=900.000000
       measured distance=862.661992
volt=0.650000 success
       expected distance=1000.000000
       measured distance=1000.479909
volt=0.600000 success
       expected distance=1100.000000
       measured distance=1085.999996
volt=0.550000 success
       expected distance=1200.000000
       measured distance=1186.521333
volt=0.500000 success
       expected distance=1300.000000
       measured distance=1306.433339
volt=0.440000 failure
       expected distance=1400.000000
       measured distance=1484.994845
volt=0.450000 failure
       expected distance=1500.000000
       measured distance=1452.038200

A bit better, but ... not so much
« Last Edit: March 08, 2021, 10:58:47 pm by DiTBho »
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #44 on: March 08, 2021, 12:50:59 pm »
Code: [Select]
H[data, tolerance = 60.00] // 60mm

Code: [Select]
volt=2.750000 success
       expected distance=150.000000
       measured distance=185.680132
volt=2.550000 success
       expected distance=200.000000
       measured distance=206.578036
volt=2.000000 success
       expected distance=300.000000
       measured distance=284.762786
volt=1.550000 success
       expected distance=400.000000
       measured distance=388.310394
volt=1.250000 success
       expected distance=500.000000
       measured distance=497.097488
volt=1.150000 success
       expected distance=600.000000
       measured distance=545.510968
volt=0.900000 success
       expected distance=700.000000
       measured distance=711.781893
volt=0.800000 success
       expected distance=800.000000
       measured distance=806.289542
volt=0.750000 success
       expected distance=900.000000
       measured distance=862.661992
volt=0.650000 success
       expected distance=1000.000000
       measured distance=1000.479909
volt=0.600000 success
       expected distance=1100.000000
       measured distance=1085.999996
volt=0.550000 success
       expected distance=1200.000000
       measured distance=1186.521333
volt=0.500000 success
       expected distance=1300.000000
       measured distance=1306.433339
volt=0.440000 failure
       expected distance=1400.000000
       measured distance=1484.994845
volt=0.450000 success
       expected distance=1500.000000
       measured distance=1452.038200

"expected" means the value in the table.
"measured" means the compute value that should approximate it.
« Last Edit: March 08, 2021, 02:46:30 pm by DiTBho »
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline emece67

  • Frequent Contributor
  • **
  • !
  • Posts: 614
  • Country: 00
Re: pow(a,b)
« Reply #45 on: March 08, 2021, 02:03:12 pm »
.
« Last Edit: August 19, 2022, 04:17:36 pm by emece67 »
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3912
  • Country: gb
Re: pow(a,b)
« Reply #46 on: March 08, 2021, 02:30:23 pm »
Yup, this topic is a solid amount of embedded things ... hacking of things, inner digital math on "grinder old CPU", and crazy stuff  :D

find a way to implement pow() in a microcontroller not using the supplied one
find a way to fit your data to a given expression (adjusting some parameters)
find an expression that fits your data

{ Yes, Yes, Yes }

Plus I am open to alternative solutions! For example, yesterday it was suggested to use a different approach for the sensor

Have you considered an optical encoder -- either linear or rotary?

and now I'm also thinking of opening the motor box and hacking it to get a quadrature sensor. I am very reluctant to do this because it is a very dirty trick, the motor box is sealed and I need to "Dremel cut", and, since a quadrature encoder is a relative-position encoder it would require the frame to "Go back home" to the "zero position" (600mm from the ground) in order to set the zero, which is also something I'd like to avoid.

Go Back home and set the zero -> offset = 600mm
now start counting, position = offset + encoder'delta * counted-steps


Dunno, I will try it the next weekend :o
« Last Edit: March 11, 2021, 07:32:46 am by DiTBho »
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: pow(a,b)
« Reply #47 on: March 11, 2021, 09:09:44 am »
Here's something really basic, that calculate log() and exp() base 2 over limited range using a really silly mix of double and floating point numbers, but should give ideas of how you could implement them when you know what range you will be working over.

Code: [Select]
#include <stdio.h>

double exp_table [16] = {
  1.000169239705302138, 1.000338508052682318, 1.000677130693066408, 1.001354719892108225,
  1.002711275050202522, 1.005429901112802726, 1.010889286051700475, 1.021897148654116627,
  1.044273782427413755, 1.090507732665257690, 1.189207115002721027, 1.414213562373095145,
  2.000000000000000000, 4.000000000000000000, 16.00000000000000000, 256.0000000000000000,
};

double my_pow2(double a) {
   int fixed = a * 4096;
   double total = 1.0;
   for(int i = 0; i < 16; i++) {
      if(fixed & (1<<i))
         total *= exp_table[i];
   }
   return total;   // 2^(a/4096)  for a limited range
}

double log_table [16] = {
 1.00000528830729185, 1.00002115339696473, 1.00004230724139576, 1.00008461627269440,
 1.00016923970530214, 1.00033850805268232, 1.00067713069306641, 1.00135471989210822,
 1.00271127505020252, 1.00542990111280273, 1.01088928605170048, 1.02189714865411663,
 1.04427378242741375, 1.09050773266525769, 1.18920711500272103, 1.41421356237309515
};

int my_log2_fixed(double a) {
   int fixed = 0;

   while(a >= 2.0) {
      fixed++;
      a /= 2.0;
   }

   while(a < 1.0) {
      fixed --;
      a *= 2.0;
   }
   fixed <<=16;

   for(int i = 15; i >= 0; i--) {
     if(a > log_table[i]) {
        a /= log_table[i];
        fixed |= 1<<i;
     }
   }
   return fixed;  // log2(a) * 65536
}
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: DiTBho

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: pow(a,b)
« Reply #48 on: March 11, 2021, 09:41:17 am »
But why bother curve-fitting at all - just interpolate between points.

Code: [Select]
#include <stdio.h>

struct {
   double x, y, k;
} data[] =
{
    { 2.75,  150.00 }, // volt, mm
    { 2.55,  200.00 },
    { 2.00,  300.00 },
    { 1.55,  400.00 },
    { 1.25,  500.00 },
    { 1.15,  600.00 },
    { 0.90,  700.00 },
    { 0.80,  800.00 },
    { 0.75,  900.00 },
    { 0.65, 1000.00 },
    { 0.60, 1100.00 },
    { 0.55, 1200.00 },
    { 0.50, 1300.00 },
    { 0.44, 1400.00 }
};

double get_value(double i_value) {
    int i;
    // Find where we are interpolating
    for(i = 0; i < sizeof(data)/sizeof(data[0])-2; i++) {
       if(i_value > data[i+1].x) {
         break;
       }
    }
    return data[i].y + (i_value-data[i].x) * data[i].k;
}

int main(int argc, char *argv[]) {
   // Calc some constants
   for(int i = 0; i < sizeof(data)/sizeof(data[0])-1; i++) {
     data[i].k = (data[i+1].y-data[i].y) / (data[i+1].x-data[i].x);
   }

   // Check against the table values
   for(int i = 0; i < sizeof(data)/sizeof(data[0]); i++) {
      printf("%5.2f, %7.2f, %7.2f\n", data[i].x, data[i].y, get_value(data[i].x));
   }
   // Check between two numbers
   printf("\n%5.2f, %7.2f, %7.2f\n", 0.85, 750.00, get_value(0.85));
}
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: DiTBho

Offline Doctorandus_P

  • Super Contributor
  • ***
  • Posts: 3342
  • Country: nl
Re: pow(a,b)
« Reply #49 on: March 11, 2021, 12:37:50 pm »
I accidentally searched:
https://html.duckduckgo.com/html?q=fractional+look+up+table

and found:
https://www.sciencedirect.com/topics/computer-science/fractional-value

I like the idea of first scaling a number between 0.5 and 1 and then using a LUT or simplified approximation.
But scaling it back would need an exponential function so I'm confuses as to whether this is applicable in this situation.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf