逆運動学でロボット制御 - Shinichi0713/LLM-fundamental-study GitHub Wiki
2次元平面上の3自由度ロボットアーム(各関節はz軸回り回転、リンク長固定)について、
逆運動学(ヤコビアン反復法)でエンドエフェクタを目標位置に到達させ、
その動作をMatplotlibでアニメーション表示するサンプルコードを示します。
1. 2次元版の順運動学・逆運動学
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 2次元の順運動学
def forward_kinematics_2d(thetas, link_lengths):
x = [0]
y = [0]
angle = 0
for i in range(len(thetas)):
angle += thetas[i]
x.append(x[-1] + link_lengths[i] * np.cos(angle))
y.append(y[-1] + link_lengths[i] * np.sin(angle))
return np.array(x), np.array(y)
# 2次元の逆運動学(ヤコビアン反復法)
def inverse_kinematics_2d(target, link_lengths, initial_thetas=None, max_iter=100, tol=1e-3):
n = len(link_lengths)
if initial_thetas is None:
thetas = np.zeros(n)
else:
thetas = np.array(initial_thetas)
trajectory = [thetas.copy()]
for _ in range(max_iter):
x, y = forward_kinematics_2d(thetas, link_lengths)
end = np.array([x[-1], y[-1]])
error = target - end
if np.linalg.norm(error) < tol:
break
# 数値ヤコビアン
J = np.zeros((2, n))
delta = 1e-5
for i in range(n):
dtheta = thetas.copy()
dtheta[i] += delta
dx, dy = forward_kinematics_2d(dtheta, link_lengths)
diff = np.array([dx[-1], dy[-1]]) - end
J[:, i] = diff / delta
dthetas = np.linalg.pinv(J) @ error
thetas += dthetas
trajectory.append(thetas.copy())
return np.array(trajectory)
2. アニメーション表示
# パラメータ
link_lengths = [1.0, 1.0, 0.7]
target = np.array([1.5, 1.2])
initial_thetas = [0.0, 0.0, 0.0]
# 逆運動学で経路を計算
trajectory = inverse_kinematics_2d(target, link_lengths, initial_thetas, max_iter=80)
# アニメーション準備
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim(-sum(link_lengths)-0.1, sum(link_lengths)+0.1)
ax.set_ylim(-sum(link_lengths)-0.1, sum(link_lengths)+0.1)
line, = ax.plot([], [], 'o-', lw=4, color='blue')
target_dot, = ax.plot(target[0], target[1], 'ro', markersize=10)
def update(frame):
thetas = trajectory[frame]
x, y = forward_kinematics_2d(thetas, link_lengths)
line.set_data(x, y)
return line, target_dot
ani = FuncAnimation(fig, update, frames=len(trajectory), interval=80, blit=True)
plt.title('2D 3-DOF Robot Arm Inverse Kinematics')
plt.show()
補足
inverse_kinematics_2d
関数が、各反復ごとの関節角度をtrajectory
として返すので、それをアニメーションで再生します。- 目標点は赤丸で表示されます。
- 3自由度ですが、リンク数や目標点は変更可能です。
- Python環境に
matplotlib
とnumpy
が必要です。
はい、ご要望は「重力・質量・慣性モーメントも考慮した多関節ロボットの動力学シミュレーション(2次元)をmatplotlibのアニメーションで可視化したい」ということですね。
これは「運動方程式(Euler-Lagrange方程式やニュートン-オイラー法)」を用いて、
各関節トルクからロボットの運動(角加速度→角速度→角度)を時系列で計算し、
その動作をアニメーション表示する、というものです。
ポイント
-
運動方程式:
[ M(\theta)\ddot{\theta} + C(\theta, \dot{\theta})\dot{\theta} + G(\theta) = \tau ]- (M):慣性行列
- (C):コリオリ・遠心力項
- (G):重力項
- (\tau):関節トルク
-
数値積分:
オイラー法やRunge-Kutta法で角加速度→角速度→角度を時間発展 -
アニメーション:
毎ステップの関節角度からロボット形状を描画
サンプル:2自由度ロボットアーム(平面、各リンクに質量・慣性あり)
(3自由度も同じ考え方で拡張できますが、まず2自由度で示します)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# ロボットパラメータ
l1, l2 = 1.0, 1.0 # リンク長
m1, m2 = 1.0, 1.0 # 質量
I1, I2 = 0.02, 0.02 # 慣性モーメント
g = 9.81 # 重力加速度
def M_matrix(theta):
c2 = np.cos(theta[1])
M11 = I1 + I2 + m2 * l1**2 + 2 * m2 * l1 * l2 * c2
M12 = I2 + m2 * l1 * l2 * c2
M21 = M12
M22 = I2
return np.array([M11, M12], [M21, M22](/Shinichi0713/LLM-fundamental-study/wiki/M11,-M12],-[M21,-M22))
def C_matrix(theta, dtheta):
s2 = np.sin(theta[1])
C11 = -m2 * l1 * l2 * s2 * dtheta[1]
C12 = -m2 * l1 * l2 * s2 * (dtheta[0] + dtheta[1])
C21 = m2 * l1 * l2 * s2 * dtheta[0]
C22 = 0
return np.array([C11, C12], [C21, C22](/Shinichi0713/LLM-fundamental-study/wiki/C11,-C12],-[C21,-C22))
def G_vector(theta):
g1 = (m1 * l1 / 2 + m2 * l1) * g * np.cos(theta[0]) + m2 * l2 / 2 * g * np.cos(theta[0] + theta[1])
g2 = m2 * l2 / 2 * g * np.cos(theta[0] + theta[1])
return np.array([g1, g2])
def forward_kinematics(theta):
x0, y0 = 0, 0
x1 = l1 * np.cos(theta[0])
y1 = l1 * np.sin(theta[0])
x2 = x1 + l2 * np.cos(theta[0] + theta[1])
y2 = y1 + l2 * np.sin(theta[0] + theta[1])
return np.array([x0, x1, x2]), np.array([y0, y1, y2])
# シミュレーション設定
dt = 0.02
steps = 200
theta = np.array([np.pi/4, np.pi/4]) # 初期角度
dtheta = np.array([0.0, 0.0]) # 初期角速度
# トルク設定(例:第2関節だけ一定トルク、他は0)
tau = np.zeros((steps, 2))
tau[:, 1] = 1.0 # 第2関節に一定トルク
# 各ステップの角度を記録
theta_hist = []
for t in range(steps):
M = M_matrix(theta)
C = C_matrix(theta, dtheta)
G = G_vector(theta)
ddtheta = np.linalg.inv(M) @ (tau[t] - C @ dtheta - G)
dtheta += ddtheta * dt
theta += dtheta * dt
theta_hist.append(theta.copy())
theta_hist = np.array(theta_hist)
# アニメーション
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim(-2.2, 2.2)
ax.set_ylim(-2.2, 2.2)
line, = ax.plot([], [], 'o-', lw=4, color='blue')
def update(frame):
x, y = forward_kinematics(theta_hist[frame])
line.set_data(x, y)
return line,
ani = FuncAnimation(fig, update, frames=steps, interval=30, blit=True)
plt.title('2D Robot Arm Dynamics Simulation')
plt.show()
説明
- M_matrix, C_matrix, G_vector で運動方程式の各項を定義
- tau で各関節のトルクを与える(例では第2関節に一定トルク)
- オイラー法で数値積分し、各時刻の関節角度を計算
- アニメーションでロボットの動作を可視化
拡張・カスタマイズ
- トルクを時間変化させたり、制御則を入れることも可能です
- 3自由度や他のリンクパラメータも同様に拡張できます
- 各リンクごとに異なる質量・慣性もOK
承知しました。
2自由度平面ロボットアーム(二連リンク)の運動方程式(Euler-Lagrange法による)は、
以下のように表されます。
2自由度平面ロボットの運動方程式
[ M(\theta)\ddot{\theta} + C(\theta, \dot{\theta})\dot{\theta} + G(\theta) = \tau ]
ここで、
- (\theta = \begin{bmatrix} \theta_1 \ \theta_2 \end{bmatrix}):各関節角度
- (\dot{\theta} = \begin{bmatrix} \dot{\theta}_1 \ \dot{\theta}_2 \end{bmatrix}):各関節角速度
- (\ddot{\theta} = \begin{bmatrix} \ddot{\theta}_1 \ \ddot{\theta}_2 \end{bmatrix}):各関節角加速度
- (\tau = \begin{bmatrix} \tau_1 \ \tau_2 \end{bmatrix}):各関節トルク
慣性行列 (M(\theta))
[ M(\theta) = \begin{bmatrix} M_{11} & M_{12} \ M_{21} & M_{22} \end{bmatrix} ]
[ \begin{align*} M_{11} &= I_1 + I_2 + m_2 l_1^2 + 2 m_2 l_1 l_2 \cos\theta_2 \ M_{12} &= I_2 + m_2 l_1 l_2 \cos\theta_2 \ M_{21} &= M_{12} \ M_{22} &= I_2 \end{align*} ]
- (l_1, l_2):各リンク長
- (m_1, m_2):各リンク質量
- (I_1, I_2):各リンクの慣性モーメント(回転中心周り)
コリオリ・遠心力項 (C(\theta, \dot{\theta})\dot{\theta})
[ C(\theta, \dot{\theta})\dot{\theta} = \begin{bmatrix}
- m_2 l_1 l_2 \sin\theta_2 (2\dot{\theta}_1\dot{\theta}_2 + \dot{\theta}_2^2) \ m_2 l_1 l_2 \sin\theta_2 \dot{\theta}_1^2 \end{bmatrix} ]
または、行列表記で [ C(\theta, \dot{\theta}) = \begin{bmatrix}
- m_2 l_1 l_2 \sin\theta_2 \dot{\theta}_2 & - m_2 l_1 l_2 \sin\theta_2 (\dot{\theta}_1 + \dot{\theta}_2) \ m_2 l_1 l_2 \sin\theta_2 \dot{\theta}_1 & 0 \end{bmatrix} ]
重力項 (G(\theta))
[ G(\theta) = \begin{bmatrix} (m_1 l_1/2 + m_2 l_1)g \cos\theta_1 + m_2 l_2/2 , g \cos(\theta_1 + \theta_2) \ m_2 l_2/2 , g \cos(\theta_1 + \theta_2) \end{bmatrix} ]
- (g):重力加速度
全体の運動方程式
[ M(\theta)\ddot{\theta} + C(\theta, \dot{\theta})\dot{\theta} + G(\theta) = \tau ]
この式を使って、関節の角加速度((\ddot{\theta}))を数値的に計算し、
角速度・角度へと時間積分していくことで、ロボットの運動をシミュレーションできます。
「キーボード入力でリアルタイムに各関節のトルクを操作できる2自由度ロボットアームの動力学アニメーション」
ポイント
- キー操作でトルク値を増減できます(例:
q/a
で第1関節、w/s
で第2関節)。 - 現在のトルク値を画面に表示します。
- アニメーションはMatplotlibで行い、
FuncAnimation
のイベントハンドラでキーボード入力を受け付けます。
サンプルコード
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# ロボットパラメータ
l1, l2 = 1.0, 1.0
m1, m2 = 1.0, 1.0
I1, I2 = 0.02, 0.02
g = 9.81
def M_matrix(theta):
c2 = np.cos(theta[1])
M11 = I1 + I2 + m2 * l1**2 + 2 * m2 * l1 * l2 * c2
M12 = I2 + m2 * l1 * l2 * c2
M21 = M12
M22 = I2
return np.array([M11, M12], [M21, M22](/Shinichi0713/LLM-fundamental-study/wiki/M11,-M12],-[M21,-M22))
def C_matrix(theta, dtheta):
s2 = np.sin(theta[1])
C11 = -m2 * l1 * l2 * s2 * dtheta[1]
C12 = -m2 * l1 * l2 * s2 * (dtheta[0] + dtheta[1])
C21 = m2 * l1 * l2 * s2 * dtheta[0]
C22 = 0
return np.array([C11, C12], [C21, C22](/Shinichi0713/LLM-fundamental-study/wiki/C11,-C12],-[C21,-C22))
def G_vector(theta):
g1 = (m1 * l1 / 2 + m2 * l1) * g * np.cos(theta[0]) + m2 * l2 / 2 * g * np.cos(theta[0] + theta[1])
g2 = m2 * l2 / 2 * g * np.cos(theta[0] + theta[1])
return np.array([g1, g2])
def forward_kinematics(theta):
x0, y0 = 0, 0
x1 = l1 * np.cos(theta[0])
y1 = l1 * np.sin(theta[0])
x2 = x1 + l2 * np.cos(theta[0] + theta[1])
y2 = y1 + l2 * np.sin(theta[0] + theta[1])
return np.array([x0, x1, x2]), np.array([y0, y1, y2])
# シミュレーション初期値
dt = 0.02
theta = np.array([np.pi/4, np.pi/4])
dtheta = np.array([0.0, 0.0])
tau = np.array([0.0, 0.0])
# matplotlibアニメーションセットアップ
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim(-2.2, 2.2)
ax.set_ylim(-2.2, 2.2)
line, = ax.plot([], [], 'o-', lw=4, color='blue')
txt = ax.text(-2, 2, '', fontsize=12, color='red')
# キーボードイベントハンドラ
def on_key(event):
global tau
step = 0.5
if event.key == 'q':
tau[0] += step
elif event.key == 'a':
tau[0] -= step
elif event.key == 'w':
tau[1] += step
elif event.key == 's':
tau[1] -= step
elif event.key == 'z': # リセット
tau[:] = 0.0
fig.canvas.mpl_connect('key_press_event', on_key)
def update(frame):
global theta, dtheta, tau
# 動力学計算
M = M_matrix(theta)
C = C_matrix(theta, dtheta)
G = G_vector(theta)
ddtheta = np.linalg.inv(M) @ (tau - C @ dtheta - G)
dtheta += ddtheta * dt
theta += dtheta * dt
# 描画
x, y = forward_kinematics(theta)
line.set_data(x, y)
txt.set_text(f'tau1: {tau[0]:.2f}, tau2: {tau[1]:.2f}')
return line, txt
ani = FuncAnimation(fig, update, frames=3000, interval=20, blit=True)
plt.title('2D Robot Arm Dynamics with Keyboard Torque Control\n[q/a]:tau1 +/- [w/s]:tau2 +/- [z]:reset')
plt.show()
操作方法
q
:第1関節トルク増加a
:第1関節トルク減少w
:第2関節トルク増加s
:第2関節トルク減少z
:両トルクリセット