Products > Programming

Negate Acceleration ramp, including Javascript Demo

(1/1)

atc1441:
Hi, so i am working on a project where i need a motor get to a set speed with some acceleration.


I tested a bit with java-script to get where i wanted to go.

I have a graph that show the motor speed and a set point slider to try.

with my code the acceleration is negative to the one i want, so it starts fast and ends slow but i need it to start slow and end fast.

Maybe someone has an idea how it needs to look, the code can simply be saved in an .html file and then it will run and can be edited.

Thanks in advantage.

Here is the code i am using.
https://atc1441.github.io/RampTest.html

In the following code block there are   signs missing so please use the link to github

--- Code: ---<!DOCTYPE html>
<html>
<body>

<canvas id="myCanvas" width="600" height="600" style="border:1px solid #c3c3c3;"></canvas><br>
Enable Slider: <input type="checkbox" id="myCheck">
<input type="range" min="1" max="1000" value="500" id="sliderDiv">
<p>Output: <span id="OutputText"></span></p>
<p>Input: <span id="InputText"></span></p>
<p>Current: <span id="currentText"></span></p>

<script>
var slider = document.getElementById("sliderDiv");
var printdiv = document.getElementById("OutputText");
var printdiv1 = document.getElementById("InputText");
var printdiv2 = document.getElementById("currentText");
var canvas = document.getElementById("myCanvas");
var checkBox = document.getElementById("myCheck");
var ctx = canvas.getContext("2d");
let myarray = [];
var output = 0;
var input = 0;

function drawPixel(x,y,y1){
  ctx.beginPath();
  ctx.moveTo(((canvas.width/1000)*x-1), ((canvas.height/1000)*y1));
  ctx.lineTo(((canvas.width/1000)*x), ((canvas.height/1000)*y));
  ctx.strokeStyle = 'blue';
  ctx.stroke();
}

function drawGraph(newpoint){
  var i;
  for (i = 1000; i > 0; i--)myarray[i]=myarray[i-1];
  myarray[0]=newpoint;
  ctx.clearRect(0,0,canvas.width, canvas.height);
  for (i = 0; i < 1000; i  )drawPixel(i,myarray[i],myarray[i-1]);
}

function calcit(invar,outvar){
  var diff = invar-outvar;
  var currentout = diff/100;
  printdiv2.innerHTML = currentout;
  return currentout;
}

function timedInterval(){
  if(checkBox.checked)input = slider.value;
if (input > output)
  output =calcit(input,output);
if (input < output)
  output-=calcit(output,input);
  printdiv.innerHTML = output;
  printdiv1.innerHTML = input;
  drawGraph(output);
  if(output>995)input=0;
  if(output<5)input=1000;
}

myVar = setInterval(timedInterval, 10);
</script>

</body>
</html>
--- End code ---

T3sl4co1l:
You meant "i++" and "+=", right?

FYI, this

--- Code: --- if (input > output)
  output+=calcit(input,output);
if (input < output)
  output-=calcit(output,input);

--- End code ---
is redundant as calcit() is signed.

Tim

atc1441:
Yes, somehow, all the + signs get deleted in this thread, just tried to add it but will still be filtered.

Have the code now here:

https://atc1441.github.io/RampTest.html

T3sl4co1l:
This is a process in the DSP domain.  You've implemented a single-order IIR filter; the recurrent equation is:

--- Code: ---output += (input - output) / 100
--- End code ---

Or written out fully,

--- Code: ---output = output * 1 + output * -0.01 + input * 0.01
--- End code ---

In general, we can construct the new sample output from some past history of output[n] and input[n], where we multiply each element of each array by some gain coefficient determined by the type of filter we want.

The general form, implemented in code, would look something like:

--- Code: ---out = 0;
for (var i = 0; i < 999; i++) {
// shift histories
input[i] = input[i+1];
output[i] = output[i+1];
out += output[i] * feedback_gain[i] + input[i] * input_gain[i];
}
output[999] = out;

--- End code ---
The name for this is a convolution.  That is, the new output is the previous outputs convolved with feedback_gain, plus the previous inputs convolved with input_gain.


Your case we can write in this canonical form as,

--- Code: ---output[n] = output[n-1] * 0.99 + input[n] * 0.01
--- End code ---
being that the gain terms are fixed constants, and the array lengths are very short, in fact just 1 (no arrays needed at all), since we get "output[n-1]" for free in this procedural programming language.


An output which depends only upon its inputs (or only the last output, in certain ways), is called an FIR (finite impulse response) filter.  An output which depends on previous inputs and outputs, is called an IIR (infinite impulse response) filter.

Your example here, does eventually stop, due to rounding error in the double-precision numbers that JavaScript uses (or at least which my browser apparently uses), but if infinite precision variables were available, it would continue along that exponential curve indefinitely.  Hence, it is an IIR type.


Note that, if we only need the previous state or two, we don't need to store all 1000 or whatever previous states, we only need a couple variables.  Any terms that have a gain of zero, we can simply cut out.  So it's not important that your example doesn't use 1000-long arrays for inputs and outputs, because all you needed was the prior state of each.

So we can use this fully general framework, and strip it down to just what's needed, no worries.


So, acceleration.  You have a linear DSP system -- that is, the output is always proportional to the input.  The acceleration, then, is also proportional.  If you wanted a fixed acceleration limit, then you need a nonlinear DSP system.

You might instead write:

--- Code: ---diff = input - output;
diff = Math.max(diff, -MAX_ACCEL);
diff = Math.min(diff, MAX_ACCEL);
output += diff / 100
--- End code ---

Note that the max of two negative numbers, equals the one closer to zero; and the min of two positive numbers, does as well.  So these expressions serve to limit the difference, and thus the acceleration.

Note you can still have it resolve to a nice linear function (the exponential curve) within the acceleration limit.  Or you can crank up the gain so that the output more closely follows the input.


This is also known as a slew rate limit.  The change of voltage on a capacitor, requires a current flow, and limiting that current within a certain range acts to limit the voltage slew rate.

This further suggests opportunities, if you can tell us more about your system.  For example, if you have a DC motor connected to a linkage with significant mass, it can be driven with a limited current, which limits the torque available from the motor, and thus its acceleration.  This has the double bonus of limiting current from the driver, protecting it from destruction even if the motor stalls.

Tim

atc1441:
Thank you very much T3sl4co21l for this very good and detailed answer :)

i read your answer and i like it.

I edited my code and added a form of this example from you, it is now doing what i wanted it to.

--- Code: ---diff = input - output;
diff = Math.max(diff, -MAX_ACCEL);
diff = Math.min(diff, MAX_ACCEL);
output += diff / 100
--- End code ---

i edited the updated the first code: https://atc1441.github.io/RampTest.html

The rounding error is not so important for me right now, as i was only interested in the mathematical accl solution. In future i will use no float numbers, at least i hope :).

The system i have uses hoverboard motors, this is my test vehicle: https://youtu.be/MQ_EklhKByk

i want to have the code as universal as possible that is why i want only to work with the speed input data and not with current regulation etc.

Navigation

[0] Message Index

There was an error while thanking
Thanking...
Go to full version