-
Notifications
You must be signed in to change notification settings - Fork 615
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
Add ArmFeedforward calculate() overload that takes current and next velocity instead of acceleration #5192
Comments
ElevatorFeedforward's model is affine, which is analytically invertible.
ArmFeedforward's model is nonlinear though. One way to implement ArmFeedforward's new calculate() overload would be to integrate the dynamics with rk4(), then use Newton's method to find the input u that minimizes the difference between the current and next reference. |
So one would need to perform rk4() on this component B⁺cₖ |
For the nonlinear portion B_d.solve(d) where d is the column vector (0, -kG cos(x) / kA) Then rk4() this function of x at current velocity? |
Something like this
|
That computation for discrete d doesn't seem right; the nonlinear dynamics are not seperable. Also, passing discrete dynamics to RK4 is invalid; RK4 integrates continuous dynamics. https://github.com/calcmogul/allwpilib/tree/wpimath-add-sleipnir can solve the least-squares problem I mentioned. I didn't make a PR yet because we didn't have a use case, and it's not very good at nonlinear problems. In any case, the formulation would look like this: #include <sleipnir/optimization/OptimizationProblem.hpp>
// Init xₖ and xₖ₊₁ here
double x_k;
double x_k1;
sleipnir::OptimizationProblem problem;
auto u_k = problem.DecisionVariable();
problem.Minimize((x_k1 - RK4(f, x_k, u_k, dt)).transpose() * (x_k1 - RK4(f, x_k, u_k, dt)));
problem.Solve();
// u_k.Value() contains solution. Where f(x, u) is the acceleration dynamics. If it has trouble converging, forward Euler could be used to make the cost function better behaved, though the solution may be less accurate. A JNI would be needed to implement the Java version. A native Java implementation of Newton's method with EJML would be slow. |
So would using the velocity, acceleration method be better in the long run for arm. |
But the whole point of this issue is to implement a more accurate method with arguments the user has more readily available. |
That's fair. I just don't think I can write a JNI :) |
What would the full form be of f(x, u) |
For an arm,
|
Another thought on this. Would it be possible to use a small angle approximation for the cosine to make the optimization take less time or is that just substituting one nonlinear for another. |
Sounds like premature optimization since the optimization hasn't yet been demonstrated to take too long. |
I've been thinking about the method signature for the arm. Would an appropriate set of arguments for calculate be (currentPos, nextPos, currentVel, nextVel, dtSeconds). We didn't need position for the simple motor or the elevator feedforwards as they don't have a dependency on position, whereas the arm does. |
I'd do |
gotcha. |
Is this a good start
|
And then something like this I know the name functionToFindZeroFor is ridiculous
|
wpimath has a function for RK4, if that's what you're trying to do (I can't tell because Java sucks at making math legible). |
Wouldn't I be trying to find the uk that makes the functionToFindZeroFor zero. I didn't think rk4 would be useful since I don't know u |
That code is so verbose, I can't tell what it does or whether it's correct. |
This would be for the calculate method. I used Theorem 7.9.2 in your book with xk_1 as nextR and xk as r to create a function to find the zero for. |
So I could solve the the input u which I guess would be the next step in the calculate method. |
I'm still not seeing it. Could you show your work if this is actually different from what rk4() does? |
Referencing Theorem 7.9.2 I'll use xk_1 as xsub(k+1) nextR is xk_1 dtseconds is h So using xk_1 = xk + (dtSecond / 6) * (k1 + 2 *k2 + 2 * k3 + k4) k1, k2, k3, k4 are all functions of xk and u. So I plugged r in for xk and into k1, k2, k3, and k4. Now I have a function strictly of u that connects xk_1 and xk |
That just sounds like NumericalIntegration.rk4(). |
Right, but I don't know which u gets me from r to nextR. Doesn't NumericalIntegration.rk4() require that I already know u |
You're passing uk as an argument to |
Yes. Wouldn't the output of the calculate method be the uk that is the zero of the functionToFindZeroFor. My next step would be to find this (these zeroes) somehow. |
So I effectively have g(uk) + xk - xk_1 = 0. Where g(uk) is h/6 ( k1 + 2 k2 + 2 k3 + k4) and the xk has already been plugged in for all the k's Note: I changed the function to g(uk) to avoid confusion with the main dynamics function f. |
Actually let me back up a second. I would have a function of uk that gives me all possible xk_1 based on the current xk Then I would have to find the uk that got me closest to nextR |
Actually it may never cross the 0 given the inputs and the timestep so minimize? |
Okay scratch everything.
|
Two questions for you. I'm assuming the cost function that you mentinoned above is the dynamics function. Also would something like this be helpful in finding the solution to the optimization problem. |
The residual would be xₖ₊₁ − rk4(xₖ, uₖ). Levenberg-Marquardt is one of many methods for minimizing it. |
Okay. I’ll see if I can implement this for the arm this week. Sent from my iPhoneOn Oct 23, 2023, at 2:34 PM, Tyler Veness ***@***.***> wrote:
The residual would be xₖ₊₁ − rk4(xₖ, uₖ). Levenberg-Marquardt is one of many methods for minimizing it.
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: ***@***.***>
|
So just as an experiment. I'm directly using the Levenberg-Marquardt code from the link.
|
@calcmogul I've thought about using the open source Levenberg-Marquardt code that utilizes EJML. I just wanted to pick your brain a little on alternate possibilities if any. |
I'd honestly rather just write a JNI. There's similar wpimath JNIs that can be used as a guide for passing matrices back and forth, and doing anything math-related in C++ is just easier. |
Okay. I'll make some attempts at this approach. Be that as it may I'll eventually need to integrate the Sleipnir Optimization problem and its dependencies into WPILIB to make this work. |
@calcmogul |
How do I get your upstream-utils-add-sleipnir branch as a branch in my repo for development. I'm sure there is a git way to do this, but I'm not aware of it. |
Nevermind I believe I got it. |
@calcmogul |
Is your feature request related to a problem? Please describe.
Motion profile classes don't include acceleration in their provided state (because it's not a state). The feedforward calculate() methods require acceleration though.
Describe the solution you'd like
We've added a calculate() overload to SimpleMotorFeedforward that takes current and next velocity instead. We should do that for all our other feedforward classes as well.
The two overloads would be
calculate(velocity, acceleration)
andcalculate(currentVelocity, nextVelocity, dt)
.Describe alternatives you've considered
Adding acceleration to the motion profile isn't ideal because the control input is inherently discrete; the acceleration continuously varies across the timestep instead of being constant. The discrete version of calculate() is more accurate. The next velocity can be obtained by sampling the motion profile one timestep into the future.
The text was updated successfully, but these errors were encountered: