@@ -600,7 +600,8 @@ void FOCMotor::updateMotionControlType(MotionControlType new_motion_controller)
600600 }
601601
602602 if (new_motion_controller == MotionControlType::angle_profile) {
603- trajectoryResetTrapezoidal (profile_state, shaft_angle, 0 .0f , target);
603+ profile_state.position = shaft_angle;
604+ profile_state.velocity = 0 .0f ;
604605 profile_state.initialized = false ;
605606 } else if (controller == MotionControlType::angle_profile) {
606607 profile_state.velocity = 0 .0f ;
@@ -825,25 +826,66 @@ void FOCMotor::move(float new_target) {
825826 // filter the measured angle to reduce noise and improve stability
826827 float measured_angle = LPF_angle (shaft_angle);
827828
828- // if acceleration limit is set, use trapezoidal profile to
829- // generate the angle and velocity set points
829+ // if acceleration limit is set, use inline trapezoidal profile (bang-bang control)
830830 if (acceleration_limit > 0 .0f ) {
831831 // calculate the time step in seconds
832832 float dt = move_time_us * 1e-6f ;
833833 if (dt <= 0 .0f || dt > 0 .5f ) dt = 1e-3f ;
834834
835- TrapezoidalProfileOutput prof = trajectoryStepTrapezoidal (profile_state,
836- measured_angle,
837- shaft_velocity,
838- dt,
839- target,
840- velocity_limit,
841- acceleration_limit);
842-
843- // update the angle set point with the value from the trapezoidal profile
844- shaft_angle_sp = prof.position ;
845- // update the feed forward velocity with the value from the trapezoidal profile
846- velocity_ff += prof.velocity ;
835+ // Initialize profile on first call
836+ if (!profile_state.initialized ) {
837+ profile_state.position = measured_angle;
838+ profile_state.velocity = shaft_velocity;
839+ profile_state.initialized = true ;
840+ }
841+
842+ // Bang-bang trapezoidal velocity profile
843+ float direction = _sign (target - profile_state.position );
844+ float distance_remaining = fabsf (target - profile_state.position );
845+ float speed_magnitude = fabsf (profile_state.velocity );
846+
847+ // IMPORTANT
848+ // for given velocity v and acceleration a, the time to stop t = v/a - (following from v = a.t )
849+ // and the distance traveled during that time is d = v.t - 0.5.a.t^2 = v^2 / (2.a)
850+ float stopping_distance = (speed_magnitude * speed_magnitude) / (2 .0f * acceleration_limit);
851+
852+ // Determine commanded acceleration (bang-bang)
853+ float commanded_accel = 0 .0f ;
854+ if (profile_state.velocity * direction < 0 .0f ) {
855+ // Moving wrong direction: accelerate toward target
856+ commanded_accel = direction * acceleration_limit;
857+ } else if (stopping_distance >= distance_remaining) {
858+ // Close enough to stop: decelerate
859+ commanded_accel = -direction * acceleration_limit;
860+ } else if (speed_magnitude < velocity_limit) {
861+ // Below speed limit and far from target: accelerate
862+ commanded_accel = direction * acceleration_limit;
863+ } else {
864+ // At speed limit: maintain
865+ commanded_accel = 0 .0f ;
866+ }
867+
868+ // Integrate: new velocity
869+ profile_state.velocity += commanded_accel * dt;
870+ profile_state.velocity = _constrain (profile_state.velocity , -velocity_limit, velocity_limit);
871+
872+ // Integrate: new position
873+ float position_step = profile_state.velocity * dt;
874+ if (distance_remaining < 1e-6f && speed_magnitude < (acceleration_limit * dt)) {
875+ // Nearly at target and slow: snap to target
876+ profile_state.position = target;
877+ profile_state.velocity = 0 .0f ;
878+ } else if (distance_remaining <= fabsf (position_step)) {
879+ // Final step would overshoot: clamp
880+ profile_state.position = target;
881+ profile_state.velocity = 0 .0f ;
882+ } else {
883+ profile_state.position += position_step;
884+ }
885+
886+ // Update setpoint with profiled values
887+ shaft_angle_sp = profile_state.position ;
888+ velocity_ff += profile_state.velocity ;
847889 }
848890
849891 // calculate velocity set point
0 commit comments