PD loop tuning

wingnut

Donator
Donator
Joined
May 10, 2013
Messages
129
Reaction score
0
Points
16
Hello Forum,

For practice I made a PD loop which is supposed to control the acceleration of the spacecraft. My problem with the loop is, that the throttle stutters back and forth between no thrust and full thrust.

I'm not sure what the problem is, maybe someone can give a hint?

PD_loop.lua (put into 'Scripts' folder)
PHP:
function PD_loop()
	local v = vessel.get_interface('GL-01')
	local hShip = oapi.get_objhandle('GL-01')
	local htg = v:get_thrustergrouphandle(THGROUP.MAIN)
	local noteTop = oapi.create_annotation()	
	local e0 = 0
	local setPoint = 9.8
	local kP = 0.01
	local kD = 0.05
	while true do
		local dt = oapi.get_simstep()
		local m = v:get_mass() --kg
		local F = 0 -- N
		local lvl = v:get_thrustergrouplevel(htg)
		local count = (v:get_groupthrustercount(htg)) - 1
		for i = 0,count do
			local ht = v:get_groupthruster(htg,i)
			max0 = v:get_thrustermax0(ht)
			F = F + max0 * lvl
		end
		local a = F / m		
		local e = setPoint - a
		local d = (e - e0) / dt
		local output = kP * e + kD * d
		local thrust = lvl + output
		v:set_thrustergrouplevel(THGROUP.MAIN,thrust)
		e0 = e
		intro = "m = " .. m .. "\nF = " .. F .. "\na = " .. a .. "\nlevel: " .. lvl .. "\noutput = " .. output .. "\nthrust = " .. thrust
		noteTop:set_text (intro)
		proc.skip()
	end
end

PD_loop()

Test scenario
Code:
BEGIN_DESC

END_DESC

BEGIN_ENVIRONMENT
  System Sol
  Date MJD 51982.5352432498
  Script PD_loop
END_ENVIRONMENT

BEGIN_FOCUS
  Ship GL-01
END_FOCUS

BEGIN_CAMERA
  TARGET GL-01
  MODE Cockpit
  FOV 50.00
END_CAMERA

BEGIN_HUD
  TYPE Docking
  NAV 0
END_HUD

BEGIN_MFD Left
  TYPE Surface
  SPDMODE 1
END_MFD

BEGIN_MFD Right
  TYPE User
  MODE Terminal MFD
END_MFD

BEGIN_PANEL
END_PANEL

BEGIN_SHIPS
GL-01:DeltaGlider
  STATUS Orbiting Earth
  RPOS 6166867.18 1869615.93 -1439405.57
  RVEL 2970.333 -5745.672 4440.711
  AROT -83.35 -68.89 -74.50
  AFCMODE 7
  PRPLEVEL 0:0.552918 1:0.877893
  NAVFREQ 0 0 0 0
  XPDR 0
  AAP 0:0 0:0 0:0
END
probe:ShuttleA
  STATUS Orbiting Earth
  RPOS 6166906.88 1869609.42 -1439407.18
  RVEL 2970.534 -5745.704 4440.703
  AROT 78.25 79.88 -123.30
  AFCMODE 7
  PRPLEVEL 0:1.000000 1:1.000000
  NAVFREQ 0 0
  XPDR 0
  PODANGLE 0.0000 0.0000
  DOCKSTATE 0 0.0000
  AIRLOCK 0 0.0000
  GEAR 0 0.0000
  PAYLOAD MASS 0.0 0
END
END_SHIPS

BEGIN_ExtMFD
END
 
Last edited:

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
Hello Forum,

For practice I made a PD loop which is supposed to control the acceleration of the spacecraft. My problem with the loop is, that the throttle stutters back and forth between no thrust and full thrust.

I'm not sure what the problem is, maybe someone can give a hint?

PD_loop.lua (put into 'Scripts' folder)
PHP:
function PD_loop()
	local v = vessel.get_interface('GL-01')
	local hShip = oapi.get_objhandle('GL-01')
	local htg = v:get_thrustergrouphandle(THGROUP.MAIN)
	local noteTop = oapi.create_annotation()	
	local e0 = 0
	local setPoint = 9.8
	local kP = 0.01
	local kD = 0.05
	while true do
		local dt = oapi.get_simstep()
		local m = v:get_mass() --kg
		local F = 0 -- N
		local lvl = v:get_thrustergrouplevel(htg)
		local count = (v:get_groupthrustercount(htg)) - 1
		for i = 0,count do
			local ht = v:get_groupthruster(htg,i)
			max0 = v:get_thrustermax0(ht)
			F = F + max0 * lvl
		end
		local a = F / m		
		local e = setPoint - a
		local d = (e - e0) / dt
		local output = kP * e + kD * d
		local thrust = lvl + output
		v:set_thrustergrouplevel(THGROUP.MAIN,thrust)
		e0 = e
		intro = "m = " .. m .. "\nF = " .. F .. "\na = " .. a .. "\nlevel: " .. lvl .. "\noutput = " .. output .. "\nthrust = " .. thrust
		noteTop:set_text (intro)
		proc.skip()
	end
end

PD_loop()

Test scenario
Code:
BEGIN_DESC

END_DESC

BEGIN_ENVIRONMENT
  System Sol
  Date MJD 51982.5352432498
  Script PD_loop
END_ENVIRONMENT

BEGIN_FOCUS
  Ship GL-01
END_FOCUS

BEGIN_CAMERA
  TARGET GL-01
  MODE Cockpit
  FOV 50.00
END_CAMERA

BEGIN_HUD
  TYPE Docking
  NAV 0
END_HUD

BEGIN_MFD Left
  TYPE Surface
  SPDMODE 1
END_MFD

BEGIN_MFD Right
  TYPE User
  MODE Terminal MFD
END_MFD

BEGIN_PANEL
END_PANEL

BEGIN_SHIPS
GL-01:DeltaGlider
  STATUS Orbiting Earth
  RPOS 6166867.18 1869615.93 -1439405.57
  RVEL 2970.333 -5745.672 4440.711
  AROT -83.35 -68.89 -74.50
  AFCMODE 7
  PRPLEVEL 0:0.552918 1:0.877893
  NAVFREQ 0 0 0 0
  XPDR 0
  AAP 0:0 0:0 0:0
END
probe:ShuttleA
  STATUS Orbiting Earth
  RPOS 6166906.88 1869609.42 -1439407.18
  RVEL 2970.534 -5745.704 4440.703
  AROT 78.25 79.88 -123.30
  AFCMODE 7
  PRPLEVEL 0:1.000000 1:1.000000
  NAVFREQ 0 0
  XPDR 0
  PODANGLE 0.0000 0.0000
  DOCKSTATE 0 0.0000
  AIRLOCK 0 0.0000
  GEAR 0 0.0000
  PAYLOAD MASS 0.0 0
END
END_SHIPS

BEGIN_ExtMFD
END

I think the problem is that you accumulate the actuator value. That way you'd have a square D component, which makes it hard to use the standard equation. Change
Code:
local thrust = lvl + output
to
Code:
local thrust = output
 

wingnut

Donator
Donator
Joined
May 10, 2013
Messages
129
Reaction score
0
Points
16
Thanks for reviewing.

The result is the same unfortunately.
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
Thanks for reviewing.

The result is the same unfortunately.

Getting PID values right is tricky. Try reducing the factors in magnitudes of 10 until you see a reasonably small reaction, then gradually increase them again until you see oscillation. Don't shy away from getting as low as E-10 if necessary.
 

BEEP

Addon Developer
Addon Developer
Joined
Apr 5, 2008
Messages
153
Reaction score
15
Points
18
I'd use
Code:
doutput= ( d + e/kD) * kG *simdt



where kD=damping factor, kG=overall gain, simdt = oapi.get_simstep

Notice that multiplying by simdt is important. Check Martin's AAP.lua.
And it's easier to investigate and handle the empiric parameters this way.


I do not have the time to do it for you but I would:

Create another DG in the same orbit as a reference.

Load the function and run it in the Lua Terminal Window.

Change the variables ( make them global) in the Terminal as the procedure runs seeing what happens.

Now and then set the reference state vectors from the reference DG to the test DG with Scenario Editor to reset it.
 
Last edited:

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
After thinking a bit about it, I think the statement about squaring of the D component was false. D is actually derivative, it means if the error changes faster, it is corrected with more actuator value. It would be the I component that gets "duplicated" - to avoid the term "square", since I don't know if it is actually mathematically correct.
 

wingnut

Donator
Donator
Joined
May 10, 2013
Messages
129
Reaction score
0
Points
16
I'd use
Code:
doutput= ( d + e/kD) * kG *simdt



where kD=Damping factor, kG=overall gain, simdt = oapi.get_simstep

Notice that multiplying by simdt is important. Check Martin's AAP.lua.
And it's easier to investigate and handle the empiric parameters this way.

Yes, I've noticed that the DG's AAP does the multiplication by simdt but I did not get why it's done.


So it seems that the problem in fact was a matter of tuning kP and kD.
Setting them to
Code:
kP = 0.001
kD = 0.0005
produces much smoother behaviour. Somehow I assumed that my initial values for kP and kD should already work quite well and that there was a typo in the formula to blame for the behaviour.
 

martins

Orbiter Founder
Orbiter Founder
Joined
Mar 31, 2008
Messages
2,448
Reaction score
462
Points
83
Website
orbit.medphys.ucl.ac.uk
It is worth remembering that the simulation, (probably) unlike real life, is advancing in discrete time steps, and this can lead to numerical instabilities in your feedback loop.

In particular, you should check that the eigenfrequency of your system is small compared to the simulation sampling frequency. If it isn't, your system won't be sampled at a sufficient rate and you might introduce an artificial resonance.

You may think that the way to deal with oscillations is to increase the damping, but you could be wrong. If you ramp up the damping term such that it reverses the direction in which your system is evolving in a single time step, then the damping term can itself generate an oscillation.

So it's probably always a good idea to estimate the sampling frequency your feedback loop requires to operate reliably (or simply try different sampling frequencies to check). You could then for example disable the autopilot when the conditions are not met. This isn't very elegant (the behaviour of the simulation shouldn't depend on the sampling), but probably unavoidable. I suspect that every autopilot will fail when the user ramps the simulation up to 1e5x.
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
Somehow I assumed that my initial values for kP and kD should already work quite well and that there was a typo in the formula to blame for the behaviour.

I played around with PID control for various aircraft aspects in PIDMfd myself, and found it rather hard to get good starting values. The control loop there was using the elevator airfoil to get a certain aircraft pitch. Perhaps you can use the code (using standard equation) from there. Just make the I factor zero and it should behave similar to a PD controller.
 

Thorsten

Active member
Joined
Dec 7, 2013
Messages
785
Reaction score
56
Points
43
I played around with PID control for various aircraft aspects in PIDMfd myself, and found it rather hard to get good starting values.

Simple but useful technique: Record yourself solving the task. Write out a short time series of errors and control inputs while you fly manually, take a look at what approximate gains you use to null errors - and that's in the vicinity of what an AP ought to use.

And... never use an Integrator term before the PD is stable. Integrators are evil.:lol:
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
Simple but useful technique: Record yourself solving the task. Write out a short time series of errors and control inputs while you fly manually, take a look at what approximate gains you use to null errors - and that's in the vicinity of what an AP ought to use.

Yeah, that's what I actually did in the end. I thought there might be a more "advanced" technique, but it seems like dead reckoning it is the way to go.
 

Thorsten

Active member
Joined
Dec 7, 2013
Messages
785
Reaction score
56
Points
43
I thought there might be a more "advanced" technique

There is - for instance there's e.g. the Ziegler-Nichols method for which I would refer you to the JSBSim reference manual p 119 ff).

Since a PD is just a damped harmonic oscillator, you can solve it analytically and just plot it with an external tool - which gives you a good approximation if your controls respond reasonably fast.

But it's widely acknowledged that finding good parameters is more an art than a science.

Also, it's worth remembering that it always makes a big difference what you try to control (sounds trivial but is not so trivial in practice - for a helicopter in hover flight, collective controls altitude, but in cruise flight collective controls speed....). And that there are situations where you do not want a proportional response and hence need to use something beyond PID (for the Shuttle in semi-ballistic flight after SRBsep, you can define vspeed errors to steer you to the nominal ascent, but you're unable to do much about them if you are coming low until some three minutes later...).

I've written a lot of controllers during the last years (both Shuttle and for a virtual co-pilot capable of taking off, hovering and landing a helicopter), and the one thing that really crystallized from that is to first observe myself how I would solve the problem, what parameters and cues I look at and then make the AP do the same, rather than work down a fixed scheme.

Requires that you're a reasonably proficient pilot of course :)
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
577
Points
153
Location
Vienna
There is - for instance there's e.g. the Ziegler-Nichols method for which I would refer you to the JSBSim reference manual p 119 ff).

Well, the academic ones like Ziegler-Nichols I know about. Unfortunately it never yielded good results for me, especially with digital controllers. I was more after practical tricks in the context of spacecraft engineering.
 

Thorsten

Active member
Joined
Dec 7, 2013
Messages
785
Reaction score
56
Points
43
Then you're probably at the 'art' stage already :thumbup:

if you have any particular problem, I'll be happy to share my insights with you (if I have any...), but it's hard to generalize at that stage.
 
Top