In the Angle control block diagram we can see that angle control feeds its error into the velocity controller which then generates the voltages to the motor.
The use of the velocity PID causes the motor to track the angle PID like a mass connected via a spring-dampener system. The angle PID then tracks the desired position much the same. This coupled dynamic is prone to oscillate and adds additional complexity to the tuning process. I am wondering why there is a need for this double-stacked approach.
A simpler approach would be to have the angle PID directly set the desired current by changing BLDCMotor.cpp like so:
case MotionControlType::angle:
// TODO sensor precision: this calculation is not numerically precise. The target value cannot express precise positions when
// the angles are large. This results in not being able to command small changes at high position values.
// to solve this, the delta-angle has to be calculated in a numerically precise way.
// angle set point
shaft_angle_sp = target;
/*
// calculate velocity set point
shaft_velocity_sp = feed_forward_velocity + P_angle( shaft_angle_sp - shaft_angle );
shaft_velocity_sp = _constrain(shaft_velocity_sp,-velocity_limit, velocity_limit);
// calculate the torque command - sensor precision: this calculation is ok, but based on bad value from previous calculation
current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity); // if voltage torque control
*/
current_sp = P_angle( shaft_angle_sp - shaft_angle );
current_sp = _constrain(current_sp,-velocity_limit, velocity_limit);
// if torque controlled through voltage
if(torque_controller == TorqueControlType::voltage){
// use voltage if phase-resistance not provided
if(!_isset(phase_resistance)) voltage.q = current_sp;
else voltage.q = _constrain( current_sp*phase_resistance + voltage_bemf , -voltage_limit, voltage_limit);
// set d-component (lag compensation if known inductance)
if(!_isset(phase_inductance)) voltage.d = 0;
else voltage.d = _constrain( -current_sp*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);
}
break;
This reduces the complexity of the controller and, at least to me, is more in line with its name. I have made the above modification to my code and it has removed the coupled dynamics that’s been giving me grief and is tracking the desired motor position just fine.
I am curious what problems the nested PID control approach addresses that a direct angle PID would be missing?