Files
5dof/DM_control.py

144 lines
4.6 KiB
Python
Raw Normal View History

2025-09-15 10:45:15 +08:00
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()