[로봇공학기초 / 2DOF] #5. Singularity (특이점)
⚠️ 특이점(Singularity)이란?
지난 글에서는 자코비안 행렬(Jacobian Matrix)을 이용해
로봇팔의 말단 속도를 계산하는 방법을 알아보았습니다.
이번에는 로봇공학에서 매우 중요한 개념인 특이점(Singularity)에 대해 정리해보겠습니다.
특이점은 종종 로봇이 잘 움직이지 못하는 지점라고 설명되지만, 더 정확하게 말하면 다음과 같습니다:
“우리가 원하는 말단 속도를 만들기 위해 관절 속도를 계산할 수 없는 지점”
이는 속도 역기구학(Inverse Velocity Kinematics)이 실패하는 지점입니다.수학적으로는 자코비안 행렬 \( J \)의 행렬식(det)이 0이 되는 지점에서 발생하며,이 경우 역행렬 \( J^{-1} \)이 존재하지 않게 됩니다.
✋ 특이점은 어떤 상황일까?
우리는 로봇을 제어할 때 보통 말단(End-effector)의 움직임을 먼저 정합니다.
즉, "끝점이 직선으로 이렇게 이동했으면 좋겠다"는 식으로 카테시안 공간 기준의 속도 \( \dot{x} \) 를 정합니다.
하지만 실제로 로봇을 움직이게 만들기 위해선 그 움직임을 만들어줄 관절 속도 \( \dot{\theta} \) 가 필요하죠.
이때 사용하는 것이 자코비안:
$$
\dot{x} = J(\theta)\dot{\theta}
$$
이걸 역으로 계산해서 관절을 제어해야 합니다:
$$
\dot{\theta} = J^{-1}(\theta)\dot{x}
$$
그런데 문제는, 특이점에서는 \( J^{-1} \) 존재하지 않습니다.
즉, 아무리 말단을 움직이고 싶어도, 그걸 실현할 수 있는 관절 속도가 존재하지 않거나 무한대가 되는 지점이 바로 특이점입니다.
📌 쉽게 말해 이런 상황입니다:
- 우리는 "말단을 이렇게 움직여줘!"라고 명령했는데,
- 로봇은 "그렇게 움직이려면 관절이 무한히 빨라야 합니다…" 라는 불가능한 답을 내놓는 상태
이런 이유로 특이점에서는 속도 제어가 불가능하거나,
움직임이 극도로 민감하고 불안정해지며,
실제로 로봇이 멈추거나 튀는 현상이 발생할 수 있습니다.
📐 2자유도 로봇팔의 자코비안 행렬과 특이점 조건 유도
두 관절을 가진 2자유도 평면 로봇팔의 자코비안 행렬은 다음과 같습니다:
$$ J(\theta_1, \theta_2) = \begin{bmatrix} -L_1 \sin{\theta_1} - L_2 \sin{(\theta_1 + \theta_2)} & -L_2 \sin{(\theta_1 + \theta_2)} \\ L_1 \cos{\theta_1} + L_2 \cos{(\theta_1 + \theta_2)} & L_2 \cos{(\theta_1 + \theta_2)} \end{bmatrix} $$
이 행렬의 행렬식은 다음과 같이 단순화됩니다:
$$ \det(J) = L_1 L_2 \sin{\theta_2} $$
따라서 행렬식이 0인 다음 두 경우에 특이점이 발생합니다:
- \( \theta_2 = 0 \): 로봇팔이 완전히 펼쳐진 경우
- \( \theta_2 = \pi \): 로봇팔이 완전히 접힌 경우
💻 특이점 시각화 Python 코드
import numpy as np
import matplotlib.pyplot as plt
# 한글 폰트 설정
plt.rcParams['font.family'] = 'NanumGothic'
plt.rcParams['axes.unicode_minus'] = False
# 자코비안 행렬식
def det_jacobian(theta2, l1=1.0, l2=0.5):
return l1 * l2 * np.sin(theta2)
# Forward kinematics
def forward_kinematics(theta1, theta2, l1, l2):
joint = np.array([l1 * np.cos(theta1), l1 * np.sin(theta1)])
end_effector = joint + np.array([l2 * np.cos(theta1 + theta2), l2 * np.sin(theta1 + theta2)])
return np.array([0, 0]), joint, end_effector
# 현재 설정
l1, l2 = 1.0, 0.5
theta1_cur = np.deg2rad(121.5)
theta2_cur = np.deg2rad(41.4)
# 현재 로봇팔 위치 계산
base, joint, end_effector = forward_kinematics(theta1_cur, theta2_cur, l1, l2)
# 특이점 조건: sin(theta2) = 0 → theta2 = 0, pi
theta1_range = np.linspace(-np.pi, np.pi, 400)
singularity_points = []
for theta1 in theta1_range:
for theta2 in [0, np.pi]: # 특이점이 일어나는 두 지점
_, _, ee = forward_kinematics(theta1, theta2, l1, l2)
singularity_points.append(ee)
singularity_points = np.array(singularity_points)
# 시각화
fig, ax = plt.subplots(figsize=(6, 6), dpi=150)
# 특이점 위치 그리기
ax.scatter(singularity_points[:, 0], singularity_points[:, 1], color='red', s=15, label='특이점 위치')
# 현재 로봇팔 자세
ax.plot([base[0], joint[0], end_effector[0]],
[base[1], joint[1], end_effector[1]],
'ko-', linewidth=2, markersize=8, label='현재 로봇팔')
# 마커 및 텍스트
ax.plot(base[0], base[1], 'ks', markersize=10)
ax.text(end_effector[0]+0.05, end_effector[1]+0.05, "End-Effector", fontsize=9)
# 스타일
ax.set_title("특이점 위치 (det(J) = 0) 및 현재 로봇팔 자세")
ax.set_xlabel("X (m)")
ax.set_ylabel("Y (m)")
ax.set_xlim(-2, 2) # ✅ X축 범위 고정
ax.set_ylim(-2, 2) # ✅ Y축 범위 고정
ax.legend()
ax.grid(True)
plt.tight_layout()
plt.savefig("singularity_points_with_arm.png", dpi=300)
plt.show()
🔧 실행 환경
- Python 3.8 이상
- numpy
- matplotlib
- Jupyter Notebook (추천)
📘 다음 편 예고
2자유도 평면 로봇팔의 자코비안 행렬을 이용해, 특이점(Singularity)이 발생하는 수학적 조건과 그 의미를 직관적으로 해석해보았습니다. 다음 글에서는 Trajectory Planning(궤적 생성)에 대해 알아보겠습니다. 로봇이 시작점에서 목표점까지 움직이는 경로를 어떻게 설계하는지, 다양한 궤적 생성 기법들을 소개할 예정입니다.