144 lines
4.6 KiB
Python
144 lines
4.6 KiB
Python
import time
|
||
import numpy as np
|
||
import matplotlib.pyplot as plt
|
||
from DM_CAN import *
|
||
import serial
|
||
from time import perf_counter, sleep
|
||
|
||
# ================= 初始化电机 =================
|
||
Motor1 = Motor(DM_Motor_Type.DM4310, 0x01, 0x11)
|
||
serial_device = serial.Serial('COM3', 921600, timeout=0.5)
|
||
MotorControl1 = MotorControl(serial_device)
|
||
MotorControl1.addMotor(Motor1)
|
||
MotorControl1.enable(Motor1)
|
||
sleep(1)
|
||
# ================= 五次多项式轨迹函数 =================
|
||
def quintic_interpolation(q0, qf, t, T):
|
||
"""
|
||
五次多项式平滑插值
|
||
q0: 起始位置
|
||
qf: 目标位置
|
||
t: 当前时间(0~T)
|
||
T: 总时间
|
||
返回:平滑位置
|
||
"""
|
||
s = t / T
|
||
s = np.clip(s, 0, 1) # 限制在0~1
|
||
alpha = s**3 * (10 - 15*s + 6*s**2)
|
||
return q0 + (qf - q0) * alpha
|
||
|
||
# ================= 执行轨迹函数 =================
|
||
def execute_trajectory(MotorControl, Motor, waypoints,
|
||
total_time=6.0, slow_down_time=2.0,
|
||
kp=50, kd=1.0, dt=0.001, read_interval=0.01,
|
||
ramp_time=2.0):
|
||
"""
|
||
五次多项式轨迹执行函数(均分时间 + 缓启动 + 缓停)
|
||
"""
|
||
# 获取当前电机位置
|
||
MotorControl.refresh_motor_status(Motor)
|
||
sleep(0.1)
|
||
MotorControl.refresh_motor_status(Motor)
|
||
current_pos = Motor.getPosition()
|
||
positions = [current_pos] + waypoints
|
||
|
||
# ===== 均分时间,不按距离 =====
|
||
num_segments = len(positions) - 1
|
||
seg_times = [total_time / num_segments] * num_segments
|
||
|
||
# ===== 轨迹执行 =====
|
||
actual_pos, desired_pos, time_axis = [], [], []
|
||
start_time = perf_counter()
|
||
next_time = start_time
|
||
last_read_time = start_time
|
||
|
||
for i in range(num_segments):
|
||
q0 = positions[i]
|
||
qf = positions[i+1]
|
||
T = seg_times[i]
|
||
steps = int(T/dt)
|
||
for j in range(steps):
|
||
t = j*dt
|
||
# 五次多项式插值
|
||
pos_cmd = quintic_interpolation(q0, qf, t, T)
|
||
# 启动缓动
|
||
ramp = 0.5*(1 - np.cos(np.pi*min(t, ramp_time)/ramp_time)) if t < ramp_time else 1.0
|
||
pos_cmd = current_pos + (pos_cmd - current_pos) * ramp
|
||
vel_cmd = 0 # 由五次插值保证平滑
|
||
|
||
MotorControl.controlMIT(Motor, kp, kd, pos_cmd, vel_cmd, 0)
|
||
|
||
# 低频读取
|
||
now = perf_counter()
|
||
if now - last_read_time >= read_interval:
|
||
MotorControl.refresh_motor_status(Motor)
|
||
actual = Motor.getPosition()
|
||
last_read_time = now
|
||
else:
|
||
actual = np.nan
|
||
|
||
actual_pos.append(actual)
|
||
desired_pos.append(pos_cmd)
|
||
time_axis.append(now - start_time)
|
||
|
||
# 固定周期
|
||
next_time += dt
|
||
while perf_counter() < next_time:
|
||
pass
|
||
|
||
current_pos = qf # 更新起点用于下一段
|
||
|
||
# ===== 缓停段 =====
|
||
final_pos = positions[-1]
|
||
MotorControl.refresh_motor_status(Motor)
|
||
start_pos = Motor.getPosition()
|
||
steps = int(slow_down_time / dt)
|
||
for i in range(steps):
|
||
t = i * dt
|
||
pos_cmd = quintic_interpolation(start_pos, final_pos, t, slow_down_time)
|
||
vel_cmd = 0
|
||
MotorControl.controlMIT(Motor, kp, kd, pos_cmd, vel_cmd, 0)
|
||
|
||
now = perf_counter()
|
||
if now - last_read_time >= read_interval:
|
||
MotorControl.refresh_motor_status(Motor)
|
||
actual = Motor.getPosition()
|
||
last_read_time = now
|
||
else:
|
||
actual = np.nan
|
||
|
||
actual_pos.append(actual)
|
||
desired_pos.append(pos_cmd)
|
||
time_axis.append(now - start_time)
|
||
|
||
next_time += dt
|
||
while perf_counter() < next_time:
|
||
pass
|
||
|
||
# ===== 绘图 =====
|
||
actual_clean = np.array(actual_pos)
|
||
desired_clean = np.array(desired_pos)
|
||
mask = ~np.isnan(actual_clean)
|
||
|
||
plt.figure()
|
||
plt.plot(np.array(time_axis)[mask], desired_clean[mask], label='Desired')
|
||
plt.plot(np.array(time_axis)[mask], actual_clean[mask], label='Actual', linestyle='--')
|
||
plt.xlabel('Time [s]')
|
||
plt.ylabel('Position [rad]')
|
||
plt.title(f'Quintic Trajectory (Ramp + Smooth Stop)')
|
||
plt.legend()
|
||
plt.grid(True)
|
||
plt.show()
|
||
|
||
|
||
# ================= 执行轨迹 =================
|
||
target_angles = [ 0.0, 0.3, 0.6, 0.88, 0.9, 1.5,
|
||
0.5, 0.2,-0.2,-0.8]
|
||
execute_trajectory(MotorControl1, Motor1, target_angles,
|
||
total_time=10.0, slow_down_time=1.0,
|
||
kp=50, kd=1, dt=0.001, read_interval=0.005,
|
||
ramp_time=1.0)
|
||
|
||
# 关闭
|
||
MotorControl1.disable(Motor1)
|
||
serial_device.close() |