Showing posts with label animation. Show all posts
Showing posts with label animation. Show all posts

Wednesday, 30 December 2015

NiCr



NiCr (read as 'nicrome') is the name of my CNC Hot Wire Foam Cutter, an OpenSource/OpenHardware project that I'm making public with this post.

The github repository: https://github.com/JMG1/NiCr

 

Description:

As the name says, the machine cuts shapes from foam blocks, being the original intention to cut wing cores for lamination and molding.

I've composed a little video about it:


I'm opening this project in exchange of all the good things that the OpenSource world has given to me (I like to think about it as 'from the OpenSource to the OpenSource').

Details:

The NiCr project can be divided in three parts: Machine, Arduino and FreeCAD


-Machine:

The machine is the physical thing, and is being designed to be built from easy to find, easy to work with materials, like extruded aluminium tubes and simple bolted joints, taking into account a low budget and the DIY factor (only cut/drill/bend operations).

It features two CoreXY frames, easy-to-find Nema17 motors, and a spring tensor for the wire. The design is also very scalable and can, possibly, be applied to other type of machines.

The design has taken place between FreeCAD and the real world:

FreeCAD pictures:

Detail of the Nema17 stepper, X axis slider and belt pulleys

Machine assembled inside FreeCAD
Real world pictures:

Frame size

Slider on the X axis

First version of the cut-wire tensor




-Arduino:

The machine movement is done with an Arduino Mega 2560 board and a Ramps shield, that, together with four stepper drivers (A4988), some limit switches and a power source form all the electronics.
The firmware is being developed specifically for this machine, you can see some of its parts in this links:


-FreeCAD:

The shapes are created using FreeCAD existing tools and converted later to .nicr (similar to GCode) in a custom workbench.

This workbench features a parametric machine, a shape-to-path algorithm, trajectory planning and simulation tools.

Some pictures of the workbench:

Workbench and parametric machine

Cut path simulation result for three wings with different precision settings

Workbench demo video:



The result of the shape-to-path algorithm is a .nicr file that contains instructions similar to GCode and with this comes a question:
Why I have not used the path workbench (in development) and the existing GCode standard?
 Because this machine produces 2.5D shapes (could do '2.75D' with the addition of a fifth axis, to be studied) and the movement is very different to the movement of a 3D printer or mill, and by using 4 axis, someone can be mistaken and use the code for the wrong machine.
Anyway, it is going to be a documented language so export-import tools can be created if needed.


Conclusion:

NiCr is in active development, at the moment I'm trying to achieve a basic stability and usability of the software before releasing (and some documentation too, maybe the hardest thing!).
Once I achieve that, the code and machine 3d parts will be uploaded to github (I have not decided the particular license yet) After a complete day of reading about licenses, I have chosen the GNU GPL.

I'll be updating this post with any news I have.
-> January 1, code uploaded to github: https://github.com/JMG1/NiCr








By the way, have a happy 2016!

Javier.


Thursday, 6 August 2015

FreeCAD: Double Slider Mechanism Animation

Since few days ago, this blog is two years old, and, also, last month it crossed the 4000 views/month barrier.
I was thinking about a way of celebrating this events, and last night I saw a .gif image about a flat mechanism at which I could be staring the whole day:


I don't know its exact name, but "double slider mechanism" seems appropriate. It belongs to the family of flat, four bar linkage mechanisms, and, possibly, there is no real use for this one. But it moves very smoothly, with the outer end of the rotating arm drawing some kind of ellipse.



The kinematics of this one are not too difficult (none of the family of four bar linkage mechanism are), and for the animation I have solved it in an analytical way.

The code:


# Javier Martinez Garcia    August 2015   GPL V2.0

from PySide import QtCore
from math import sin, cos, radians

# retrieve the objects from the document
slider_x = FreeCAD.ActiveDocument.getObject("Pad003002")
slider_y = FreeCAD.ActiveDocument.getObject("Pad003001")
arm = FreeCAD.ActiveDocument.getObject("Pad002001")


# store initial placement (needed to restore initial position)
slider_x_placement = slider_x.Placement
slider_y_placement = slider_y.Placement
arm_placement = arm.Placement

# store object placements in a new variable
r_slider_x_pl = slider_x.Placement
r_slider_y_pl = slider_y.Placement
r_arm_pl = arm.Placement


def reset():
  # function to restore initial position of the objects
  slider_x.Placement = r_slider_x_pl
  slider_y.Placement = r_slider_y_pl
  arm.Placement = r_arm_pl


# In this mechanism, "i" represents the angle of the rod in degrees
i = 0

# update function calculates object position as f(i) and increases i
def update():
  global i
  alpha = radians( i )
  x = 150.0*cos( alpha )
  y = 150.0*sin( alpha )
  slider_x.Placement = FreeCAD.Placement( slider_x_placement.Base + FreeCAD.Vector( 150-x, 0, 0 ),
                                          slider_x_placement.Rotation )
  
  slider_y.Placement = FreeCAD.Placement( slider_y_placement.Base + FreeCAD.Vector( 0, y, 0 ),
                                          slider_y_placement.Rotation )
  
  arm.Placement = FreeCAD.Placement( arm_placement.Base + FreeCAD.Vector( 0, y, 0 ),
                                     FreeCAD.Rotation( FreeCAD.Vector( 0,0,1), i))
  # update the scene
  FreeCAD.Gui.updateGui()
  # increase mechanism input position
  i += 1


# create a timer object
timer = QtCore.QTimer()
# connect timer event to function "update"
timer.timeout.connect( update )
# start the timer to trigger "update" every 10 ms
timer.start( 10 )



Download the .fcstd model and animation script here, on github.


Have fun!

Monday, 13 July 2015

What's going on this summer?

Hello!

I write this brief post to explain, among other things, what is currently happening with the sheet metal workbench:

The workbench at the moment is at 30%: Document structure is almost done, simple unfold is working and there are tools, like this one, to create even more complex and powerful features.


Also, I talked about some crowdfounding campaign or paid development for this workbench: all it is stopped because I've found a powerful sponsor (to be revealed in a future).

 

Am I working at sheet metal?

No. I'm going to be studying from now until I finish my degree, somewhere around December. But this does not mean a complete shutoff, there are things and important works on the way.

For example, for  the "maker" community, I am developing a new machine that is being born by the end of this year (and is part of my degree project). An open source machine with stepper motors, completely designed with FreeCAD, that works using Arduino and Python and is not a 3D printer.




Also, I've been working in improvements at the "Exploded Assembly Animation workbench" and additions to the macro "WorkFeatures"


In conclusion, things are going to freeze a bit, but no project is going to disappear.


Have a nice summer!!


Javier.

Friday, 4 July 2014

FreeCAD: Animated Spring

Not really a spring, but..

... this script creates a helix object and constantly adjust its pitch and height to create a compression-like effect.
As always, the function that calculates and commands the changes is called repetitively by a timer.

Video:


While the animation is running, you can change the values of pitch, length and compression by just typing in, for example:

Pitch = 3
Compression = 0.1   # Relative to the length of the "spring" 


The code:

from __future__ import division 

from PyQt4 import QtCore
import math as mt
import FreeCADGui
App.ActiveDocument.addObject("Part::Helix","Helix")
App.ActiveDocument.Helix.Pitch=5.00
App.ActiveDocument.Helix.Height=20.00
App.ActiveDocument.Helix.Radius=5.00
App.ActiveDocument.Helix.Angle=0.00
App.ActiveDocument.Helix.LocalCoord=0
App.ActiveDocument.Helix.Style=1
App.ActiveDocument.Helix.Label='Helix'
FreeCADGui.ActiveDocument.getObject("Helix").LineColor = (1.00,0.67,0.00)

i = 0
Length = 20
Pitch = 5
Compression = 0.5

def Spring():
  global i, Length, Pitch, Compression
  
  i+=0.01

  R = Pitch / Length
  IH = Compression*Length*mt.cos(i)
  P = Pitch + (R*IH)
  App.ActiveDocument.Helix.Height = Length + IH
  App.ActiveDocument.Helix.Pitch = P
  
  if i == 1000:
    i=0


timer = QtCore.QTimer()
timer.timeout.connect(Spring)
timer.start(5)



Bye!
 :D

Monday, 23 June 2014

FreeCAD: Pseudo-mill simulator

Before starting, I must warn that this is not intended to produce any realistic simulation.
I created it as a proof of concept and for fun.


How it works:

The milling script removes material by cutting the workpiece with the tool object (a rectangular box) in a simple Part.cut(tool) operation.
The tool object position is determined by taking the last point and the next point from the path points list. Then, the script creates vector and walks through it by steps. At every step, the tool cuts the workpiece, and every 4 steps, the document is refreshed. Refreshed means the command Part.show(workpiece). Because this command creates a shape every time is called, before Part.show(workpiece), the previous shape is removed.
Wild and dirty.

Below I try to explain how to use this script. You can get the full code here


Creating toolpath:

The first thing we need is the list of points that the "tool" will follow.
This list has the form:

points = ( ( x,y,z ), ( x1,y1,z1 ), ... , ( xn, yn, zn), ( x,y,z ))

But manually creating the points is not the most adequate way, at least for more-less complex or large toolpaths.

If we want to do something like this...



...it could take forever manually, so I wrote a pocket function instead:
from FreeCAD import Gui
from PyQt4 import QtCore
from FreeCAD import Base, Draft, Part
import math as mt

# Basical definitions
raw_size=(30,30,10)

Tool_radius = 0.5 
Tool_heigh = 8.0 
feed_rate = 10.0 

# Path Generator

Program=[(-5,0,30)] # Start position

def pocket(V0,L,H):
  global Program, Tool_radius
  YCycles = int(mt.floor((L / (2*Tool_radius))/2)) 
  V0 = (V0[0] + Tool_radius, V0[1] + Tool_radius, Program[0][2])
  Program.append(V0)
  V0 = (V0[0], V0[1], H)
  Program.append(V0)
  for i in range(YCycles):
    V0 = (V0[0]+(L-2*Tool_radius), V0[1], H)
    Program.append(V0)
    V0 = (V0[0],(2*Tool_radius)+V0[1], H)
    Program.append(V0)
    V0 = (V0[0]-(L-2*Tool_radius),V0[1], H)
    Program.append(V0)
    V0 = (V0[0],2*Tool_radius+V0[1], H)
    Program.append(V0)
  V0 = (V0[0]+(L-2*Tool_radius), V0[1], H)
  Program.append(V0)
  V0 = (V0[0], V0[1], Program[0][2])
  Program.append(V0)

It generates a pocket with initial position (lower-right corner) V0(x,y,z), side length L and absolute deepness H.

An screenshot of pocket((2,2,15),25,8)


Imagine to create that zigzag pattern by hand. 

The function pocket itself does not represent the points, just creates the list. To create the wire that represents the toolpath, I have coded this:

Wire_done = False  # wire end condition
i=0
while Wire_done == False:
  i+=1
  if i == len(Program):
    Wire_done = True
    break
    
  if i == 1: # Starts the wire by creating the first line
    Line0 = Part.makeLine(Program[i-1], Program[i])
    Wire0 = Part.Wire([Line0])
  else: #Creates the rest of the wire
    Line1 = Part.makeLine(Program[i-1],Program[i])
    Trajectory_Wire = Part.Wire([Wire0,Line1])
    Wire0 = Trajectory_Wire

TjWire = App.ActiveDocument.addObject("Part::Feature", "Trajectory") 
TjWire.Shape = Trajectory_Wire  #
TjWire_UserName = TjWire.Label
FreeCADGui.ActiveDocument.getObject(TjWire_UserName).LineColor = (1.00,0.67,0.00)

It iterates over the points list (named Program) and generates the tool trajectory wire.

You can generate multiple pockets, this is the result of...



pocket((4,4,10),20,6)
pocket((8,8,10),10,3)
pocket((8,20,5),5,3)
pocket((15,20,5),5,3)
pocket((21,20,5),3,3)
pocket((10,10,5),5,2)



Create workpiece:

Raw = Part.makeBox(raw_size[0],raw_size[1],raw_size[2])
Raw_shape = App.ActiveDocument.addObject("Part::Feature", "Workpiece")
Raw_shape.Shape = Raw
Gui.ActiveDocument.getObject("Workpiece").Visibility=False

Create tool (the one that performs the cut):

Tool = Part.makeBox(Tool_radius*2, Tool_radius*2, Tool_heigh)
Tool_shape = App.ActiveDocument.addObject("Part::Feature", "Tool")
Tool_shape.Shape = Tool
Tool_shape_gui = Tool_shape.Label
FreeCADGui.ActiveDocument.getObject(Tool_shape_gui).ShapeColor = (0.33,0.33,1.00)
 
Tool_shape.Placement = App.Placement(App.Vector(Program[0]),ToolR )
Gui.ActiveDocument.getObject("Tool").Visibility=False

Create the animated tool:

AnimatedTool_shape= Part.makeCylinder(Tool_radius,Tool_heigh)
RTTH = Part.makeBox(Tool_radius/3.0, Tool_radius*2, Tool_heigh*1.1)

for i in range(5):
  alpha = i*360/5
  RTTH = Part.makeBox(Tool_radius/3.0, Tool_radius*2, Tool_heigh*1.1)
  RTTH.translate(Base.Vector((2*Tool_radius*mt.cos(mt.radians(i))/3.0,2*Tool_radius*mt.sin(mt.radians(i))/3.0,0)))
  RTTH.rotate(Base.Vector(0,0,0),Base.Vector(0,0,1), 15 + alpha)
  AnimatedTool_shape = AnimatedTool_shape.cut(RTTH)


AnimatedTool= App.ActiveDocument.addObject("Part::Feature","AnimatedTool")
AnimatedTool.Shape = AnimatedTool_shape
AnimatedTool_gui = AnimatedTool.Label
FreeCADGui.ActiveDocument.getObject(AnimatedTool_gui).ShapeColor = (0.33,0.33,1.00)
AnimatedTool.Placement = App.Placement(App.Vector(Program[0]), App.Rotation(App.Vector(0,0,1),0))

Pseudo-Mill core:


This is the function that performs the milling-like action:

L1 = Raw.cut(Tool_shape.Shape)
Part.show(L1)

i=0
n=0.0
swd = 3 # cut refreshing interval
s=0


def Machining():
  global n, i,feed_rate, L1, Tool_radius, swd, s
  if i <= len(Program):
    Current_position = Tool_shape.Placement.Base
    Vector_trajectory = App.Vector(Program[i+1])-App.Vector(Program[i])
    Vector_direction = (App.Vector(Program[i+1])-App.Vector(Program[i])).normalize()
    VT_modulus = Vector_trajectory.Length
    VD_modulus = 1.0
    if VT_modulus > VD_modulus*n*feed_rate:
      Next_position = App.Vector(Program[i])+Vector_direction.multiply(n*feed_rate)+App.Vector(-Tool_radius, -Tool_radius)
      n+=0.1
      
    else:
      Next_Position = App.Vector(Program[i+1])
      i+=1
      n=0.0
    
    s += 1
    if s > swd:
      App.ActiveDocument.removeObject("Shape")
      L1 = L1.removeSplitter()
      Part.show(L1)
      s = 0
    Tool_shape.Placement = App.Placement(Next_position, ToolR)
    AnimatedTool.Placement = App.Placement(Next_position+App.Vector(Tool_radius,Tool_radius,0), App.Rotation(App.Vector(0,0,1),n*43))
    L1 = L1.cut(Tool_shape.Shape)
Then, by calling repeatedly Machining() with a timer starts the animation:

timer=QtCore.QTimer()
timer.timeout.connect(Machining)
timer.start(1)
With the tool-path created above this is the result:



What's next?

Well, I've been playing with creating the points list using a script that follows the contour of a FreeCAD part. That way you will only need to execute that script and then run the simulation.


The green line is the output of the script.



Bye!

Friday, 13 June 2014

FreeCAD: Inline-Four engine animation

Another more-less simple engine animation:




Find the errors, there are a lot of them!
:D

Sorry, no files or code this time, but keep in mind this is as difficult as this one


Bye!

Tuesday, 11 March 2014

FreeCAD: Simple Engine Animation.


A very simple animation to show the basics:


I do not have the time needed to go on detail plus this information will be obsolete once FreeCAD has an assembly module. So I'm posting just what I have.

Steps to animate something inside FreeCAD:


-Create parts

-Export them to STEP

-Create new document and import parts

-Determine how FreeCAD names the parts (in my script you can see "Part_Feature003", or "Part_Feature")

-Create a function that defines mathematically the mechanism (crank-slider type in mine, very simple)

-Create a timer that calls the function to make it move (look at the end of the script)


This is the python script, you can download parts here:


from PyQt4 import QtCore, QtGui

import Part
from FreeCAD import Base
import math as mt

i=0
def engine():
  global i
  
  i+=0.1
  
  cig=mt.degrees(i)
  
  
  B=mt.asin((mt.cos(i)*40.0)/140.0)
  AB=mt.asin((mt.sin(i)*(40.0/140.0)))
  PP=mt.cos(i)*40.0+mt.cos(AB)*140.0
  alphaB=mt.radians(90.0)-B
  
  br=FreeCAD.Rotation(FreeCAD.Vector(0,0,1),alphaB)
  bd=App.Vector(mt.sin(i)*40,mt.cos(i)*40.0,20.0)
  bdgc=FreeCAD.Vector(mt.sin(i)*40,mt.cos(i)*40.0,20.0)
  
  App.getDocument("montadoanimacion").Part__Feature002.Placement=App.Placement(App.Vector(0,0,0),App.Rotation(FreeCAD.Vector(0,0,1),-cig))
  
  #biela
  
  App.getDocument("montadoanimacion").Part__Feature003.Placement=App.Placement(bd,App.Rotation(FreeCAD.Vector(0,0,1),(mt.degrees(AB))))
  
  #piston
  
  App.getDocument("montadoanimacion").Part__Feature.Placement=App.Placement(App.Vector(0,PP-35,30),App.Rotation(-0.5,0.5,0.5,0.5))

timer=QtCore.QTimer()
timer.timeout.connect(engine)
timer.start(10)



Open the file and then copy-paste this script. It should move. 

That's all.

Now I hope to see how people creates new and more complex animations :)


Bye!

Sunday, 19 January 2014

3D Mechanism Animation with PyQtGraph

In a study break I have coded an animated 3d mechanism. Is very simple in order to get it working quickly, but shows the basics of 3d plots with pyqtgraph.

Screenshot:



Code:

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import pyqtgraph as pg
import numpy as np


app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 40
w.show()
w.setWindowTitle('3d wire crankshaft')

i=0
crankshaft=np.array(([0,0,0],[2,0,0],[2,np.cos(i),np.sin(i)],[2.5,np.cos(i),np.sin(i)],[2.5,-np.cos(i),-np.sin(i)],[3,-np.cos(i),-np.sin(i)],[3,0,0],[6,0,0]))
plt=gl.GLLinePlotItem(pos=crankshaft)
w.addItem(plt)

conrod1=np.array(([2.25,np.cos(i),np.sin(i)],[2.25,np.cos(i)+2.5,0]))
plt1=gl.GLLinePlotItem(pos=conrod1)
w.addItem(plt1)

conrod2=np.array(([3.25,-np.cos(i),-np.sin(i)],[3.25,-np.cos(i)-2.5,0]))
plt2=gl.GLLinePlotItem(pos=conrod2)
w.addItem(plt2)

def crankshaft():
  global i
  crankshaft=np.array(([0,0,0],[2,0,0],[2,np.cos(i),np.sin(i)],[2.5,np.cos(i),np.sin(i)],[2.5,-np.cos(i),-np.sin(i)],[3,-np.cos(i),-np.sin(i)],[3,0,0],[6,0,0]))
  conrod1=np.array(([2.25,np.cos(i),np.sin(i)],[2.25,np.cos(i)+2.5,0]))
  conrod2=np.array(([2.75,-np.cos(i),-np.sin(i)],[2.75,-np.cos(i)-2.5,0]))
  plt.setData(pos=crankshaft)
  plt1.setData(pos=conrod1)
  plt2.setData(pos=conrod2)
  i+=0.05

time=QtCore.QTimer()
time.timeout.connect(crankshaft)
time.start(0.1) 



This time I'm not explaining line by line because I think is easy to see what is happening. 
Any doubts, comment. 


Bye!


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!