Austin from the referenced post here - always love to see the shoutout! What you did is pretty much what I wanted to do when I set out. But definitely not in JS haha, not a huge fan of it
Anyways, I chose to go with PID control for my control systems, which worked really well right "out of the box" with some somewhat generic values for P, I, and D.
I read/skimmed all the way through your post. It looks like your control system is more or less a P & D controller? Any reason you didn't just go with PID loops? Is this part of needing things to be stateless to work for JS (I am no JS expert, just saw a few mentions of that in the conclusion)?
It's basically P-D-and-a-little-second-derivative, but the straight PD controller code just doesn't like the whole "you only get to run every half second" timing aspect. Things take far too long to ramp up, and the amount of overshoot you get is just ridiculous. There's no way (at least, not that I've been able to mitigate in anything even close to acceptably) to avoid super obvious oscillation, even if it eventually converges, at the low rate we're running.
Someone could, if they wanted (as one of the "what's left" options =D) replace all of this with PD code but they'll probably need to run at a much higher data rate for things to actually behave, and optimize the SimConnect calls to get the dataframes necessary without tying up a thread.
I see, yes controlling at 2 Hz will not be easy no matter how you slice it. What's preventing you from doing more? At one point I experimented with mine. I think below 5 Hz things got choppy. Greater than 10 didn't do a whole lot.
I'd be interested to know how your autopilot reacts with just the P & I portions. Everything I've read/learned about PID loops say to incorporate each term sequentially, meaning do P first, then introduce I to correct long term error, and finally introduce D to prevent overshoot. Not many use only P & D.
MSFS itself, mostly - it's pretty much always CPU bottlenecked already, and you can do fast polling using C++ (by basically allocating a piece of shared memory that MSFS keeps dumping updates into), but when you're using an FFI layer in Python, JS, etc. you end up transferring "fat" objects and running real time without bogging down an average specced machine gets tricky. The game itself will generally run well below 60fps, so realistically the fastest you're going to get updates is 20~30Hz (which should be enough) but then you might also have your AP code competing with MSFS itself if they decide to run on the same core.
It would be cool to add a completely new chapter going "so we got all of this working pretty well: time to delete all our code and now redo it using control loops on an unused core" but that's one of those "if I ever have time again" things =P
> Everything I've read/learned about PID loops say to incorporate each term sequentially
Aye, but the problem is we don't have the luxury of tweaking the controller ourselves beforehand: the goal is an autopilot "that works for all planes", so the challenge would be to write a self-tuning PID solution that can do the job without any prior knowledge (because we're not profiling every plane in MSFS, that'd be a full time job). If we were writing an autopilot for our favourite, single plane, then tweaking the controller would be relatively easy, but the solution has to work for everything from a Top Rudder Solo 103 to a DC-3, which complicates things a little.
I'm not in love with the current implementation in erms of having quite a bit of "emergency detection" that changes control parameters if the current values end up steering us into bad control territory, but at the rate we're getting updates, it's at least serviceable.
I haven't grok'ed things properly yet, its a hefty read for my Monday morning, but the thing that leaps into my mind is to ask you why Javascript, of all things, and not something like Lua?
Isn't Javascript just too finicky for something like this? Are other languages/approaches to integration with the execution environment possible/feasible?
(I apologize for my newbie question, hope you don't mind..)
Because no one's paying me, so my choice in language is about how easy I can do things like async code, hot reloading, and fast enough performance to not feel like the language itself is the bottleneck, and that makes JS a no-brainer (especially as Modern JS is quite fast, too!)
> Isn't Javascript just too finicky for something like this?
Maybe you're thinking of JS as a browser language, in which case: no, it's been a general purpose programming language with a standard library for over a decade now in the form of Node.js, which has been keeping in lockstep with the development of the language itself (which has changed a lot over the last decade, so if you've not looked at modern JS circa 2022~2024, it's rather different now =)
Thanks for taking the time to reply, I had not really thought my question through .. I'm going to set aside some time to experiment with your project in my own MS Flight instance, and see how far I can take my understanding of things without getting on your radar. ;) I appreciate the patience it took to explain my dumb question, in hindsight it of course all makes sense.
What’s your definition of finicky? JavaScript is something like 3-10x faster than Lua for most benchmarks (1). If you’re referring to the event loop, it shouldn’t be all that relevant for singleminded code like this that is only processing a single event every half second. If you’re referring to mathematical oddities, it is also not relevant as JavaScripts love of floats is well suited here.
Thanks for the enlightenment, I can see I have to work on my understanding of JS a bit more .. I did not know that it surpassed Lua in terms of performance. I guess Mike Pall is sorely missed.
Anyways, I chose to go with PID control for my control systems, which worked really well right "out of the box" with some somewhat generic values for P, I, and D.
I read/skimmed all the way through your post. It looks like your control system is more or less a P & D controller? Any reason you didn't just go with PID loops? Is this part of needing things to be stateless to work for JS (I am no JS expert, just saw a few mentions of that in the conclusion)?