Wednesday, March 18, 2015

Please do not ask me questions via e-mail

Private questions and private answers are useless for the community. If you have a problem, ask in the comments or (even better) ask on the FreeCAD Help forum.

Thank you.

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):


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

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

        angle = math.degrees(
        App.Console.PrintMessage(str(angle)+" ")
        App.ActiveDocument.getObjectsByLabel("drivingskt")[0].setDatum(5,App.Units.Quantity(str(angle)+' deg'))
        p1 = App.ActiveDocument.
        p2 = App.ActiveDocument.
        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):

animation = Animation()

To stop the animation, type:



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.
        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:

After 50 ms my_update is executed:
QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.my_update) and angle is the crankshaft angle in radians ( is 0.01 to 6.28 with 0.01 step) and degrees respectively: = + 0.01 if < (2 * math.pi) else 0.0
        angle = math.degrees(

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

FreeCAD Python API

Version: 0.14.3653 (Git)

Tuesday, September 24, 2013

How to test the assembly module [updated]

While assembly tools are not available in the official packages, you can compile FreeCAD yourself  with the assembly module enabled.
Note: the module is unfinished, unstable and buggy. 

Instructions are similar to those provided in the older post. Alternatively you can use the dubstar-04's script.

Update 30.06.2014: This post is outdated now, the assembly module looks different, and there is no jriegel/dev-assembly branch anymore. As stated here:
* jriegel/dev-assembly-old is the old data model with the working solver of ickby and the new PartDesign of jreinlaender
* jriegel/dev-assembly2 is the new data model with the solver temporarelly switched of. Also this branch is at the moment not usably.....

Update: There is more up-to-date GitHub repository

Clone sources:

git clone

Change the directory and checkout the "assembly" branch:

cd FreeCAD_constraint
git checkout assembly

Repeat the normal steps:

mkdir Build && cd Build
cmake - DCMAKE_BUILD_TYPE=Debug ../
make -j8

Additional informations in that forum thread.

Old post:
Install development packages listed on the FreeCAD wiki. Then you can clone the sources from GitHub:

git clone git:// free-cad-code

Change the directory:

cd free-cad-code/

Now you can list available remote branches:

git branch -r
  origin/HEAD -> origin/master

Switch to the "jriegel/dev-assembly" branch:

git checkout jriegel/dev-assembly

Create a new directory for a build:

mkdir Build && cd Build

Run cmake and enable debug build (FreeCAD may be a little slower but easier for debugging):

cmake - DCMAKE_BUILD_TYPE=Debug ../

Compile, the number should be equivalent to the number of threads in your CPU:

make -j8

Run FreeCAD:


Example 1: an assembly created from external STEP files.

You have to download four STEP files:
All parts (piston, conrod, pin and pinring) was created in FreeCAD and then exported to the STEP files.

Open the Assembly module and create a new file. Double click on the Product in the Tree view to activate it. If the Product does not exist, you can create one choosing Add a new Component.
Next we have to add an part from an external file. Select Add a existing Component or File to the active Assembly and choose the piston.stp file.

Select a surface on the piston and use the Fix a part in its rotation and translation constraint. The piston cannot move now. Notice the new constraint in the Tree view.

In a exactly same way add the pin.stp file. You need two constraints between piston and pin.

Select a side face of pin and a corresponding face of piston (inside pin ring groove) using [Ctrl]. Then use the Align the selected entities constraint.
You have three options:
  • Equal - both faces normal vectors are equal,
  • Opposite - vectors are opposite,
  • Parallel - vectors are equal or opposite. 
In our case please select Equal.

In the next step select cylinder surfaces on pin and inside piston. Use the Make the selected entities coincident (Equal).

Please add conrod.stp selecting Add a existing Component or File to the active Assembly. As in earlier pair use the Align the selected entities (but this time select "Opposite) and the Make the selected entities coincident.

The conrod can be rotated in a specified angle relative to piston. To set the angle select the bottom conrod face.

Then, holding [Ctrl] key, select the top piston face and use the Set the angle between two selected entities. The angle will be 160.

Finally you have to insert pin rings. The procedure is similar to pin adding. You need an align constraint.

And a coincident constraint.

Repeat the procedure for the second pin ring.

Download file.

Download RAW Video. (Right click to save as...)

Example 2: an assembly created from the Part Design-created files.

In this example we will create an assembly containing fully parametric, Part Design parts. They will be created during assembly designing process - there is no graphical import tool for files containing feature tree yet.

Open the Assembly workbench and activate the Product object (double click in the Tree view). Add two new parts using Add a new Part into the active Assembly.

Add two new parts using Add a new Part into the active Assembly.

Expand the first part (click on the small triangle in the tree) and activate the Body. An active workbench should be changed to Part Design automatically. The Body is a container for all features.  

Select the XY plane and create a new sketch. Important: in the current implementation first sketch have to lie on XY. Subsequent sketches can be placed on other planes.

Draw a rectangle, constrain it and close the sketch.

Pad the sketch.

Select a face on the cuboid and place a second sketch.

Sketch a circle.

Use the Pocket tool to create an hole.

You finished the first part, let's try create another one. Expand Part001 and double click on the Body001 to activate it.

As before, select the XY plane and sketch a rectangle.

Revolve the rectangle on horizontal axis to create a cylinder.

You have two parts: a box with a hole and a cylinder. Try place the cylinder in the hole. Double click on the Product in the tree to activate. The Assembly workbench should be active now.

Select a surface on the box and use the Fix a part in its rotation and translation constraint.
Select cylinder surfaces on the both parts with [Ctrl] and use the Make the selected entities coincident (Parallel) tool.

Next use planar surfaces to align parts.

What if you want change something? Try translate the hole position. Activate the Body and then the Sketch001 inside the Pocket feature.

Edit the circle position and close the sketch.

Note: editing works fine only for features placed on the XY plane at this moment. This is a known issue.

Download file, download video.

More information

Assemebly Basic Tutorial - FreeCAD wiki
PartDesign Bearingholder Tutorial I (very useful!)
PartDesign Bearingholder Tutorial II

Used FreeCAD version:

Version: 0.14.2654 (Git)
Branch: jriegel/dev-assembly
Hash: 3d4dd4f64f24255dbaec25767cff7b74bb98fc6d

Trivia: today examples was created on fully open machine (except x86 microcode and some firmware;) - FreeCAD on Ubuntu x86-64 with R600g graphics driver.