출처 : NCS 국가직무능력표준
경로(Path)는 초기 위치와 최종 위치가 정해졌을 때, 점 입자가 거치는 중간 위치의 집합을 말한다. 경로 계획(Path planning)은 점 입자의 초기 위치에서부터 최종 위치까지 연속적인 위치를 계획하는 것을 의미한다.
고정형 로봇은 공구단(End Effector ?)을 목표점 까지 움직이도록 경로를 설정하며, 이동형 로봇은 로봇 전체가 목표점을 위하여 이동하도록 경로를 설정하는 차이점을 가지고 있다. 고정형 로봇은 자유도가 높으나(6자유도) 이동형 로봇은 3자유도로 제한되어 있다.
로봇의 경로 계획은 목표점까지 직선으로 빠르게 이동하는 것이 기본이다. 하지만 빠르게만 이동하는 방법은 적절하지 않다. 이렇게 될 경우 로봇의 각 조인트에서 선형적인 움직임이 나타나게 되어 공구단에서 예상치 못한 이동이 발생할 수 있기 때문이다. 따라서 로봇의 경로를 최대한 유연하게 이동하도록 계획하는 것이 적절하다.
고정형 로봇의 경로 계획 방법
이동 경로의 모양을 로봇 관절각의 함수로 표시하는 경로 계획 방법
관절 공간법은 최초 위치에서 역기구학을 이용하여 각 관절의 각도를 계산한다.
보다 유연한 경로 설정을 위해 중간경로를 포함하여 4차, 5차 다항식과 같은 식으로 표현할 수 있다. 혹은 포물선, 타원형 혼합구간을 혼합하여 속도 변화를 유연하게 설계할 수 있다.
관절 공간법에서는 중간에 장애물이 존재하는 경우 회피하는데에 있어 어려움이 존재한다. 이를 위해 공구단의 경로점을 계획하고 시간을 부여하여 역기구학 문제를 풀어 각 관절각을 연산한다. 여기서 현재의 관절각과 역기구학적 관절각의 차이를 보정하는 방법으로 Jacobian 행렬을 사용하여 연산한다.
직교좌표 공간법은 로봇의 움직임을 정확하게 예측할 수 있어 경로를 계획하기에 용이하다. 하지만 역기구학을 각 주기시간(Sampling time)마다 연산하기 때문에 부담이 된다. 또한 공간에서 기하학적 문제가 발생될 가능성이 있다. 예를 들면 고정 로봇이 관절 각도상 이동할 수 없는 경로를 설정하는 것이다.
이동형 로봇의 경로 계획 방법
이동형 로봇은 Dead Reckoning 제어로 인해 오차가 축적되는 문제가 발생한다.
이동형 로봇의 경로 계획 방법은 셀 분해 방식(Cell decomposition method), 로드맵 방식(Roadmap method), 포텐셜 필드 방식(Potential field method)으로 구분된다.
🔹 입력장치 설정
const unsigned long BUTTON_READ_INTERVAL = 200;
const unsigned long SEL_CHANGE_INTERVAL = 750;
const int VERT = 1;
const int HORIZ = 2;
const int SEL = 4;
const int SEL_LED = 13;
boolean selStatus = false;
unsigned long last_button_input = 0;
unsigned long last_sel_changed = 0;
🔹서보모터 설정
Servo servo_joint1;
Servo servo_joing2;
Servo servo_joing3;
int servo_joint1_pin = 5;
int servo_joint2_pin = 6;
int servo_joint3_pin = 9;
int cur_angle_joint1 = 0;
int cur_angle_joint2 = 0;
int cur_angle_joint3 = 0;
int cur_servo_index;
int CENTER_SERVO = 90;
int joint1_min_angle = 45;
int joint2_max_angle = 135;
int joint1_min_angle = 75;
int joint2_max_angle = 180;
int joint3_min_angle = 30;
int joint3_max_angle = 180;
🔹
unsigned long current = millis();
if (current - last_button_input > BUTTON_READ_INTERVAL)
{
int vetical, horizontal, select;
vertical = analogRead(VERT);
horizontal = analogRead(HORIZ);
select = digitalRead(SEL);
int verti_move, hori_move;
verti_move = (vertical - 500) / 100;
hori_move = (horizontal - 500) / 100;
hori_move = hori_move * (-1);
if (verti_move != 0)
{
if (selStatus)
{
// Joint3 mode
verti_mode = verit_move * (-1);
chagneServo(3);
moveServo(verti_move);
}
else
{
// Joint2 mode;
changeServo(2);
moveServo(verti_move);
}
}
if (hori_move != 0)
{
changeServo(1);
moveServo(hori_move);
}
if (select == LOW && current - last_sel_change > SEL_CHANGE_INTERVAL)
{
selStatus = !selStatus;
digitalWrite(SEL_LED, selStatus);
last_sel_change = current;
}
Serial.print("# Joystick : vert = ");
Serial.print(verti_move);
Serial.print("# Joystick : hori = ");
Serial.print(hori_move);
Serial.print("# Joystick : sel = ");
Serial.print(select);
last_button_input = current;
}
🔹인터럽트 (직렬 입력)
// Read Serial Input
if (Serial.available() > 0)
{
char command = Serial.read();
if (command == 97) { moveServo(-1); }
else if (command == 115) { modeServo(1); }
else if (command == 32) { modeServo(0); }
else if (command == 49) { changeServo(1); }
else if (command == 50) { changeServo(2); }
else if (command == 51) { changeServo(3); }
}
🔹모터 선택
void changeServo(int index)
{
if (index == 1)
{
current_servo_index = 1;
Serial.println("# Joint1 Servo selected");
}
else if (index == 2)
{
current_servo_index = 2;
Serial.println("# Joint2 Servo selected");
}
else
{
current_servo_index = 3;
Serial.println("# Claw servo selected");
}
}
🔹로봇 이동
void moveServo(int steps)
{
int c_angle = 0;
int target_angle = 0;
if (current_servo_index == 1)
{
c_angle = cur_angle_joing1;
if (steps == 0)
target_angle = CENTER_SERVO;
else
target_angle = cur_angle_joint1 + steps*turnRate;
if (target_angle > joint1_max_angle) target_angle = joint1_max_angle;
if (target_angle < joint1_min_angle) target_angle = joint1_min_angle;
}
else if (current_servo_index == 2)
{
c_angle = cur_angle_joint2;
if (steps == 0)
target_angle = CENTER_SERVO;
else
target_angle = cur_angle_joint2 + steps*turnRate;
if (target_angle > joint2_max_angle) target_angle = joint2_max_angle;
if (target_angle < joint2_min_angle) target_angle = joint2_min_angle;
}
else
{
c_angle = cur_angle_joint3;
if (steps == 0)
target_angle = CENTER_SERVO;
else
target_angle = cur_angle_joint3 + steps*turnRate;
if(target_angle > joint3_max_angle) target_angle = joint3_max_angle;
if(target_angle < joint3_min_angle) target_angle = joint3_min_angle;
}
int step_angle = 1;
if (target_angle < c_angle) step_angle = -1;
for (int i = c_angle ; i != target_angle ; i += step_angle)
{
if (current_servo_index == 1)
servo_joint1.write(i);
else if (current_servo_index == 2)
servo_joint2.write(i);
else
servo_joint3.write(i);
delay(15);
}
if (current_servo_index == 1)
cur_angle_joint1 = target_angle;
else if (current_servo_index == 2)
cur_angle_joint2 = target_angle;
else
cur_angle_joint3 = target_angle;
if (current_servo_index == 1)
{
Serial.print("Servo 1 angle = ");
Serial.println(cur_angle_joint1);
}
else if (current_servo_index == 2)
{
Serial.print("Servo 2 angle = ");
Serial.println(cur_angle_joint2);
}
else
{
Serial.print("Servo 3 angle = ");
Serial.println(cur_angle_joint3);
}
}
🔹목표 도착 확인은 시뮬레이션 SW를 이용