I'm impressed that your bot holds position without explicit feedback which further confirms the robustness of your solution. I'd assumed from the outset that position feedback would be essential so mine has optical encoders. It uses DC motors so speed and drive voltage are linearly related - my encoders would seem to be redundant. I'll base my solution as closely as possible on yours and see how that progresses.
One more question about the algorithm. Why do you convert to integers in the output of stability() and the input to speedcontrol()?
In desperation I did consider a different approach to control. The starting point would be a control system which would drive the robot to a specific position. With the bot stationary you read the filtered angle and, from the gyro, its first derivative. From those calculate the position to which the base would have to be moved so that, at a future time, the angle and its first derivative would reach zero. Then move to that position and await the outcome. This could be repeated whenever the angle error exceeded a threshold; I've not heard of anyone using this approach. It would lead to rather jerky motion and I much prefer a linear solution but I remain curious as to whether it could be made to work.
I'll let you know how I get on with adapting your code
