Pages: [1]   Go Down
Author Topic: Map() function for curve  (Read 933 times)
0 Members and 1 Guest are viewing this topic.
South East USA
Offline Offline
God Member
*****
Karma: 4
Posts: 636
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm needing to map a value, but I'd like to figure out a formula or function to give me a slight curve in the mapped graph.  Here's the current map:
Code:
int val = map(reading, neutralVal, maxVal, 0, 799);

This is to map an analog reading to a motor speed. 0-799 being the motor's speed, off and 100%.
What I'd like is a curve so that 50% of the analog reading give about 25% motor speed.  I remember from Algebra there's a way to figure out the formula, but ...that was tooling ago.
Here's a graph depicting what I have I mind.


* image.jpg (72.65 KB, 1024x768 - viewed 16 times.)
Logged

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 180
Posts: 8096
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Your analog reading would be 0-1023 with a mid-point at 512.

If you square the reading (reading * reading) you will get a range of 0 to 1,046,529 (be sure to use LONG integers).  The 512 would then be 262,144 or about 1/4 of the range.  Divide by 1,309 to get a range of 0 to 799.

Code:
unsigned long reading = analogRead(inputPin); // 0-1023
reading = reading * reading;  // 0-1046529
reading = reading / 1309;  // 0-799
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

South East USA
Offline Offline
God Member
*****
Karma: 4
Posts: 636
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks. I think I will need to do it with variables though, as my high and low limits can change, depending on other factors (READING is from neutralVal to MaxVal rather than 0 to 1024) . I think it shouldn't be a problem though. Would this do it?

Code:
unsigned long reading = analogRead(inputPin);
unsigned long SQneutral = NeutralVal * NeutralVal;
unsigned long SQmax = MaxVal * MaxVal;
reading = reading * reading;

reading = map(reading, SQNeutral, SQmax, 0, 799);

Logged

Offline Offline
Edison Member
*
Karma: 28
Posts: 2047
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The map() function is linear  and is not going to give you a parabola type curve.

You need to do what johnwasser said.   if some of the parameters change,  use those
the parameters in the equation instead of the constant numeric literals.
Logged

South East USA
Offline Offline
God Member
*****
Karma: 4
Posts: 636
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, so if I've got variables to work with instead of numbers, it'd be like this?

Code:
unsigned long reading = analogRead(inputPin);
unsigned long SQneutral = NeutralVal * NeutralVal;
unsigned long SQmax = MaxVal * MaxVal;
reading = reading * reading;
reading = reading / (reading / 799);
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 170
Posts: 12480
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Check out my multimap() - http://playground.arduino.cc/Main/MultiMap - code. It is made for this...
Logged

Rob Tillaart

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

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 180
Posts: 8096
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks. I think I will need to do it with variables though, as my high and low limits can change, depending on other factors (READING is from neutralVal to MaxVal rather than 0 to 1024) .

For a range of NeutralVal to MaxVal I would try something like this:
Code:
unsigned long NeutralVal, MaxVal;
unsigned long SQmax = (MaxVal-NeutralVal) * (MaxVal-NeutralVal);

unsigned long reading = analogRead(inputPin) - NeutralVal;
reading = reading * reading;
reading = reading / (SQmax / 799);

Of course if your 'reading' ever goes below NeutralVal you will cause an underflow (trying to store a negative number in an unsigned variable).
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

South East USA
Offline Offline
God Member
*****
Karma: 4
Posts: 636
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks.  I'll test this out and see how it works.

Logged

Siena Italia
Offline Offline
Full Member
***
Karma: 2
Posts: 149
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Attached are some procedures, from a much larger sketch, that I've used to scale and curve the outputs of a 2-axis Hall-effect sensor joystick whose normal outputs run from 0.05V to 4.95 V (assuming 5V reference source).  These procedures:
(1) reduce output to 0 for voltages < 0.05V and > 4.95 V as that indicates a broken or shorted lead or internal joystick failure.
(2) establishes a deadband as no joystick will perfectly self-center.
(3) maps the voltage to a -1000 to +1000 scale (distinguishing between forward/reverse or left/right).
(4) applies five levels of scaling ranging from linear to squared.
The result to this point is illustrated in the attached graph.

After that, these functions:
(5) re-map the throttle axis to a new when going in reverse, and re-map the steering axis, with different steering sensitivities when going forward or revers.
(6) re-map Throttle and Steering in accord with a Speedpot connected to third analog input pin.

As the results of these calculations are fed into a CANbus network, there are probably some lines of code that refer to sending CAN messages, and I have not included the #define statements or variable declarations that precede all of this, but this does illustrate one way that one can get curving and scaling and so on using entirely integer arithmetic.  It's probably not the most efficient code for this, so any suggestions would, of course, be welcome.
Ciao,
Lenny


* Scaling illustration graph.jpg (84.64 KB, 780x447 - viewed 20 times.)
* READ_JOYSTICK.ino (4.08 KB - downloaded 7 times.)
Logged

South East USA
Offline Offline
God Member
*****
Karma: 4
Posts: 636
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks, Robbins!  That's what I'm doing, too.  Even made some crude out of bounds voltages to detect connection failure, so things don't go haywire on a broken wire! 
I should've post a little detail about what I was doing/needing for the benefit of other readers.
I'll take a look at your code...
Logged

Worst state in America
Offline Offline
God Member
*****
Karma: 23
Posts: 672
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm needing to map a value, but I'd like to figure out a formula or function to give me a slight curve in the mapped graph.  Here's the current map:
Code:
int val = map(reading, neutralVal, maxVal, 0, 799);

This is to map an analog reading to a motor speed. 0-799 being the motor's speed, off and 100%.
What I'd like is a curve so that 50% of the analog reading give about 25% motor speed.  I remember from Algebra there's a way to figure out the formula, but ...that was tooling ago.
Here's a graph depicting what I have I mind.

The easiest way is to either do a polynomial curve fit of the data and then either run the A/D values through the polynomial (slow but small code) or use a pre-calculated lookup table (larger code, but fast).

The particular curve you show looks a lot like a square function... you may be able to come close using a simple square function, then scale the data back down (normalize it).

The way to do it RIGHT is use a polynomial curve fit though....
Logged

South East USA
Offline Offline
God Member
*****
Karma: 4
Posts: 636
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This works very nicely.  Will be a nice utility to have in my 'toolbox'.
An exponential map function

http://playground.arduino.cc/Main/Fscale
Logged

Siena Italia
Offline Offline
Full Member
***
Karma: 2
Posts: 149
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That is a fine way to create an exponential curve, if you can afford the time and memory it takes to do floating point calculations.  For a real-time application, such as modifying joystick response, that may not be the best choice.  Same for things like polynomial curve fitting - computation intensive and overkill as well unless you want to precisely fit a curve who's characteristics you already know.  Again, for joystick response, the thing we want is to get the right "feel" and that's something we're going to have to find out by trial and error.  In the stuff I posted, the four curves are generated by:

(1) taking the analog value and squaring it (using long as the number can get pretty big)
(2) making different linear combinations of the straight analog value and the squared value:
     a - original
     b - (2 * original + 1* squared)/3
     c - (original + squared)/2
     d - (original + 2*squared)/3
     e - squared

The results of these integer manipulations are quite close (better than 1% at all points) to the curves shown in the graph for the exponents shown in the graph legend.  As I can't move a joystick with anywhere near that precision, I figure that this is more than good enough, and if one of these five curves doesn't give me the right feel I can always try another combination of original and squared (or cubed if need be, but I highly doubt that).  Running on a pair of CAN-bus nodes, these integer calculations (not just curving for the 2 axes, but deadband, speedpot range setting and different scaling for forward and reverse as well) are still using substantially more time than the CAN messaging, or the rest of loop () for that matter.  The more time that is taken for calculating things, the more the joystick will have a rubber-band feel to it, and I don't want that.
Ciao,
Lenny

Logged

Pages: [1]   Go Up
Jump to: