-
Notifications
You must be signed in to change notification settings - Fork 20
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
Illustrative ex for viz #11
base: master
Are you sure you want to change the base?
Changes from 8 commits
06398c4
11a0059
d5f2d44
a61e602
e69d5cf
89db91f
b10d0e5
b1a923c
aa6a338
17f8f0b
5a79f75
134bf88
f769203
31e7b65
7c348e9
9c10bf6
bcbca28
f0aa71b
3c82531
8441d29
ae2a57b
e60fe75
f950c97
cb5ba46
8314218
d11bd84
5ef35c4
733f2ca
85b715e
9fcf6bb
4ea2488
0fec414
18ad2b2
cde87c8
53bddd4
896a29f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
#This file contains all the essential methods for the | ||
#illustrative example | ||
#It only contains the basic implementations of pydy-viz | ||
#also it doesnot check for TypeErrors etc. | ||
|
||
class MeshShape(object): | ||
def __init__(self,name,point_list,color='grey',origin=[0,0,0]): | ||
self._name = name | ||
self._points = point_list | ||
self._color = color | ||
self._origin = origin | ||
|
||
@property | ||
def name(self): | ||
return self._name | ||
|
||
@name.setter | ||
def name(self,new_name): | ||
self._name = new_name | ||
|
||
@property | ||
def points(self): | ||
return self._points | ||
|
||
@points.setter | ||
def points(self,new_point_list): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's a bad idea that this field exists, but I think the more likely use case would be that someone makes an OBJ file in blender and wants to create a MeshShape from that file. So maybe there should be a constructor that takes an OBJ file and generates a point list from that, or that there's a separate shape that is an OBJShape or something. That would not be a high priority at all. Just running my mouth (my fingers?). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it will should be, we should have both options, this setter, and the import_from_obj() method . |
||
self._points = new_point_list | ||
|
||
@property | ||
def color(self): | ||
return self._color | ||
|
||
@color.setter | ||
def color(self,new_color): | ||
self._color = new_color | ||
|
||
@property | ||
def origin(self): | ||
return self._origin | ||
|
||
@color.setter | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. color.setter? what does that have to do with the origin |
||
def origin(self,new_origin): | ||
self._origin = new_origin | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will need to document the format/type for this attribute well. |
||
|
||
|
||
class VisualizationFrame(object): | ||
def __init__(self,name,rigidbody,shape=None): | ||
#It is only to be used for rigidbody here, as per the | ||
#specific requirements of the illustrative example | ||
self._name = name | ||
self._reference_frame = rigidbody.get_frame() | ||
self._point = rigidbody.get_masscenter() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't remember if we were going to rename this to |
||
self._shape = shape | ||
self._transformation_matrix = \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. np.eye(4) |
||
[[1, 0, 0, 0], \ | ||
[0, 1, 0, 0], \ | ||
[0, 0, 1, 0], \ | ||
[0, 0, 0, 1]] | ||
|
||
def homogeneous_transformation(self,rframe): | ||
rotation_matrix = self._reference_frame.dcm(rframe) | ||
for i in range(0,3): | ||
for j in range(0,3): | ||
self._transformation_matrix[i][j] = rotation_matrix | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do you mean: You can avoid the loops with clever indexing: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why call the attribute There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that clever indexing perk , Thats why I just love python .. :-) |
||
return self._transformation_matrix | ||
|
||
def add_simulation_data(self,file_name=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure why this is here either. See comment below about numerics. |
||
#TODO | ||
pass | ||
|
||
def generate_json(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Look at built-in JSONEncoder class. Just an aside. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that generate_json belongs in the VizFrame class. The json file will contain numerics and you don't get numerics until you deal with simulate and scene. Seems like the scene should generate all of the json stuff. What will this actually do in this class? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when we provide a add_simulation_data to Vframe class instance, we provide a filename, where numerical data is stored .. [vframe1: {name: ..., shape: ... , shape_related_numerics: ..., simulation_data: /filename.json , tranform_matrix:[ [],[],[]] ..] and when we call [Scene1:{width:800,heigth:800, ... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider making this method hidden; only the Scene's generate_json() needn't be hidden. Also I don't think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we are also having confusion about where the symbolic side mechanics changes to numerics. I don't see why VizFrame should have any numerical values. So if there are no numeric values then generating a json file will only have metadata at the VizFrame level. This is what I'm imagining: # the viz frame has symbolics and the main purpose is to provide a symbolic transformation matrix
viz_frame = VisualizationFrame(shape, shape_reference_frame, shape_point)
# the we add these to the scene so we have all the info for all shapes in the scen
scene = Scene(newtonian_reference_frame, origin_point, viz_frames=(viz_frame,))
# now when we simulate the system we can tell it there is an animation scene associated with this
data = scene.simulate(kane, and_any_other_stuff_thats_needed)
# the simulation data is numerical time series for everything that we need for the three.js
jsonified_data = scene.jsonify(data)
# it uses the data in the json description to pass to the js code and three.js
scene.show() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah okay. In my head, I thought that the symbolic equations would end up in some javascript code somewhere, and the constants and state values (data) would be available in the javascript code also. Then, the JSON file contains constants, and symbolic equations, but not time series data. But the way you describe this, Jason, is that the JSON actually contains time series data. This mostly makes sense to me. Two questions:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and_any_other_stuff_thats_needed also includes masses and other numerical data(gravity etc.) |
||
#TODO | ||
pass | ||
|
||
class Scene(): | ||
def __init__(self,name,reference_frame,origin,height=800,width=800): | ||
self._name = name | ||
self._reference_frame=reference_frame | ||
self._origin = origin #contains point | ||
self._child_vframes = [] | ||
self._height = height | ||
self._width = width | ||
|
||
@property | ||
def vframes(self): | ||
return self._child_vframes | ||
@vframes.setter | ||
def vframes(self,vframes): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion, this property shouldn't have a setter. Instead there should be a method I also discourage the use of the |
||
for vframe in vframes: | ||
self._child_vframes.append(vframe) | ||
|
||
|
||
def generate_json(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The package / module should be set up so that classes MUST implement this method; this could be done using the ABC module, and something like a parent object "Object" or something. Haven't thought about this too much. But say someone wants to extend your work; they shouldn't be able to create a new subclass of Shape, for example, without defining this method. |
||
#TODO | ||
pass | ||
|
||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#Export method calls and namespace | ||
# from three_link_pendulum example .. | ||
|
||
from three_link_pendulum import * | ||
from essential import * | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It isn't the best practice to use the from essential import something, another_thing then the code is more coherent. |
||
|
||
#setting some shapes for the pendulums .. | ||
|
||
shape1 = MeshShape('shape1',[[1,1,1], [0,1,1], [2,1,1]], \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make these variable names descriptive |
||
color='red',origin=[0,0,0]) | ||
|
||
shape2 = MeshShape('shape2',[[1,1,1], [0,1,1], [2,1,1]], \ | ||
color='blue',origin=[0,0,0]) | ||
|
||
shape3 = MeshShape('shape3',[[1,1,1], [0,1,1], [2,1,1]], \ | ||
color='green',origin=[0,0,0]) | ||
|
||
#Setting up some vframes ... | ||
|
||
frame1 = VisualizationFrame('frame1', link1, shape=shape1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is there no point defintion here? Don't you need a vector to locate the VizFrame in the Scene? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The point is taken automatically from link1, which is a rigidbody. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so why do all the links show up in the same position in the visualization? Shouldn't they be spaced out and look like a chain of cylinders? Also, add spheres for the three particles. |
||
print frame1.homogeneous_transformation(I) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking about it more, it might be okay if we just call this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will call it transformation() meanwhile .. |
||
|
||
frame2 = VisualizationFrame('frame2', link2, shape=shape2) | ||
frame3 = VisualizationFrame('frame3', link3, shape=shape3) | ||
|
||
scene = Scene('scene1',I,O) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The scene might be able to define its base frame and the O on its own; and just let the user access them if necessary. Haven't thought too much about this though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you'll have to pass in a frame and point. Not sure where these would come from other wise? You can get the frame from the KanesMethod object if that was around anywhere, but the point isn't anywhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the point is something that is necessarily part of the dynamics derivation? I was thinking the Scene could just create it, but I suppose if the rigidbodys' dynamics depend on that point, it needs to be given to the Scene. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The point I'm imagining is the "origin" of the global reference frame that three.js uses. We need map that point in three.js to a point we define with mechanics. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay I think that makes sense to me. |
||
scene.vframes = [frame1,frame2,frame3] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better if there was not a setter like this; see other comment. |
||
print scene.vframes | ||
scene.generate_json() | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
#For this one we assume RigidBody links | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this file here? I though you have this in another pull request. Do not include it in both PR's. |
||
from sympy import symbols,sympify | ||
from sympy.physics.mechanics import * | ||
|
||
#Number of links = 3 | ||
N_links = 3 | ||
|
||
#Number of masses = 3 | ||
N_bobs = 3 | ||
|
||
#Defining Dynamic Symbols ................ | ||
|
||
#Generalized coordinates(angular) ... | ||
|
||
alpha = dynamicsymbols('alpha1 alpha2 alpha3') | ||
beta = dynamicsymbols('beta1 beta2 beta3') | ||
|
||
#Generalized speeds(angular) ... | ||
alphad = dynamicsymbols('alpha1 alpha2 alpha3',1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are not technically the generalized speeds. The generalized speeds should be defined as: omega = alpha' You need to define some generalized speeds, not just the derivatives of the coordinates. so: omega1, omega2, omega3 = dynamicsymbols('omega1 omega2 omega3') And similarly for beta. |
||
betad = dynamicsymbols('beta1 beta2 beta3',1) | ||
|
||
#Mass of each bob: | ||
m = symbols('m:'+str(N_bobs)) | ||
|
||
|
||
#Length and mass of each link .. | ||
l = symbols('l:' + str(N_links)) | ||
M = symbols('M:' + str(N_links)) | ||
#For storing Inertia for each link : | ||
Ixx = symbols('Ixx:'+str(N_links)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PEP8: spaces around operators |
||
Iyy = symbols('Iyy:'+str(N_links)) | ||
|
||
#gravity and time .... | ||
g, t = symbols('g t') | ||
|
||
|
||
#Now defining an Inertial ReferenceFrame First .... | ||
|
||
I = ReferenceFrame('I') | ||
|
||
#And some other frames ... | ||
|
||
A = ReferenceFrame('A') | ||
A.orient(I,'Body',[alpha[0],beta[0],0],'ZXY') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PEP8: space after comma |
||
B = ReferenceFrame('B') | ||
B.orient(I,'Body',[alpha[1],beta[1],0],'ZXY') | ||
C = ReferenceFrame('C') | ||
C.orient(I,'Body',[alpha[2],beta[2],0],'ZXY') | ||
|
||
|
||
#Setting angular velocities of new frames ... | ||
A.set_ang_vel(I, alphad[0] * I.z + betad[0] * I.x) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these should be A.set_ang_vel(I, omega1 * I.z + gamma1 * I.x) |
||
B.set_ang_vel(I, alphad[1] * I.z + betad[1] * I.x) | ||
C.set_ang_vel(I, alphad[2] * I.z + betad[2] * I.x) | ||
|
||
|
||
|
||
# An Origin point, with velocity = 0 | ||
O = Point('O') | ||
O.set_vel(I,0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PEP8, space after comma |
||
|
||
#Three more points, for masses .. | ||
P1 = O.locatenew('P1', l[0] * A.y) | ||
P2 = O.locatenew('P2', l[1] * B.y) | ||
P3 = O.locatenew('P3', l[2] * C.y) | ||
|
||
#Setting velocities of points with v2pt theory ... | ||
P1.v2pt_theory(O, I, A) | ||
P2.v2pt_theory(P1, I, B) | ||
P3.v2pt_theory(P2, I, C) | ||
points = [P1,P2,P3] | ||
|
||
Pa1 = Particle('Pa1', points[0], m[0]) | ||
Pa2 = Particle('Pa2', points[1], m[1]) | ||
Pa3 = Particle('Pa3', points[2], m[2]) | ||
particles = [Pa1,Pa2,Pa3] | ||
|
||
|
||
|
||
#defining points for links(RigidBodies) | ||
#Assuming CoM as l/2 ... | ||
P_link1 = O.locatenew('P_link1', l[0]/2 * A.y) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PEP8: Space around operators. |
||
P_link2 = O.locatenew('P_link1', l[1]/2 * B.y) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be: P_link2 = P1.locatenew('P_link1', l[1] / 2 * B.y) |
||
P_link3 = O.locatenew('P_link1', l[2]/2 * C.y) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be: P_link3 = P2.locatenew('P_link1', l[2] / 2 * C.y) |
||
#setting velocities of these points with v2pt theory ... | ||
P_link1.v2pt_theory(O, I, A) | ||
P_link2.v2pt_theory(P_link1, I, B) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P_link1 and P_link2 do not exist on the same rigid body so this is incorrect. It should be: P_link2.v2pt_theory(P1, I, B) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did you meant do not exist on same referenceframe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rigid body and reference frame are synonymous in this case. The point should be the one that is at the joints. |
||
P_link3.v2pt_theory(P_link2, I, C) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above. |
||
|
||
points_rigid_body = [P_link1,P_link2,P_link3] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PEP8: Spaces after commas. |
||
|
||
|
||
#defining inertia tensors for links | ||
|
||
inertia_link1 = inertia(A,Ixx[0],Iyy[0],0) | ||
inertia_link2 = inertia(B,Ixx[1],Iyy[1],0) | ||
inertia_link3 = inertia(C,Ixx[2],Iyy[2],0) | ||
|
||
#Defining links as Rigid bodies ... | ||
|
||
link1 = RigidBody('link1', P_link1, A, M[0], (inertia_link1, O)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you define your inertia about the origin? Isn't the better way to define your inertia about the center of mass of each link. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I defined on the rotation axis of each rigidbody, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "clean" way to define your inertia tensor is about the body's reference frame and the center of mass. (at least in this case). Other times it may be useful to define inertia about other points, depending on the problem. |
||
link2 = RigidBody('link2', P_link2, B, M[1], (inertia_link2, P1)) | ||
link3 = RigidBody('link3', P_link3, C, M[2], (inertia_link3, P2)) | ||
links = [link1,link2,link3] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PEP8 |
||
|
||
|
||
#Applying forces on all particles , and adding all forces in a list.. | ||
forces = [] | ||
for particle in particles: | ||
|
||
mass = particle.get_mass() | ||
point = particle.get_point() | ||
forces.append((point, -mass * g * I.y) ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A one liner: forces = [(p.get_point(), -p.get_mass() * g * I.y) for p in particles] |
||
|
||
#Applying forces on rigidbodies .. | ||
for link in links: | ||
mass = link.get_mass() | ||
point = link.get_masscenter() | ||
forces.append((point, -mass * g * I.y) ) | ||
kinetic_differentials = [] | ||
for i in range(0,N_bobs): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
kinetic_differentials.append(alphad[i] - alpha[i]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be kinetic_differentials.append(omega1 - alphad[i]) omega = alpha' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And it really should be |
||
kinetic_differentials.append(betad[i] - beta[i]) | ||
|
||
#Adding particles and links in the same system ... | ||
total_system = [] | ||
for particle in particles: | ||
total_system.append(particle) | ||
|
||
for link in links: | ||
total_system.append(link) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be a one liner:
|
||
|
||
q = [] | ||
for angle in alpha: | ||
q.append(angle) | ||
for angle in beta: | ||
q.append(angle) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. q = alpha + beta There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you may have to do: q = list(q) + list(beta) |
||
print q | ||
u = [] | ||
|
||
for vel in alphad: | ||
u.append(vel) | ||
for vel in betad: | ||
u.append(vel) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. u = omega + delta |
||
|
||
print u | ||
kane = KanesMethod(I, q_ind=q, u_ind=u, kd_eqs=kinetic_differentials) | ||
fr, frstar = kane.kanes_equations(forces, total_system) | ||
|
||
print fr | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
This is a 3 link pendulum problem, assuming links as RigidBodies .. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this file here? |
||
I will add a fig soon .. |
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.
spaces after commas