-
-
Notifications
You must be signed in to change notification settings - Fork 212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parameter representation for controls #1088
Comments
One design feature I'd propose is the ability to specify control laws that take states, non-control parameters, and time as inputs. SciML is machine-learning focused as the name suggests, so perhaps the use case I'm about to describe isn't a top use-case for primary SciML users. That said, I know (as a user) I'm primarily interested in the SciML ecosystem as an aerospace controls engineer. The spacecraft separation dynamics use-case for Since a new parameter design is up for discussion here, I thought I'd describe one desired use case as an example, and use it to motivate a tweak to the Proposed Code section above. In short – I think it would be nice to have the control functions take Flight Control Use-caseAs a graduate student, I worked with public models for a sub-scale model aircraft that NASA uses for flight control research. Researchers published a paper which described the model aircraft dynamics, and presented nonlinear flight control analysis. The general equations of motion are shown here. I'm now realizing that LaTeX math doesn't show here in GitHub-flavored Markdown! All symbols in the equations of motion can be divided into the following groups. Note that I'm using the terms "variables" and "parameters" with their colloquial definitions, not with
Link with
|
I 100% agree with @cadojo on making the controller state and time dependent. Usually Currently, if you want this to be differentiable with DiffEqFlux (afaik), you need to augment the state with the control vector, and have dx[...] = 0. Then the control can be updated by a PeriodicCallback. I just want to make sure that this is taken into account if possible. See #894. |
That's a control that should be in a differencing term, very different.
You can. I wonder if having this in this form too is good, or a bad form of redundancy. |
I think a benefit for providing an abstract If I export a model from a package, say Without modifying the model, users could specify If we don't add some functionality to specify state-varying and time-varying control laws, the user would have to construct a new model of the system for every new control law, right? Also, leaving controls as a symbolic parameter in the dynamics allows for functionality like |
Good point. Should it be a function of
Another good point. |
Edit: I'm realizing I didn't answer the question "should the control just hold the
Now that we're talking this out, maybe controls should be entirely separate from parameters. Folks often talk about controls as "control parameters" because they typically don't have their own dynamics (when they do, they can be appended to the state vector). I wanted to avoid a breaking change, so I designed the new Maybe that's not the right approach though. When you write a control law as a function, that law It would be great for this change to break as few things as possible, of course. Maybe controls for a system still default to an empty array, but instead of controls being a subset of parameters, they're defined separately by the user, and they're implemented separately by For a spring-mass-damper, today a user would write... @parameters t fₑ d k
@variables x(t) ẋ(t)
δ = Differential(t)
eqs = [
δ(x) ~ ẋ,
δ(ẋ)~ - d*ẋ - k*x + fₑ
]
model = ODESystem(eqs, t, [x, ẋ], [fₑ, d, k]; name = :HarmonicOscillator) If we want @parameters t d k
@variables x(t) ẋ(t)
@controls fₑ
δ = Differential(t)
eqs = [
δ(x) ~ ẋ,
δ(ẋ)~ - d*ẋ - k*x + fₑ
]
# signature could look like ODESystem(eqs, iv = iv(eqs), states = states(eqs), params = params(eqs), ctrls = ctrls(eqs)
model = ODESystem(eqs, t, [x, ẋ], [d, k], [fₑ]; name = :HarmonicOscillator) For The generated function dxdt_(dx,x,p,t)
# p.p == (d, k)
# p.c == (fₑ) where fₑ = (x,p,t) -> (some scalar output)
dx[1] = x[2]
dx[2] = -p.p[1] * x[2] - p.p[1] * x[1] + p.c(x,p.p,t)
end |
And my proposal already does exactly that division between control parameters and other parameters. Those parameters are just stored inside of the control functions, and then the adjoint procedure would have a way to include/exclude them. I guess the only remaining question is then, since they have internal parameters, whether we want them to be functions |
So one thing I think we should make explicit here is what we are all talking about when we say "controls". For most engineers, the term used by itself mostly means closed-loop (feedback) control. Most of the controls-related work in ModelingToolkit has been focused on open-loop optimal control that is solved offline. Both are important to have an interface for in ModelingToolkit, but they're mostly separate things and I'm not sure giving them the same interface is what we actually want. For aerospace (and a lot of other industries), you'll need both--sometimes within the same model. That can go both ways: an offline trajectory optimization that uses a model with internal feedback control or a closed-loop feedback system that solves for open-loop optimal trajectories online (albeit at a coarser time step). @cadojo it looks like what you're looking for here is a more structured way to do closed-loop control. I usually handle this in ModelingToolkit with alias variables (for example, For one, it isn't easy to calculate the control Jacobian and linear control methods require you to handle the control Jacobian separately from the state Jacobian. The other issue is discrete-time variables. Since your controller will eventually need to run on digital hardware, control laws are almost always written as difference equations of the discretely sampled continuous state variables. I usually handle this right now by making continuous state variables with zero dynamics and then update them in a callback. This gets a little clunky, though. It would be really nice to have a discrete independent variable that you could use to write difference equations. For example: @parameters t
@parameters d k
@variables x(t) ẋ(t) fₑ(t)
δ = Differential(t)
n = DiscreteSampleTime(t; dt=0.01)
eqs = [
δ(x) ~ ẋ,
δ(ẋ) ~ - d*ẋ - k*x + fₑ
fₑ(n+1) ~ -x(n) + x(n-1) + fₑ(n) # not a meaningful control law, just an example
] |
That's part of #894. You're close. My plan is to instead have that like: @parameters t
@parameters d k
@variables x(t) ẋ(t) fₑ(t)
δ = Differential(t)
D = Difference(t; dt=0.01)
eqs = [
δ(x) ~ ẋ,
δ(ẋ) ~ - d*ẋ - k*x + fₑ
D(fₑ) ~ -x + fₑ # not a meaningful control law, just an example
] And then we want to add delay handling, like: eqs = [
δ(x) ~ ẋ,
δ(ẋ) ~ - d*ẋ - k*x(t-1) + fₑ # delay differential equation
D(fₑ) ~ -x + x(t-dt) + fₑ # delay term in the update equation
] But that handling of the |
Won't the delay in the difference equation be sensitive to floating point in the case where you are getting the delayed state of a discrete variable (because you're asking it to retrieve a value right at the discontinuity)? Or are delayed discrete variables intended to be handled with higher-order difference equations? |
Symbolically that should be fine. I think this would be a bit easier to parse than the I guess the remaining question is just whether to have the controls be |
I can offer an additional control-oriented perspective: Even in the case one actually wants to compute an open-loop optimal control trajectory, there is most likely a feedback controller in the picture making sure the system is actually following the computed trajectory. In robotics, where trajectory optimization is fairly common (perhaps not so much in industry, but in academia at least), the interaction with the servo/joint controllers can not be neglected, and an optimized trajectory will always be executed by a feedback-controlled system in the end. Simulating the interaction between the feedback controller and the feedforward system/trajectory optimizer is very important to make any use of optimal control at all. |
So is the solution then provide some standard types like |
I think this will be hard to do since each and every implementation of a PID controller is slightly different, and most often include things like integrator anti-widup, saturations and filters :/ I have experimented a bit on my own and in my implementations I had controllers always be their own |
Is there some way to do both function signatures for control inputs? The control function passed as an input to |
I’m still loosely of the opinion that putting For example, space missions are designed by solving trajectory optimization problems of the form |
I agree that they are not meaningfully interchangeable, but it might sometimes be very interesting to co-optimize them. Correct me if I'm wrong (no space experience at all here :P) but in the planning of space missions, the optimal control problem and the feedback control problem has time constants separated by several orders of magnitudes, and treating them as completely different problems makes total sense. Putting this in other words, the feedback controller is so fast that the controlled system can be considered completely disturbance/uncertainty free from the view of the optimal control problem. In a field like robotics this might not at all be the case, where quick movements with flexible structures causes a very tight interaction between the feedback controller that is rejecting disturbances, and the optimal trajectory, and the possibility to co-optimize them would be very valuable. It's not all that common to do so nowadays, at least as far as I know, but if the complexity this entails could be managed by future tools, it would allow us to treat these two as two components of the same joint problem. I recognize that the ability to co-optimize open and closed-loop control can be separated from the interface to the two problems, which is perhaps what @jonniedie is primarily talking about? As long as the feedback case is represented somehow, and that open and closed-loop controllers can be both simulated and optimized together, I think MTK would open up possibilities that require quite a lot of custom implementation today. |
Yep, that's exactly it. Space trajectory optimization is typically treats the vehicle as a point-mass because the attitude dynamics are much faster than what matters for the problem. But I guess that also kinda distracts from the point I was making. The real issue is not that they are separate, but that then might need to be used at the same time.
Yeah, this is mostly what I was getting at. If the interface is the same for both problems, I feel like it would be hard to handle cases where you want to do open-loop optimization with a model that has closed-loop controls inside of it. |
|
Since this was discussed here I would like to add the copy I posted on Slacks I don't know if I understood the problem correctly, but wouldn't having the controlled parameters be dictated by a separate subsystem (something along the lines of an OptimizationProblem system) be possible? I might also be biased, but in Control System descriptions the controlled parameters are typically set by a separate system (be it an MPC routine or just a plain LQR, where the Riccati equation would be the "state" of the optimization subsystem and the output would be the optimal K feedback multiplier). In MATLAB you would have such a description as a seperate "block", however I don't know if this would conflict with #1059 . I think I missed the point of the seperation p.p and p.ctrl somewhere while reading, too. I obviously thought of a seperation between Optimizer and actual Plant, but my ideas might be too narrow for something that should be general. |
Yes, that would be possible but it would require full recompilation and codegen every time the controller is changed. This is how it's done in Modelica, and I think that makes the optimal control implementation a bit more difficult. Or even just experimenting with controls. |
Hi! I did not fully understand the proposal to handle the problem. I just want say that sometimes the control function can be very complicated. In my case, it has the entire representation of a satellite embedded software (controller and estimator). Currently, I am using |
You can do this by connecting a parameteric function / component to an input variable, so essentially the v9 parameter types is all that's needed and a separate interface isn't required. |
The underlying problem is that we need to represent the generated
f(du,u,p,t)
in a way wherep
can hold the necessary parameters while also being a valid object for the optimal control. Without that, #1059 will not be able to generate an ODEProblem. But what kind of ODEProblem should it be generating? This issue is to specify the interface for the forward simulation and describe why that needs to be the case.Proposed code
In a modified version of https://diffeqflux.sciml.ai/dev/examples/optimal_control/ where
dx[1]
also has a parameter, i.e.i.e. the parameters are held in some kind of struct:
In this form, we need that each
ctrl
is actrl(t)
which internally holds its parameterization. Then we would just build it like:Sounds good? Okay now the hard part.
Parameter Representation for Optimal Control
Now we want to optimize
p
. That can mean multiple things: optimizep.p
, optimizep.ctrl
, or optimizep
in full. How do we make this happen? Well we can havep
itself act as a vector. Iftrainctrl == false
, thenp[i] = p.p[i]
. Iftrainp == false
thenp[i] == ...?
. So let's talk about that part.We need to make the parameter interface recursive.
ctrl
should be something that follows the interface that it acts like a parameter vector, so thatctrl[i]
is something that acts like a parameter vector, so thatctrl[i][j]
is something that acts like a parameter vector. If that's the case, we just mapi
to continue over each next parameter vector.DataInterpolations.jl was setup in 2018 with this in mind (gotta appreciate that forward thinking 🤔 😉 ) so if you did
ConstantInterpolation{true}(u,t)
, theu
andt
would be trainable, andConstantInterpolation(u,t) = ConstantInterpolation{false}(u,t)
would just make the valuesu
trainable and the node pointst
of the controller would be fixed. To make this work, we would just need to havelength(ConstantInterpolation(u,t))
work, and thenparamlength(p) = paramlength(p.p) + paramlength(p.ctrl)
. Now if you have multiple controls,p.ctrl
can be a tuple of controls (or vector, depending on the type stability), and thenparamlength(x::Tuple) = sum(paramlength,x)
. Thenlength(p::ParameterController ) = paramlength(p)
.Given this indexing,
pvec = Vector(p)
would "just work" via Julia's generic dispatches. However, we would still need a way to change a vector back intop
, which we should implement by overloadingArrayInterface.restructure(p,pvec)
(though it might just work by default?). Once this is the case, then all of GalacticOptim, adjoints, DiffEqFlux, etc. should be able to train the values of the parameters and the controllers in this form.Pieces that are made to be constant would just be things that are kept out of the vectorization of the parameter struct.
Where to put it
Since this is a generally nice interface, it might make sense to have this be a package that we point people towards which defines a nice structure to parameters. This is also the general solution to a long-standing issue SciML/DiffEqFlux.jl#178 and SciML/SciMLSensitivity.jl#80, it might be good to call it SciMLParameters.jl or something and make it a full interface so that it's documented and people know how to make their parameters "fit" with all of the packages in the ecosystem. Having this would work make
p = TupleParams(p1,p2)
work in DiffEqFlux, if it's using thisparamlength
where ontuple
it knows to just extend. ModelingToolkit.jl would then just use this new "SciML common parameter interface"The text was updated successfully, but these errors were encountered: