Your error calculation does not seem to accumulate error when working with the derivative term, and you may want to modify this since only derivative term is able to react to fast changes in the process.
If I got it right your code
// Determine the error delta
dE = abs(last_error - new_error);
last_error = new_error;
will always calculate control term based on the current error, which is the traditional way how the PID was implemented. It is fine since the I term is supposed to take care of accumulated error anyway.
However, I had a customer who came up with the following idea you might want to try out. Since you have a part of the process curve where more aggressive changes are needed, you can let even the the D part error to accumulate:
if(TD) // Calculate D term
{
Last_C += (Error - Last_C) / TD; // D term simulates
Dterm = (Error - Last_C) * KD; // capacitor discharging
}
else
Dterm = 0; // D term is OFF (TD is 0)
There are two interesting things to note here:
The TD value is not the derivative gain (which is KD) but the
derivative time, a user constant which controls the time for error to
accumulate. If it was set to zero, the D part of the PID is disabled
disregarding the KD gain value set.
Note how the current error was used to 'charge' the Last_C value before taking it over to the D part calculation. The
Last_C variable is acting like a capacitor, it would build up while
the error was large, so that your derivative part would act based
also on a recent 'history' of the error, and after that (when error
was smaller) this 'history' will discharge like a capacitor.
Of course, you should limit the total output the way you probably already do (anti windup reset, bumpless auto to manual transfer, and other usual stuff).
I can post more details about other terms of my PID algorithm if you find it useful, but you might want to try this and see what happens. It served my customer well for years.
Best Answer
The main point of a stepper motor is that you get discrete steps. However, the cost is larger size and lower efficiency than a continuous motor of the same torque. Stepper motors also have a low upper speed.
The advantage of discrete steps can outweigh the various disadvantages when the system can be controlled open loop. If you're going to provide feedback and close the loop anyway, then the stepper motors gives you the worst of both worlds. You might as well use a position encoder with feedback, or a motor with position feedback (like some brushless DC with Hall sensors).
Added
As Dmitry pointed out in a comment, a control loop around something that can only be adjusted in discrete steps can very easily lead to oscillation. The system will continually dither between the two steps adjacent to the exact answer if there is any undamped I response. When the discrete steps are mechanical, that can cause higher power drain, wear on the parts, and undesirable user experience.