I just dealt with this problem myself. No matter what, I couldn't get the vertical joint of my arm to reach its target. I'm using effort controllers, but the circumstances are probably the same for velocity controllers. Here is how I solved it: (I'm also giving some values, applicable to my case, for reference)
- I increased the joint effort by a lot (4x), as well as the P gain of the PID (on the 10s of thousands)
- The above makes the joint movements very aggressive. To combat this, I added damping in the joint (500) and I maxed out the D gain of the PID (1000)
- Then there was drifting in the joint. For this, I added friction in the joint (20)
- The last part is the steady-state error. For this, you need to work with the I components of the PID. Too little, or too much, keeps the joint away from the target. You need to find the right values ({i:150, i_clamp_min: -150, i_clamp_max: 150, antiwindup: false})
With all of the above, I was able to control my joint with submillimeter precision.
Keep in mind that this calibration of the PID is dependent on the state of the robot. By that, I mean that if your robot carries a (heavy) object, the steady state of the joint will change. If the error is too much, then you'll have to recalibrate the PID for that case. In my simulation, I can detect when objects are in the gripper. I plan to use this, to dynamically recalibrate the PID for the current state (should it become problematic). The PID is a reconfigure server, so you can use a client to update the values.
Btw depending on what you intend to do in your simulation, you might want to consider position controllers. They are not affected by dynamics, so you won't have this problem. But then again, there are other problems with those.