Go Down

Topic: From linear to exponential PWM output (Read 21770 times) previous topic - next topic

backbone

Hi,

Anyone with more knowledge then I have at the present moment about the following?
A trigger handle moves from 0 to 100% in 0 to 255 steps which present the X-axis.
The Y-axis presents the output (PWM) that has the values ranging from 0 to 100% so too 0 to 255.
So currently the output value is linear to the position of the trigger handle.
Now I like to make this output not linear but exponential in both directions so negative and positive in relation to the position of the trigger handle.
The value of making the linear line curved is coming from a byte length variable called speedcurveValue and the final output byte length variable is called speedfinalValue.

With google I found the equation for a straight line is "y = mx + b", in which "m" and "b" are constants.
So simple as it looks exponential equation is "y = axb", in which "a" and "b" are constants.
If "b" = 1, then this is simply a straight line with a y-intercept of zero and a slope equal to "a" (in this case, 1).  If "b" is greater than 1, then the graph curves upward.  If "b" is less than 1 but greater than 0, then the graph flattens out
So how can I rework the formula so the output depending on the trigger position is making an exponential out output with calculate in either directions.

So 1 to 1,5 to get negative exponential and 1 to 0,5 to get positive exponential

Any help in the right direction would be swell.

Thanks, Paco
Never to old to learn and I learn every day

PaulS

Quote
So simple as it looks exponential equation is "y = axb", in which "a" and "b" are constants.

Most equations are written with spaces between the variables and operands. The use of superscripts is important, too.

It really isn't clear what you are trying to do, with negative values. The Arduino doesn't do negative voltages. The Arduino doesn't do anything but constant PWM values.

Perhaps if you drew a picture of what you are trying to accomplish, with properly labeled X and y axes, it would make more sense.

backbone

Hi Paul,

Here the requested graph with the current linear and the negative and positive exponential curve.
I hope this clear things up.
If not let me know

Paco
Never to old to learn and I learn every day

DuaneB

Hi,

I can guess what this is for.

it's a common adjustment on RC controllers, it increases or decreases sensitivity around the throttle and steering centre points. It looks like a simple function that but my maths is way to rusty to attempt a definition.

A naive implementation would be to use a sine function with an offset, if you move the offset forwrd, you get the r shaped curve, if you move it back, you get the j shaped part of the curve. I am certain there is a better method but this might help other grasp what you are after, unless I am mistaken.

Duane B

rcarduino.blogspot.com
Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

MarkT

You are talking about "exponential" but actually mean "power law".  Exponential would be  y = a^x, you are talking x^a, for some constant a.

You want something like
Code: [Select]

  float y = a * pow (x, b) ;


In your graph what you call negative isn't negative, its powers between 0 and 1.  Negative powers are infinite at x=0.  What you call positive is powers greater than 1.
[ I won't respond to messages, use the forum please ]

el_supremo

This is quadratic, not exponential.
For the "negative" curve you can use: y = x * x / 255.0;
For the "positive" curve use: y = sqrt(x * 255.);

Pete

MarkT

We've established its a power law with the power potentially varying from 0.5 to 1.5 at least, controlled by an 8 bit variable called speedcurveValue.
Code: [Select]

  float x = analogRead (inpin) / 1024.0 ;
  float y = pow (x, speedcurveValue / 128.0) ;  // assume speedcurveValue is 128 * the power
  analogWrite (outpin, (int) (y * 255)) ;


Is the sort of thing I believe to flesh out previous answer.
[ I won't respond to messages, use the forum please ]

DVDdoug

#7
May 10, 2012, 10:58 pm Last Edit: May 10, 2012, 11:01 pm by DVDdoug Reason: 1
Suggestion -   Put some numbers in a spreadsheet.  Make an input columm and an output column.   Your ADC input values go from 0-1023, and your PWM output values go from 0-255.   At this point we don't know the formula or exponent, so just put-in the approximate "answer" you want.   And, don't start-out with all 256 output values...   20 or 25 should be enough.    If it makes it easier, put-in values from 0-100 for both the input and output.    You can scale/tweak for the real values later.

Now, make a 3rd column an stick a formula in it.    Just experiment with the exponential formula 'till you get numbers close to the answers you want.   Excel can make a graph too, if that helps you to compare the calculated values with what you ideally want.

There are a couple of ways to handle negative values.   If we are talking about negative voltages, that has to be done in hardware.    To get a negative value (in software) from the ADC, you can simply subtract 512 (or 511) from the reading (then "half-way" will read zero).    Same thing on the output-side.  half-way is 128 (or 127).   I'm not sure if this will be necessary, but you can use different formulas for the "positive" and "negative" values, if it makes things easier.

For exponents, Excel uses POWER(), and the Arduino uses pow(), and they are used slightly-differently.  So, check the Excel help file & Arduino language reference.


robtillaart

#8
May 10, 2012, 11:13 pm Last Edit: May 10, 2012, 11:15 pm by robtillaart Reason: 1
if my math still works at this late hour, negative curves are in general:

y = pow(x, n) / pow(255, n-1);

n=0  =>  y = x^0 / 255^-1 =>  y = 255   flatliner ;)
n=1  =>  y = x^1 / 255^0  =>  y = x
n=2  =>  y = x^2 / 255^1  =>  y=  x^2/255;
n=3  =>  y = x^3 / 255^2

n=3.14159  is also possible

the positive curves a

y = pow(x * pow(255, n-1), 1/n);

n=1 =>  y = x;
n=2 =>  y = (x * 255)^0.5
n=3 =>  y = (x * 255^2)^0.33333

(please verify in excel ...)
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

backbone

#9
May 11, 2012, 09:01 am Last Edit: May 11, 2012, 09:14 am by backbone Reason: 1
Whow,

Thanks for all suggestions sofar from all of you.

Duane is 50% right. The word exponential is to be found in many rc controllers fr many years to control the power output as shown in the graph.
But in this case it is not for the RC world but for the slotcar world.

For the user for now it is not needed to know how the exp curve runs exactly.
Slot racers are people who are working a lot with the feel of the interaction of the car in the slot and things they do with the trigger.
They are not so interested by the real figures they dial in.
However my slotrace controller project is in the end to show all figures and values they use on PC or Android Mobilephone or tablet by BT.

Although the word negative is used I know the Arduino cant do anything with negative values.
We do not want to output negative values but we want curves that are expected to be different then the linear striagth line when they are following the value of the trigger.

For extra info.
We take the value from a contactless linear hall sensor and map that to 0 to 255

What is mend with negative is the way the curve behaves compared to the linear straigth line.
So if the curve goes below the linear straight line it is a negative curve value.

Here some code:
Code: [Select]
// control the outputs with output cross block function  to prevent speed and brake to be ON together
     sensorValue = analogRead(sensorspeedPin); // read the raw value from the linear hall sensor:
     sensorValue = map(sensorValue, sensorMin, sensorMax, 0, 255);// apply the calibration to the sensor reading
     if (sensorValue > 250) {sensorValue = 255;} // to make sure full 100% PWM value is reached even if sensor value is not 100%
     speedValue = map(sensorValue, deathbandValue, 255, 0, 255);
       
     speedvalueWorked = map(speedValue, sensorMin, sensorMax, speedstartValue, 255); // added startspeed value
     >>>>>>>> speedcurveValue = speedvalueWorked with the quotation to calculate the exponential power curve // added speedcurve value
     speedfinalValue = speedcurvevalue
     if (sensorValue > deathbandValue) {analogWrite(pwmoutspeedPin, speedfinalValue); analogWrite(pwmoutbrakePin, 0);} // startspeed and curve included
     else
     {analogWrite(pwmoutspeedPin, 0); analogWrite(pwmoutbrakePin, brakeValue); digitalWrite(ledPin, HIGH);}


The triggerhandle use a deathband around the 0 point to be sure the mechanical tolerances are taken out.
As long as the sensorvalue is inside the deathband value the breaks are ON. Deathband variable in byte length is adjustable from 0 to 15.
As soon as the sensorvalue is outside the deathband zone the PWM output is activated.
You can add startspeed so the motor does not start at 0 but at a higher value depending on the motor charactristics.
If the speed curve is linear the motor will follow that straight line from lets say startspeed 45 to 255.
When the speed curve is an other value this straight line must be curved.

I hope this clears up more (if not let me know) and other can benefit from it in other projects.

will work on it and let you know th results.
Other tips and tricks are always welcome.

Paco
Never to old to learn and I learn every day

Techylah

Ok. I've read everybody.  MarkT has equation right.  No need for variable exponent though.
Try this first to verify the math is what you want.  It is exponential gamma correction with "gamma" being the exponent.
Then you can change gamma to, say 0.5 or 0.75.
When you have the curve you want use real input for x instead of the loop index.

float gamma = 1.5;
for (x=0; x<256; x++)
{
   float normx = x / 255.0;
   float normy = pow (normx, gamma);
   int y = (int) (0.5 + normy * 255);

   serial.print("x: ");
   serial.print(x, DEC);
   serial.print("   y: ");
   serial.println(y, DEC);
}

Techylah

Another suggestion.  Since this curve is to be applied repeatedly, there is no sense wasting time always computing the correction with slow floating point math.   Build a table as an array of ints.    Fill it properly in the setup code, using the gamma value you have chosen.
Then as you need it, use tableCorrection
  • instead of x.  Much faster; no floating point involved.

robtillaart

indeed a table version is much faster and can be stored in progmem. Only drawback of a table is that one cannot use a dynamic (adjusted) gamma factor.

Another option is multiMap - http://arduino.cc/playground/Main/MultiMap - approach any graph with multiple linear lookup tables.


Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

backbone

Hi,

I looked into the extra suggestions.
Getting more confused as my knowledge in this, is way below the things you guys offer.
Willing to learn so I started with the Excel option to see the proposed curves.
Now here again some expect one to build things as easy as it sounds.  :)

I can create collums and graphs in Excel but putting the fourmula and copy them to the cells is the tricky part.
I have done it manually but know this could be performed automaticly.
Probably used the wrong formula.

Paco
Never to old to learn and I learn every day

robtillaart

#14
May 12, 2012, 09:09 am Last Edit: May 12, 2012, 09:13 am by robtillaart Reason: 1
IN Excel you can show the formula in the graph which you need to convert to code.

Select the line in the graph an right mouse you have options to select trend-line(?) [don't have an English Excel here ]
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Go Up