123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- #pragma once
- #include <cmath>
- #include <cstdint>
- #include <algorithm>
- class LinStepAccelerator
- {
- public:
- inline int32_t prepareMovement(int32_t currentPos, int32_t targetPos, uint32_t targetSpeed, uint32_t pullInSpeed, uint32_t pullOutSpeed, uint32_t a);
- inline int32_t updateSpeed(int32_t currentPosition);
- inline uint32_t initiateStopping(int32_t currentPosition);
- inline void overrideSpeed(float fac, int32_t currentPosition);
- LinStepAccelerator() = default;
- protected:
- LinStepAccelerator(const LinStepAccelerator &) = delete;
- LinStepAccelerator &operator=(const LinStepAccelerator &) = delete;
- int32_t s_0, ds;
- uint32_t vs, ve, vt;
- int64_t vs_sqr, ve_sqr, vt_sqr;
- uint32_t two_a;
- int32_t accEnd, decStart;
- };
- // Inline Implementation =====================================================================================================
- int32_t LinStepAccelerator::prepareMovement(int32_t currentPos, int32_t targetPos, uint32_t targetSpeed, uint32_t pullInSpeed, uint32_t pullOutSpeed, uint32_t a)
- {
- vt = targetSpeed;
- vs = pullInSpeed; // v_start
- ve = pullOutSpeed; // v_end
- two_a = 2 * a;
- s_0 = currentPos;
- ds = std::abs(targetPos - currentPos);
- vs_sqr = vs * vs;
- ve_sqr = ve * ve;
- vt_sqr = vt * vt;
- int32_t sm = ((ve_sqr - vs_sqr) / two_a + ds) / 2; // position where acc and dec curves meet
- // Serial.printf("ve: %d\n", ve);
- // Serial.printf("vs: %d\n", vs);
- // Serial.printf("ds: %d\n", ds);
- // Serial.printf("sm: %i\n", sm);
- if (sm >= 0 && sm <= ds) // we can directly reach the target with the given values vor v0, ve and a
- {
- int32_t sa = (vt_sqr - vs_sqr) / two_a; // required distance to reach target speed
- if (sa < sm) // target speed can be reached
- {
- accEnd = sa;
- decStart = sm + (sm - sa);
- //Serial.printf("reachable accEnd: %i decStart:%i\n", accEnd, decStart);
- }
- else
- {
- accEnd = decStart = sm;
- //Serial.printf("limit accEnd: %i decStart:%i\n", accEnd, decStart);
- }
- }
- else
- {
- // hack, call some error callback instead
- while (1)
- {
- digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
- delay(25);
- }
- }
- return vs;
- }
- int32_t LinStepAccelerator::updateSpeed(int32_t curPos)
- {
- int32_t s = std::abs(s_0 - curPos);
- // acceleration phase -------------------------------------
- if (s < accEnd)
- {
- return sqrtf(two_a * s + vs_sqr);
- }
- // constant speed phase ------------------------------------
- if (s < decStart)
- {
- return vt;
- }
- //deceleration phase --------------------------------------
- if (s < ds)
- {
- // return sqrtf(two_a * ((stepsDone < ds - 1) ? ds - stepsDone - 2 : 0) + vs_sqr);
- return sqrtf(ve_sqr + (ds - s - 1) * two_a);
- }
- //we are done, make sure to return 0 to stop the step timer
- return 0;
- }
- uint32_t LinStepAccelerator::initiateStopping(int32_t curPos)
- {
- int32_t stepsDone = std::abs(s_0 - curPos);
- if (stepsDone < accEnd) // still accelerating
- {
- accEnd = decStart = 0; // start deceleration
- ds = 2 * stepsDone; // we need the same way to decelerate as we traveled so far
- return stepsDone; // return steps to go
- }
- else if (stepsDone < decStart) // constant speed phase
- {
- decStart = 0; // start deceleration
- ds = stepsDone + accEnd; // normal deceleration distance
- return accEnd; // return steps to go
- }
- else // already decelerating
- {
- return ds - stepsDone; // return steps to go
- }
- }
|