# Question PID rotation not work because of 0 stops??

Discussion in 'Physics' started by tsmspace, Sep 3, 2023.

1. ### tsmspace

Joined:
Oct 6, 2019
Posts:
46
super newb asks questions:

I was thinking about trying to figure out how to make a PID controller for my spaceship rotation stabilization.
presently I do not have any rotation stabilization as friction falls outside of the premise of my game.

However, a proper PID rotation stabilizer would be perfectly within!! (it would be terribly hard to use in certain circumstances, for example while fixed-jointed to a weight)

Upon my first attempt, and then some more searches,, I found that rotation PID in unity is largely undesirable as it is quite a bit more work than translation stabilization given that there is "extra math" involved.

My question is,, is this because of quaternions, or is it because rotating past 0 and 360 doesn't work? a combination?? I know that when I make a simple camera script so that player input can aim the camera,, the camera bonks into 0 points on at least one axis, and the player then needs to spin the camera to go back the other way or something like that. ((this one too, I would like to eliminate. I suppose I would need to make the camera go from 0 - 360 and from 360 - 0 with an if statement? ))

but elsewhere I saw that one problem is that the rotation is not stored in degrees, it's stored in what,, radians? Then the other issue gets to be that rotation is a local factor, while it is also a global factor, and this can be an issue,, but I think with mine it is possible to simply have the one ship be within a single local gameobject, so perhaps not my issue.

bear in mind, it's not as though my first attempt worked even a little. I did get SOMETHING happening, but I never got it to go the right direction. It seemed whether I used -pid or just pid,, it always accelerated away from my setpoints rotation, rather than towards it , and then I definitely encounted the zero-point behavior of the rotation because sometimes it would actually bounce off of it. and just oscillate in that bounce.

I also saw some posters that did appear to be quite knowledgeable who still commented later that they had to stop using the physics engine and instead switch to setting the transform for rotation stabilization,, which DID fundamentally change the rotation behavior. This is unfortunately out of the question with no exceptions. If i can't use the physics engine basically,, I will not pursue it at all. (I won't really use it anyway,, it's just that I think it would be interesting to think about,, I only play full manual thrust rotation. )

PIDs are probably not that easy without the unity specific issues,, but I can find lots of information about that,, I'm hoping with this question to understand which unity specific design issues I need to address to make it so that it just works as in the translation stabilization where although I did not try I am to understand in forum posts does work normally.

2. ### tjmaul

Joined:
Aug 29, 2018
Posts:
464
If you're in for a simple, cheap, but paid solution, use this https://assetstore.unity.com/packages/tools/physics/quaternion-pid-controller-40096

I'm not affiliated with the asset.

If you'd like to do it youself, it's not actually that hard. There are many ways to implement this, and this would be my approach:

Create a GameObject somewhere just for storing the target rotation of you spaceship. This GameObject obviously must not be a child of your spaceship. The player input for the ships rotation modifies said transform using
``transform.Rotate(Vector3 euler, Space.Self)``
.

Now that we have a target rotation and an actual rotation, we just need to align these two. We'll do that for the forward axis (roll) and then do the same again for one other axis (either pitch or yaw, doesn't matter).

Code (CSharp):
1. var targetAxis = proxyTransform.forward;
2. var currentAxis = spaceship.transform.forward;
3.
4. var pRotation = Vector3.Cross(targetAxis, currentAxis);
5.
6. // Project that rotation on the forward axis, to only affect current axis
7. var pRotationProjected = Vector3.Project(pRotation, proxyTransform.forward);
8.
9. // Also project the current angular velocity onto forward axis to know the current angular velocity around said axis
10. var dAngularVelocityProjected = Vector3.Project(spaceship.angularVelocity, proxyTransform.forward);
11.
12. // Now use the angle error and angular velocity to calculate torque
13. var torque = K_p * pRotationProjected - K_d * dAngularVelocityProjected;
14.
15. spaceship.AddTorque(torque);
I didn't test this, so maybe you have to swap the cross products arguments. K_p and K_d should be positive numbers. Set K_d = 0 first and set K_p so that the spaceship keeps oscillating around the set point with a period of roughly 1 second. Then slowly increase K_d until your happy with the damping.

In your case, there is no need for the "I" part of a PID controller, so that's just a PD-controller.

Do the same for the "up" (yaw) or "right" (pitch) axis and you're done. Btw. you can also use the magnitude of the torque vector as an input for a visual representation of thrusters.

3. ### tsmspace

Joined:
Oct 6, 2019
Posts:
46
thanks for the tip on the asset, I will try it. I am at least interested in having the project to go over, and at best it may even completely answer to my needs.

I will also try to understand your code but like I will need to with the asset, I don't even know all of the methods you're using so I need to sort of come back later.

thank you very much!! ,,, My controller,, btw,, does need to work in all 3 axis,, and I technically still have not figured out what to know about the "bouncing off the zero point" should mean to me,, it defied the physics of the ship as for me the thrust is not enough to produce that kind of bounce. I read a lot about how euler angles don't work well for this sort of thing as the returned euler angle from a quaternion conversion in unity is not always the same "side of zero" basically.

edit:: I am looking at the asset,, it looks like it's mostly entirely the system of interacting with the quaternions in unity, which is good as this is part of what was going to be beyond me. Then,, it just does ultimately addtorque. Thanks again,, this definitely will help! not a bad price for such a good lesson and starter kit.

Last edited: Sep 4, 2023
tjmaul likes this.
4. ### tsmspace

Joined:
Oct 6, 2019
Posts:
46
may I ask why this is for two axis?? why not 3 or I can do it with 3?

5. ### tsmspace

Joined:
Oct 6, 2019
Posts:
46
Ok,, further conversation. Just because I'm thinking it through. So,, now what I need to do,, is rather than drive the PID controller based on a setpoint, I need the controller to read the rate of rotation, determine how to achieve zero rotation , and then achieve zero rotation at whatever "setpoint" that would result in. Then,, while the player is providing input, the controller would be off (for that given axis), and allow the player to accelerate at any value they choose, and then when the input returns to deadzone basically,, the controller would take over, and eliminate the rotation ,, and rather than achieving a setpoint, it would merely achieve a zero magnitude rotation.

6. ### tsmspace

Joined:
Oct 6, 2019
Posts:
46

Ok,, I did buy that asset, but now am on something else first. --- Here's what I'm doing. (all of this is based off another post about setting local angular velocity) From here I think a simple PD or PID would be much easier to impliment, because at this point I don't have to interact anymore with euler angles or anything of the like. It seems to be working fine at present (a little weak, but now I can do the whole learn about PIDs thing)

Code (CSharp):
1. if (Rigidbody.angularVelocity.magnitude > 0)
2. {
3.     Vector3 localAngularVelocity = drone.transform.InverseTransformDirection(Rigidbody.angularVelocity);
4.     if (inputsZero)
5.     {
6.         if (localAngularVelocity.x > 0)
7.         {
8.             if (localAngularVelocity.x > 1)
9.             {
10.                 torqueX = -Torque;
11.             }
12.             else
13.             {
14.                 torqueX = -Torque * localAngularVelocity.x;
15.             }
16.         }
edit ::: actually I'm just going with this for now.
Code (CSharp):
1. if (Rigidbody.angularVelocity.magnitude > 0)
2. {
3.     Vector3 localAngularVelocity = drone.transform.InverseTransformDirection(Rigidbody.angularVelocity);
4.     if (inputsZero)
5.     {
6.         if (localAngularVelocity.x > 0)
7.         {
8.             if (localAngularVelocity.x > .1)
9.             {
10.                 torqueX = -Torque;
11.             }
12.             else
13.             {
14.                 torqueX = -Torque *10* localAngularVelocity.x;
15.             }
16.         }
17.         else if (localAngularVelocity.x < 0)
18.         {
19.             if (localAngularVelocity.x < -.1)
20.             {
21.                 torqueX = Torque;
22.             }
23.             else
24.             {
25.                 torqueX = Torque * 10 * -localAngularVelocity.x;
26.             }
27.         }
28. ///these are the thruster effect
29. if (rotationAssist && inputsZero)
30. {
31.     SetRCSAxisPower(RCSPitchDown, RCSPitchUp, torqueX);
32.     SetRCSAxisPower(RCSYawLeft, RCSYawRight, torqueY);
33.     SetRCSAxisPower(RCSRollLeft, RCSRollRight, torqueZ);
34. }
35. else
36. {
37.     SetRCSAxisPower(RCSPitchDown, RCSPitchUp, rotationInput.x);
38. SetRCSAxisPower(RCSYawLeft, RCSYawRight, rotationInput.y);
39. SetRCSAxisPower(RCSRollLeft, RCSRollRight, rotationInput.z);
40.
41. }
42.
43.

Last edited: Sep 5, 2023
7. ### tjmaul

Joined:
Aug 29, 2018
Posts:
464
In 3D Space, when you align two axes, the third axis is automatically aligned.

Concerning your other posts: sorry, I found them confusing

Please describe what you’re trying to achieve. I was under the impression that you would like to have use a PID controller to reach a certain rotation because you were mentioning actual angles.

Do you instead want a controller that works like other spaceship games where when you press “up”, the nose of the ship starts going “down” and when you press right or left, the ship rolls clockwise/counterclockwise and when you let go of the buttons, it should stop rotating?

if so, it’s kind of just the D-portion of the controller and instead of setting a desired rotation, you set the desired angular velocity, but before, again, please try to concisely describe the desired behavior as short as possible.

8. ### tsmspace

Joined:
Oct 6, 2019
Posts:
46
yes that is one feature I want of the controller, and the first feature. ,, no I don't understand how the third axis will automatically align,, it takes 3 axis to define attitude and they are fully independent of each other,, If I pitch and yaw then nothing will cause roll unless I then roll.

Again the first feature I want is to stabilize rotation (normally handled just by allowing drag, I think) ,, but I do have interest in pursuing other stabilization features, so I do want to sort of learn it all.

9. ### tsmspace

Joined:
Oct 6, 2019
Posts:
46

no I think it's pretty much how euler angles work. For example, if i use euler angles to point a camera, by having joystick input gradually pan the camera,, it can move horizontally 360 degrees no problem (like turning your head all the way around) ,, but it automatically stops the camera at straight up and straight down,, and in fact does sort of "bounce" right there. I found it quite strange, though, when my drone which was thrusting to align to my "setpoint" object would bounce off it's "exactly front" point. (starting point facing 0,0,0 ,, if it rotated away from that, and then back to it, it would bounce like it hit something)

10. ### tjmaul

Joined:
Aug 29, 2018
Posts:
464
You’re right, you need 3 numbers to define the rotation of an object. I failed to explain more clearly what I meant:

if you click an object in the editor, and you’re in local mode, it shows the red green and blue axes. If you have another object oriented totally different, you can make the two objects rotations match by first aligning any of the axes and then rotate around said axis to match any other axis. That's what I meant when I wrote that you only need two axes.

Anyhow, I created an example controller to show what I meant and it's working by only aligning two axes. I hope that's not too counter-intuitive.

To test, download the unitypackage, create a new 3d project (built-in renderer), import the package, open the scene and press play. The keys are:
• A, D: Roll
• W, S: Pitch
• Q, E: Yaw
You'll see two spaceships. The left one is without a rigidbody and controlled by just updating the rotation based on user input (PlayerInput.cs). The other one follows the first ones' rotation by applying the algorithm I proposed (actually, the code above will not work, I projected on the wrong axis, but the idea is the same).

See "FollowController.cs" to find out how it actually works. I think the only odd thing is the cross product (Vector3.Cross). If you don't know exactly what it does, please read elsewhere about it. It's one of the three most beloved functions of linear algebra.

Note that nowhere in the feedback controller code there is anything about actual angles. It's all vectors and that's how it should be. No exceptions, no branches, no headaches.

If the controller feels too sluggish, you can increase both the P and D value by the same factor, just play with it to see what they do. If it still feels too sluggish, we can also implement something called feed-forward to give the controller some extra information about the rotation speed that you'd like to achieve and make it even more snappy.

File size:
1.5 MB
Views:
14