When I use that setup and velocity is zero, the motor still wants to position itself “on the cogging point”. Which means there is a significant Iq of ~340mA at standstill, result of a nonzero Vq.
The sign of the current changes when I manually move the motor “over the hump”.
I tried that mode with digital and linear halls, but it doesn’t make a difference.
Can I set Vq to zero, when velocity/motor.target is zero? I would rather like to understand what’s the cause.
I didn’t have time to write a proper response, but I did have code that I know works, so I thought maybe you could adapt it to the velocity case. But now that I think about it, that would only work for pseudo-velocity mode using angle_nocascade and incrementing the angle like velocity_openloop. Regular velocity mode should be satisfied if the rotor isn’t moving.
Since error and prev_error should both be zero, integral just gets set to integral_prev, i.e. it’s probably sitting there non-zero commanding torque, but since the motor is unable to move due to stiction/cogging, nothing ever causes it to change. But it looks like it’s been that way since the beginning, so I don’t know how it could have gone unnoticed for so long, nor do I have enough experience with PID controllers to propose a solution.
Try printing out motor.PID_Velocity.integral_prev to verify this is the problem. Comment out protected: on line 41 of pid.h so all the variables are accessible by outside code.
Setting I=0 did the trick, but now comes the hard part. How to change the I-behaviour?
One way would be to skip integral_prev float integral = I*dt*(error + error_prev);
Adding up (error+error_prev) is already an integration, no?
I will ask “BigBrother”, it usually clears up my mind
“The current PID implementation is mathematically correct. However, a velocity controller has no concept of an idle state. Once the velocity error reaches zero, the integrator preserves whatever torque was accumulated, which can result in unnecessary holding current (e.g. due to cogging torque) even when the application does not require holding torque. This isn’t really a PID issue but an idle-state policy issue. Perhaps SimpleFOC could support configurable idle behavior or integrator retention policies (hold, leak, reset, freeze), leaving the default behavior unchanged for applications that do require holding torque.”
What the integral part does: it keeps memorizing the torque/current it takes to overcome the cogging point, which helps reacting to a new velocity target faster.
If that holding torque isn’t necessary, it’s up to the user to take measures (eg. target == 0 > motor.disable). There were other suggestions, how to implement different motor.idle modes.
leaky integral = slowly forgets the holding torque
reset integral = immediately sets I=0
reduce holding torque after a given idle time (that’s what RRF does with 3D printer steppers)
I can share the whole conversation, if anyone is interested.
We’ll have to defer to @Antun_Skuric on this one. Changing the PID is serious business.
Actually the PID routine stays basically untouched, but a new motor.idle function would allow users to choose the behaviour during standstill. My favourite would be reduced holding torque (in%) after n seconds of idle. Based on a min-velocity threshold or directly triggered by motor.target. float integral = integral_prev*idle_coeff + I*dt*0.5f*(error + error_prev); would be the only change, while idle_coeff is 1.0 by default.
It's better to use float output = proportional + integral*idle_coeff + derivative; to temporarily set a new holding torque without overwriting integral_prev
If I were in angle mode, that would be my solution, but I don't see how it helps in velocity mode?
– o_lampe