Skip to content

Commit 839d277

Browse files
committed
a bit more integrated TVP implementation
1 parent f16da1c commit 839d277

4 files changed

Lines changed: 267 additions & 134 deletions

File tree

src/common/base_classes/FOCMotor.cpp

Lines changed: 92 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ FOCMotor::FOCMotor()
2525

2626
// default target value
2727
target = 0;
28-
profile_angle = 0;
29-
profile_velocity = 0;
30-
profile_acceleration = 0;
31-
profile_target = 0;
32-
profile_initialized = false;
28+
profile_state.position = 0;
29+
profile_state.velocity = 0;
30+
profile_state.acceleration = 0;
31+
profile_state.target = 0;
32+
profile_state.initialized = false;
3333
open_loop_velocity = 0;
3434
voltage.d = 0;
3535
voltage.q = 0;
@@ -411,7 +411,6 @@ void FOCMotor::monitor() {
411411
}
412412

413413

414-
415414
// Function (iterative) generating open loop movement for target velocity
416415
// - target_velocity - rad/s
417416
// it uses voltage_limit variable
@@ -423,18 +422,18 @@ float FOCMotor::velocityOpenloop(float target_velocity){
423422
// quick fix for strange cases (micros overflow + timestamp not defined)
424423
if(Ts <= 0 || Ts > 0.5f) Ts = 1e-3f;
425424

426-
float accel = fabsf(acceleration_limit);
427-
if (_isset(accel) && accel > 0.0f) {
428-
float dv_max = accel * Ts;
429-
float dv = target_velocity - open_loop_velocity;
430-
if (dv > dv_max) dv = dv_max;
431-
else if (dv < -dv_max) dv = -dv_max;
425+
// if acceleration limit is set, use it to calculate the target velocity
426+
if (_isset(acceleration_limit) && acceleration_limit > 0.0f) {
427+
// limit the change in velocity to the acceleration limit
428+
float dv_max = acceleration_limit * Ts;
429+
float dv = _constrain(target_velocity - open_loop_velocity, -dv_max, dv_max);
432430
open_loop_velocity += dv;
433431
} else {
434432
open_loop_velocity = target_velocity;
435433
}
436434

437435
// calculate the necessary angle to achieve target velocity
436+
// integrate the open loop velocity to get the new shaft angle
438437
shaft_angle = _normalizeAngle(shaft_angle + open_loop_velocity*Ts);
439438
// for display purposes
440439
shaft_velocity = open_loop_velocity;
@@ -459,22 +458,23 @@ float FOCMotor::angleOpenloop(float target_angle){
459458
// quick fix for strange cases (micros overflow + timestamp not defined)
460459
if(Ts <= 0 || Ts > 0.5f) Ts = 1e-3f;
461460

462-
float accel = fabsf(acceleration_limit);
463-
bool accel_enabled = _isset(accel) && accel > 0.0f;
464-
465461
float err = target_angle - shaft_angle;
466462
float target_velocity = 0.0f;
467-
if (accel_enabled) {
468-
float vel_lim = fabsf(velocity_limit);
469-
float stop_vel = sqrtf(2.0f * accel * fabsf(err));
470-
target_velocity = _sign(err) * _constrain(stop_vel, 0.0f, vel_lim);
471-
472-
float dv_max = accel * Ts;
473-
float dv = target_velocity - open_loop_velocity;
474-
if (dv > dv_max) dv = dv_max;
475-
else if (dv < -dv_max) dv = -dv_max;
463+
// if acceleration limit is set, use it to calculate the target velocity
464+
if (acceleration_limit > 0.0f) {
465+
// calculate the necessary velocity to stop at the target angle with the given acceleration limit
466+
float stop_vel = sqrtf(2.0f * acceleration_limit * fabsf(err));
467+
// limit the target velocity to the velocity limit
468+
target_velocity = _sign(err) * _constrain(stop_vel, 0.0f, velocity_limit);
469+
470+
// limit the change in velocity to the acceleration limit
471+
float dv_max = acceleration_limit * Ts;
472+
float dv = _constrain(target_velocity - open_loop_velocity, -dv_max, dv_max);
473+
474+
// update the open loop velocity
476475
open_loop_velocity += dv;
477476

477+
// integrate the open loop velocity to get the new shaft angle
478478
float step = open_loop_velocity * Ts;
479479
if ((fabsf(err) <= fabsf(step)) || (fabsf(err) < 1e-6f && fabsf(open_loop_velocity) <= dv_max)) {
480480
shaft_angle = target_angle;
@@ -484,20 +484,20 @@ float FOCMotor::angleOpenloop(float target_angle){
484484
}
485485
shaft_velocity = open_loop_velocity;
486486
} else {
487-
488-
// calculate the necessary angle to move from current position towards target angle
489-
// with maximal velocity (velocity_limit)
490-
// TODO sensor precision: this calculation is not numerically precise. The angle can grow to the point
491-
// where small position changes are no longer captured by the precision of floats
492-
// when the total position is large.
493-
if(abs( target_angle - shaft_angle ) > abs(velocity_limit*Ts)){
494-
shaft_angle += _sign(target_angle - shaft_angle) * abs( velocity_limit )*Ts;
495-
shaft_velocity = velocity_limit;
496-
}else{
497-
shaft_angle = target_angle;
498-
shaft_velocity = 0;
499-
}
500-
open_loop_velocity = shaft_velocity;
487+
// if no acceleration limit is set, use the velocity limit to calculate the target velocity
488+
// calculate the necessary angle to move from current position towards target angle
489+
// with maximal velocity (velocity_limit)
490+
// TODO sensor precision: this calculation is not numerically precise. The angle can grow to the point
491+
// where small position changes are no longer captured by the precision of floats
492+
// when the total position is large.
493+
if(abs( target_angle - shaft_angle ) > abs(velocity_limit*Ts)){
494+
shaft_angle += _sign(target_angle - shaft_angle) * abs( velocity_limit )*Ts;
495+
shaft_velocity = velocity_limit;
496+
}else{
497+
shaft_angle = target_angle;
498+
shaft_velocity = 0;
499+
}
500+
open_loop_velocity = shaft_velocity;
501501
}
502502

503503
// save timestamp for next call
@@ -510,6 +510,11 @@ float FOCMotor::angleOpenloop(float target_angle){
510510
return current_limit;
511511
}
512512

513+
// Update acceleration limit value in controllers when changed
514+
void FOCMotor::updateAccelerationLimit(float new_acceleration_limit) {
515+
acceleration_limit = new_acceleration_limit;
516+
}
517+
513518
// Update limit values in controllers when changed
514519
void FOCMotor::updateVelocityLimit(float new_velocity_limit) {
515520
velocity_limit = new_velocity_limit;
@@ -597,15 +602,12 @@ void FOCMotor::updateMotionControlType(MotionControlType new_motion_controller)
597602
}
598603

599604
if (new_motion_controller == MotionControlType::angle_profile) {
600-
profile_angle = shaft_angle;
601-
profile_velocity = 0.0f;
602-
profile_acceleration = 0.0f;
603-
profile_target = target;
604-
profile_initialized = false;
605+
trajectoryResetTrapezoidal(profile_state, shaft_angle, 0.0f, target);
606+
profile_state.initialized = false;
605607
} else if (controller == MotionControlType::angle_profile) {
606-
profile_velocity = 0.0f;
607-
profile_acceleration = 0.0f;
608-
profile_initialized = false;
608+
profile_state.velocity = 0.0f;
609+
profile_state.acceleration = 0.0f;
610+
profile_state.initialized = false;
609611
}
610612

611613
if (new_motion_controller == MotionControlType::angle_openloop || new_motion_controller == MotionControlType::velocity_openloop) {
@@ -803,109 +805,70 @@ void FOCMotor::move(float new_target) {
803805
// and if
804806
if(!enabled) return;
805807

806-
807808
// upgrade the current based voltage limit
808809
switch (controller) {
809-
case MotionControlType::torque:
810-
current_sp = target;
811-
break;
812-
case MotionControlType::angle_nocascade:
810+
case MotionControlType::torque: {
811+
current_sp = target;
812+
break;
813+
}
814+
case MotionControlType::angle_nocascade: {
815+
// calculate the torque command - sensor precision: this calculation is ok, but based on bad value from previous calculation
816+
current_sp = P_angle(shaft_angle_sp - LPF_angle(shaft_angle));
817+
break;
818+
}
819+
case MotionControlType::angle_profile: {
813820
// TODO sensor precision: this calculation is not numerically precise. The target value cannot express precise positions when
814821
// the angles are large. This results in not being able to command small changes at high position values.
815822
// to solve this, the delta-angle has to be calculated in a numerically precise way.
816823
// angle set point
817824
shaft_angle_sp = target;
825+
// save the velocity feed forward value
826+
float velocity_ff = feed_forward_velocity;
827+
828+
// filter the measured angle to reduce noise and improve stability
829+
float measured_angle = LPF_angle(shaft_angle);
830+
831+
// if acceleration limit is set, use trapezoidal profile to
832+
// generate the angle and velocity set points
833+
if (acceleration_limit > 0.0f) {
834+
// calculate the time step in seconds
835+
float dt = move_time_us * 1e-6f;
836+
if (dt <= 0.0f || dt > 0.5f) dt = 1e-3f;
837+
838+
TrapezoidalProfileOutput prof = trajectoryStepTrapezoidal(profile_state,
839+
measured_angle,
840+
shaft_velocity,
841+
dt,
842+
target,
843+
velocity_limit,
844+
acceleration_limit);
845+
846+
// update the angle set point with the value from the trapezoidal profile
847+
shaft_angle_sp = prof.position;
848+
// update the feed forward velocity with the value from the trapezoidal profile
849+
velocity_ff += prof.velocity;
850+
}
851+
852+
// calculate velocity set point
853+
shaft_velocity_sp = velocity_ff + P_angle( shaft_angle_sp - measured_angle );
854+
shaft_velocity_sp = _constrain(shaft_velocity_sp, -velocity_limit, velocity_limit);
818855
// calculate the torque command - sensor precision: this calculation is ok, but based on bad value from previous calculation
819-
current_sp = P_angle(shaft_angle_sp - LPF_angle(shaft_angle));
856+
current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity);
820857
break;
821-
case MotionControlType::angle:
858+
}
859+
case MotionControlType::angle:
822860
// TODO sensor precision: this calculation is not numerically precise. The target value cannot express precise positions when
823861
// the angles are large. This results in not being able to command small changes at high position values.
824862
// to solve this, the delta-angle has to be calculated in a numerically precise way.
825863
// angle set point
826864
shaft_angle_sp = target;
865+
827866
// calculate velocity set point
828867
shaft_velocity_sp = feed_forward_velocity + P_angle( shaft_angle_sp - LPF_angle(shaft_angle) );
829868
shaft_velocity_sp = _constrain(shaft_velocity_sp, -velocity_limit, velocity_limit);
830869
// calculate the torque command - sensor precision: this calculation is ok, but based on bad value from previous calculation
831870
current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity);
832871
break;
833-
case MotionControlType::angle_profile: {
834-
float dt = move_time_us * 1e-6f;
835-
if (dt <= 0.0f || dt > 0.5f) dt = 1e-3f;
836-
837-
float accel = fabsf(acceleration_limit);
838-
if (!_isset(accel) || accel <= 0.0f) {
839-
// Fall back to standard angle mode if acceleration limit is disabled.
840-
shaft_angle_sp = target;
841-
shaft_velocity_sp = feed_forward_velocity + P_angle(shaft_angle_sp - LPF_angle(shaft_angle));
842-
shaft_velocity_sp = _constrain(shaft_velocity_sp, -velocity_limit, velocity_limit);
843-
current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity);
844-
break;
845-
}
846-
847-
float measured_angle = LPF_angle(shaft_angle);
848-
float vel_lim = fabsf(velocity_limit);
849-
if (!profile_initialized) {
850-
profile_angle = measured_angle;
851-
profile_velocity = shaft_velocity;
852-
profile_acceleration = 0.0f;
853-
profile_target = target;
854-
profile_initialized = true;
855-
}
856-
profile_target = target;
857-
858-
float err_profile = profile_target - profile_angle;
859-
float dir = _sign(err_profile);
860-
float desired_accel = 0.0f;
861-
float distance_remaining = fabsf(err_profile);
862-
float speed_along_path = profile_velocity * dir;
863-
float speed_mag = fabsf(profile_velocity);
864-
float stop_dist = (speed_mag * speed_mag) / (2.0f * accel);
865-
866-
if (distance_remaining < 1e-6f && speed_mag < (accel * dt)) {
867-
profile_angle = profile_target;
868-
profile_velocity = 0.0f;
869-
desired_accel = 0.0f;
870-
} else {
871-
if (speed_along_path < 0.0f) {
872-
// Recover from reverse motion first, then follow trapezoid phases.
873-
desired_accel = dir * accel;
874-
} else if (stop_dist >= distance_remaining) {
875-
// Deceleration phase.
876-
desired_accel = -dir * accel;
877-
} else if (speed_mag < vel_lim) {
878-
// Acceleration phase.
879-
desired_accel = dir * accel;
880-
} else {
881-
// Cruise phase.
882-
desired_accel = 0.0f;
883-
}
884-
885-
profile_velocity += desired_accel * dt;
886-
profile_velocity = _constrain(profile_velocity, -vel_lim, vel_lim);
887-
888-
float step = profile_velocity * dt;
889-
if (fabsf(err_profile) <= fabsf(step)) {
890-
profile_angle = profile_target;
891-
profile_velocity = 0.0f;
892-
desired_accel = 0.0f;
893-
} else {
894-
profile_angle += step;
895-
}
896-
}
897-
898-
profile_acceleration = desired_accel;
899-
shaft_angle_sp = profile_angle;
900-
901-
// Tracking law: trajectory feed-forward plus position feedback correction.
902-
float tracking_error = profile_angle - measured_angle;
903-
float velocity_ff = profile_velocity + (0.5f * profile_acceleration * dt) + feed_forward_velocity;
904-
float velocity_fb = P_angle(tracking_error);
905-
shaft_velocity_sp = _constrain(velocity_ff + velocity_fb, -vel_lim, vel_lim);
906-
current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity);
907-
break;
908-
}
909872
case MotionControlType::velocity:
910873
// velocity set point - sensor precision: this calculation is numerically precise.
911874
shaft_velocity_sp = target;

src/common/base_classes/FOCMotor.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "../defaults.h"
1111
#include "../pid.h"
1212
#include "../lowpass_filter.h"
13+
#include "../trajectory.h"
1314

1415
#define MOT_ERR "ERR-MOT:"
1516
#define MOT_WARN "WARN-MOT:"
@@ -356,6 +357,13 @@ class FOCMotor
356357
*/
357358
void updateVoltageLimit(float new_voltage_limit);
358359

360+
/**
361+
* Update acceleration limit value in controllers when changed
362+
* @param new_acceleration_limit - new acceleration limit value
363+
*
364+
*/
365+
void updateAccelerationLimit(float new_acceleration_limit);
366+
359367
/**
360368
* Update torque control type and related controller limit values
361369
* @param new_torque_controller - new torque control type
@@ -445,11 +453,7 @@ class FOCMotor
445453
float open_loop_velocity = 0.0f;
446454

447455
// angle profile state
448-
float profile_angle = 0.0f;
449-
float profile_velocity = 0.0f;
450-
float profile_acceleration = 0.0f;
451-
float profile_target = 0.0f;
452-
bool profile_initialized = false;
456+
TrapezoidalProfileState profile_state;
453457

454458
// function pointer for custom control method
455459
float (*customMotionControlCallback)(FOCMotor* motor) = nullptr;

0 commit comments

Comments
 (0)