RBE1001Lib
Motor.cpp
Go to the documentation of this file.
1 /*
2  * Motor.cpp
3  *
4  * Created on: May 31, 2020
5  * Author: hephaestus
6  */
7 
8 #include <Motor.h>
9 static const char *TAG = "Motor Class";
10 
11 bool Motor::timersAllocated = false;
13  NULL,
14 };
15 static TaskHandle_t complexHandlerTask;
16 //portMUX_TYPE mmux = portMUX_INITIALIZER_UNLOCKED;
17 
18 float myFmapBounded(float x, float in_min, float in_max, float out_min,
19  float out_max)
20 {
21 
22  if (x > in_max)
23  return out_max;
24  if (x < in_min)
25  return out_min;
26  return ((x - in_min) * (out_max - out_min) / (in_max - in_min)) + out_min;
27 }
33 {
34  if (!isAttached)
35  attach();
36  float interpElapsed = (float)(millis() - startTime);
37  if (interpElapsed < duration && duration > 0)
38  {
39  // linear elapsed duration
40  unitDuration = interpElapsed / duration;
42  {
43  // sinusoidal ramp up and ramp down
44  float sinPortion = (cos(-PI * unitDuration) / 2) + 0.5;
45  unitDuration = 1 - sinPortion;
46  }
47  if (mode == BEZIER)
48  {
49  if (unitDuration > 0 && unitDuration < 1)
50  {
51  float t = unitDuration;
52  float P0 = 0;
53  float P1 = BEZIER_P0;
54  float P2 = BEZIER_P1;
55  float P3 = 1;
56  unitDuration = pow((1 - t), 3) * P0 + 3 * t * pow((1 - t), 2) * P1 + 3 * pow(t, 2) * (1 - t) * P2 + pow(t, 3) * P3;
57  }
58  }
59  if (mode == TRAPEZOIDAL)
60  {
61  float lengthOfLinearMode = duration - (TRAPEZOIDAL_time * 2);
62  float unitLienear = lengthOfLinearMode / duration;
63  float unitRamp = ((float)TRAPEZOIDAL_time) / duration;
64  float unitStartRampDown = unitLienear + unitRamp;
65  if (unitDuration < unitRamp)
66  {
67  // ramp up
68  // range from 1 to 0.5
69  float increment = 1 - (unitDuration) / (unitRamp * 2);
70  // range 0 to 1
71  float sinPortion = 1 + cos(-PI * increment);
72  unitDuration = sinPortion * unitRamp;
73  }
74  else if (unitDuration > unitRamp && unitDuration < unitStartRampDown)
75  {
76  // constant speed
77  }
78  else if (unitDuration > unitStartRampDown)
79  {
80  float increment = (unitDuration - unitStartRampDown) / (unitRamp * 2) + 0.5;
81  float sinPortion = 0.5 - ((cos(-PI * increment) / 2) + 0.5);
82  unitDuration = (sinPortion * 2) * unitRamp + unitStartRampDown;
83  }
84  }
85  return unitDuration;
86  }
87  return 1;
88 }
89 void onMotorTimer(void *param)
90 {
91  ESP_LOGI(TAG, "Starting the PID loop thread");
92  TickType_t xLastWakeTime;
93  const TickType_t xFrequency = 1;
94  xLastWakeTime = xTaskGetTickCount();
95  while (1)
96  {
97  vTaskDelayUntil(&xLastWakeTime, xFrequency);
98  //
99  for (int i = 0; i < MAX_POSSIBLE_MOTORS; i++)
100  {
101  if (Motor::list[i] != NULL)
102  {
103 
104  Motor::list[i]->loop();
105  }
106  }
107  //
108  }
109  ESP_LOGE(TAG, "ERROR Pid thread died!");
110 }
115 void Motor::allocateTimer(int PWMgenerationTimer)
116 {
118  {
119  ESP32PWM::allocateTimer(PWMgenerationTimer);
120  xTaskCreatePinnedToCore(onMotorTimer, "PID loop Thread", 8192, NULL, 1,
121  &complexHandlerTask, 0);
122  }
123  Motor::timersAllocated = true;
124 }
125 
126 Motor::Motor(int pwmPin, int dirPin, int encAPin, int encBPin)
127 {
128  MotorPWMPin = pwmPin;
129  directionFlag = dirPin;
130  MotorEncAPin = encAPin;
131  MotorEncBPin = encBPin;
132  pwm = NULL;
133  encoder = NULL;
134 }
135 
137 {
138  encoder->pauseCount();
140  delete (encoder);
141  delete (pwm);
142 }
143 
150 void Motor::setSetpointWithTime(float newTargetInDegrees, long msTimeDuration,
152 {
153  if (!isAttached)
154  attach();
155 
156  float newSetpoint = newTargetInDegrees / TICKS_TO_DEGREES;
157  //portENTER_CRITICAL(&mmux);
158  closedLoopControl = true;
159  if (newSetpoint == setpoint && msTimeDuration == duration && this->mode == mode)
160  return;
162  endSetpoint = newSetpoint;
163  startTime = millis();
164  duration = msTimeDuration;
165  this->mode = mode;
166  if (msTimeDuration < 1)
167  {
168  setpoint = newSetpoint;
169  }
170  ESP_LOGI(TAG, "Starting Interpolated move %.4f deg, %ld ms %d mode",
171  newTargetInDegrees, msTimeDuration, mode);
172  //portEXIT_CRITICAL(&mmux);
173 }
174 
182 void Motor::moveTo(float newTargetInDegrees, float speedDegPerSec)
183 {
184  if (!isAttached)
185  attach();
186 
187  float deltaMove = getCurrentDegrees() - newTargetInDegrees;
188  setSetpointWithBezierInterpolation(newTargetInDegrees,
189  fabs(deltaMove / speedDegPerSec) * 1000.0, 0.2, 1);
190 }
191 
192 float Motor::startMoveFor(float deltaTargetInDegrees, float speedDegPerSec)
193 {
194  if (!isAttached)
195  attach();
196 
197  float newSetPoint = getCurrentDegrees() + deltaTargetInDegrees;
199  fabs(deltaTargetInDegrees / speedDegPerSec) * 1000.0, 0.2, 1);
200  return newSetPoint;
201 }
202 
214 {
215  if (!isAttached)
216  attach();
217 
218  // First wait for the interpolation to finish
220  {
221  ESP_LOGD(TAG, "Move Interpolation Remaining: %.4f", getInterpolationUnitIncrement());
222  return false;
223  }
224  float distanceToGo = fabs(
226 
227  if (distanceToGo > 0.75)
228  { // more than 1 degree from target
229  ESP_LOGD(TAG, "Move Remaining: %.4f", distanceToGo);
230  return false;
231  }
232  // wait for the velocity to be below 10deg/sec
233  // 5deg/sec is lower bound of detection
234  if (fabs(getDegreesPerSecond()) > 10)
235  {
236  ESP_LOGD(TAG, "Move Speed: %.4f", getDegreesPerSecond());
237  return false;
238  }
239  // All moving checks came back passed!
240  return true;
241 }
242 
250 {
251  if (!isAttached)
252  attach();
253 
254  while (!isMotorDoneWithMove())
255  {
256  delay(10);
257  }
258 }
259 
269 void Motor::moveFor(float deltaTargetInDegrees, float speedDegPerSec)
270 {
271  if (!isAttached)
272  attach();
273 
274  startMoveFor(deltaTargetInDegrees, speedDegPerSec);
276 }
277 
295 void Motor::setSpeed(float newDegreesPerSecond)
296 {
297  if (!isAttached)
298  attach();
299 
300  if (closedLoopControl == false)
302  if (fabs(newDegreesPerSecond) < 0.1)
303  {
305  //ESP_LOGI(TAG, "Stopping");
306  return;
307  }
308  milisecondPosIncrementForVelocity = (-newDegreesPerSecond * (((float)-1.0) / 1000.0)) / TICKS_TO_DEGREES;
309  // ESP_LOGI(TAG,"Setting Speed "+String(newDegreesPerSecond)+
310  // " increment "+String(milisecondPosIncrementForVelocity)+
311  // " scale "+String(TICKS_TO_DEGREES)
312  // +" setpoint "+String(setpoint*TICKS_TO_DEGREES)
313  // );
314  //setpoint = nowEncoder;
316  closedLoopControl = true;
317 }
318 
326 void Motor::setSpeed(float newDegreesPerSecond, long miliseconds)
327 {
328  if (!isAttached)
329  attach();
330 
331  if (miliseconds < 1)
332  {
333  // 0 time will set up "Red Queen" (sic) interpolation
334  setSpeed(newDegreesPerSecond);
335  return;
336  }
337  float currentPos = getCurrentDegrees();
338  float distance = currentPos + (-newDegreesPerSecond * (((float)miliseconds) / 1000.0));
339  setSetpointWithTime(distance, miliseconds, LINEAR_INTERPOLATION);
340 }
341 
348 {
350  if (closedLoopControl)
351  {
352  //portEXIT_CRITICAL(&mmux);
353  if (mode == VELOCITY_MODE)
354  {
355  if (fabs(currentEffort) < 0.95) // stall detection
357  }
358  else
359  {
361  if (unitDuration < 1)
362  {
363  float setpointDiff = endSetpoint - startSetpoint;
364  float newSetpoint = startSetpoint + (setpointDiff * unitDuration);
365  setpoint = newSetpoint;
366  }
367  else
368  {
369  // If there is no interpoation to perform, set the setpoint to the end state
371  }
372  }
373  float controlErr = setpoint - nowEncoder;
374 
376  {
377  // no i term during interpolation
378  runntingITerm = 0;
379  }
380  else
381  {
382  // shrink old values out of the sum
384  // running sum of error
385  runntingITerm += controlErr;
386  }
387 
388  currentEffort =
389  -(controlErr * kP + ((runntingITerm / I_TERM_SIZE) * kI));
390 
391  //portEXIT_CRITICAL(&mmux);
392  }
393 
394  else
395  {
400  else
402  }
403 
405  if (interruptCountForVelocity == 50)
406  {
408  float err = prevousCount - nowEncoder;
409  cachedSpeed = err / (0.05); // ticks per second
411  }
412  // invert the effort so that the set speed and set effort match
414 }
418 void Motor::setGains(float p, float i, float d)
419 {
420  if (!isAttached)
421  attach();
422 
423  //portENTER_CRITICAL(&mmux);
424  kP = p;
425  kI = i;
426  kD = d;
427  runntingITerm = 0;
428  //portEXIT_CRITICAL(&mmux);
429 }
430 
431 void Motor::setGainsP(float p)
432 {
433  if (!isAttached)
434  attach();
435 
436  kP = p;
437  runntingITerm = 0;
438 }
439 void Motor::setGainsI(float i)
440 {
441  if (!isAttached)
442  attach();
443 
444  kI = i;
445  runntingITerm = 0;
446 }
447 void Motor::setGainsD(float d)
448 {
449  if (!isAttached)
450  attach();
451 
452  kD = d;
453  runntingITerm = 0;
454 }
455 
457 {
458  if (isAttached)
459  return;
460  isAttached = true;
461  // Motor timer must be allocated and the thread must be started before starting
463  {
464  Motor::allocateTimer(0); // used by the DC Motors
465  }
466 
467  pwm = new ESP32PWM();
468  encoder = new ESP32Encoder();
470 
471  pwm->attachPin(MotorPWMPin, 20000, 12);
473  pinMode(directionFlag, OUTPUT);
474  // add the motor to the list of timer based controls
475  for (int i = 0; i < MAX_POSSIBLE_MOTORS; i++)
476  {
477  if (Motor::list[i] == NULL)
478  {
479  // String message ="Allocating Motor " + String(i) + " on PWM "+ String(MotorPWMPin);
480  // ESP_LOGI(TAG,message.c_str());
481  Motor::list[i] = this;
482  return;
483  }
484  }
485 }
486 
487 /*
488  * \brief effort of the motor, proportional to PWM
489  *
490  * @param effort a value from -1 to 1 representing effort
491  * 0 is brake
492  * 1 is full speed clockwise
493  * -1 is full speed counter clockwise
494  */
495 void Motor::setEffort(float effort)
496 {
497  if (!isAttached)
498  attach();
499 
500  if (effort > 1)
501  effort = 1;
502  if (effort < -1)
503  effort = -1;
504  //portENTER_CRITICAL(&mmux);
505  closedLoopControl = false;
506  targetEffort = effort;
507  //portEXIT_CRITICAL(&mmux);
508 }
509 /*
510  * effort of the motor
511  * @return a value from -1 to 1 representing effort
512  * 0 is brake
513  * 1 is full speed clockwise
514  * -1 is full speed counter clockwise
515  */
517 {
518  if (!isAttached)
519  attach();
520  return currentEffort;
521 }
522 /*
523  * effort of the motor
524  * @param effort a value from -1 to 1 representing effort
525  * 0 is brake
526  * 1 is full speed clockwise
527  * -1 is full speed counter clockwise
528  */
529 void Motor::setEffortLocal(float effort)
530 {
531  if (!isAttached)
532  attach();
533 
534  if (effort > 1)
535  effort = 1;
536  if (effort < -1)
537  effort = -1;
538  if (effort > 0)
539  digitalWrite(directionFlag, LOW);
540  else
541  digitalWrite(directionFlag, HIGH);
542  pwm->writeScaled(fabs(effort));
543 }
552 {
553  if (!isAttached)
554  attach();
555 
556  float tmp;
557  //portENTER_CRITICAL(&mmux);
558  tmp = cachedSpeed;
559  //portEXIT_CRITICAL(&mmux);
560  return -tmp * TICKS_TO_DEGREES;
561 }
569 {
570  if (!isAttached)
571  attach();
572 
573  float tmp;
574  //portENTER_CRITICAL(&mmux);
575  tmp = nowEncoder;
576  //portEXIT_CRITICAL(&mmux);
577  return tmp * TICKS_TO_DEGREES;
578 }
void setEffortLocal(float effort)
Definition: Motor.cpp:529
static void allocateTimer(int timerNumber)
Definition: ESP32PWM.cpp:25
float getEffort()
Definition: Motor.cpp:516
int MotorEncBPin
Definition: Motor.h:181
static const char * TAG
Definition: Motor.cpp:9
int MotorEncAPin
Definition: Motor.h:177
int prevousCount
Definition: Motor.h:66
void setSetpoint(float newTargetInDegrees)
Definition: Motor.h:366
float startMoveFor(float deltaTargetInDegrees, float speedDegPerSec)
Definition: Motor.cpp:192
void detachPin(int pin)
Definition: ESP32PWM.cpp:250
float kP
Definition: Motor.h:78
Motor(int pwmPin, int dirPin, int encAPin, int encBPin)
A PID Motor class using FreeRTOS threads, ESP32Encoder and ESP32PWM.
Definition: Motor.cpp:126
void setSetpointWithTime(float newTargetInDegrees, long miliseconds, interpolateMode mode)
Definition: Motor.cpp:150
virtual ~Motor()
Definition: Motor.cpp:136
void attachPin(uint8_t pin)
Definition: ESP32PWM.cpp:231
interpolateMode mode
Definition: Motor.h:140
float targetEffort
Definition: Motor.h:112
#define TICKS_TO_DEGREES
Definition: Motor.h:17
float setpoint
Definition: Motor.h:74
Definition: Motor.h:27
void setGainsI(float i)
Definition: Motor.cpp:439
float kD
Definition: Motor.h:86
void setSpeed(float newDegreesPerSecond)
Definition: Motor.cpp:295
void setGainsP(float p)
Definition: Motor.cpp:431
float currentEffort
Definition: Motor.h:116
float duration
Definition: Motor.h:120
float runntingITerm
Definition: Motor.h:90
void setGainsD(float d)
Definition: Motor.cpp:447
const float DELTA_EFFORT
Definition: Motor.h:20
void attach()
Attach the motors hardware.
Definition: Motor.cpp:456
int directionFlag
Definition: Motor.h:173
void setGains(float p, float i, float d)
Definition: Motor.cpp:418
void setEffort(float effort)
Definition: Motor.cpp:495
int interruptCountForVelocity
Definition: Motor.h:62
#define MAX_POSSIBLE_MOTORS
Definition: Motor.h:13
bool closedLoopControl
Definition: Motor.h:108
float kI
Definition: Motor.h:82
interpolateMode
Definition: Motor.h:22
float endSetpoint
Definition: Motor.h:128
float getCurrentDegrees()
Definition: Motor.cpp:568
float BEZIER_P0
BEZIER Control Point 0.
Definition: Motor.h:152
#define I_TERM_SIZE
Definition: Motor.h:18
int64_t pauseCount()
enum puType ESP32Encoder
float startSetpoint
Definition: Motor.h:132
static TaskHandle_t complexHandlerTask
Definition: Motor.cpp:15
float milisecondPosIncrementForVelocity
Definition: Motor.h:146
void moveTo(float newTargetInDegrees, float speedDegPerSec)
Definition: Motor.cpp:182
float TRAPEZOIDAL_time
the amount of time to ramp up and ramp down the speed
Definition: Motor.h:163
A PID Motor class using FreeRTOS threads, ESP32Encoder and ESP32PWM.
Definition: Motor.h:43
float cachedSpeed
Definition: Motor.h:70
int64_t getCount()
void loop()
Definition: Motor.cpp:347
static Motor * list[MAX_POSSIBLE_MOTORS]
Definition: Motor.h:200
float BEZIER_P1
BEZIER Control Point 1.
Definition: Motor.h:158
ESP32PWM * pwm
Definition: Motor.h:49
bool isMotorDoneWithMove()
Check to see if the motor is done with a move.
Definition: Motor.cpp:213
static bool timersAllocated
Definition: Motor.h:194
ESP32Encoder * encoder
Definition: Motor.h:53
static void allocateTimer(int PWMgenerationTimer)
Definition: Motor.cpp:115
void setSetpointWithBezierInterpolation(float newTargetInDegrees, long miliseconds, float Control_0=0.5, float Control_1=1.0)
Definition: Motor.h:413
static enum puType useInternalWeakPullResistors
Definition: ESP32Encoder.h:52
float getDegreesPerSecond()
Definition: Motor.cpp:551
void moveFor(float deltaTargetInDegrees, float speedDegPerSec)
Definition: Motor.cpp:269
void onMotorTimer(void *param)
Definition: Motor.cpp:89
int MotorPWMPin
Definition: Motor.h:169
void attachFullQuad(int aPintNumber, int bPinNumber)
void blockUntilMoveIsDone()
wait for the motor to arrive at a setpoint
Definition: Motor.cpp:249
float unitDuration
Definition: Motor.h:136
float myFmapBounded(float x, float in_min, float in_max, float out_min, float out_max)
Definition: Motor.cpp:18
bool isAttached
Definition: Motor.h:57
float getInterpolationUnitIncrement()
Definition: Motor.cpp:32
void writeScaled(float duty)
Definition: ESP32PWM.cpp:159
float startTime
Definition: Motor.h:124
int64_t nowEncoder
Definition: Motor.h:190