Go Down

Topic: logarithmic scaling (Read 3727 times) previous topic - next topic

vini_i

my team and i built a robot. it is driven via WiFi tank style. we pass it a byte of data for motor speed. stop is zero. full forward is 127 and full reverse is -127. the robot is quite large and the motors can't just be commanded to the desired speed. we use a function that compares the current speed with the commanded speed and then increments and decrements the current speed accordingly. the incrementation is done with a timer. if current time minus the previous time is greater than a preset constant the speed is incremented by one step. this ramp function is linear.

i want to command the motors in a more logarithmic fashion. the closer the current speed is to zero (stopped) the longer the wait time is between steps and the closer it is to full speed the shorter the time between steps is.

for the life of me i can't think of a simple way to do this. one way that i've thought of is to use exponential decay. divide the delay time by a constant to the current speed power. the problem is i don't want to burden the processor with lengthy floating point calculations.

another way is to use an array with all of the delay times precalculated. the problem would be that the array is very large. 255 integers would eat the 328's memory alive.

are there any tricks that can be used to ramp the motor speeds in a logarithmic fashion?

PaulS

The first thing I'd want to know was what kind of motors you have.

The second thing I'd do was to define your non-linear function, in code, and call it with a variety of values to see how long it actually takes. You might not "burden the processor with lengthy floating point calculations.", if the processor would otherwise be accomplishing nothing.
The art of getting good answers lies in asking good questions.

KeithRB

Once you determine the response, you can probably build a look-up table.

el_supremo

How long does your current linear method take to get from stopped to full speed and what is the preset constant?

Quote
the closer it is to full speed the shorter the time between steps is

I'm not sure that this is what you should do - it probably won't look right. I think the acceleration at the beginning and end should be lower than it is in the middle. But you'd have to experiment with it to find the right acceleration curve.

One way to approximate the exponential curve would be to break it into several linear pieces. At the moment, at any point in the acceleration (and, presumably, deceleration) you are using the same linear constant to increase the speed. You could keep track of what stage of the acceleration you are at (as you do now) and map that into one of, say, five constants such as 1, 3, 7, 3 and 1, or whatever is appropriate. If it doesn't take long to get up to full speed I doubt that you'll notice the difference between that and a true exponential.

Pete

Don't send me technical questions via Private Message.

krupski

#4
Dec 12, 2013, 04:43 pm Last Edit: Dec 12, 2013, 05:06 pm by Krupski Reason: 1

for the life of me i can't think of a simple way to do this. one way that i've thought of is to use exponential decay. divide the delay time by a constant to the current speed power. the problem is i don't want to burden the processor with lengthy floating point calculations.


I've done the exact same thing to smoothly control a motor... do this: (not verbatim, just get the idea!)

Code: [Select]
while (abs(speed - setpoint) > 5) {
       speed += ((setpoint - speed) * some_constant);
}


See that the CHANGE in speed is proportional to the DIFFERENCE between the setpoint and the speed so that as speed gets closer to setpoint, the CHANGE in speed gets smaller.

Notice that when the difference is "close enough" (i.e. less than 5) the loop is satisfied.

Tweak the numbers to your needs.

Hope this helps......

(edit added a graph of the code showing how it converges on the setpoint)
Gentlemen may prefer Blondes, but Real Men prefer Redheads!

MarkT

#5
Dec 12, 2013, 05:11 pm Last Edit: Dec 12, 2013, 05:13 pm by MarkT Reason: 1
I think what is needed is a jerk-limited acceleration curve, where jerk is the derivative
of acceleration.  http://en.wikipedia.org/wiki/Jerk_%28physics%29

Something like:

Code: [Select]

long enforce_limit (long value, long limit)
{ // clip acceleration and jerk values to a maximum absolute value
 if (value > limit)  return limit ;
 if (value < -limit)  return -limit ;
 return value ;
}
....

 velocity_diff = set_velocity - current_velocity ;

 velocity_diff = enfore_limit (velocity_diff, max_accel_step) ;  // limit max acceleration

 accel_diff = enforce_limit (velocity_diff - last_velocity_diff, max_jerk_step) ; // limit max jerk
 velocity_diff = last_velocity_diff + accel_diff ;  // correct velocity diff for jerk limit

 current_velocity += velocity_diff ;  // calculate new velocity value
 last_velocity_diff = velocity_diff ;  // update for next time
 update_motor (current_velocity) ;
 delay (time_step) ;  // or better to use blinkWithoutDelay style polling.
 

long values are probably needed due to the two levels of integration involved, and
selecting a scaling factor that allows suitable values for jerk is needed.  If for instance
you update at 256Hz then you need 8 more bits per level of integration (so 8 bits for
jerk, 16 for acceleration, 24 for velocity - in practice the velocity value is right shifted
before use because of this).

[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Vaclav

I'll need something similar - be able to adjust tunning speed ( frequency steps) depending on speed of the dial.
SO far I got it working in increments of multiples of 10 and it sure looks funky. I hope the Uno will be able to hack it in ms scale between 20 us calls from the  interrupt.
Cheers
Vaclav

vini_i

the motors we are using are ampflow A23-150. these motors are definitely powerful enough the problem is that the robot weighs 51 kilos. getting that kind of mass moving is tough.

you guys convinced me of the look up table idea. i checked and an int array[255] only takes up a quarter of the uno's memory.

what i did was use matlab to generate a matrix like what el_supremo suggested

Quote
One way to approximate the exponential curve would be to break it into several linear pieces. At the moment, at any point in the acceleration (and, presumably, deceleration) you are using the same linear constant to increase the speed. You could keep track of what stage of the acceleration you are at (as you do now) and map that into one of, say, five constants such as 1, 3, 7, 3 and 1, or whatever is appropriate. If it doesn't take long to get up to full speed I doubt that you'll notice the difference between that and a true exponential.


but i took it to the next level. i generated a time delay value for every motor speed and then i used the current motor speed as an index to look up the dely time.

the pseudo code looks kind of like
if ((currentTime - storedTime) > lookUPtable[currentSpeed + 127]) // the plus 127 shifts the index to align speed with delay
{storedTime = currentTime;
increment motor speed + or - one}



here is a graphical representation of that data set. close to zero speed the delay between steps is long ( 77 milliseconds)
out at the fringes of either reverse or forward the delay between steps is short ( 2 milliseconds).
i also scaled the total delay from stop to full speed such that it was no longer than the linear implementation which was 10 milliseconds. with 127 steps it ends up 1.27 seconds .

if anyone is interested i can share the matlab code

PeterH


an int array[255] only takes up a quarter of the uno's memory.


That's fair enough if you can afford to squander that much memory, but since this data is constant you could use the PROGMEM modifier to leave the table in flash memory and not use any RAM at all. (You'd need to read the relevant value from program memory using pgm_read_byte() when you need to use it).

Go Up