那就不写了,直接上代码(骂骂咧咧)
代码注释很详细了,希望n天后我回来还能看得懂
import numpy as np
import matplotlib.pyplot as plt
import math
class PID_posi_2:
"""位置式实现2"""
def __init__(self, k=[1., 0., 0.], target=1.0, upper=1.0, lower=-1.0):
self.kp, self.ki, self.kd = k
# 初始化误差
self.e = 0
self.pre_e = 0
self.sum_e = 0
# 初始化目标值
self.target = target
# 初始化输出范围
self.upper_bound = upper
self.lower_bound = lower
def cal_output(self, state): # 计算输出值
self.e = state - self.target # 计算当前状态与目标状态的误差
# 计算PID输出值
u = self.e * self.kp + self.sum_e * self.ki + (self.e - self.pre_e) * self.kd
# 判断输出角度输出值是否超出范围,如果超出范围则强制纠正
if u < self.lower_bound:
u = self.lower_bound
elif u > self.upper_bound:
u = self.upper_bound
# 更新误差
self.pre_e = self.e
self.sum_e += self.e
return u # 返回PID计算值
class KinematicModel_3:
"""定义一个三自由度的车辆运动模型假设控制量为转向角delta_f和速度v"""
def __init__(self, x, y, psi, v, dt):
self.x = x
self.y = y
self.psi = psi #航向角Ψ
self.v = v
# 实现是离散的模型
self.dt = dt
def update_state(self, v, delta_psi):
"""更新车辆状态"""
self.psi = self.psi + delta_psi * self.dt
self.v = v
self.x = self.x+self.v*math.cos(self.psi)*self.dt
self.y = self.y+self.v*math.sin(self.psi)*self.dt
def generate_human_trajectory(n_steps):
# 定义每一步的转角和步长
n_steps = n_steps - 1 #这样输入30就只生成30维的数据,而不是0-30共31维度
angles = np.zeros(n_steps)
distances = np.zeros(n_steps)
# 生成每一步的转角和步长
for i in range(n_steps):
# 随机生成转角,范围在 [-pi/6, pi/6] 之间
angle = (np.random.rand() - 0.5) * np.pi / 3
# 如果两个相邻时刻的轨迹连线夹角超过 10 度,则重新生成转角
while i > 0 and abs(np.degrees(np.arctan2(np.sin(angle - angles[i-1]), np.cos(angle - angles[i-1])))) > 10:
angle = (np.random.rand() - 0.5) * np.pi / 3
angles[i] = angle
# 随机生成采样步长,范围在 [0.15, 0.2] 米之间,代表每1s采样得到的人类位置,不是每步的距离
distance = 0.15 + np.random.rand() * 0.05
distances[i] = distance
# 根据转角和步长计算行走轨迹
x = np.zeros(n_steps+1)
y = np.zeros(n_steps+1)
for i in range(1, n_steps+1):
# 计算当前步的坐标
x[i] = np.round(x[i-1] + distances[i-1] * np.cos(angles[i-1]),2)
y[i] = np.round(y[i-1] + distances[i-1] * np.sin(angles[i-1]),2)
return x, y # 返回每一时刻的轨迹点坐标
# 初始化角度PID控制器,输出角度范围[-pi*2.8, pi*2.8],因为机器人每1s最大转2.8π
PID_angle = PID_posi_2(k=[1, 0.01, 0.01], target=0, upper=np.pi*2.8, lower=-np.pi*2.8)
# 初始化速度PID控制器,机器人最大速度为0.2m/s
PID_v = PID_posi_2(k=[0.3, 0, 0.01], target=0, upper=0.2, lower=0)
def main():
# 假设初始状态为x=-1,y=0,偏航角=0rad,速度为0m/s,采样时间间隔为1秒
Robot = KinematicModel_3(0, 0.5, 0, 0, 1)
n_step = 100 #人类轨迹步数
x_human, y_human = generate_human_trajectory(n_step) #生成人类随机轨迹
human_trajectory_point = np.column_stack((x_human, y_human))
d = 0.5# 设置安全距离d
Robot_real_state = np.zeros((n_step,2)) #机器人实际轨迹坐标点
robot_tracking_point = np.zeros((n_step,2)) #存储机器人待追踪点
Robot_real_state[0:1] = [Robot.x, Robot.y] #记录机器人的初始化真实状态点
robot_tracking_point[0:1] = [Robot.x, Robot.y] #添加第一个时刻的追踪点,就是机器人自身状态点
for i in range(n_step-1):
# 利用人类轨迹点(x,y)生成机器人待追踪轨迹点(x_tar,y_tar)
if i > 0 :
x_tar=np.round(x_human[i]+d*((y_human[i-1]-y_human[i])/np.sqrt((y_human[i-1]-y_human[i])**2+(x_human[i]-x_human[i-1])**2)),2)
y_tar=np.round(y_human[i]+d*((x_human[i]-x_human[i-1])/np.sqrt((y_human[i-1]-y_human[i])**2+(x_human[i]-x_human[i-1])**2)),2)
robot_tracking_point[i,:] = [x_tar, y_tar]
# alpha是机器人与待追踪点的夹角
alpha = math.atan2(robot_tracking_point[i][1]-Robot_real_state[i-1][1], robot_tracking_point[i][0]-Robot_real_state[i-1][0])
l_d = np.linalg.norm(robot_tracking_point[i]-Robot_real_state[i-1]) # 前视距离:机器人中心离追踪目标点的距离
theta_e = alpha-Robot.psi #psi是机器人自身航向角
delta_psi = PID_angle.cal_output(theta_e) #用机器人与目标偏角计算航向角
v = PID_v.cal_output(l_d)# 用前视距离计算速度
Robot.update_state(v, delta_psi)
Robot_real_state[i,:] = [Robot.x, Robot.y]
plt.cla()
plt.xlim([0, 20]) # 设置x轴范围
plt.ylim([-5, 5]) # 设置y轴范围
plt.plot(human_trajectory_point[0:i,0], human_trajectory_point[0:i,1], 'go-', markersize=2, label='Human')
plt.plot(human_trajectory_point[i-1,0], human_trajectory_point[i-1,1], 'go', markersize=5)
# plt.plot(robot_tracking_point[0:i,0], robot_tracking_point[0:i,1], 'ro', markersize=1) #绘制机器人待追踪路线点
plt.plot(Robot_real_state[0:i,0], Robot_real_state[0:i,1], 'bo-', markersize=2, label='Robot')
plt.plot(Robot_real_state[i-1,0], Robot_real_state[i-1,1], 'bo', markersize=5)
plt.legend()
plt.grid(True) #显示网格线
plt.pause(0.1)
plt.show()
if __name__=='__main__':
main()