AADRL Instructing Machines

Page 1

INSTRUCTING MACHINES Workshop_2 Tutors: Shajay Bhooshan, Alicia Nahmad Vazquez, Vishu Bhooshan, Dave Reeves Albert Yen, Federico Borello, Fernando Alvarenga, Philipp Siedler



INSTRUCTING MACHINES _04 _06 _34 _34 _46 _58 _70 _80 _92

Introduction 1_ Computer Instruction C++ Code 2_ Catalogue Behavior Types 2.1_ Particles to Target 2.2_ Particles to Particles 2.3_ Particles to Environment

3_ Robotic Arm End Effector Design 4_ Robot Instruction and Analysis Drawing Conclusion


Introduction Workshop Introduction This digital workshop is called instructing machines and aimed to teach C++, with predifined framework and libraries also known as Level 2 C-Programming, in a close relation to the real world, instructing a six axis robot. More generally speaking, instructing a machine with a computer language as the medium. A behaviour driven, generative code had to be written, drawing points and lines onto a digital planar canvas. This simple geometric elements had to be compiled to a machine readable code, a G-Code, to be executed by the NACHI MZ07 6 Axis Robot. The only constraints for the drawing of points and lines were the robots motion abilities, its mounting position and the given physical operating canvas with the size of an A3 landscape. Both, the digital and the robotic work could be treated individually, as long as the output of the digital world were points on the XYPlane written as (x,y,z), to be compiled and interpolated by the NACHI Software to a G-Code. Between the actual point output of the code and the NACHI compiler software of the teams choice could be used as possible manipulator. Since natural systems are highly generative, a logic and appealing consequence for this project was to analyze a system in the physical world, translate and describe it in the digital world in form of a code and feed it back to the physical world with a 6 Axis robot and its customized end effector as tool of expression.

4


WORKFLOW generative code

global variables

model

view

controller

target force

particles + targets

randomize particles

gravity force

randomize targets

spin force

switch charge targets

particle to particle force

switch charge p to p

drag force

magnetic force

cells class

“chemical deposition� force

export .eps

Adobe Illustrator

export g-code .txt

Rhinoceros

grid cells

Grasshopper

Nachi compiler

Nachi Robot

5


1_ Computer Instruction C++ Code


C++ served as the main coding language in this project. As frame work the Zaha Hadid CODE group under Shajay Bhooshan provided the team with various libraries to include for example a 3D space as canvas and the nachi robot. The general partisions of the generative code we wrote are Model including: “General Variables”, “Setup” and “Update”, View including: “Draw” and Controller including: “Key Press”, “Mouse Press” and “Mouse Motion”. The generative code is divided into three elements: 1. Particles, 2. Targets and 3. Forces manipulating the particles position. In the “Model” partition the 3D workspace, the “Targets”, “Particles” and “Forces” are initialized. The targets are setup by fixed locations and charges, particles are randomly spread into a defined frame. The force field is created by a set of forces which are differenciated in their behaviour: 1. Forces which act from the global environment onto the particles: Gravity Force, Spin Force, Drag Force. 2. Forces acting between particles and targets: Target Force, Cell Center “Chemical Deposition” Force. 3. Forces inbetween particles: Particle to Particle Force. The “Update” partition updates the position of the particles, their color coding, their possible trail and the color coding of the cell where the particles “deposit chemicals”. Under “Draw”, particles, particle trails, targets and colored cells are initialized to be visualized. “View” visualizes the generative process of particle movement and the “Controller” partition enables the user to: reverse the charge of the targets, reverse attraction to repulsion force between the particles, reset the setup, switch to the top view, reset and reorganize targets and export an .eps vector files.

7


1_ Computer Instruction C++ Code #include “main.h” // include “main” class #include “Cells.h” // include “Cells” class #include “gl2ps.h” // include “gl2ps” (.eps export) class /// --- --- --- /// GLOBAL VARIABLES /// --- --- --- /// #define Res 200 // setting up resolution of canvas #define MaxTrail 1 // trail length of particles #define MaxParticles 100 // particle count #define MaxTargets 1 // target count float unitX = 200/Res; // cell size x float unitY = 200/Res; // cell size y float dirLength; // --float cellsColor[Res*Res]; // one dimensional array for cells color float charges[MaxTargets]; // target charge float AttRep = -1; // attraction/repulsion multiplicator value (particle to particle) float dist[MaxTargets]; float min, max; // min and max value for color remap of “chemical deposition” in cells double timeStep = 0.01; // time step size double targetStrength = 0.2; // target attraction/repulsion strength (target to particle/particle to target) double gravityStrength = 0.0; // gravity strength (environment to particle) double spinStrength = 0.0; // spin strength (environment to particle) double dragStrength = 0.1; // drag strength (environment to particle) double collisonStrength = 0.5; // particle attraction/repulsion strength (particle to particle) double collisonRadius = 10; // particle attraction/repulsion radius (particle to particle) double magneticStrength = 0.1; // target magnetic force (target to particle) double chemicalStrength = 0.1; // cell center point attraction strength (particle to cell “chemical deposition”)

8


int frm = 0;

// initial integer for frame (frm) counter

bool run = false;

// initial boolean value to run code

string seq = “Sequence: “; // sequence label of keypress sequence vec gravity(0, 0, -1); // direction gravity vector vec chemicalForce[MaxParticles][Res*Res]; // two dimensional array for “chemical deposition” of particles in cells vec dir[MaxTargets]; // one dimensional array of vec target[MaxTargets]; // one dimensional array of target position point vectors vec pos[MaxParticles]; // one dimensional array of particle position point vectors vec vel[MaxParticles]; // one dimensional array of velocity vectors vec forces[MaxParticles]; // one dimensional array of applied forces vector array vec trail[MaxParticles][MaxTrail]; // two dimensional array of particles and their trail position point vectors vec chemF[MaxParticles]; // one dimensional array of particle to cell with “chemical deposition” vectors vec cellCenterPt[Res * Res]; // cell center position point vectors Cells mycells[Res*Res]; // one dimensional array for cells SliderGroup S; // slider function

9


1_ Computer Instruction C++ Code ButtonGroup B;

// button function

p2

/// --- --- --- /// MODEL /// --- --- --- /// /// PARTICLE - PARTICLE TO PARTICLE FORCES vec PtoP(int particleId1, int particleId2) // particle to particle vector { float distance = pos[particleId1].distanceTo(pos[particleId2]); // distance between particles vec dir = pos[particleId2] - pos[particleId1]; // direction vectors from particle to particle vec collisionForce(0, 0, 0); // reset collisionForce vector if (distance < collisonRadius) // checking distance between particles to be affected by collision force { collisionForce = dir * AttRep; // calculation of collision force vector collisionForce.normalise(); // normalizing collision force vector } return collisionForce; // output collision force }

t3

d3 d1

t1

p1

p3

d2 d

p0

t2 p2

/// GRAVITY FORCE void applyGravity() // environment gravity force vector function { for (int i = 0; i < MaxParticles; i++) // loop through particles { forces[i] += gravity * gravityStrength; // integrating gravity force into global field of forces } }

p t3

d3 d1 d2

10

t2

g t1


/// PARTICLE - TARGET - MAGNETIC FORCES void applyMagneticForce() // target magnetic force function { p1 for (int i = 0; i < MaxParticles; i++) // loop through particles p3 p0 +c1 -c3 { 2 1/d d for (int j = 0; j < tMaxTargets; j++) // loop through targetsp 1/d2 0 3 { p0 d3 float distance = pos[i].distanceTo(target[j]); // distances between particles and targets +c2 vec dir = target[j] - pos[i]; // direction vectors from particles to targets d1 dir.normalise(); // normalizetdirection vetors 1 p2 vec f = (dir * magneticStrength * charges[j]) / ((distance + 0.01) * (distance + 0.01)); // calculating magnetic force vector f.normalise(); // normalize magnetig force vector d2 // integrating magnetic force into global field of forces forces[i] += f * 0.1; } } }

t2

/// SPIN FORCE

p0 t3

d3

d1 void applySpinForce() // target spin force function t1 { for (int i = 0; i < MaxParticles; i++) // loop through particles d2 { for (int j = 0; j < MaxTargets; j++) p // loop through targets t2 { float distance = pos[i].distanceTo(target[j]); // distances between particles and targets vec dir = target[j] - pos[i]; // direction vectors from particles to targets g dir.normalise(); // normalize direction vetors vec UpVec(0, 0, 1); // ---

t1

t1 p0

p

d1

p0

d1

11 g

v


1_ Computer Instruction C++ Code

p vec spinDir = dir.cross(UpVec);

d1

p0

// -- forces[i] += spinDirp* spinStrength; // integrating spin force into global field of forces 1 } p3 +c1 } g } d 1/d2 p0

-c3 1/d

2

p0

/// DRAG FORCE void applyDragForce() // particle drag force function { for (int i = 0; i < MaxParticles; i++) // loop through p2 particles { vec dragDir = vel[i] * -1; // direction vectors from particles to targets forces[i] += dragDir * dragStrength; // integrating drag force into global field of forces } } /// PARTICLE - ENVIRONMENT - “CHEMICAL DEPOSITION” FORCE

+c2

p0

v

p0 void cellAttForce() // cell center point attraction function { t3 for (int i = 0; i < MaxParticles; i++) // loop through particles d3 { d1 deposition” chemF[i] = vec(0,0,0); // “chemical force array t1 int currentI = pos[i].x; // particle position x int currentj = pos[i].y; // particle position y for (int u = currentI - unitX; u < currentI + d 2*unitX; u+=unitX) // loop through neighbouring cell in x direction of particle 2 { for (int z = currentj - unitY; z < currentj + 2*unitY; z+=unitY) // loop through neighbouring cell in y direction of particle

12

t2


{ int dx; // initializing x int dy; // initializing y dx = floor(u / unitX); // x position in cell grid dy = floor(z / unitY); // y position in cell grid int id = dx * Res + dy; // index in one dimensional array of cells if (id >= 0 && id < Res * Res) // if condition to make sure index != outside of array { cellCenterPt[id] = mycells[id].p + vec(unitX / 2, unitY / 2, 0); // cell center vector vec nVec = cellCenterPt[id] - pos[i]; // attraction vector particle to cell center nVec.normalise(); // normalise particel to cell center vector chemF[i] += nVec; // vector addition of particel to cell center vector chemF[i].normalise(); // normalise resulting vector } else continue; // if index larger than array count continue } } forces[i] += chemF[i] * chemicalStrength; // integrating “chemical deposition� force into global field of forces } } /// INTEGRATE FORCES void integrateForces() // integrating forces { for (int i = 0; i < MaxParticles; i++) { vec acc = forces[i] / 1; // acceleration = force / mass vec newVel = vel[i] + acc * timeStep; // new velocity = old velocity + acceleration * timestep vec newPos = pos[i] + newVel * timeStep; // new position = old position + new velocity * timestep

13


1_ Computer Instruction C++ Code pos[i] = newPos; // update particle position vel[i] = newVel; // update velocity value } } /// UPDATE FUNCTION void updateFunction() // update { // deposit chemical for (int i = 0; i < MaxParticles; i++) // loop through neighbouring cell in y direction of particle { int dx; // initializing x int dy; // initializing y dx = floor(pos[i].x / unitX); // x position in cell grid dy = floor(pos[i].y / unitY); // y position in cell grid int id = dx * Res + dy; // index in one dimensional array of cells if (id >= 0 && id < Res * Res) // if condition to make sure index != outside of array { mycells[id].chemicalA += 10; // add value to “chemical deposition” array } } // find min max of chemical A ; min = 100000000; // initializing min max = -1 * min; // initializing max for (int i = 0; i < Res * Res; i++) // loop through cell array { min = MIN(mycells[i].chemicalA, min); // calculating min value of “chemical deposition” max = MAX(mycells[i].chemicalA, max); // calculating max value of “chemical deposition”

14


} // reset forces for (int i = 0; i < MaxParticles; i++) // loop through particles { forces[i] = vec(0, 0, 0); // reset forces } // calculate particle to particle forces for (int i = 0; i < MaxParticles; i++) // loop through particles { for (int j = i + 1; j < MaxParticles; j++) // loop through particles { if (i == j) continue; vec PtoPForce = PtoP(i, j); // initialising particle to particle vector force forces[i] += PtoPForce * collisonStrength; // integrating particle to particle force into global field of forces forces[j] += PtoPForce * collisonStrength * -1; // integrating particle to particle force into global field of forces } } // target force for (int i = 0; i < MaxParticles; i++) // loop through particles { float leastD = -100000; // initialize lowest number int idofTargets; // initialize target index int idofParticles = i; // initialize particle index for (int j = 0; j < MaxTargets; j++) // loop through targets { float dist = pos[i].distanceTo(target[j]); // distance between particle and target if (dist > leastD) // if statement to sort distances to largest and smallest value {

15


1_ Computer Instruction C++ Code leastD = dist; // replace distance idofTargets = j; // relate to target index } } vec p = pos[idofParticles]; // particle position vector vec dir = target[idofTargets] - p; // particle to target vector dir.normalise(); // normalise particle to target vector float dirLength = dir.mag(); // magnitude value of particle to target vector if (dirLength < 10) // if condition to calculate closest particle to target { forces[i] += dir * targetStrength; // integrating target force into global field of forces } else continue; } //applyGravity(); // call apply gravity function cellAttForce(); // call apply “chemical deposition� force function applyDragForce(); // call apply drag force function applySpinForce(); // call apply spin force function applyMagneticForce(); // call apply magnetic force function integrateForces(); // call intergrate forces function // update trail for (int i = 0; i < MaxParticles; i++) // loop through particles { trail[i][0] = pos[i]; // put the current position of particle into first place of myTrail array for (int j = MaxTrail - 1; j >= 1; j--) { trail[i][j] = trail[i][j - 1]; // shift the other elements in the array } } frm++; // update frames }

16


/// SETUP void setup() { backGround(0.75,0.75,0.75,0); // background color // initialise points/particles & velocity for (int i = 0; i < MaxParticles; i++) // loop through particles { vel[i] = vec(0, 0, 0); // reset velocity vector pos[i] = vec(ofRandom(75, 125), ofRandom(75, 125), 0); // random position of particles } for (int i = 0; i < MaxTargets; i++) // loop through targets { target[0] = vec(100, 100, 0); // target 0 position target[1] = vec(150, 50, 0); // target 1 position target[2] = vec(50, 50, 0); // target 2 position target[3] = vec(150, 150, 0); // target 3 position target[4] = vec(50, 150, 0); // target 4 position } for (int i = 0; i < MaxTargets; i++) // loop through targets { charges[0] = -1; // target 0 charge charges[1] = 1; // target 1 charge charges[2] = -1; // target 2 charge charges[3] = -1; // target 3 charge charges[4] = +1; // target 4 charge } // creating cell grid for (int i = 0; i < Res; i++) // loop through first dimension of cell array Res

17


1_ Computer Instruction C++ Code { for (int j = 0; j < Res; j++) // loop through second dimension of cell array Res { int id = i * Res + j; // index of Res array mycells[id].p = vec(unitX*i, unitY*j, 0); // position of lower left corner of cell in grid array mycells[id].unitX = unitX; // first dimension of array mycells[id].unitY = unitY; // second dimension of array mycells[id].chemicalA = 0.0; // reset “chemical deposition” force } } // slider and button on canvas S = *new SliderGroup(); // call slider function B = *new ButtonGroup(vec(50, 500, 0)); // call button function

B.addButton(&run, “run”); // run button

S.addSlider(&timeStep, “timeStep”); // slider 0 S.sliders[0].minVal = 0.01; // slider 0 min value S.sliders[0].maxVal = 1.00; // slider 0 max value

S.addSlider(&gravityStrength, “gravityStrength”); // slider 1 S.sliders[1].minVal = 0.00; // slider 1 min value S.sliders[1].maxVal = 10.00; // slider 1 max value

S.addSlider(&targetStrength, “targetStrength”); // slider 2 S.sliders[2].minVal = 0.00; // slider 2 min value S.sliders[2].maxVal = 10.00; // slider 2 max value

S.addSlider(&spinStrength, “spinStrength”); // slider 3 S.sliders[3].minVal = 0.00; // slider 3 min value

18


S.sliders[3].maxVal = 1.00; // slider 3 max value

S.addSlider(&dragStrength, “dragStrength”); // slider 4 S.sliders[4].minVal = 0.00; // slider 4 min value S.sliders[4].maxVal = 10.00; // slider 4 max value

S.addSlider(&collisonStrength, “collisonStrength”); S.sliders[5].minVal = 0.00; // slider 5 min value S.sliders[5].maxVal = 10.00; // slider 5 max value

S.addSlider(&collisonRadius, “collisonRadius”); // slider 6 S.sliders[6].minVal = 0.01; // slider 6 min value S.sliders[6].maxVal = 100.00; // slider 6 max value

S.addSlider(&magneticStrength, “magneticStrength”); S.sliders[7].minVal = 0.01; // slider 7 min value S.sliders[7].maxVal = 10.00; // slider 7 max value

// slider 7

S.addSlider(&chemicalStrength, “chemicalStrength”); S.sliders[8].minVal = 0.00; // slider 8 min value S.sliders[8].maxVal = 10.00; // slider 8 max value

// slider 8

// slider 5

} /// UPDATE void update(int value) // call update function { if (run)updateFunction(); }

19


1_ Computer Instruction C++ Code

/// --- --- --- /// VIEW /// --- --- --- /// void draw() // draw { glEnable(GL_POINT_SMOOTH); S.draw(); // - B.draw(); // --

// --

// draw particles for (int i = 0; i < MaxParticles; i++) // loop through particles { glPointSize(1); // particle size 1 vec4 color = getColour(i, 0, MaxParticles); // particle color mapping glColor3f(255,255,255); // particle color drawPoint(pos[i]); // draw particle point } // draw targets glColor3f(1, 0, 0); // target color for (int i = 0; i < MaxTargets; i++) // loop through targets { drawPoint(target[i]); // draw target point } // draw particle trail for (int i = 0; i < MaxParticles; i++) // loop through particles { int end = frm % MaxTrail; // frame per trail size if (MaxTrail < frm) end = MaxTrail;

20


int trailNum = MaxTrail; for (int j = 0; j < end-1; j++) // loop through trail related to time (frame) { float val = ofMap(i, 0, MaxParticles, 0, 1); // map particles to 0 to 1 value float valTrail = ofMap(j, 0, end, 0, 255); // map trails to 0 to 255 value glColor4f(0, val, valTrail, valTrail); // trail color glPointSize(0.3); // trail point size 0.3 //drawPoint(trail[i][j]); //drawLine(trail[i][j], trail[i][j + 1]); } } // draw grid cell for (int j = 0; j < Res * Res; j++) // loop through one dimensional cell array { glPointSize(5); // point size 5 vec4 clr = getColour(mycells[j].chemicalA, min,max); // cell color map related to “chemical deposition� value glColor3f(clr.r,clr.g,0); // cell color drawRectangle(mycells[j].p, mycells[j].p + vec(unitX, unitY, 0)); // draw rectangle as cell }

// setup2d(); glColor3f(0, 0, 0); drawString(seq, 50, 600); restore3d();

}

21


1_ Computer Instruction C++ Code /// --- --- --- /// CONTROLLER /// --- --- --- /// /// CONTROLLER - KEY PRESS void keyPress(unsigned char k, int xm, int ym) // key press function { if (k == ‘t’) { seq += “t”; // if “t” is pressed add to sequence label on canvas } if (k == ‘v’) { seq += “v”; // if “v” is pressed add to sequence label on canvas for (int i = 0; i < MaxTargets; i++) // loop through targets { charges[i] *= -1; // switch charge of targets } } if (k == ‘c’) { seq += “c”; AttRep *= -1; } if (k == ‘s’) { setup(); }

22

// if “c” is pressed add to sequence label on canvas // switch from attraction to repulsion of particle to particle force

// reset setup


if (k == ‘t’) { topCamera(); }

// switch to top view

if (k == ‘w’) { for (int i = 0; i < MaxTargets; i++) // loop through targets { target[i] = vec(ofRandom(-100, 100), ofRandom(-100, 100), 0); } }

// randomize target

if (k == ‘A’) // to print high res EPS image { FILE *fp; // initialize file function int state = GL2PS_OVERFLOW, buffsize = 0; // prevent overflow fp = fopen(“out.eps”, “w”); printf(“Writing ‘out.eps’... “); while (state == GL2PS_OVERFLOW) { buffsize += winW * winH; gl2psBeginPage(“test”, “gl2psTestSimple”, NULL, GL2PS_EPS, GL2PS_SIMPLE_SORT, GL2PS_USE_CURRENT_VIEWPORT, GL_RGBA, 0, NULL, 0, 0, 0, buffsize, fp, “out.eps”); draw(); state = gl2psEndPage(); } fclose(fp); printf(“Done!\n”); }

23


1_ Computer Instruction C++ Code if (k == ‘u’) { updateFunction(); // update function by step } } /// CONTROLLER - MOUSE PRESS void mousePress(int b, int s, int x, int y) { if (GLUT_LEFT_BUTTON == b && GLUT_DOWN == s) { S.performSelection(x, y, HUDSelectOn); B.performSelection(x, y); } } /// CONTROLLER - MOUSE MOTION void mouseMotion(int x, int y) { S.performSelection(x, y, HUDSelectOn); }

24


#ifndef CELLS #define CELLS #include “main.h” class Cells { public: // to make it accessable from everywhere in the code // global variables vec p; // initializing vector p float unitX; // size x of cell float unitY; // size y of cell float chemicalA; // “chemical deposition” value Cells() // constructor { p = vec(0,0,0); // vector p unitX = 1; // size x of cell unitY = 1; // size y of cell chemicalA = 0; // “chemical deposition” value }; Cells(vec _p, float _unitX, float _unitY, float _chemicalA) { p = _p; unitX = _unitX; unitY = _unitY; chemicalA = _chemicalA; }

// overload constructor

}; #endif

25


1_ Computer Instruction C++ Code #include “main.h” #include “ALICE_ROBOT_DLL.h” using namespace ROBOTICS;

// import robot library // set robot library namespace to ROBOTICS

/// --- --- --- /// GLOBAL VARIABLES /// --- --- --- /// #define Z 21.79

// define Z value from robot working plate

Robot_Symmetric nachi; // initialize Robot_Symmetric class with nachi istance SliderGroup S; // initialize SliderGroup class with S istance ButtonGroup B; // initialize ButtonGroup class with B istance importer ptsReader; // initialize importer class with ptsReader istance string mesh_names[5] = { “data/link0.obj”, “data/link1.obj”, “data/link2.obj”, “data/link3.obj”, “data/link4.obj” }; ofstream myfile, myfile_write; // initialize ofstream class with myfile and myfile_write instances int ptcnt = 0; // point counter bool computeAO = false; char gcode[600];

// boolean type set to false

// array of characters to store the GCODE data

vec target(10, 0, 0); // vector position for target vec diff; // vector difference /// --- --- --- /// MODEL /// --- --- --- /// /// SETUP void setup() { vec pt = nachi.ForwardKineMatics(nachi.inv_rot); // setup forward kinematics calculation nachi.addMeshes(); // import robot meshes

26

//initialize string class with mesh_names istance


S = *new SliderGroup(); // declare new slider group // sliders connected to joints rotation S.addSlider(&nachi.rot[0], “J1”); // slider J1 S.addSlider(&nachi.rot[1], “J2”); // slider J2 S.addSlider(&nachi.rot[2], “J3”); // slider J3 S.addSlider(&nachi.rot[3], “J4”); // slider J4 S.addSlider(&nachi.rot[4], “J5”); // slider J5 S.addSlider(&nachi.rot[5], “J6”); // slider J6 // sliders connected to joints rotation S.sliders[0].attachToVariable(&nachi.rot[0], -170, 170); S.sliders[1].attachToVariable(&nachi.rot[1], -45, 170); S.sliders[2].attachToVariable(&nachi.rot[2], -67, 120); S.sliders[3].attachToVariable(&nachi.rot[3], -190, 190); S.sliders[4].attachToVariable(&nachi.rot[4], -120, 120); S.sliders[5].attachToVariable(&nachi.rot[5], -370, 370);

// slider 0 // slider 1 // slider 2 // slider 3 // slider 4 // slider 5

nachi.rot[1] = 90; // set J2 rotation to 90 degrees target = vec(nachi.joints[5].x, nachi.joints[5].y, nachi.joints[5].z); // set position of joints coordinates ptsReader = *new importer(“data/1/White.txt”, 10000, 1.0); // imports points coordinates ptsReader.readPts_p5(); // read points } /// UPDATE void update(int value) { vec pt = nachi.ForwardKineMatics(nachi.rot); // update forward kinematics calculation }

27


1_ Computer Instruction C++ Code /// --- --- --- /// VIEW /// --- --- --- /// void draw() { backGround(0.75); // set background color drawGrid(20.0); // draw grid 20x20 S.draw(); // display slider S group B.draw(); // display slider B group nachi.draw(true); // display nachi robot glColor3f(1, 0, 1); // set color drawCircle(target, 5, 32); // draw circle in target vector posistion glPointSize(3); // set point size glColor3f(0, 0, 1); // set color for (int i = 0; i < ptsReader.nCnt; i++) drawPoint(ptsReader.nodes[i].pos); // display imported points drawLine(nachi.joints[5], nachi.joints[5] - diff); // display line from J6 to vector position diff // display joints rotation values char s[200]; double l = 0.; for (int i = 1; i < DOF; i++) l += nachi.joints[i].distanceTo(nachi.joints[i - 1]); sprintf_s(s, “ link lengths %1.4f”, l); setup2d(); drawString(s, winW - 250, winH - 50); glColor3f(1, 0, 0); sprintf_s(s, “ TCP_X %1.4f %1.4f %1.4f”, nachi.TCP_x.x, nachi.TCP_x.y, nachi.TCP_x.z); drawString(s, winW * 0.5, 25); glColor3f(0, 1, 0); sprintf_s(s, “ TCP_Y %1.4f %1.4f %1.4f”, nachi.TCP_y.x, nachi.TCP_y.y, nachi.TCP_y.z);

28


drawString(s, winW * 0.5, 50); glColor3f(0, 0, 1); sprintf_s(s, “ TCP_Z %1.4f %1.4f %1.4f”, nachi.TCP_z.x, nachi.TCP_z.y, nachi.TCP_z.z); drawString(s, winW * 0.5, 75); vec pp = nachi.joints[5] + vec(0, 0, -24.98); sprintf_s(s, “ %1.4f %1.4f %1.4f”, pp.x, pp.y, pp.z); drawString(s, winW * 0.5, 125); drawString(gcode, winW * 0.5, 100); restore3d(); }

// restore 3d view

/// --- --- --- /// CONTROLLER /// --- --- --- /// void keyPress(unsigned char k, int xm, int ym) { if (k == ‘r’) { nachi.inv_rot.setConstant(0.); // set rotation constant to 0 degrees nachi.inv_rot(1) = 90.0; // set rotation to 90 degrees for (int i = 0; i < DOF; i++)nachi.rot[i] = nachi.inv_rot(i); } if (k == ‘n’) { target = ptsReader.nodes[ptcnt].pos; // assign points imported as target for the robot to check reachability target.z += Z; // lift the Z value of the target of the value defined in the global variables #define federico #ifdef federico vec diff1 = vec(-9.21906, 12.4999, 221.047);

// define vector diff1

29


1_ Computer Instruction C++ Code vec diff0 = vec(0, 0, 0); // define vector diff0 diff = (diff1 - diff0); // vector subtraction between diff1 - diff0 to get the vector distance float d = diff.mag(); // conversion from vector to float diff = diff.normalise() * d * 0.1; // define increment of movement target = ptsReader.nodes[ptcnt].pos + diff; // movement of the robot to target points vec x1 = vec(46.1273, 12.7838, 123.621); vec x0 = vec(45.642, 11.9514, 123.353); vec y1 = vec(46.3664, 11.3972, 123.763); // calculation of the vector distances to get the orientation of the TCP vec z1 = vec(45.1524, 11.9563, 124.225); vec x = (x1 - x0).normalise(); vec z = (z1 - x0).normalise() * -1; vec y = z.cross(x).normalise(); // cross product between vectors to get the perpendicular x.normalise(); y.normalise(); z.normalise(); #endif

// normalize the vectors to unit vectors

#ifndef federico vec x(1, 0, 0); vec y(0, 1, 0); vec z(0, 0, -1); #endif //#ifndef federico // vec x(1, 0, 0); // vec y(0, 1, 0); // set TCP orientation to straight // vec z(0, 0, -1); //#endif nachi.TCP_x = x; nachi.TCP_y = y; // assign X,Y,Z vectors to the TCP vector positions nachi.TCP_z = z; nachi.inverseKinematics_analytical(target, false); // calculate inverse kinematics

30


ptcnt++; if (ptcnt >= ptsReader.nCnt)ptcnt = 0; // reset point counter if greater the number of points imported } if (k == ‘N’) { myfile_write.open(“data/Nachi Files/1/MZ07-01-A.050”, ios::out); // open a new text file if (myfile_write.fail())cout << “ error in opening file “ << “MZ07-01-A.083” << endl; // check if the file has been correctly opened for (int i = 0; i < ptsReader.nCnt; i++) { cout << target.z << endl; // close the text file target = ptsReader.nodes[i].pos; target.z += Z; #ifdef federico vec diff1 = vec(-9.21906, 12.4999, 221.047); vec diff0 = vec(0, 0, 0); diff = (diff1 - diff0); float d = diff.mag(); diff = diff.normalise() * d * 0.1; target = ptsReader.nodes[i].pos + diff; vec x1 = vec(46.1273, 12.7838, 123.621); vec x0 = vec(45.642, 11.9514, 123.353); vec y1 = vec(46.3664, 11.3972, 123.763); vec z1 = vec(45.1524, 11.9563, 124.225); vec x = (x1 - x0).normalise(); vec z = (z1 - x0).normalise() * -1; // x = z.cross(vec(1, 0, 0)).cross(z).normalise(); vec y = z.cross(x).normalise(); x.normalise();

31


1_ Computer Instruction C++ Code y.normalise(); z.normalise(); #endif #ifndef federico vec x(1, 0, 0); vec y(0, 1, 0); vec z(0, 0, -1); #endif //#ifndef federico //vec x(1,0,0); //vec y(0,1,0); //vec z(0, 0, -1); //#endif nachi.TCP_x = x; //vec(1, 0, 0); nachi.TCP_y = y;// vec(0, 1, 0); nachi.TCP_z = z;// vec(0, 0, -1); nachi.inverseKinematics_analytical(target, false); vec pt = nachi.ForwardKineMatics(nachi.rot); { float e_x, e_y, e_z, r, p, y; e_x = nachi.rot[0]; e_y = nachi.rot[1]; e_z = nachi.rot[2]; r = 0; // set a fixed angle to joint J4 p = nachi.rot[4]; y = 270; // set a fixed angle to joint J6; sprintf_s(gcode, “MOVEX A=6,AC=0,SM=0,M1J,P,( %1.2f,%1.2f,%1.2f,%1.2f,%1.2f,%1.2f),T=0.1,H=3,MS, CONF=0001�, e_x, e_y, e_z, r, p, y); write a text file with the GCODE informations myfile_write << gcode << endl; // close the text file

32

//


} } myfile_write.close(); } if (k == ‘=’)target.x += 1.1; if (k == ‘-’)target.x -= 1.1; if (k == ‘0’)target.y += 1.1; if (k == ‘9’)target.y -= 1.1; // move the target position manually along X,Y,Z if (k == ‘7’)target.z += 1.1; if (k == ‘8’)target.z -= 1.1; if (k == ‘ ‘)computeAO = !computeAO; if (k == ‘c’)myfile.close(); } void mousePress(int b, int state, int x, int y) { if (GLUT_LEFT_BUTTON == b && GLUT_DOWN == state) { S.performSelection(x, y, HUDSelectOn); // move S sliders along X and Y B.performSelection(x, y); // move B slider along X and Y } } void mouseMotion(int x, int y) { S.performSelection(x, y, HUDSelectOn); }

// make the S sliders selectable

33


2_ Catalogue Behavior Types 2.1_ Particles to Target



2_ Catalogue Behavior Types SETUP Particles 1000

Gravity Strength

0.00

Target Strength

0.00

Spin Strength

0.00

Drag Strength

0.20

Collision Strength

0.50 2.00

0.00 0.00

36

Targets 8 Time Step

0.50

0.10

Trails 500

Collision Radius Magnetic Strength Chemical Radius Chemical Strength


SETUP Particles 1000

Gravity Strength

0.00

Target Strength

0.00

Spin Strength

0.00

Drag Strength

0.20

Collision Strength

0.50 2.00

0.00 0.00

Targets 8 Time Step

0.50

0.10

Trails 500

Collision Radius Magnetic Strength Chemical Radius Chemical Strength

37


2_ Catalogue Behavior Types SETUP Particles 1000

Trails 500

Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50

Collision Radius

1.00

Magnetic Strength

0.10 5.0 0.50

38

Targets 8

Chemical Radius Chemical Strength


SETUP Particles 1000

Trails 500 0.50

Target Strength

0.00

Spin Strength

0.00

Drag Strength

0.20

Collision Strength

0.50 2.00

0.00 0.00

Time Step Gravity Strength

0.00

0.10

Targets 8

Collision Radius Magnetic Strength Chemical Radius Chemical Strength

39


2_ Catalogue Behavior Types SETUP Particles 1000

Gravity Strength

0.00

Target Strength

0.00

Spin Strength

0.00

Drag Strength

0.20

Collision Strength

0.50 2.00

0.00 0.00

40

Targets 8 Time Step

0.50

0.10

Trails 500

Collision Radius Magnetic Strength Chemical Radius Chemical Strength


SETUP Particles 1000

Gravity Strength

0.00

Target Strength

0.00

Spin Strength

0.00

Drag Strength

0.20

Collision Strength

0.50 2.00

0.00 0.00

Targets 8 Time Step

0.50

0.10

Trails 500

Collision Radius Magnetic Strength Chemical Radius Chemical Strength

41


2_ Catalogue Behavior Types SETUP Particles 1000

Gravity Strength

0.00

Target Strength

0.00

Spin Strength

0.00

Drag Strength

0.20

Collision Strength

0.00 2.00

0.00 0.00

42

Targets 8 Time Step

0.50

0.10

Trails 500

Collision Radius Magnetic Strength Chemical Radius Chemical Strength


SETUP Particles 1000

Gravity Strength

0.00

Target Strength

0.00

Spin Strength

0.00

Drag Strength

0.20

Collision Strength

0.50 2.00

0.00 0.00

Targets 8 Time Step

0.50

0.10

Trails 500

Collision Radius Magnetic Strength Chemical Radius Chemical Strength

43


2_ Catalogue Behavior Types SETUP Particles 1000

Trails 500

Targets 8 Time Step

1.00

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.80

Collision Strength

1.50

Collision Radius

0.10 7.00

Chemical Radius

0.50 5.00

44

Magnetic Strength

Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

1.00

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.80

Collision Strength

1.50

Collision Radius

0.10 7.00

Magnetic Strength Chemical Radius

0.50 5.00

Chemical Strength

45


2_ Catalogue Behavior Types 2.2_ Particle to Particle



2_ Catalogue Behavior Types

SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50

Collision Radius

10.00

Magnetic Strength

0.10

Chemical Radius

0.50 5.00

48

Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50 5.00

Collision Radius Magnetic Strength

0.10

Chemical Radius

0.50 5.00

Chemical Strength

49


2_ Catalogue Behavior Types

SETUP Particles 1000

Gravity Strength

0.00

Target Strength

0.10

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50 3.00

0.00 0.50

50

Targets 8 Time Step

0.20

0.10

Trails 500

Collision Radius Magnetic Strength Chemical Radius Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50

Collision Radius

2.00

Magnetic Strength

0.10

Chemical Radius

0.50 5.00

Chemical Strength

51


2_ Catalogue Behavior Types

SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50 9.00

Magnetic Strength

0.10

Chemical Radius

0.50 5.00

52

Collision Radius

Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50 6.00

Collision Radius Magnetic Strength

0.10

Chemical Radius

0.50 5.00

Chemical Strength

53


2_ Catalogue Behavior Types

SETUP Particles 1000

Trails 500

Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50

Collision Radius

4.00

Magnetic Strength

0.10

Chemical Radius

0.50 5.00

54

Targets 8

Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.050

Drag Strength

0.10

Collision Strength

0.50 6.00

Collision Radius Magnetic Strength

0.10

Chemical Radius

0.50 5.00

Chemical Strength

55


2_ Catalogue Behavior Types

SETUP Particles 1000

Trails 500

Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.10

Drag Strength

0.10

Collision Strength

0.70

Collision Radius

4.00

Magnetic Strength

0.10

Chemical Radius

0.50 5.00

56

Targets 8

Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.20

Collision Radius

4.00

Magnetic Strength

0.10

Chemical Radius

0.50 5.00

Chemical Strength

57


2_ Catalogue Behavior Types 2.3_ Particles to Environment



2_ Catalogue Behavior Types

SETUP Particles 1000

Trails 500

Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50

Collision Radius

1.00

Magnetic Strength

0.10

Chemical Radius

0.50 5.00

60

Targets 8

Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50

Collision Radius

1.00

Magnetic Strength

0.10 10.00 0.50

Chemical Radius Chemical Strength

61


2_ Catalogue Behavior Types

SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50

Collision Radius

1.00

Magnetic Strength

0.10 19.90 0.50

62

Chemical Radius Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.00

Spin Strength

0.00

Drag Strength

0.00

Collision Strength

0.50

Collision Radius

5.0095

Magnetic Strength

0.10 10.00 0.50

Chemical Radius Chemical Strength

63


2_ Catalogue Behavior Types

SETUP Particles 1000

Trails 500

Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50

Collision Radius

0.5091

Magnetic Strength

0.10 5.00 0.50

64

Targets 8

Chemical Radius Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.00

Spin Strength

0.00

Drag Strength

0.00

Collision Strength

0.50

Collision Radius

2.0098

Magnetic Strength

0.10 10.00 0.50

Chemical Radius Chemical Strength

65


2_ Catalogue Behavior Types

SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

0.20

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50 10.00

Magnetic Strength

0.10 5.00 0.50

66

Collision Radius

Chemical Radius Chemical Strength


SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.20

Gravity Strength

0.00

Target Strength

5.00

Spin Strength

0.00

Drag Strength

0.10

Collision Strength

0.50 10.00

Magnetic Strength

0.10 5.00 0.50

Collision Radius

Chemical Radius Chemical Strength

67


2_ Catalogue Behavior Types

SETUP Particles 1000

Trails 500

Targets 8 Time Step

0.010

Gravity Strength

0.00

Target Strength

0.50

Spin Strength

0.0250

Drag Strength

0.80

Collision Strength

0.50 10.00 6.1539 0.00 0.0100

68

Collision Radius Magnetic Strength Chemical Radius Chemical Strength


SETUP Particles 1000 0.0100 0.00 0.50 0.10 0.10 0.50

Trails 500

Targets 8 Time Step Gravity Strength Target Strength Spin Strength Drag Strength Collision Strength Collision Radius

10.00 0.10 0.00 0.0100

Magnetic Strength Chemical Radius Chemical Strength

69


3_ Robotic Arm End Effector Design

70


NACHI FUJIKOSHI spans a wide range of manufacturing fields. These include products for the machining industry, such as tools, machine tools and ultra-precision machines, as well as robotic systems for automating production lines and reducing power consumption. We are using the Nachi MZ07 robotic arm, itself is an ultra highspeed motion capability with advanced through-arm dress capabilities to simplify routing of hoses and cables for material handling. In this workshop we will be designing custom pen effector to attached at the end of the robotic arm agent.

71


3_ Robotic Arm End Effector Design

Fabrication Method: 3D Print Material: PLA 2.75 mm Fabrication Time: 8 hours Machine: Ultimaker 2

Top View

Front view

72

Bottom view

This custom 3d printed end effector is design to hold four UNI POSCA marker individually, three medium size, one large size. Inside the bottom of pen holder, is design to store magnetic sphere to attach with markers, for convince replacing different type of colors.

Perspective


Bottom view

1.5 mm

1.5 mm

1.5 mm

1.5 mm

Top view

Dots Lines Colors 1.3 mm and 1.5 mm

Orthogonal

Perspective Front view

Front view

Orthogonal

73


3_ Robotic Arm End Effector Design

NACHI MZ07

Agent A

Agent B

Effector

Bolt

74


G3 NACHI MZ07 Agent A Agent B

G2

Effector Bolt

G4

G1

G5 G0 Top View

Perspective

Front view

Side view

After 3d printed custom effector, the effector attached to the agent that is connect between Nachi robotic arm and effector with four bolt on the side.

75


3_ Robotic Arm End Effector Design

NACHI MZ07 Working Envelope IK (Inverse Kinematics) Translation of 6th axis and consequent calculation of the joints angles G2

G3

G4

G5 Top view

Perspective

G1 G0

170째

723m

m

170째

Side view

76

Front view


77


3_ Robotic Arm End Effector Design

78


Nachi Agent

Once effector is connected to Nachi, in order to allow Nachi to draw in the center point of effect, it required to calculate the correct cordination of the center axis, due to effector center axis is 60째 offset between each pen.

Effector Agent

X TCP

X TCP -0.86613190, 0.00000000, -0.49981550

End Effector

Nachi Center Axis

Effector Center Axis 90째

Y TCP

60째

Z TCP

0.50001100 , 0.00006667, -0.86601905 Y TCP

Z TCP

79


4_ Robot Instruction and Analysis Drawing

80


Chapter 4 presents the vector output of the code and the resulting drawings of the 6 axis robotic arm Nachi, drawing with four dispersion felt pens onto black pvc sheets. For each drawing a series of pie charts were created, each showing the angular-motion of one of six axis. A first set of pie charts mapping the angular motion for each axis from minimum in dark blue to the maximum in red of the angular motion. The second set of pie charts situates the angular motion of each axis in the movement constraints of the Nachi robot. The pie charts show the motion over time, starting from the top 12 o’clock moving forward clock-wise 360 degree. This analysis is able to support a optimization process of manufacturing efficiency. Variables like machine time and so speed and accuracy could be enhanced. Also at this point the g-code is randomly compiled and executed which means the robot might start from one end and will continue on the other side instead of continuing working its way over, which is highly visible in the pie charts high noise.

81


4_ Robot Instruction and Analysis Drawing

Photograph

Vector Image EPS file exported from code Photograph

82


G0

G1

G2

G3

G4

83


4_ Robot Instruction and Analysis Drawing

Photograph

Photograph Vector Image EPS file exported from code

84


G0

G1

G2

G3

G4

85


4_ Robot Instruction and Analysis Drawing

Photograph

Photograph Vector Image EPS file exported from code

86


G0

G1

G2

G3

G4

87


4_ Robot Instruction and Analysis Drawing

Photograph

Photograph

88

Vector Image EPS file exported from code


G0

G1

G2

G3

G4

89


4_ Robot Instruction and Analysis Drawing

Issue_1 In this first drawing attempt, the end effector was unable to draw complete straight lines between two points, due to TCP is not perfectly calibrated which caused a radial shift of the pen. The interpolation of points in the robot path with a 3 degree curve caused the anticipation of the z shifting in each last point of each curve, leaving this incomplete.

Radial Error due to the imperfect calibration of the TCP

90


Issue_2 In this drawing attempt, the effector was unable to reach bottom partition of the drawing, due to the platform itself is unbalanced because the weight of Nachi, causing the platform bending of 2 degree. After the measurement the bottom partition of the paper is offset 1-2mm to the horizontal level.

Plane Error due to the weekness of the wooden plate which bended with the robot weight

91


4_ Robot Instruction and Analysis Drawing

92

Issue: sixth axis calibration Solution: GCODE calibration

Issue: sixth axis calibration Solution: GCODE calibration

Issue: sixth axis calibration Solution: GCODE calibration

Issue: Z TCP axis calibration Solution: GCODE calibration

Issue: Z TCP axis calibration Solution: GCODE calibration

Issue: Z TCP axis calibration Solution: GCODE calibration


Issue: sixth axis calibration Solution: GCODE calibration

Issue: sixth axis calibration Solution: GCODE calibration

Issue: sixth axis calibration Solution: GCODE calibration

Issue: Z TCP axis calibration Solution: GCODE calibration

Issue: Z TCP axis calibration Solution: GCODE calibration

Issue: Z TCP axis calibration Solution: GCODE calibration 93


Conclusion Gaining more and more control over the code, creating a highly responsive, generative, magnetic field behaviour like working script, combined with branches of notions also known from swarm intelligence theory, was a logic consequence as time went by. Fast simulation and visualization made quick progress in the digital world possible. Moving forward translating output of the script into the G-Code to be physically drawn by the robot brought up many new challenges which urged to be understood and their solutions fed back into the code to optimize and drive to perfection. 1. A main challenge was to calibrate the offset of the robots TCP: Because the customized endeffector has four radial outward oriented tool tips the TCP could not be used as default center. The codes interpolation had to be offseted and rotated into four different directions. 2. The physical mounting of the robot created inaccuracies executing and driving the robot. 3. A non fixed canvas enables for exact calibration of the center point of the drawing but creates inconsistencies in the level of the canvas plate, which also led to a necessary recalibration of each of four pens. 4. An unsolved challenge was the spheric notion of the robots operation space, which is caused by not accurately mapping the G-Code to the planar digital canvas of the robots firmware. 5. Differenciation between points and lines. The interpolation of the NACHI compiler, really interpolates all the path points, each line of the G-Code with an curve third or even fifth grade as it’s tool path. Since the drawing is supposed to be on a planar surface, already

94

sharp edges will be round and just used as point to pass through instead of treating such as a point to stop and move into a direction with different tangent. Of course, the robot needs to be instructed to go move in z-axis at some point, otherwise there will be one continues line instead of line segments with a start point and end point. The problem with interpolation is exactly the same in this case, just in z direction. Little hooks on the ends of the line segments, or even a broke tool tip is a result of this type of compiling. There are many great and complex behaviours which are almost perfectly controllable and translateable in code, but feeding back real world conditions into code is crucial in a context of computer aided manufacturing. Therefor understanding the machine in depth and using the right medium to feed back information to the code is fundamental. Creating a rigid high performing and to the thousands of decimals calculateable code is one thing, making it able to respond to a constantly changing and absolutely non-calculateable surrounding world the other. Challenge accepted!




Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.