Closed mooomooo closed 5 months ago
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.
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.
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 acalculate()
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.
@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.
@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:
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, ..."
@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?
It currently appears that the same
Mechanism
object cannot support bothcalculate()
anditerate()
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:
(This may also help with some DRY -- the iterate code could call
calculate()
rather than doing its own math)