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

[Refactor] Allow both solving and animation #5

Closed
mooomooo opened this issue Jun 1, 2024 · 7 comments
Closed

[Refactor] Allow both solving and animation #5

mooomooo opened this issue Jun 1, 2024 · 7 comments
Labels
documentation Improvements or additions to documentation enhancement New feature or request

Comments

@mooomooo
Copy link

mooomooo commented Jun 1, 2024

It currently appears that the same Mechanism object cannot support both calculate() and iterate() operations simultaneously -- Mechanism.pos is assumed to be unchanging (or at least always of the same type). It would be helpful if the class object itself left the independent variables unspecified, so that the same object could be used to solve for multiple specific configurations, or looped to make an animation / result table, and so on back and forth.

example proposed use case:

  • Define joints, links, loop function
  • Create Mechanism object
  • Calculate configuration for input = (0, 0)
  • Animate configurations for input = (linspace(0,1,10), 0)
  • Animate configurations for input = (0, linspace(0,2*pi,50))
  • Calculate configuration for input = (0.5, pi/2)
  • ... (etc)

(This may also help with some DRY -- the iterate code could call calculate() rather than doing its own math)

@gabemorris12
Copy link
Owner

gabemorris12 commented Jun 1, 2024

I definitely agree that the iterate() function should simply call calculate(). I wrote this code during my getting the feet wet part of learning python, so I was still pretty new to everything. Now then, I think you can still do what you're asking as it is. Let me whip up an example maybe tomorrow. I think I need to add better documentation, but there is a way to clear the mechanism for following calls of iterate() and calculate(). I think I at least had this in mind as I was coding this.

@gabemorris12 gabemorris12 added documentation Improvements or additions to documentation enhancement New feature or request labels Jun 1, 2024
@mooomooo
Copy link
Author

mooomooo commented Jun 1, 2024

Thanks for your prompt reply! I forgot to add another operation that would be nice -- plotting an individual configuration, i.e. some kind of show() after a calculate() that's not animated.

@gabemorris12
Copy link
Owner

Thanks for your prompt reply! I forgot to add another operation that would be nice -- plotting an individual configuration, i.e. some kind of show() after a calculate() that's not animated.

This definitely exists. Check the plot_at_instant example in the examples folder. This is the distinguishing factor between calculate() and iterate(). My intention is to use the latter for animations only and the other for just looking at a snapshot in time.

@gabemorris12
Copy link
Owner

@mooomooo There's another feature that I haven't really advertised very well, but you can play/pause and go frame by frame in an animation with the arrow keys and space bar. This is available only on the latest 1.1.8 version, which I just published to pypi a few minutes ago. So, you can do pip install mechanism==1.1.8. This to me is a game changer and is what I would expect people to do if they wanted to see the mechanism at different configurations as you are suggesting. For all the versions before 1.1.8, if you have your mouse, you can use the left and right clicks to go frame by frame and the scroll wheel to play pause. I changed it to key bindings so that this can happen without the mouse.

I am planning on making a YouTube video on this repository tomorrow.

@gabemorris12
Copy link
Owner

@mooomooo This script below will perform what I believe you are asking. The only thing that you are mentioning that it does not address is the multiple uses of both iterate and calculate. I do not intend on adding that specifically because I cannot think of an instance where this is necessary and/or worth the added effort. I think the play pause in the animation is more than able to accomplish your specific needs. If I am wrong about this, then please let me know. If not then I am ready to close the issue.

Check out this script of a four bar linkage to address your specific question about plotting with no animation and at multiple positions without the need to reinstantiate the mechanism object. The only gotcha is to call the clear_joints() method between different calls of calculate(). Though, I did come across a bug, so be sure you're using 1.1.9 now.

from mechanism import *
import numpy as np
import matplotlib.pyplot as plt

# Declare the joints that make up the system.
O, A, B, C = get_joints('O A B C')

# Declare the vectors and keep in mind that angles are in radians and start from the positive x-axis.
a = Vector((O, A), r=5)
b = Vector((A, B), r=8)
c = Vector((O, C), r=8, theta=0, style='ground')
d = Vector((C, B), r=9)

# Define the known input to the system.
angular_velocity = 50*np.pi/3  # This is 500 RPM in rad/s

theta = np.deg2rad(30)  # Initial position of the crank

# Guess the unknowns
pos_guess = np.deg2rad([45, 90])
vel_guess = np.array([1000, 1000])
acc_guess = np.array([1000, 1000])


# Define the loop equation(s)
def loop(x, i):
    return a(i) + b(x[0]) - c() - d(x[1])


# Create the mechanism object
mechanism = Mechanism(vectors=(a, b, c, d), origin=O, loops=loop, pos=theta, vel=angular_velocity, acc=0,
                      guess=(pos_guess, vel_guess, acc_guess))

for angle in [30, 45, 60]:
    mechanism.pos = np.deg2rad(angle)
    mechanism.clear_joints()
    mechanism.calculate()
    fig, ax = mechanism.plot(velocity=True, acceleration=True)
    ax.set_title(f'Crank at {angle} degrees')

# Show the animation
plt.show()

This will output the following frames:
scratch30
scratch45
scratch60

@mooomooo
Copy link
Author

mooomooo commented Jun 1, 2024

Thanks @gabemorris12, these examples are super helpful! It addresses part of my hopeful use case, and being able to step through an animation is definitely a nice touch.

multiple uses of both iterate and calculate

The main point of this would be for non-interactive scripting use, e.g. preparing a design report on a particular mechanism (probably after you're done playing around in real time). Once you've settled on the final design, you could run a script to generate and save all the necessary outputs at once to be able to say "See attached movie 1 to show moving the slider only, movie 2 for turning the knob only, figure 1 for both widgets at position 0, figure 2 for slider at 0 and knob at 11, ..."

@gabemorris12
Copy link
Owner

@mooomooo I think the main issue with this is the storing of information. The way things are set up now, I am just modifying attributes of the vector instances. So in the above example, I am overwriting the attributes of the vectors with each call of calculate(). With your application, I think it would be better to work with vector copies and have multiple instances of the overall mechanism object. This will make it to where you can access the different outputs for each input in memory. Just curious, what are you making?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants