얼마전에 제가 즐겨보는 예능의 PD인 나영석 피디의 인터뷰 중 이런 이야기가 있더군요.
“그냥, 정당한 인간적 대우를 해주면 된다. 70~80명의 스태프가 거기 있는 이유는 각자 하나하나 소중한 역할이 있어서다. 예를 들어 배차 담당을 하는 친구가 있는데 그 일이 사실 도드라지진 않지만 잘 안되면 욕은 욕대로 무지하게 먹는 자리다. 그럼 그 친구한테 권한을 주고 ‘네가 책임을 지고 해줘’ 맡기고 ‘고맙다, 수고했다, 너니까 했다’ 이런 얘길 하는 게 내가 할 수 있는 다다. 그렇게만 해도 ‘아, 피디님한테 칭찬받았어’가 아니라 ‘나도 1박2일을 위해 뭔가를 하고 있어’가 되는 거다. 그런 주인의식을 갖는 게 진짜 하늘과 땅 차이다. 그런 사소한 차이가 100%를 채운다고 믿는다.”
-. 출연자 뿐만 아니라 스태프까지 자발적 참여를 이끌어내는 비결을 묻는 질문에 대한 대답.-
출처 : 한겨례 신문 [바로가기]
어쩐지 요즘 삼시세끼가 재미있더라구요..ㅎㅎㅎ. 편안하면서 말이죠...^^
아무튼 오늘은 지난번에 이야기한 기구학의 아주아주 기초인 정방향(forward kinematics) 기구학의 기초를 다루면서 예제로 이야기[바로가기]한 Two Link Planar를 다룰려고 합니다.
그때도 이야기했지만... 이 그림은 제가 그린것이 아니며 출처를 잃어버렸습니다.ㅠㅠ.
당시엔 저걸 정방향 기구학에 대한 예제로 하나 다루면서 MATLAB을 사용했는데요. 아무래도 좀 심심했죠? 그래서... 이번에는 Processing으로 구현해볼까 하는 겁니다.^^.
float[][] RotM(char axis, float theta) { if (axis=='a') { return new float[][]{{cos(theta),-sin(theta), 0,0},{sin(theta),cos(theta), 0,0},{0,0,1,0},{0,0,0,1}}; } else if (axis=='o') { return new float[][]{{cos(theta),0,sin(theta),0},{0,1,0,0},{-sin(theta),0,cos(theta),0},{0,0,0,1}}; } else { return new float[][]{{1,0,0,0},{0,cos(theta),-sin(theta),0},{0,sin(theta),cos(theta),0},{0,0,0,1}}; } } float[][] TransM(float x, float y, float z) { return new float[][]{{1,0,0,x},{0,1,0,y},{0,0,1,z},{0,0,0,1}}; }
먼저 회전행렬과 이동행렬을 구현합니다.
위 행렬들이 이전[바로가기]에 이야기했던 이동행렬과, 회전행렬들인데요. 위 코드는 프로그램으로 표현한것일 뿐이죠. 그리고, 마우스를 이용해서 1번과 2번 링크를 회전하도록 할 겁니다. 그렇게 구현한 부분이...
void drawPanel() { fill(50); textFont(titleFont); text("Kinematics Example of Two Link Planar",140,25); textFont(smallFont); text("by PinkWink",500,45); stroke(150); line(50,550,280,550); line(320,550,550,550); if (grabSlider==1) { theta1 = (float(mouseX)-(50+280)/2)/115*PI; } if (grabSlider==2) { theta2 = (float(mouseX)-(320+550)/2)/115*PI; } slidePos1 = theta1*115/PI + (50+280)/2; slidePos2 = theta2*115/PI + (320+550)/2; stroke(100); fill(255); if (grabSlider==1) {fill(200);} ellipse(slidePos1,550,10,10); fill(255); if (grabSlider==2) {fill(200);} ellipse(slidePos2,550,10,10); } void mousePressed() { if (mouseButton==LEFT) { if (abs(mouseX-(50+280)/2)<165 && abs(mouseY-550)<5) { grabSlider = 1; } if (abs(mouseX-(320+550)/2)<165 && abs(mouseY-550)<5) { grabSlider = 2; } } } void mouseReleased() { if (mouseButton==LEFT) { grabSlider = 0; } }
이부분은 사실 제가 이전에 시리얼 통신의 데이터를 그래프로 표현하기라는 글[바로가기]에서도 사용한 방법이지요. 마우스가 눌러진 위치가 내가 버튼(혹은 슬라이더)라고 생각한 범위인지 판별하는 거지요. 뭐 GUI 툴박스를 사용하는 것도 좋지만.. 그냥 Processing 답게 이렇게 사용하는 것이 더 편하답니다.^^
그리고, 링크1에 대한 변환
과.. 링크2에 대한 변환
도 이전에 이야기했으니. 그냥 가져다 쓰기만 하면 되겠죠~~~. 여기서 위 변환행렬을 적용한 부분이...
float[][] T_0_1 = Mat.multiply(RotM('a', theta1), TransM(a1,0,0)); float[][] T_1_2 = Mat.multiply(RotM('a', theta2), TransM(a2,0,0)); float[][] T_total = Mat.multiply(T_0_1, T_1_2); float[][] y0_1 = Mat.multiply(T_0_1, y0); float[][] y0_2 = Mat.multiply(T_total, y0); float[] originX = {y0[0][3], y0_1[0][3], y0_2[0][3]}; float[] originY = {y0[1][3], y0_1[1][3], y0_2[1][3]};
참... 위 코드에서 Mat이라고 된 행렬 연산 기능은 지난번에 소개했던 Papaya 라이브러리[바로가기]를 사용하고 있습니다. 사실 기구학중에서 쉬운 정방향 기구학은 저렇게 변환행렬이 단지 행렬의 곱셈으로 나타나기 때문에 아주 쉽지요^^.
위 그림이 시뮬레이션 결과 입니다. 아래에 있는 슬라이드 바를 움직이면 각각 1번 링크와 1번 링크가 움직입니다. 그냥 이렇게 가면 좀 허전한듯하니 동영상(으로 볼것도 없지만)으로 한번 보시죠^
이미 GitHub[바로가기]에도 올려두어서 다운로드 하시기 편하실 테지만, 그래도 전체 코드는
import papaya.*; float theta1 = 0; float theta2 = 0; float a1 = 100; float a2 = 100; float centerPos = 300; float sizeOfAxis = 10; int grabSlider = 0; float slidePos1, slidePos2; float[][] y0 = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}; float[][] pol0 = {{10,20,10,10},{10,10,20,10},{0,0,0,0},{1,1,1,1}}; PFont titleFont = createFont("Helvetica",20,true); PFont smallFont = createFont("Helvetica",12,true); PFont tinyFont = createFont("Helvetica",9,true); void setup() { size(600,600); } void draw() { background(255); drawPanel(); float[][] T_0_1 = Mat.multiply(RotM('a', theta1), TransM(a1,0,0)); float[][] T_1_2 = Mat.multiply(RotM('a', theta2), TransM(a2,0,0)); float[][] T_total = Mat.multiply(T_0_1, T_1_2); float[][] y0_1 = Mat.multiply(T_0_1, y0); float[][] y0_2 = Mat.multiply(T_total, y0); float[] originX = {y0[0][3], y0_1[0][3], y0_2[0][3]}; float[] originY = {y0[1][3], y0_1[1][3], y0_2[1][3]}; noFill(); stroke(200); ellipse(centerPos + y0[0][3], centerPos - y0[1][3], a1*2, a1*2); ellipse(centerPos + y0_1[0][3], centerPos - y0_1[1][3], a2*2, a2*2); strokeWeight(2); stroke(color(#8E8E8E)); line(centerPos + originX[0], centerPos - originY[0], centerPos + originX[1], centerPos - originY[1]); line(centerPos + originX[1], centerPos - originY[1], centerPos + originX[2], centerPos - originY[2]); drawBodyAxis(y0, centerPos, sizeOfAxis); drawBodyAxis(y0_1, centerPos, sizeOfAxis); drawBodyAxis(y0_2, centerPos, sizeOfAxis); fill(50); textFont(tinyFont); text(nfs(theta1*180/PI,0,2), centerPos + originX[0], centerPos - originY[0] + 20); text(nfs(theta2*180/PI,0,2), centerPos + originX[1], centerPos - originY[1] + 20); String endPos = "( "+nfs(originX[2],0,2)+", "+nfs(originY[2],0,2)+" )"; text(endPos, centerPos + originX[2]+20, centerPos - originY[2]+20); float[][] pol0 = {{10,20,10},{10,10,20},{0,0,0},{1,1,1}}; float[][] pol0_1 = Mat.multiply(T_0_1, pol0); float[][] pol0_2 = Mat.multiply(T_total, pol0); drawObject(pol0, centerPos); drawObject(pol0_1, centerPos); drawObject(pol0_2, centerPos); } float[][] RotM(char axis, float theta) { if (axis=='a') { return new float[][]{{cos(theta),-sin(theta), 0,0},{sin(theta),cos(theta), 0,0},{0,0,1,0},{0,0,0,1}}; } else if (axis=='o') { return new float[][]{{cos(theta),0,sin(theta),0},{0,1,0,0},{-sin(theta),0,cos(theta),0},{0,0,0,1}}; } else { return new float[][]{{1,0,0,0},{0,cos(theta),-sin(theta),0},{0,sin(theta),cos(theta),0},{0,0,0,1}}; } } float[][] TransM(float x, float y, float z) { return new float[][]{{1,0,0,x},{0,1,0,y},{0,0,1,z},{0,0,0,1}}; } void drawObject(float[][] obTarget, float centerPos) { stroke(color(#000000)); noFill(); beginShape(); vertex(centerPos + obTarget[0][0], centerPos - obTarget[1][0]); vertex(centerPos + obTarget[0][1], centerPos - obTarget[1][1]); vertex(centerPos + obTarget[0][2], centerPos - obTarget[1][2]); endShape(CLOSE); } void drawBodyAxis(float[][] bodyMat, float centerPos, float sizeOfAxis) { stroke(color(#FF0000)); line(centerPos + bodyMat[0][3], centerPos - bodyMat[1][3], centerPos + bodyMat[0][3] + bodyMat[0][0]*sizeOfAxis, centerPos - (bodyMat[1][3] + bodyMat[1][0]*sizeOfAxis)); stroke(color(#0017FF)); line(centerPos + bodyMat[0][3], centerPos - bodyMat[1][3], centerPos + bodyMat[0][3] + bodyMat[0][1]*sizeOfAxis, centerPos - (bodyMat[1][3] + bodyMat[1][1]*sizeOfAxis)); } void drawPanel() { fill(50); textFont(titleFont); text("Kinematics Example of Two Link Planar",140,25); textFont(smallFont); text("by PinkWink",500,45); stroke(150); line(50,550,280,550); line(320,550,550,550); if (grabSlider==1) { theta1 = (float(mouseX)-(50+280)/2)/115*PI; } if (grabSlider==2) { theta2 = (float(mouseX)-(320+550)/2)/115*PI; } slidePos1 = theta1*115/PI + (50+280)/2; slidePos2 = theta2*115/PI + (320+550)/2; stroke(100); fill(255); if (grabSlider==1) {fill(200);} ellipse(slidePos1,550,10,10); fill(255); if (grabSlider==2) {fill(200);} ellipse(slidePos2,550,10,10); } void mousePressed() { if (mouseButton==LEFT) { if (abs(mouseX-(50+280)/2)<165 && abs(mouseY-550)<5) { grabSlider = 1; } if (abs(mouseX-(320+550)/2)<165 && abs(mouseY-550)<5) { grabSlider = 2; } } } void mouseReleased() { if (mouseButton==LEFT) { grabSlider = 0; } }
입니다. 항상 하는 생각이지만, 요즘은 참 결과를 확인하는 일이 점점 쉬워지는 것 같습니다. 얼마전에 끝난 국가 직무 능력 개발인 NCS 개발에 저도 미력하지만 참여 개발진이었는데요. 그 때 한분이 말씀하신게 기억나네요. 이제 시스템 아키텍쳐만 잘 설계하면 그게 결과로 나오는 툴이 개발될려는게 아닐까?? 하는 말이죠. 응? 요딴걸 코드랍시고 공개하고서는 그런 거창한 이야기를 하다니... 하시겠지만 말이죠^^. 뭐 아무튼 이 글은 아마 금요일에 예약 발행될테니.. 즐거운 불타는 금요일 되세용~~~^^
'Software > Processing' 카테고리의 다른 글
프로세싱 Processing에서 modelX 명령으로 pushMatrix의 개념을 이해하기... (2) | 2016.07.08 |
---|---|
Processing에서 구현해 보는 Robot Arm 소개 (6) | 2016.07.01 |
Processing으로 구현한 Two Link Planar 로봇의 정구학과 역기구학 시뮬레이션 (32) | 2015.01.13 |
Processing에서 Papaya library를 이용해서 행렬(Matrix) 연산하기 (22) | 2014.11.06 |
Processing에서 진자 운동을 애니메이션으로 시뮬레이션하기 (17) | 2014.10.24 |
Processing에서 시리얼통신으로 받은 데이터를 그래프로 표현하기 (32) | 2013.10.23 |
Processing 프로세싱 언어를 소개합니다. (14) | 2013.09.30 |