As I figured out a way to do this, I’ll post it as an answer to my own question. Still, I’m open to more answers. I will even use a one-dimensional approach and if solutions are found appropriate to multiple dimensions I will look at them with pleasure and possibly give the correct answer to a different response from mine.
Just remembering that in multiple dimensions can have curves, that is, the velocity vector can point or not to the end point and so require a change of angle. We know that one can use an approach where the velocity vector only points to the endpoint (which can cause very large angle curves and destroy the desired smoothness) and this approach is easy to implement based on the one-dimension solution, so I have no interest in that. Another approach is to work with a variable angle that changes along the movement smoothly, inserting in the formulas in some way.
Now we go to the solution by addressing a single dimension.
Adopted Strategy
First, take into account that the algorithm receives:
cv
= Current value = current value,
cs
= Current speed = current speed,
ma
= Acceleration magnitude = magnitude of acceleration,
fv
= final value = final value,
dt
= delta time
and that he returns:
ncv
= new Current value = new current value,
ncs
= new Current speed = new current speed.
Furthermore, it is taken into account that:
- when decelerating the park value before reaching the
fv
, then it would be better to accelerate as quickly as possible for a while to gain speed until the moment the deceleration stops at the right point
- and otherwise, even decelerating can be parked both in
fv
how much further (to cross the point of arrival and need to return) and in both cases the immediate deceleration is necessary to have the inertia at the nearest point of fv
.
Thus, it is considered that in fact there are two uniformly varied rectilinear movements, each with a different formula (one with positive acceleration and the other with negative acceleration). According to the situation, the correct formula should be selected and possibly one may need the use of one formula in a stretch of time and in the remainder, the other.
To make the correct selection of the formula, we have to remember that the deceleration moment occurs when the velocity and acceleration signals are opposite (s*a<0
), therefore:
vap(t) = cv + t*( cs + t*ma/2 )
= value formula in positive acceleration from the current moment (vap(0) = cv
),
van(t) = cv + t*( cs - t*ma/2 )
= value formula in negative acceleration from the current moment (van(0) = cv
),
sap(t) = cs + t*ma
= Speed formula in positive acceleration from the current time (sap(0) = cs
),
san(t) = cs - t*ma
= speed formula in negative acceleration from the current moment (san(0) = cs
),
- is used when
cs>=0
at the time of acceleration or cs<0
at deceleration the function formulae vap
and function sap
,
- and is used when
cs>0
at the time of deceleration or cs<=0
at the time of acceleration the function formulae van
and function san
.
Yes, in the inertia (cs=0
) both acceleration values (positive or negative) indicate acceleration, never deceleration. This gives the need for an exception case treatment: in inertia, inevitably one must accelerate to the side that leads to the fv
, that is, the acceleration signal is fv-cv
. If cv=fv
, then it is already stopped at the end value and there is nothing to do. Now leaving the exception cases...
With this, we can know if when decelerating we cross the final value or stop on top or even before, so we know if first accelerates to then decelerate or if it already starts decelerating, reverses speed by accelerating and then changing formula to slow down again.
Whatever the case, the algorithm boils down to starting with an acceleration or deceleration formula until accounting for the entire dt
or before that reach the moment of transition of movement (or the very fv
, that mathematically it is later seen that it is worth treating as a transition point), there if it reaches the transition without emptying the dt
the formula is exchanged and applied until all the dt
remaining or achieving the fv
.
To find out if time passes before dt
or if it reaches a moment of transition or the end of the movement, it is necessary to know what is the moment when the transition or completion occurs and then to verify which time is shorter, which happens first. The formulas of the times compared can possibly be simplified.
First Abstraction of the Algorithm
First we need to define whether it starts with positive or negative acceleration. The exception cases have already been addressed. In general cases, it is known that:
when cs>0
, deceleration to inertia occurs in a time cs/ma
accelerating -ma
and the point where it stops is cv+(cs^2)/(2*ma)
, therefore if cv+(cs^2)/(2*ma) < fv
(cs^2 < 2*ma*(fv-cv)
) then it is better to start accelerating (positive acceleration)
and when cs<0
, deceleration to inertia occurs in a time -cs/ma
accelerating ma
and the point where it stops is cv-(cs^2)/(2*ma)
, therefore if cv-(cs^2)/(2*ma) > fv
(cs^2 < 2*ma*(cv-fv)
) then it is better to start accelerating (negative acceleration).
In short, we see situations like this.
if cs=0:
if fv=cv:
MovimentoJáEmEstadoFinal: ncv=cv & ncs=cs
if fv>cv:
MovimentosDeAceleraçãoPositivaDepoisNegativa
if fv<cv:
MovimentosDeAceleraçãoNegativaDepoisPositiva
if cs>0:
if cs^2 < 2*ma*(fv-cv):
MovimentosDeAceleraçãoPositivaDepoisNegativa
if cs^2 > 2*ma*(fv-cv):
MovimentosDeAceleraçãoNegativaDepoisPositiva
if cs^2 = 2*ma*(fv-cv):
MovimentoDeAceleraçãoNegativa
if cs<0:
if cs^2 < 2*ma*(cv-fv):
MovimentosDeAceleraçãoNegativaDepoisPositiva
if cs^2 > 2*ma*(cv-fv):
MovimentosDeAceleraçãoPositivaDepoisNegativa
if cs^2 = 2*ma*(cv-fv):
MovimentoDeAceleraçãoPositiva
So what remains to be found is:
MovimentosDeAceleraçãoPositivaDepoisNegativa
,
MovimentosDeAceleraçãoNegativaDepoisPositiva
,
MovimentoDeAceleraçãoPositiva
and
MovimentoDeAceleraçãoNegativa
.
Calculation of positive or negative exclusive shut-down or acceleration
When the movement is already in its final state (the MovimentoJáEmEstadoFinal
), That means you’re worth vf
and in inertia, where it must remain, therefore it is not even necessary to make calculations, because it is only necessary to return the results ncv=fv
and ncs=0
.
Already when the calculation that needs to be done is only of a formula, only one is necessary to reach the inertia in the final value and to make the calculation of the algorithm it is only necessary to verify if this update of the movement arrives in the final state (i.e., the time to arrive is calculated and compared with the dt
) and, accordingly, define the values of ncv
and ncs
.
In the case of MovimentoDeAceleraçãoPositiva
, to know if it arrives at the end of the movement (which ends in inertia), one must take into account that the time it takes to get there from the current instant is -cs/ma
, so if it comes when dt >= -cs/ma
. If it does, the results are ncv=cv
and ncs=cs
, otherwise are ncv=vap(dt)
and ncs=sap(dt)
.
In the case of MovimentoDeAceleraçãoNegativa
, the time it takes to finish the furniture from the current instant is cs/ma
, so if it comes when dt >= cs/ma
. If it does, the results are ncv=cv
and ncs=cs
, otherwise are ncv=van(dt)
and ncs=san(dt)
.
Positive acceleration followed by negative motion calculation
When the movement to be calculated is divided into two movements, one of positive acceleration and the other of negative acceleration, it means that it is necessary to work mathematically with the moment of transition and the moment of termination.
In the case of positive acceleration movement followed by negative, i.e., the calculation of MovimentosDeAceleraçãoPositivaDepoisNegativa
, the transition instant and the completion from the current moment depend on a square root calculation result, which is tmp=sqrt(0.5*cs^2+(fv-cv)*ma)
. The instant of translation is tt=(tmp-cs)/ma
. If dt <= tt
, then the results are ncv=vap(dt)
and ncs=sap(dt)
. Otherwise, we need to know the moment the two movements end, which is ft=(2*tmp-cs)/ma
. If dt >= ft
, then the results are ncv=fv
and ncs=0
, otherwise are ncv=cv-dt*(cs+dt*ma/2-tmp-tmp)-(cs-tmp)^2/ma
and ncs=2*tmp-dt*ma-cs
.
Calculation of negative acceleration movement followed by positive acceleration
It is similar to the positive acceleration movement followed by negative, but with minor changes in the formulas. In the case of the calculation of MovimentosDeAceleraçãoPositivaDepoisNegativa
, it differs from the previous by tmp=sqrt(0.5*cs^2+(cv-fv)*ma)
, tt=(tmp+cs)/ma
, if dt <= tt
then the results are ncv=van(dt)
and ncs=san(dt)
, otherwise, ft=(2*tmp+cs)/ma
, if dt >= ft
then the results are ncv=fv
and ncs=0
, otherwise are ncv=cv-dt*(cs-dt*ma/2+tmp+tmp)+(cs+tmp)^2/ma
and ncs=dt*ma-2*tmp-cs
.
Implementation and Testing
The algorithm was implemented and tested in C language, forming the following function that saves the results in *newCurrentValue
and *newCurrentSpeed
. The calculations were well divided, parts of the results saved into many temporary variables. In addition, the square root calculation was avoided as much as possible, adapting even the conditions.
In the first abstraction of the algorithm, we observed a total of nine possibilities of cases, but when implementing and testing we realized that it works to include the exception cases in others, thus defining four cases. For each one, there was one goto
to a point in the program where the calculation occurs, either by assuming first positive and then negative acceleration motion (_ap_an
) or first negative and then positive (_an_ap
).
Next is the uniformly accelerated motion data update function.
# include <math.h>
void UpdateUam( float currentValue , float currentSpeed , float *newCurrentValue , float *newCurrentSpeed , float deltaTime , float finalValue , float accelerationMagnitude ){
float das , das2 , s2av , s2avs , da=deltaTime*accelerationMagnitude , s2=currentSpeed*currentSpeed ;
float a2=accelerationMagnitude+accelerationMagnitude , av=a2*(finalValue-currentValue) ;
if( currentSpeed < 0 )
if( s2+av < 0 ) goto _an_ap ;
else goto _ap_an ;
else
if( s2-av < 0 ) goto _ap_an ;
_an_ap:
das = currentSpeed-da ;
if( das < 0 ){
das2 = das*das ;
s2av = 0.5f*( s2-av ) ;
if( das2 > s2av ){
if( das2 >= 4*s2av ){
*newCurrentValue = finalValue ;
*newCurrentSpeed = 0 ;
return ;
}
s2av = sqrt( s2av ) ;
s2avs = s2av+currentSpeed ;
*newCurrentValue = currentValue-deltaTime*( currentSpeed-0.5f*da+(s2av+s2av) )+s2avs*s2avs/accelerationMagnitude ;
*newCurrentSpeed = da-(s2av+s2av)-currentSpeed ;
return ;
}
}
*newCurrentValue = currentValue+deltaTime*( currentSpeed-0.5f*da ) ;
*newCurrentSpeed = das ;
return ;
_ap_an:
das = currentSpeed+da ;
if( das > 0 ){
das2 = das*das ;
s2av = 0.5f*(s2+av) ;
if( das2 > s2av ){
if( das2 > 4*s2av ){
*newCurrentValue = finalValue ;
*newCurrentSpeed = 0 ;
return ;
}
s2av = sqrt( s2av ) ;
s2avs = s2av-currentSpeed ;
*newCurrentValue = currentValue-deltaTime*( currentSpeed+0.5f*da-(s2av+s2av) )-s2avs*s2avs/accelerationMagnitude ;
*newCurrentSpeed = (s2av+s2av)-da-currentSpeed ;
return ;
}
}
*newCurrentValue = currentValue+deltaTime*( currentSpeed+0.5f*da ) ;
*newCurrentSpeed = das ;
}
The code has been tested in function main
where the data is initialized in local variable iv
(initial value), is
(initial speed), fv
(final value) and a
(magnitude of acceleration). It performs for times dt
ever larger (0.2 additional units per loop) an update with such an interval and also eight time updates dt/8
until the exact same results of the previous cycle are found, which is when you park at the end point.
Next, the function of main
.
# include <stdio.h>
int main(){
float iv=0 , is=0 , fv=0 , a=1 ;
float v=iv , s=is , v2=999 , s2=999 , c=0 ;
for( float dt=0 ; ( v2*v2+s2*s2>0 )||( c<1.5f ) ; dt+=0.2f , c++ ){
v2 = v ; s2 = s ;
UpdateUam(iv,is,&v,&s,dt,fv,a) ;
printf(" Time=%4.1f --> v=%+7.3f s=%+7.3f",dt,v,s) ;
UpdateUam(iv,is,&v,&s,0.125f*dt,fv,a) ;
UpdateUam(v,s,&v,&s,0.125f*dt,fv,a) ;
UpdateUam(v,s,&v,&s,0.125f*dt,fv,a) ;
UpdateUam(v,s,&v,&s,0.125f*dt,fv,a) ;
UpdateUam(v,s,&v,&s,0.125f*dt,fv,a) ;
UpdateUam(v,s,&v,&s,0.125f*dt,fv,a) ;
UpdateUam(v,s,&v,&s,0.125f*dt,fv,a) ;
UpdateUam(v,s,&v,&s,0.125f*dt,fv,a) ;
printf(" |--> v=%+7.3f s=%+7.3f\n",v,s) ;
v2 -= v ; s2 -= s ;
}
return 0 ;
}
No cases have been found where the function gives unexpected results. In addition, Maple 2015 tested the following function that returns the new updated value. In this case, we have not implemented the speed formulae, which are simply derived from the value formulae.
Using the following code, where the first line defines the arguments and in the second a list of accelerations magnitudes, one can test in several cases the values and velocities visualizing the graphs, in the first line the values graphs (for each acceleration starting from the initial values and reaching just beyond the final values of the movement) and on the second line, the velocity using finite differences.
No unexpected results were found in the graphs. Finally, the C function was converted to the next one in C#.
public static void UpdateUam( float currentValue , float currentSpeed , out float newCurrentValue , out float newCurrentSpeed , float deltaTime , float finalValue , float accelerationMagnitude ){
float das , das2 , s2av , s2avs , da=deltaTime*accelerationMagnitude , s2=currentSpeed*currentSpeed ;
float a2=accelerationMagnitude+accelerationMagnitude , av=a2*(finalValue-currentValue) ;
if( currentSpeed < 0 )
if( s2+av < 0 ) goto _an_ap ;
else goto _ap_an ;
else
if( s2-av < 0 ) goto _ap_an ;
_an_ap:
das = currentSpeed-da ;
if( das < 0 ){
das2 = das*das ;
s2av = 0.5f*( s2-av ) ;
if( das2 > s2av ){
if( das2 >= 4*s2av ){
newCurrentValue = finalValue ;
newCurrentSpeed = 0 ;
return ;
}
s2av = (float)System.Math.Sqrt( s2av ) ;
s2avs = s2av+currentSpeed ;
newCurrentValue = currentValue-deltaTime*( currentSpeed-0.5f*da+(s2av+s2av) )+s2avs*s2avs/accelerationMagnitude ;
newCurrentSpeed = da-(s2av+s2av)-currentSpeed ;
return ;
}
}
newCurrentValue = currentValue+deltaTime*( currentSpeed-0.5f*da ) ;
newCurrentSpeed = das ;
return ;
_ap_an:
das = currentSpeed+da ;
if( das > 0 ){
das2 = das*das ;
s2av = 0.5f*(s2+av) ;
if( das2 > s2av ){
if( das2 > 4*s2av ){
newCurrentValue = finalValue ;
newCurrentSpeed = 0 ;
return ;
}
s2av = (float)System.Math.Sqrt( s2av ) ;
s2avs = s2av-currentSpeed ;
newCurrentValue = currentValue-deltaTime*( currentSpeed+0.5f*da-(s2av+s2av) )-s2avs*s2avs/accelerationMagnitude ;
newCurrentSpeed = (s2av+s2av)-da-currentSpeed ;
return ;
}
}
newCurrentValue = currentValue+deltaTime*( currentSpeed+0.5f*da ) ;
newCurrentSpeed = das ;
}
This function was used in Unity 2018 to update the x
of the position of an object relative to the mouse position, only when the mouse button is pressed so that it can pause the movement and test in very sudden changes of conditions.
if( Input.GetMouseButton(0) ){
float x=obj.transform.position.x ;
UpdateUam( x , s , out x , out s , Time.deltaTime , camera.ScreenToWorldPoint(Input.mousePosition).x , 99 ) ;
obj.transform.position = new Vector3(x,0,0) ;
}
No anomalies were found in the movement and softness was visually observed in the changes in values. Therefore, I hope that everything is correct, after all in all the corrected versions of implemented codes saw satisfactory results, although in fact some corrections have been necessary.
How many dimensions should I consider? In thesis 2 would be enough to illustrate everything, but the same math can be done for n dimensions, without taking or for anything.
– Jefferson Quesado
By the way, will the given acceleration be a vector? Or does it always point "down"?
– Jefferson Quesado
@Jeffersonquesado I think that if considering acceleration as vector will not imply greater difficulties in the solution, then I see no reason not to generalize.
– Woss
Sorry, I disappeared but I came back. The question was asked in a generic way, saying that it can express not just displacement but anything. Speaking of dimensions, I thought of only one, but I accept abstractions that treat the variable as a vector of n dimensions, n=1,2,3,... and that formulas use mathematical or physical patterns abstractly, as you prefer.
– RHER WOLF
Still, however much I accept generic formulas where "value" is n-dimensional vector, speed is a "value" per unit of time and acceleration a "value" per unit squared of time (and positive module), still I asked a question thinking of a single dimension to facilitate, because there are many ways to treat the need to make curves and I believe that with the algorithm in one dimension it is possible to treat multiple dimensions in several ways.
– RHER WOLF