Go Down

### Topic: Map() function for curve (Read 3723 times)previous topic - next topic

#### SouthernAtHeart

##### Jan 28, 2013, 02:48 pm
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: [Select]
` 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.

#### johnwasser

#1
##### Jan 28, 2013, 02:59 pm
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: [Select]
`unsigned long reading = analogRead(inputPin); // 0-1023reading = reading * reading;  // 0-1046529reading = reading / 1309;  // 0-799`
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

#### SouthernAtHeart

#2
##### Jan 28, 2013, 04:05 pm
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: [Select]
`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);`

#### michinyon

#3
##### Jan 28, 2013, 07:06 pm
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.

#### SouthernAtHeart

#4
##### Jan 28, 2013, 07:27 pm
Ok, so if I've got variables to work with instead of numbers, it'd be like this?

Code: [Select]
`unsigned long reading = analogRead(inputPin); unsigned long SQneutral = NeutralVal * NeutralVal;unsigned long SQmax = MaxVal * MaxVal;reading = reading * reading; reading = reading / (reading / 799);`

#### robtillaart

#5
##### Jan 28, 2013, 09:46 pm

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

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

#### johnwasser

#6
##### Jan 28, 2013, 11:19 pm

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: [Select]
`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).
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

#### SouthernAtHeart

#7
##### Jan 28, 2013, 11:24 pm
Thanks.  I'll test this out and see how it works.

#### LROBBINS

#8
##### Jan 29, 2013, 01:18 pm
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

#### SouthernAtHeart

#9
##### Jan 29, 2013, 01:25 pm
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...

#### Krupski

#10
##### Jan 29, 2013, 08:48 pm

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: [Select]
` 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....
Gentlemen may prefer Blondes, but Real Men prefer Redheads!

#### SouthernAtHeart

#11
##### Feb 10, 2013, 06:54 pm
This works very nicely.  Will be a nice utility to have in my 'toolbox'.
An exponential map function

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

#### LROBBINS

#12
##### Feb 10, 2013, 08:12 pm
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

Go Up

Please enter a valid email to subscribe