Pages

Sunday, June 15, 2014

Piston + conrod sketch-based animation

This is simple sketch-driver piston + conrod animation. It does not use the assembly module at all. Piston position and conrod position plus rotation are based on the sketch points positions.





Quick demo

Open the following file: conrod-piston-anim.fcstd and paste in the FreeCAD Python console:

import FreeCAD as App, FreeCADGui as Gui, Part, time, sys, math, Draft
from PyQt4 import QtGui,QtCore

class Animation(object):
    def __init__(self):
        App.Console.PrintMessage('init')

        App.ActiveDocument.recompute()

        self.timer = QtCore.QTimer()
        QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.my_update)
        self.timer.start(50)

        self.an = 0.1

    def my_update(self):
        string = '{0}'.format(self.an)
        self.an = self.an + 0.01 if self.an < (2 * math.pi) else 0.0

        angle = math.degrees(self.an)
        App.Console.PrintMessage(str(angle)+" ")
        App.ActiveDocument.getObjectsByLabel("drivingskt")[0].setDatum(5,App.Units.Quantity(str(angle)+' deg'))
        App.ActiveDocument.recompute()
        p1 = App.ActiveDocument.
getObjectsByLabel("drivingskt")[0].Shape.Vertexes[0].Point
        p2 = App.ActiveDocument.
getObjectsByLabel("drivingskt")[0].Shape.Vertexes[1].Point
        p1t = p1 + App.Vector(0, 37, 0)
        conrodangle = math.degrees(Draft.DraftVecUtils.angle(App.Vector(0, 1, 0),(p1-p2)))
        App.ActiveDocument.getObjectsByLabel("Piston")[0].Placement = App.Placement(p1t,App.Rotation(App.Vector(0, 1, 0), 90))
        App.ActiveDocument.getObjectsByLabel("Conrod")[0].Placement = App.Placement(p2,App.Rotation(App.Vector(0, 0, 1),conrodangle))

    def stop(self):
        self.timer.stop()
       

animation = Animation()


To stop the animation, type:

animation.stop()

How-to

You need conrod and piston files (created during making of older tutorials):
Open them in FreeCAD. Create a third, blank file. Next, copy last features from the piston and the conrod to the new file - features names are "Fusion" and "Pocket002" respectively. Confirm dependent object copying.


In the new file you should see both parts (as in the below image). There are some small problems: you don't know conrod length, pin hole in piston position, finally the piston is rotated around Y axis by 90 degrees.
Try change piston rotation, adjusting "Placement" in the Data tab and observe Python console. Setting rotation axis to Y and rotating piston by 90 deg you should see something similar to:
App.Placement((App.Vector(0, 1, 0),App.Rotation(App.Vector(0, 1, 0), 90))
First argument is the piston position (X, Y, Z), second describes rotation axis (0, 1, 0 means Y), third argument is angle in degrees.


The conrod length can be measured using the Part Workbench measure tools. Select the bottom (big end) face and hole cylinder generatrix.


How to find piston pin hole position? Back to the original piston model and find a sketch used to cut the hole or make pin socket. 


Note the sketch (Sketch004) placement.  It is translated -37 mm in Y direction. Notice, in our script, the line:
p1t = p1 + App.Vector(0, 37, 0)


Finally, the most important step, a driving sketch. Create a sketch on the XY plane. 


Sketch a poliline as on above image (0, 0 point is the imaginary crankshaft center, vertical axis is the cylinder symmetry axis). Change the angle constraint and observe Python console output. You should see something similar to:
App.ActiveDocument.Sketch.setDatum(5,App.Units.Quantity('60.000000 deg'))
Note the constraint number (5). It is used in the script in line:
App.ActiveDocument.getObjectsByLabel("drivingskt")[0].setDatum(5,App.Units.Quantity(str(angle)+' deg'))

Rename objects to Conrod, Piston and drivingskt respectively. We will use convenient method getObjectsByLabel() Important: the method returns a list (one or several objects), you need only one object. Use [0] to select the first object, in the script:
App.ActiveDocument.getObjectsByLabel("drivingskt")[0].setDatum(5,App.Units.Quantity(str(angle)+' deg'))
App.ActiveDocument.getObjectsByLabel("Piston")[0].Placement = App.Placement(p1t,App.Rotation(App.Vector(0, 1, 0), 90))
App.ActiveDocument.getObjectsByLabel("Conrod")[0].Placement = App.Placement(p2,App.Rotation(App.Vector(0, 0, 1),conrodangle))

Now you need to know sketch vertices names. Simple preselect a vertex and observe the status bar. You should see the vertex name and position.

The piston position is based on the top vertex (Vertex1 in my example, Vertexes[0], because the list is counted starting from 0):
        p1 = App.ActiveDocument.getObjectsByLabel("drivingskt")[0].Shape.Vertexes[0].Point
        p1t = p1 + App.Vector(0, 37, 0)

        App.ActiveDocument.getObjectsByLabel("Piston")[0].Placement = App.Placement(p1t,App.Rotation(App.Vector(0, 1, 0), 90))

The conrod position is based on the middle vertex (Vertex2, Vertexes[1]):
        p2 = App.ActiveDocument.getObjectsByLabel("drivingskt")[0].Shape.Vertexes[1].Point
        App.ActiveDocument.getObjectsByLabel("Conrod")[0].Placement = App.Placement(p2,App.Rotation(App.Vector(0, 0, 1),conrodangle))


The conrod rotation is calculated using both (Vertex1 and Vertex2) vertices and (0, 1, 0) vector (Y direction):
        p1 = App.ActiveDocument.getObjectsByLabel("drivingskt")[0].Shape.Vertexes[0].Point
        p2 = App.ActiveDocument.
getObjectsByLabel("drivingskt")[0].Shape.Vertexes[1].Point
        conrodangle = math.degrees(Draft.DraftVecUtils.angle(App.Vector(0, 1, 0),(p1-p2)))
        App.ActiveDocument.getObjectsByLabel("Conrod")[0].Placement = App.Placement(p2,App.Rotation(App.Vector(0, 0, 1),conrodangle))



How the sketch angle is changing?
I used timer from Qt libraries. The timer timeout is 50 ms:
self.timer.start(50)

After 50 ms my_update is executed:
QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.my_update)

self.an and angle is the crankshaft angle in radians (self.an is 0.01 to 6.28 with 0.01 step) and degrees respectively:

        self.an = self.an + 0.01 if self.an < (2 * math.pi) else 0.0
        angle = math.degrees(self.an)



That's all. Have fun and check FreeCAD forum and Google+ community!

Inspiration
FreeCAD Python API

FreeCAD
Version: 0.14.3653 (Git)