Monday, 30 December 2013

Mechanism animation with PyQtGraph

Linux isn't only FreeCAD.

I have always wanted to create animated mechanisms using python and a plot library. Not only the mechanisms, but a small program that could be used without typing a word of code at all (a personal project).
I found matplotlib very similar to matlab, but not very well suited to create a graphical interface with it. 
But yesterday I tried PyQtGraph and was amazed with how easy is to create dialog boxes and very complex plots, plus people says is faster than matplotlib. 
The con has been a step learning curve.

The install was easy downloading the deb package from the site and using gdebi. I did not need anything extra to get it working.

Slider-Crank Mechanism

Once installed PyQtGraph, open a terminal (ctrl+alt+t) and type "python" (without quotes), then copy-paste this code:


import math
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui

window=pg.plot(title="Crank-Slider Mechanism")


i=0

def update():  
  global i
  x1=[0,math.cos(i),2+math.cos(i)]
  y1=[0,math.sin(i),0]
  
  if(i<360):
    i+=0.003
  else:
    i=0
    
  window.plot(x1,y1,clear=True)

time=QtCore.QTimer()

time.timeout.connect(update)
time.start(0.1) 

  
A window like this should appear in your screen with a slider-crank mechanism moving inside.


Step by Step

To create a plot window:

window=pg.plot(title="Crank-Slider Mechanism")

"window" is the variable where we save the plot window, pg is the name we give to the imported pyqtgraph library and the part inside brackets gives name to the plot window.

The function "update()" plots the mechanism advancing one step every time is called. The crank angle (i) is increased until it reaches 360º, then i resets to 0.

x1 and y1 are x and y point values at every step. This is no the "formal" way of doing this things, but is easy and fast.  If someone wants to dig more, crank is 1 unit long and crank to slider bar is 2 units long.

window.plot(x1,y1,clear=True)

It plots x,y data inside "window" and with the parameter "clear" it erases previous plot before plotting the new one.

The timer events is the most obscure part of the script, what I think it does is:

time=QtCore.QTimer()  # Creates a timer

time.timeout.connect(update) # Executes a function when time is up

time.start(0.1) # Starts timer and counts to 0.1 milliseconds


And this is all I have learnt of pyqtgraph at the moment, next step could be something like dialog boxes that modify bar dimensions.  

Hope you could see the mechanism moving, if not, comment something. (If you liked it, you can comment too ;) )

bye!

Tuesday, 24 December 2013

Bearings from scripted sketches

Hi all, I'm back. I have been very busy with my college but now I have a little time to play with this things again.

Let's script!

Latest thing i did was about increasing the speed of the bearing generator macro. I realized that any boolean involving a torus will spend a lot of time to generate.

Torus is needed to create the groove of the ball bearing and the only way I've imagined to do something similar without using torus was creating the bearing ring profile and revolving.

This is what I am going to explain in this post, how to create bearings using scripted sketches.

Note: full code at the bottom

This picture should explain easily how we want to achieve that:


Area A equals to the profile of the outter ring, area B to the profile of the inner ring and C is the groove of the bearing. 

A and B rectangles have they edges opened because we want to round them (once we are sketching, is just easier this way than using booleans for rounding corners). This rounding is accomplished by arcs.

I'm explaining only one ring with detail, inner and outter ring are actually the same process (copy-paste)

First of all, libraries needed:

import Part
import math
from FreeCAD import Base

Profile definition: Lines and Arcs


To introduce a line object from A to B in FreeCAD by command line type:

LineName=Part.makeLine((AX,AY,AZ),(BX,BY,BZ))


To create an arc object with radius, center position, orientation and angle, do:

ArcName=Part.makeCircle(Radius, Base.Vector(CenterX,CenterY,CenterZ), BaseVector(DirectionX,DirectionY,DirectionZ), StartingAngle, EndingAngle)


The vector BaseVector(DirectionX,DirectionY,DirectionZ)  must be perpendicular to the plane where we want the arc to be placed.
For example, if we ant the arc to be in a XY plane, one perpendicular vector would be (0,0,1).

Starting angle and ending angle provides the ability to create arcs and is also dependent of the plane where we have placed the arc.  This is harder to explain but anyone wanting to go further just read a bit about cross product

For what we are doing here, our direction is (0,0,1) which makes starting and ending angles to be specified in the normal counter-clockwise format.




Now take some time to obtain the points A to B of every line (z always 0):

A means starting point.
B means ending point.

Line 1 (L1):

L1 A X position= r1
L1 A Y position= Radial thickness

L1 B X position= Bearing thickness - r1
L1 B Y position= Radial thickness

L1 command:

L1=Part.makeLine((r1, Radial thickness, 0), (Bearing thickness - r1, Radial thickness, 0))

Line 2 (L2):

L2 A X position=Bearing thickness
L2 A Y position=Radial thickness - r1

L2 B X position=Bearing thickness
L2 B Y position=r2

L2 command:

L2=Part.makeLine((Bearing thickness, Radial thickness - r1), (Bearing thickness, r2))


This is more tedious than complicated, I think is easy to continue and create the 2 lines left. If you have problems, please, comment.


Arc

Arc is more less the same but has an extra feature that is the starting and ending point.

To create the upper left arc:

Center position:

X=r1
Y=Radial thickness - r1
Z=0

Angle:

Start angle*=90º
End angle*=180º

*using vector (0,0,1) as arc placement

Arc command:

A1=Part.makeCircle(r1, Base.Vector(r1, Radialthickness - r1, 0), Base.Vector(0,0,1), 90, 180)


Upper right arc:

Center position:

X=Bearing thickness - r1
Y=Radial thickness - r1
Z=0

Angle:

Start angle*=0º
End angle*=90º

*using vector (0,0,1) as arc placement

Arc command:

A1=Part.makeCircle(r1, Base.Vector(Bearing thickness - r1, Radialthickness - r1, 0), Base.Vector(0,0,1), 0, 90)


The same as lines, I think is easy to create the two arc remaining.


Now we have arcs and lines that aren't nothing more by themselves. To link everything together, we create a wire.

Wire command is easy:

Wiredthing=Part.Wire( [ thingA, thingB, thingC, ... , thing N ] )


If you followed my naming method, your wire command should be something like:

ProfileWire=Part.Wire( [L1,A1,L2,A2,L3,A3,L4,A4] )


From wire we can create a face by typing:

ProfileFace=Part.Face(ProfileWire)


If you give some value to Radial thickness and Bearing thickness, this should be the output of the whole thing above (remember Part.show(NAME) to make it visible):


You can see here the orientation:



Faces can be involved in boolean operations and to create the groove of the bearing, we are going to cut our surface with a circle.

To create a circle:

Circle=Part.makeCircle(CircleRadius, Base.Vector(0,0,0), Base.Vector(0,0,1),0, 360)

Circle=Part.Wire([Circle])

Circle=Part.Face(Circle)

Part.show(Circle)




Positioning and cutting: 

This is exactly the same that has been used in previous scripts. 

To move something just:

TraslationVector(X, Y, Z)

Something.translate(TranslationVector)


To cut two parts:

CuttedPart=PartA.cut(PartB)

Where par B cuts partA.


Be careful when moving our ring section because the translation vector will position the parte from the relative zero where it was created.
The circle relative zero is positioned on its center.


At the end we should se something like this (depending where you have moved your circle you will see something different or nothing at all if both surfaces do not intersect):



The outer ring profile is done with this steps. Inner ring is almost equal, if you have problems after reading the full code, comment.


Revolving faces

With the profile defined and ready, we can go from 2d to 3d by revolving the face. 

Command:

RevolutedFace=Face.revolve(Base.Vector(0,0,0), Base.Vector(360,0,0))

First Base.Vector has something to do about axis scale, we do not want to distort our face so just keep it (0,0,0). Second Base.Vector tells the rev axis and how many degrees of revolution we want. 360 means full revolution.


Screenshot:



Few milliseconds and we have the outer ring, with groove and rounded corners. This could take up to 4-5 seconds when using the torus method. 



Using the same method to the inner ring and adapting the ball generator from previous scripts, we have a bearing like this one:



Not too different from previous versions, but faster to load. 

Plus this method gives me the ability to create new types of bearings with increased complexity, like conical thrust bearing. 


CODE

This code, as is, will generate the above bearing and any other you need only changing the input parameters.



#############################################################
#######  JMG December 2013 Linux Mint 15 KDE  ###############
#############################################################

import Part
import math
from FreeCAD import Base

################  INPUT PARAMETERS  #####################

rout=20             ###External radius
rin=10              ###Inner radius
bth=8               ###Bearing thickness

##########################################

DeltaR=rout-rin
Lb=((rout-rin)/2)+rin
Rb=0.3*bth

##############  Outer ring (a)  ##############################:

r1a=1
r2a=0.5
btha=bth                  ###UPPER RING THICKNESS
rtha=rout-Lb-0.8*Rb       ###UPPER RING RADIAL THICKNESS

#Lines

L1a=Part.makeLine((r1a,rtha,0),(btha-r1a,rtha,0))

L2a=Part.makeLine((btha,rtha-r1a,0),(btha,r2a,0))

L3a=Part.makeLine((btha-r2a,0,0),(r2a,0,0))

L4a=Part.makeLine((0,r2a,0),(0,rtha-r1a,0))

#Corner rounding

A1a=Part.makeCircle(r1a,Base.Vector(btha-r1a,rtha-r1a,0),Base.Vector(0,0,1),0,90)

A2a=Part.makeCircle(r2a,Base.Vector(btha-r2a,r2a,0),Base.Vector(0,0,1),270,360)

A3a=Part.makeCircle(r2a,Base.Vector(r2a,r2a,0),Base.Vector(0,0,1),180,270)

A4a=Part.makeCircle(r1a,Base.Vector(r1a,rtha-r1a,0),Base.Vector(0,0,1),90,180)

##################

Ur=Part.Wire([L1a,A1a,L2a,A2a,L3a,A3a,L4a,A4a])

Ur=Part.Face(Ur)

VUR=(0,rout-rtha,0)

Ur.translate(VUR)

################Inner ring(b):################################:

r1b=0.5
r2b=0.5
bthb=bth
rthb=rtha

#Lines

L1b=Part.makeLine((r1b,rthb,0),(bthb-r1b,rthb,0))

L2b=Part.makeLine((bthb,rthb-r1b,0),(bthb,r2b,0))

L3b=Part.makeLine((bthb-r2b,0,0),(r2b,0,0))

L4b=Part.makeLine((0,r2b,0),(0,rthb-r1b,0))

#Corner rounding

A1b=Part.makeCircle(r1b,Base.Vector(bthb-r1b,rthb-r1b,0),Base.Vector(0,0,1),0,90)

A2b=Part.makeCircle(r2b,Base.Vector(bthb-r2b,r2b,0),Base.Vector(0,0,1),270,360)

A3b=Part.makeCircle(r2b,Base.Vector(r2b,r2b,0),Base.Vector(0,0,1),180,270)

A4b=Part.makeCircle(r1b,Base.Vector(r1b,rthb-r1b,0),Base.Vector(0,0,1),90,180)

###########################################################

Ir=Part.Wire([L1b,A1b,L2b,A2b,L3b,A3b,L4b,A4b])

Ir=Part.Face(Ir)

VIR=(0,rin,0)

Ir.translate(VIR)


####################  GROOVE  #############################


Rb=0.25*(rout-rin)
  
groove=Part.makeCircle(Rb,Base.Vector(0,0,0),Base.Vector(0,0,1),0,360)
  
groove=Part.Wire([groove])
  
groove=Part.Face(groove)

VGROOVE=(bth/2,Lb,0)

groove.translate(VGROOVE)
  
#Cut
  
Ur=Ur.cut(groove)
Ir=Ir.cut(groove)


######################## 3D ############################

Ur=Ur.revolve(Base.Vector(0,0,0),Base.Vector(360,0,0))

Ir=Ir.revolve(Base.Vector(0,0,0),Base.Vector(360,0,0))




################### Ball Creator ########################

BN=(math.pi*Lb)*0.8/(Rb)   ### BALL NUMBER

BN=math.floor(BN)

BN=int(BN)

for i in range (BN):
 Sph=Part.makeSphere(Rb)
 Alpha=(i*2*math.pi)/BN
 VSph=(bth/2, Lb*math.sin(Alpha), Lb*math.cos(Alpha))
 Sph.translate(VSph)
 Part.show(Sph)



### Show parts

Part.show(Ur)

Part.show(Ir)





I want to create more scripted parts this holidays, stay tuned.

Bye!

Wednesday, 25 September 2013

6 pistons, two blocks, 6 conrods, 1 crankshaft...

And that's all by the moment:

Screenshots


Conical view:






I was having problems with freecad and I couldn't finish the crankshaft, so I worked on the assembly and mounted what you've seen.

It starts to look like an engine!


6 types of bearing coded, so far!

Current bearing collection:

Deep groove single-row ball bearing:


Deep groove double-row ball bearing:

Detail of internals:


Needle bearing (needles only):


Thrust ball bearing


Cylindrical roller bearing:


Sealed bearing:



 All code has been re-written in order to make easier to create bearings reading their dimensions contained from a file. This way, bearings can be added using the BOLTS  in a easy way. At the moment only an old version of the deep groove ball bearing is included, I have to talk with jreinhardt and see what we do. I have tried to include these bearings into bolts myself, but without result.
Some of this bearings have colours while others are just plain grey. This is something I am stuck with until someones helps me finding a command that allows to change the material/color of a shape
And another thing to do is convert the bearing into a single part. This is possibly what I will do next.
Without forgetting to create more bearings :D

Sorry, I will not upload the code of the bearings until they are concluded.

PD.This post will be edited to add new bearings. 

Bye!


Tuesday, 24 September 2013

New Bearings!

Things have speed up this days!

Because of the association with jreinhardt at freecad forum, things are accelerating. At the moment, this are the new type of bearings (code at footer)

Needle Bearing



Needle bearing detail



Axial thrust bearing


To stay up to date and get the last code or project state, go to the Freecad forum

BEARING CODE:

Axial thrust bearing----------- Function: ATB(outer radius, inner radius, bearing width)

import Part
import math

def ATB(rout,rin, bth):
  fth=0.3*bth  #Thrust plate widh 
  #Edge fillet value 
  if rout<70:
    RR=1
  else:
    RR=1.5
  #shapes--
  #shapes=[]
  #Lower ring--------------------------
  lr1=Part.makeCylinder(rout,fth)
  lr2=Part.makeCylinder(rin,fth)
  lr=lr1.cut(lr2)
  lre=lr.Edges
  lr=lr.makeFillet(RR,lre)
  #Upper ring--------------------------
  ur1=Part.makeCylinder(rout,fth)
  ur2=Part.makeCylinder(rin,fth)
  ur=ur1.cut(ur2)
  ure=ur.Edges
  ur=ur.makeFillet(RR,ure)
  #Positioning Vector
  Vur=(0,0,bth-fth)
  ur.translate(Vur)
  #Balltracks---------------------------
  tbigradius=((rout-rin)/2.00)+rin
  tsmradius=(bth/2.00)-(0.75*fth)
  Vtorus=(0,0,bth/2.00)
  torus=Part.makeTorus(tbigradius,tsmradius)
  #Positioning vector
  torus.translate(Vtorus)
  #Booleans------------------------------
  lr=lr.cut(torus)
  ur=ur.cut(torus)
  Part.show(lr)
  Part.show(ur) 
  #shapes.append(ur)
  #shapes.append(lr)
  #Balls--------------------------------
  RBall=tsmradius
  CBall=tbigradius
  #Ball number (constant multiplied by radius and rounded)
  NBall=(2*math.pi*CBall)/(2*RBall)
  NBall=math.floor(NBall)
  NBall=NBall*0.9 
  NBall=int(NBall)
  #Ball creator
  for i in range (NBall):  
    Ball=Part.makeSphere(RBall)
    Alpha=(i*2*math.pi)/NBall  
    BV=(CBall*math.cos(Alpha),CBall*math.sin(Alpha),bth/2.00) 
    Ball.translate(BV) 
    Part.show(Ball)
    #shapes.append(Ball)
    
  #BearingAT=Part.Compund(shapes)
  #Part.show(BearingAT)
  Gui.SendMsgToActiveView("ViewFit")


Needle Bearing--------------- Function: NB(outer radius, inner radius, length)

import Part
import math
def NB(rout,rin,bth):
    rnd=(rout-rin)/2.00
    cnd=((rout-rin)/2)+rin
    nnd=2*math.pi*cnd/(1.8*2*rnd) #Needle number
    nnd=math.floor(nnd)
    nnd=int(nnd)   
    #needle cage--------------
    ncrout=cnd+0.175*(rout-rin)
    ncrin=cnd-0.175*(rout-rin)
    nc1=Part.makeCylinder(ncrout,bth)
    nc2=Part.makeCylinder(ncrin,bth)
    nc=nc1.cut(nc2)
    #needle space on the cage-
    rsnd=rnd*1.2
    thsnd=bth*0.8
    for i in range(nnd):
      snd=Part.makeCylinder(rsnd,thsnd)
      Alpha=(i*2*math.pi)/nnd
      nv=(cnd*math.cos(Alpha),cnd*math.sin(Alpha),0.1*bth)
      snd.translate(nv)
      nc=nc.cut(snd)      
    #Needle creation----------
    for i in range(nnd):
      nd=Part.makeCylinder(rnd,thsnd)
      Alpha=(i*2*math.pi)/nnd
      nv=(cnd*math.cos(Alpha),cnd*math.sin(Alpha),0.1*bth)
      nd.translate(nv)
      Part.show(nd)
    Part.show(nc)
    Gui.SendMsgToActiveView("ViewFit")


All this scripts have the same philosophy than the explained in the bearing script 1 post. 
The needle bearing is the more particular one because it carves the space for the needle in the ring and then creates the needle in that space. But this is nothing new, just a different combination of the same. Because of this there will not be a detailed guide for bearings until there is one that really needs of it. 




In the engine part, I have worked in a new crankshaft. Not finished, but the major part is done.

Stay tuned :D

Friday, 16 August 2013

FreeCAD 2100cc 6 Cylinder opposed-piston engine

I want to know which are the limits and the needs of FreeCAD and to do that, I am building up a 6 cylinder engine from scratch. All comes out from my imagination, so do not expect that it "runs" smoothly ;)

Pictures!

Piston:


Rear view



One of the two motor blocks:


More-less a connecting rod:




Pistons inside:


Pistons, block and connecting rods:



FreeCAD has shown some capabilities that I did not expect and some lacks that made my work tedious. I am not able to create symmetries in sketches, have not found any way to copy-paste and sketch and attach it to a new face and few other things that I will comment in further posts. 

¿What do you think about the engine?

I want to create something that creates interest in people and makes them give a try to FreeCAD.

Bye!

Tuesday, 13 August 2013

FreeCad Bearing Script 2

Embedded balls on rings are not really good looking, but until today I didn't figure how to create them without revolving a sketch. Solution has been as simple as creating a torus between rings and substract it to them. (full script at the end of the post)


Code:

T=Part.makeTorus(R1,R2)

This instruction will create a torus with big radius R1 and small radius R2 at (0,0,0).

To move it to its position in the bearing:

V=(0,0,TH/2)

TH/2 because track is in the middle of the bearing z axis (remember TH variable defined in previous post "FreeCad Bearing Script")

Show:

Part.show(T)

With this you have a groove with a radius R2 a radial distance R1 just in middle of the bearing, a little improvement to our bearing.


Added: Round edges:

After a bit of research I found how to select edges and how to fillet them, just two commands.

First, we create a new variable that I`ve called RR and contains the value of the rounding radius.

RR=1 

To explain the commands I am going to use a box object:

B=Part.makeBox(30,30,30)

Do not create a very tiny box or making fillet will crash freecad.

Getting the box edges (all) and store them in Bedges new variable:

Bedges=B.Edges

Fillet:

BFillet=B.makeFillet(RR,Bedges)

This instructions creates BFillet object that is our B box with all its edges rounded with an RR radius.

Part.show(BFillet)

This should be the result:


How can we apply this to our bearing? (understand "FreeCad Bearing Script 1" needed)
This way:

Create inner ring:

B1=Part.makeCylinder(R1,TH)
B2=Part.makeCylinder(R2,TH)
IR=B2.cut(B1)

Now just round the ring:

FI=IR.Edges
IR=IR.makeFillet(RR,FI)

And its done!

Part.show(IR)



Full code:

import Part
import math

#VARIABLE VALUES#

R1=15.0
R2=25.0
R3=30.0
R4=40.0
TH=15.0
NBall=10
RBall=5.0
RR=1

#Program#

B1=Part.makeCylinder(R1,TH)
B2=Part.makeCylinder(R2,TH)
IR=B2.cut(B1)
FI=IR.Edges
IR=IR.makeFillet(RR,FI)


B3=Part.makeCylinder(R3,TH)
B4=Part.makeCylinder(R4,TH)
OR=B4.cut(B3)
FO=OR.Edges
OR=OR.makeFillet(RR,FO)

T1=Part.makeTorus(R2+(RBall/2),RBall)
VT=(0,0,TH/2)
T1.translate(VT)

IR=IR.cut(T1)
OR=OR.cut(T1)

Part.show(IR)
Part.show(OR)

CBall=((R3-R2)/2)+R2
PBall=TH/2

for i in range(NBall):
  Ball=Part.makeSphere(RBall) 
  Alpha=(i*2*math.pi)/NBall  
  BV=(CBall*math.cos(Alpha),CBall*math.sin(Alpha),TH/2)
  Ball.translate(BV)
  Part.show(Ball)

  

  The output of that command is the picture at the beginning of this post.

Added: Video of the script working



Next things to do are grouping all entities in the same object and create a standard bearing generator script.

I have realized that there is no way (at least graphically) to create section views or cuts to 3d parts. A very useful tool to show internals. Also I have not found at the moment any way to measure distance or angles of points, edges, axis... Maybe in the next release.

Bye!


FreeCad Bearing Script 1

I started to study FreeCad Python Scripts yesterday and today I have managed to create a very simple script that builds up a very simple bearing. It can be used to figure how will it look in the assembly.

Let's code!

To accomplish this task we will need two libraries, Part and math.

The basic body of a conventional fixed ball-bearing can be built up in several ways. We are going to create 4 cylinders to achieve a very basic body. 

Commands:

Import the libraries needed

import Part 
import math 

Now create the inner ring of the bearing with two cylinders (give your own values to variables):

B1=Part.makeCylinder(R1,TH)  #B1=part name,  R1=Cylinder radius, TH=Bearing thickness

B2=Part.makeCylinder(R2,TH) #As B1 (variable values in concordance)

At the moment we have created 2 cylinders. Maybe you ask where are them. Ok, when you create an object in FreeCad its view is not automatically refreshed, so if you want to see what is happening, just type

Part.show(B1) 

Let's do the boolean cut that will create the inner ring of the bearing. The next line creates a boolean cut of B1 and B2:

IR=B2.cut(B1)  

Where IR="inner ring" new variable name. B2 part cutted by B1

if you run: Part.show(IR) you should see a ring like this:


Following the same path, we can build the outer ring:


B3=Part.makeCylinder(R3,TH)

B4=Part.makeCylinder(R4,TH)



OR=B4.cut(B3)


To solve getting the cylinders mixed with the rings, avoid Part.show() until all operations are finished. 

Now a bit tricky part: the balls of the ball bearing.

The command to create a sphere is:

Ball=Part.makeSphere(RBall)

Ball is the name of that sphere and RBall the radius. This will create a sphere at (0,0,0) global coordinates system. Like this:


The balls of the ball bearing are not arranged in that position, so new command to move this:

Ball.translate(V)

That will move the sphere from (0,0,0) to the end of the V vector. To get this working:

V=(0,CBall,PBall)

Ball.translate(V)

CBall (Ball circle) is the radius of the ball ring, the distance between rings measured from (0,0,0). 
PBall is the middle bearing thickness position (If TH=10, PBall=5)

Running: Part.show(Ball) should show this:


Now we have the sphere in position. But we need more than just one to give this rough bearing a better looking.

Carefully, we can write this "for" loop that creates a number(NBall) of spheres in a equidistant position:

for i in range(NBall):    #Starts loop

  Ball=Part.makeSphere(RBall) #Creates a sphere called Ball

  Alpha=(i*2*math.pi)/NBall    #Gets angle between spheres and increases it every time a new sphere is created

  BV=(CBall*math.cos(Alpha),CBall*math.sin(Alpha),TH/2)  #Sphere translation vector to a given alpha value  

  Ball.translate(BV)    #Translation
  Part.show(Ball)      #Screen refresh


This bucle contains the complicated math part, but the concept is very easy:

Create a ball, move it to its position and increase the angle of the position vector.

The result:



Just a dummy bearing :D
Yes, not very useful at using Freecad, but as FC-python introduction it does the job (I think)


Full code:

import Part
import math

#VALUES#

R1=15.0
R2=25.0
R3=30.0
R4=40.0
TH=15.0
NBall=10
RBall=5.0

#Program#

B1=Part.makeCylinder(R1,TH)
B2=Part.makeCylinder(R2,TH)
IR=B2.cut(B1)


B3=Part.makeCylinder(R3,TH)
B4=Part.makeCylinder(R4,TH)
OR=B4.cut(B3)

Part.show(IR)
Part.show(OR)

CBall=((R3-R2)/2)+R2
PBall=TH/2

for i in range(NBall):
  Ball=Part.makeSphere(RBall) 
  Alpha=(i*2*math.pi)/NBall  
  BV=(CBall*math.cos(Alpha),CBall*math.sin(Alpha),TH/2)
  Ball.translate(BV)
  Part.show(Ball)



And this is all I know at the moment about FreeCad scripts. The next step is to round the ring edges to get a more realistic look:


I want to develop a tool that, with a shaft or hole selected, drafts down the bearing that better suits, using a database containing the measures of standard bearings. Maybe too much ambition I think. Okay, I did it  :D

Freecad documentation is poor or hidden and understand errors and commands take time.
I have tried to write this tutorial and explain things as easy as I could. But if you need more explanations, do not hesitate to contact me. But don not think I must know the answer, I am just new here ;).

Thanks for reading.