-
Notifications
You must be signed in to change notification settings - Fork 23
Add example for a multiphase problem. #333
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
Draft
moorepants
wants to merge
1
commit into
csu-hmc:master
Choose a base branch
from
moorepants:multiphase
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| """ | ||
| Multiphase Collision | ||
| ==================== | ||
|
|
||
| A block is sliding on a surface with Coulomb friction. Push it with a (limited) | ||
| rightward force until it hits a wall 1m on the right. When it hits the wall | ||
| enforce a Newtonian collision with a coefficient of restitution e so that the | ||
| block bounces off the wall and slides backwards to its original location. Apply | ||
| this force such that the block reaches its original location in the minimal | ||
| amount of time and that each phase has the same duration. | ||
|
|
||
| """ | ||
|
|
||
| import numpy as np | ||
| import sympy as sm | ||
| from opty import Problem | ||
| import matplotlib.pyplot as plt | ||
|
|
||
| # %% | ||
| # Start with defining the fixed duration and number of nodes. | ||
| num_nodes = 400 | ||
|
|
||
| # %% | ||
| # Symbolic equations of motion, note that we make two sets: one before the # | ||
| # collision and one after. | ||
| m, e, mu, g, t, h = sm.symbols('m, e, mu, g, t, h', real=True) | ||
| xr, vr, xl, vl, F = sm.symbols('x_r, v_r, x_l, v_l, F', cls=sm.Function) | ||
|
|
||
| state_symbols = (xr(t), vr(t), xl(t), vl(t)) | ||
| constant_symbols = (m, e, mu, g) | ||
| specified_symbols = (F(t),) | ||
|
|
||
| eom = sm.Matrix([ | ||
| xr(t).diff(t) - vr(t), | ||
| m*vr(t).diff(t) + mu*m*g - F(t), | ||
| xl(t).diff(t) - vl(t), | ||
| m*vl(t).diff(t) - mu*m*g, | ||
| ]) | ||
| sm.pprint(eom) | ||
|
|
||
| # %% | ||
| # Specify the known system parameters. | ||
| par_map = { | ||
| m: 1.0, | ||
| e: 0.8, | ||
| mu: 0.6, | ||
| g: 9.81, | ||
| } | ||
|
|
||
|
|
||
| # %% | ||
| # Specify the objective function and it's gradient. | ||
| def obj(free): | ||
| """Return h (always the last element in the free variables).""" | ||
| return free[-1] | ||
|
|
||
|
|
||
| def obj_grad(free): | ||
| """Return the gradient of the objective.""" | ||
| grad = np.zeros_like(free) | ||
| grad[-1] = 1.0 | ||
| return grad | ||
|
|
||
|
|
||
| # %% | ||
| # Specify the symbolic instance constraints, i.e. initial and end conditions | ||
| # using node numbers 0 to N - 1 | ||
| dur = (num_nodes - 1)*h | ||
| instance_constraints = ( | ||
| xr(0*h) - 0.0, | ||
| xr(dur) - 1.0, | ||
| vr(0*h) - 0.0, | ||
| # TODO : figure out how to let the collision happen at any time, not just | ||
| # the halfway point in time | ||
| vl(0*h) + e*vr(dur), | ||
| xl(0*h) - 1.0, | ||
| xl(dur) - 0.0, | ||
| ) | ||
|
|
||
| bounds = { | ||
| F(t): (0.0, 160.0), | ||
| h: (0.0, 0.5), | ||
| vr(t): (0.0, np.inf), | ||
| vl(t): (-np.inf, 0.0), | ||
| } | ||
|
|
||
| # %% | ||
| # Create an optimization problem. | ||
| prob = Problem(obj, obj_grad, eom, state_symbols, num_nodes, h, | ||
| known_parameter_map=par_map, | ||
| instance_constraints=instance_constraints, | ||
| time_symbol=t, | ||
| bounds=bounds) | ||
|
|
||
| # %% | ||
| # Use a zero as an initial guess. | ||
| initial_guess = np.zeros(prob.num_free) | ||
|
|
||
| # %% | ||
| # Find the optimal solution. | ||
| solution, info = prob.solve(initial_guess) | ||
| print(info['status_msg']) | ||
| print(info['obj_val']) | ||
|
|
||
| # %% | ||
| # Plot the optimal state and input trajectories. | ||
| prob.plot_trajectories(solution) | ||
|
|
||
| # %% | ||
| # Plot the constraint violations. | ||
| prob.plot_constraint_violations(solution) | ||
|
|
||
| # %% | ||
| # Plot the objective function as a function of optimizer iteration. | ||
| prob.plot_objective_value() | ||
|
|
||
| plt.show() | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could change the requirement that the block simply travels 2 meters total and is stationary at the start and the end. Maybe a solution that had varying periods of no motion at the start and end is possible, where these two stationary periods are not equal in time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In your original set-up the block was not at rest at the end, you left its speed open. So I also did it in my simulation.
My set up mimicks exactly your original set-up, except I let opty decide the point of impact - and it does find a better one! :-)
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For clarification:
The plots above were created by moving the time of impact around, from early on in the journey to late in the journey.
But every time the time of impact was predetermined! I only wanted to see if the time of impact relative to total journey had an influence on the length of the journey - otherwise there would be nothing to optimize.
It does have an influence of the length of the journey, so there was a chance for opty to find a good time of impact - which it does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how you can move the time of impact in my example that requires the duration before impact to be the same as the duration after impact. That is is hardcoded in my example here.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it like this:
The time of impact is still predetermined, just does not have to be the middle of the journey.
But this is not the main point of my simulation. The main point is that opty decides the time of impact - which it seems to do.
x_impact is trhe location of the wall, x_impact=1.0 in your original example. Of no importance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I understand, your problem's duration is much longer. It simulates xr past the impact (> ~ 0.11 seconds). That is nonsense for xr, because it shouldn't continue past 1 meter. If you then look at xl at t=0 it start at 2.5 meter (not 1 meter!). So it seems you are not solving the problem as stated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
YES, mine is much longer.
I think, it solves the physical problem, but one after another, not simultaneously so to speak.
Now I understand what you meant by different time intervals: To solve simultaneously with t_impact != (tf - t0)/2 one would need different length time intervals.
What is the advantage of doing it simultaneously? I guess saving CPU time?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I now see what you did and that it is likely a valid solution. I would have never thought about simulating the equations of motion outside of the x = 0 to 1 meter range. But if you relax that restriction (a hack), then you can find a solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are solving the problem outside of the bounds of interest. So you have to also ensure that portion can solve. This will depend on CPU as well as your ability to get a solution. It is trivial for this test example, but would not be for a complex model.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even with this simple example, when I moved t_event too close to t0 or to tf, it would not find a solution easily.
(But I did not pay too much attention to this: for just about any simulation I have played around with, it took some trying different things before it would converge...)
From this step it was only a small step to let opty find t_impact. But again it took trying things before it would converge.
My feel is, it is hard to judge from the equations, whether a problem is hard: The brachistochrone were very simple equations, but hard to get a solution.