Skip to content
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

Added a example: crane moving a load #197

Merged
merged 14 commits into from
Sep 5, 2024

Conversation

Peter230655
Copy link
Contributor

A load hanging on the horizontal jib of a crane is to be moved from A to B as fast as possible. However, the load must not 'swing over' the final position B, say there is a wall it should not crash into. opty's solution is quite different from what I naively expected.

@Peter230655
Copy link
Contributor Author

I tried to copy all 'formalities' from an existing example in examples_gallery, so I hope it will run with sphinx.

@Peter230655
Copy link
Contributor Author

This seems to look o.k. in html

@moorepants moorepants changed the title 1. commit of crane_moving_a_load Added a example: crane moving a load Jul 30, 2024
@moorepants
Copy link
Member

Each plot should be associated with a new cell so that the plots are maximized in size and readable. These are not:
image

@moorepants
Copy link
Member

I ran this and it failed:

message from optimizer: b"Restoration phase failed, algorithm doesn't know how to proceed."
Iterations needed 338
Objective value  2.338e-02

Examples should be robust and always converge. The most reliable way is to give a better initial guess (possibly even a solution).

@moorepants
Copy link
Member

This seems to look o.k. in html

Yes, nice work!

@@ -0,0 +1,294 @@
# %%
"""
Crane moving a load
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Crane moving a load
Crane Moving a Load


A load is moved by a crane. The load is rotably connected to the crane by a
massless rod. The goal is to move the load from the initial position to the
final position in the shortest possible time. The load must not 'overswing'
Copy link
Member

@moorepants moorepants Jul 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
final position in the shortest possible time. The load must not 'overswing'
final position in the shortest possible time. The load must not over-swing

**Constants**

- l: length of the rod attaching the load to the crane [m]
- m1: mass of mover attached to the arm of the crane [kg]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variables should be latex or code:

- :math:`m_1` OR ``m1``

Copy link
Contributor Author

@Peter230655 Peter230655 Jul 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variables should be latex or code:

- :math:`m_1` OR ``m1``

I had played around with :math:..... but did not get it to work right.
Will it only work in lines like:

"""

  • :math: ... .

"""
not in lines starting with # ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Math in RestructuredText is explained here:

https://ikerdocs-sphinx.readthedocs.io/syntax/math.html

Copy link
Contributor Author

@Peter230655 Peter230655 Jul 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Math in RestructuredText is explained here:

https://ikerdocs-sphinx.readthedocs.io/syntax/math.html

This is what I looked at, but never got it to work. I will play around some more.
As I understand, :math: or ..math: only works in docstrings. In simulations for examples_gallery there is a docstring at the very beginning.
Question: are docstrings allowed further down in the text, or to be avoided?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sphinx-gallery treats the module level "docstring" as a "cell" of RestructuredText.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has not bee addressed.

- uxc: velocity of the mover in x-direction [m/s]
- uxl: velocity of the load in x-direction [m/s] dependent speed
- uyl: velocity of the load in y-direction [m/s] dependent speed
- u: angular velocity of the rod [rad/s]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The states are technically only the minimal set of coordinates and speeds, which are likely q and u.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The states are technically only the minimal set of coordinates and speeds, which are likely q and u.

I did not know this definition, I will correct this and the other suggestions. I needed the dependent coordinates to model the non over . swing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see below you are doing a non-minimal coordinate formulation. The states are still then only the independent coordinates and speeds.

Copy link
Contributor Author

@Peter230655 Peter230655 Jul 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The states are technically only the minimal set of coordinates and speeds, which are likely q and u.

they are xc, uxc, q, u.

@Peter230655
Copy link
Contributor Author

Each plot should be associated with a new cell so that the plots are maximized in size and readable. These are not: image

I know how cto fix it, fill come soon in the next commit.


"""
import sympy.physics.mechanics as me
from collections import OrderedDict
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OrderedDict is no longer needed, it is a remnant of Python 2 not ordering dictionaries by default.


# %%
# Set up Kane's equations of motion.
# Just the standard way of doing it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the second line, unnecessary.

P1a = me.Particle('P1a', P1, m1)
P2a = me.Particle('P2a', P2, m2)

BODY = [P1a, P2a]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
BODY = [P1a, P2a]
bodies = [P1a, P2a]

Use PEP8 naming conventions if at all possible.

@Peter230655
Copy link
Contributor Author

I ran this and it failed:

message from optimizer: b"Restoration phase failed, algorithm doesn't know how to proceed."
Iterations needed 338
Objective value  2.338e-02

Examples should be robust and always converge. The most reliable way is to give a better initial guess (possibly even a solution).

Strange. I never happened with me.


BODY = [P1a, P2a]

FL = [(P1, F * N.x - m1*g*N.y), (P2, -m2*g*N.y)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
FL = [(P1, F * N.x - m1*g*N.y), (P2, -m2*g*N.y)]
forces = [(P1, F * N.x - m1*g*N.y), (P2, -m2*g*N.y)]

configuration_constraints=config_constr,
velocity_constraints=speed_constr)

(fr, frstar) = KM.kanes_equations(BODY, FL)
Copy link
Member

@moorepants moorepants Jul 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(fr, frstar) = KM.kanes_equations(BODY, FL)
fr, frstar = KM.kanes_equations(bodies, loads=forces)

velocity_constraints=speed_constr)

(fr, frstar) = KM.kanes_equations(BODY, FL)
EOM = kd.col_join(fr + frstar)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
EOM = kd.col_join(fr + frstar)
eom = kd.col_join(fr + frstar)

# Set up the optimization problem and solve it.
# Note: While opty could calculate the reaction forces on, say, the mover,
# (add them to the state variables), my trials showed that even with this
# small example the calculation time increases enourmously.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The note is not helpful in this example, as it is vaguely explained and likely just distracts. I recommend removing it.

state_symbols = tuple((*q_ind, *q_dep, *u_ind, *u_dep))
constant_symbols = (l, m1, m2, g)
specified_symbols = (F,)
h_opty = sm.symbols('h_opty')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curions why you've chosen to use h_opty. I changed the variable name internally to this so that there would be no clashes with using h as a symbol name by the user. It is my intention that a user would never know about this naming convention for the internals and showing it here reveals that!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curions why you've chosen to use h_opty. I changed the variable name internally to this so that there would be no clashes with using h as a symbol name by the user. It is my intention that a user would never know about this naming convention for the internals and showing it here reveals that!

Now I understand your intention with h_opty. Will change back to h.

interval_value = h_opty

# Specify the known system parameters.
par_map = OrderedDict()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
par_map = OrderedDict()
par_map = {}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had wondered about the OrderedDict - but just copied it from your examples. Will change it.

grad[-1] = 1.
return grad

# start location and and final location of the load.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make a cell so text is rendered.


# start location and and final location of the load.
anfang = 0.0
ende = 15.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is preferable if the documentation is all in English to be consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is preferable if the documentation is all in English to be consistent.

I did this because in my early python times, I used English words, which were standard Python terms - and then I wondered why my program did not run (like I called a variable 'range'. Will change it to English.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good method, but we should just make the docs consistent in language. Sphinx does have mechanisms to make multiple versions of the docs in different languages. I've never done that, but I'm sure it would be helpful to many learners.

constant_symbols = (l, m1, m2, g)
specified_symbols = (F,)
h_opty = sm.symbols('h_opty')
unknown_symbols = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used below?

No. I also copied from some example, maybe from Timo. Allows the prob = Problem(..) to be always the same.
Better delete it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it isn't used, then delete it.

)
# %%
# Using a 'reasonable' initial guess speeds up the optimization process a lot.
# 'Bad' initial guesses may even prevent convergence.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just state what initial guess you are constructing. You can plot it easily with plot_trajectories() to show the reader what it is.



# this allows to change the maximum number of iterations.
# Standard is 3000.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still a code comment. Render as cell text.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or remove.

# Standard is 3000.
prob.add_option('max_iter', 1000)
# Find the optimal solution.
for _ in range(1):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this loop if there is only one iteration.

Ideally all examples solve with a single call to .solve().

print(f"Objective value {solution[-1]: .3e}")

# %%
# plot the accuracy of the solution.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# plot the accuracy of the solution.
# Plot the accuracy of the solution.

@Peter230655
Copy link
Contributor Author

I changed, say, q to q, but it seems I cannot use `` in commit comments.

@moorepants
Copy link
Member

I made a number of formatting fixes and tried to correct the fps. You can see what I changed here:

082d9eb

You can do git pull origin plot_crane_moving_a_load to get my updates in your branch.

Two issues remain:

  1. One out of three times this fails to find an optimal solution for me. If the initial guess can be improved, maybe that will not happen.
  2. I don't think the animation is animating to the final time, as it doesn't look like it ever hits the wall. This may be due to the way you are generating the animation frames and that arange() will skip the actual last moment in time when it hits the wall.

@Peter230655
Copy link
Contributor Author

Peter230655 commented Aug 5, 2024

I made a number of formatting fixes and tried to correct the fps. You can see what I changed here:
I finally understood! Embarrassing how long it takes me at times!

082d9eb

You can do git pull origin plot_crane_moving_a_load to get my updates in your branch.

Two issues remain:

1. One out of three times this fails to find an optimal solution for me. If the initial guess can be improved, maybe that will not happen.

2. I don't think the animation is animating to the final time, as it doesn't look like it ever hits the wall. This may be due to the way you are generating the animation frames and that `arange()` will skip the actual last moment in time when it hits the wall.
  1. I improved the initial guess by using the solution of a previous run. It never failed to find a solution with me, at least always an acceptable one. What I pushed found an optimal solution with me.
  2. I thought arange would get the last point. my test showed: The last point of np.arange(...) is 5.85, while the real end = (num_nodes - 1) * h = 5.8541. Could this small difference account for what you are seeing?
  3. I just got this error from Github:
    image
    This is the file where I store the solution as initial guess for later runs.. I had no problem with it doing make html ?
    What I mean is this:
    I add the file in my branch.
    Then I switch to the Anaconda Prompt and I give the command make html.
    After this is finished, I go to docs in my explorer, and search for index.html
    There are three of them and the 3rd one is correct. It shows the crane correctly.

Is this Github error a fatal one, so that my simulation cannot be merged like this? Then of course I will change it.
Thanks!

@Peter230655
Copy link
Contributor Author

Peter230655 commented Aug 24, 2024

In this example, I stored the result of a run, and use this result like this: initial_guess = np.load('solution of a previous run').
This makes it a bit faster.
Is this acceptable for examples-gallery? Will examples-gallery 'have' this previous solution?

I think, the failing test is because Github does not 'have' the previous solution ?

# - i4 = [-par_map[l] for _ in range(num_nodes)]
# - i5 = [0.0 for _ in range(5*num_nodes)]
# - i6 = [0.01]
# - initial_guess = np.array(i1 + i2 + i3 + i4 + i5 + i6)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this to a full comment cell as an explanation, something like:

# %%
# This initial guess was used to get a solution which forms the actual initial
# guess::
#
#    i1 = ...
#    i2 = ...


anim = animation.FuncAnimation(fig, update,
frames=np.arange(t0, tf, 1/fps),
interval=1/fps*1000)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The animation does not run until it hits the for me. Maybe it is running out of memory. I suggest reduce the fps and number of frames.

# range(num_nodes)]
# guess::
#
# - i1 = [(ending_location - starting_location)/num_nodes*i for i in range(num_nodes)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spacing needs to be such that it renders as code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@Peter230655 Peter230655 Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spacing needs to be such that it renders as code.

I tried to go as per the sphinx instructions you sent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your idea to execute it but then just overwrite the initial guess with the solution is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your idea to execute it but then just overwrite the initial guess with the solution is better.

I will change this and make a new PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't make new prs for the same topic, just update this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't make new prs for the same topic, just update this one.

Sorry, I meant I would add to this one only!

# 'solution.npy', to speed up convergence. To get this solution, the initial
# guess above was used, which is now overwritten.

initial_guess = np.load('solution.npy')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rename the file to crane_moving_a_load_solution.npy because we will likely add more of these files. I think even another PR has one named the same and they are all in the same directory.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rename the file to crane_moving_a_load_solution.npy because we will likely add more of these files. I think even another PR has one named the same and they are all in the same directory.

Makes emminent sense! Will do so right now

@moorepants
Copy link
Member

You need to git rm solution.npy.

@Peter230655
Copy link
Contributor Author

You need to git rm solution.npy.

..and then push again? (By the time I will be 75 I'll be o.k. :-) )

@moorepants
Copy link
Member

Yes

@Peter230655
Copy link
Contributor Author

Yes

Can I push while the old check is still running?

@moorepants
Copy link
Member

Yes


# %%
# The initial guess is the solution of some previous run, saved in the file
# 'solution.npy', to speed up convergence. To get this solution, the initial
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs updating to new filename.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs updating to new filename.

How come you see EVERYTHING?!? I guess, because you are a teacher by trade. Will change it right now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, maybe grading papers causes that.

@moorepants
Copy link
Member

Thanks! Nice example.

@moorepants moorepants merged commit 276b08d into csu-hmc:master Sep 5, 2024
21 checks passed
@Peter230655
Copy link
Contributor Author

Thanks! Nice example.

Thank YOU! I must admit, it makes me feel good! :-)
What I like about this example: Intuitively I had expected that it would crawl towards the right wall. Actually, it goes full speed!
opty often (in my toy eamples) finds an unexpected (by me) solution.
NB: I am quite pleased that I found a solution to convert the variable h into a correctly rounded number for plot_constraint_violations. :-)

@Peter230655 Peter230655 deleted the plot_crane_moving_a_load branch September 6, 2024 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants