@@ -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
514519void 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;
0 commit comments