目录
1. 前言
神经网络直接逆控制是控制和神经网络最简单基础的结合!
在控制领域,神经网络控制是一种强大的工具,能够处理复杂的非线性系统和不确定性问题。其中,直接逆控制是一种简单而有效的神经网络控制方法,它通过训练神经网络来模拟被控对象的逆动态特性,从而实现对系统的精确控制。本文将详细介绍直接逆控制的原理,并通过一个完整的 PyTorch 实现案例,帮助读者理解和应用这一技术。
2. 什么是直接逆控制?
直接逆控制是一种基于神经网络的控制方法,其核心思想是利用神经网络学习被控对象的逆动态模型。具体来说,被控对象的动态特性可以表示为:
其中,y 是系统的输出,u 是系统的输入,f 是系统的动态模型。直接逆控制的目标是训练一个神经网络 g,使得:
其中,yref 是期望的系统输出。通过这种方式,神经网络 g 模拟了被控对象的逆动态特性,从而实现对系统的精确控制。
2.1 直接逆控制的优点
简单直接:直接逆控制不需要复杂的控制算法,只需训练一个神经网络即可。
适应性强:能够处理非线性和不确定性的系统。
易于实现:只需要被控对象的输入输出数据即可训练。
2.2 直接逆控制的局限性
依赖数据质量:训练数据的质量直接影响神经网络的性能。
实时性要求高:在实时控制系统中,神经网络的计算速度需要足够快。
3. 直接逆控制的实现步骤
3.1 数据准备
为了训练神经网络,我们需要被控对象的输入输出数据。这些数据可以通过实验或仿真获得。
3.2 神经网络设计
设计一个神经网络来模拟被控对象的逆动态特性。通常,可以选择多层感知机(MLP)作为网络结构。
3.3 训练神经网络
使用输入输出数据训练神经网络,使其能够准确预测系统的输入 u,给定期望输出 yref。
3.4 控制实现
在实际控制中,将期望输出 yref 输入到训练好的神经网络中,得到控制输入 u,并将其应用于被控对象。
4. 使用 PyTorch 实现直接逆控制
4.1 问题描述
假设我们有一个简单的被控对象,其动态特性可以表示为:
我们的目标是通过直接逆控制,使系统的输出 y 跟踪一个给定的参考信号 yref。
4.2 数据生成
首先,我们需要生成被控对象的输入输出数据。
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# 设置随机种子
np.random.seed(42)
torch.manual_seed(42)
# 生成训练数据
def generate_data(steps=1000):
y = np.zeros(steps)
u = np.random.uniform(-1, 1, steps)
for t in range(1, steps):
y[t] = 0.5 * y[t-1] + u[t-1] + np.sin(y[t-1])
# 准备输入输出数据
X = y[:-1].reshape(-1, 1)
y_ref = y[1:].reshape(-1, 1)
u_data = u[:-1].reshape(-1, 1)
return X, y_ref, u_data
X, y_ref, u_data = generate_data(steps=1000)
4.3 神经网络设计
设计一个简单的多层感知机(MLP)来模拟被控对象的逆动态特性。
class InverseModel(nn.Module):
def __init__(self):
super(InverseModel, self).__init__()
self.fc1 = nn.Linear(1, 64)
self.fc2 = nn.Linear(64, 64)
self.fc3 = nn.Linear(64, 1)
self.relu = nn.ReLU()
def forward(self, x):
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
# 初始化模型
model = InverseModel()
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
4.4 训练神经网络
使用生成的数据训练神经网络。
# 转换为 PyTorch 张量
X_tensor = torch.tensor(X, dtype=torch.float32)
y_ref_tensor = torch.tensor(y_ref, dtype=torch.float32)
# 训练模型
num_epochs = 1000
losses = []
for epoch in range(num_epochs):
optimizer.zero_grad()
outputs = model(y_ref_tensor)
loss = criterion(outputs, torch.tensor(u_data, dtype=torch.float32))
loss.backward()
optimizer.step()
losses.append(loss.item())
if (epoch+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
# 绘制损失曲线
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss')
plt.show()
结果如下:
可以看出,给定目标输出,用神经网络和用普通模型控制几乎无异。
4.5 控制实现
使用训练好的神经网络进行控制。
# 生成参考信号
def generate_reference_signal(steps=100):
t = np.linspace(0, 10, steps)
y_ref = np.sin(t) * 0.5 # 使用正弦波作为参考信号
return y_ref
# 控制过程
def control_process(model, y_ref, steps=100):
y = np.zeros(steps)
u = np.zeros(steps)
for t in range(steps-1):
# 使用神经网络计算控制输入
y_ref_tensor = torch.tensor([[y_ref[t]]], dtype=torch.float32)
u[t] = model(y_ref_tensor).detach().numpy()[0][0]
# 更新系统状态
y[t+1] = 0.5 * y[t] + u[t] + np.sin(y[t])
return y, u
# 生成参考信号并进行控制
y_ref_control = generate_reference_signal(steps=100)
y_control, u_control = control_process(model, y_ref_control)
# 绘制结果
plt.figure(figsize=(12, 6))
plt.plot(y_ref_control, label='Reference Signal')
plt.plot(y_control, label='System Output')
plt.xlabel('Time Step')
plt.ylabel('Value')
plt.title('Control Performance')
plt.legend()
plt.show()
5. 优化与改进完整代码
即便训练部分表现优异,但是以上控制实现部分代码实际效果很不好,主要因为训练太单一,只输入一个给定目标值很难判断该系统应该如何控制,以上训练阶段实际上是过拟合。
改进后主要改进点为:
数据生成:
现在生成的数据包含当前状态和期望的下一状态
直接根据系统方程计算所需的控制输入
数据范围扩大到[-2, 2]以更好覆盖操作空间
模型结构:
输入层改为2个节点(当前状态 + 目标状态)
增加网络深度和容量以更好学习非线性关系
控制过程:
现在使用当前状态和下一时刻的参考信号来预测控制输入
初始状态与参考信号对齐
添加了跟踪误差的可视化
训练过程:
使用更合理的训练数据配对
增加训练数据量到10000个样本
代码如下:
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# 设置随机种子
np.random.seed(42)
torch.manual_seed(42)
# 生成训练数据(修正后的版本)
def generate_data(num_samples=10000):
# 随机生成当前状态和期望的下一个状态
y_current = np.random.uniform(-2, 2, num_samples)
y_ref_next = np.random.uniform(-2, 2, num_samples)
# 根据系统方程计算所需的控制输入
u = y_ref_next - 0.5 * y_current - np.sin(y_current)
# 组合特征:当前状态 + 目标状态
X = np.column_stack((y_current, y_ref_next))
return X, u.reshape(-1, 1)
X, u_data = generate_data(num_samples=10000)
class InverseModel(nn.Module):
def __init__(self):
super(InverseModel, self).__init__()
self.fc1 = nn.Linear(2, 64) # 输入两个特征:当前状态和目标状态
self.fc2 = nn.Linear(64, 64)
self.fc3 = nn.Linear(64, 1)
self.relu = nn.ReLU()
def forward(self, x):
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
# 初始化模型
model = InverseModel()
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 转换为 PyTorch 张量
X_tensor = torch.tensor(X, dtype=torch.float32)
u_tensor = torch.tensor(u_data, dtype=torch.float32)
# 训练模型(修正后的训练循环)
num_epochs = 1000
losses = []
for epoch in range(num_epochs):
optimizer.zero_grad()
outputs = model(X_tensor)
loss = criterion(outputs, u_tensor)
loss.backward()
optimizer.step()
losses.append(loss.item())
if (epoch + 1) % 100 == 0:
print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')
# 绘制损失曲线
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss')
plt.show()
# 生成参考信号(保持相同)
def generate_reference_signal(steps=100):
t = np.linspace(0, 10, steps)
y_ref = np.sin(t) * 0.5 # 使用正弦波作为参考信号
return y_ref
# 控制过程(修正后的版本)
def control_process(model, y_ref, steps=100):
y = np.zeros(steps)
u = np.zeros(steps)
y[0] = y_ref[0] # 初始状态与参考信号对齐
u_real_control=np.zeros(steps)
for t in range(steps - 1):
# 使用当前状态和下一时刻的参考信号预测控制输入
y_ref_next = y_ref[t + 1]
input_tensor = torch.tensor([[y[t], y_ref_next]], dtype=torch.float32)
u[t] = model(input_tensor).detach().numpy()[0][0]
u_real_control[t]=y_ref_next - 0.5 * y[t] - np.sin(y[t])
# 更新系统状态
y[t + 1] = 0.5 * y[t] + u[t] + np.sin(y[t])
return y, u,u_real_control
# 生成参考信号并进行控制
y_ref_control = generate_reference_signal(steps=100)
y_control, u_control,u_real_control = control_process(model, y_ref_control)
# 绘制结果(添加跟踪误差显示)
plt.figure(figsize=(12, 10))
plt.subplot(3, 1, 1)
plt.plot(y_ref_control, label='Reference Signal', linestyle='--')
plt.plot(y_control, label='System Output')
plt.xlabel('Time Step')
plt.ylabel('Value')
plt.title('Control Performance')
plt.legend()
plt.subplot(3, 1, 2)
plt.plot(y_ref_control - y_control, label='Tracking Error', color='red')
plt.xlabel('Time Step')
plt.ylabel('Error')
plt.title('Tracking Error')
plt.legend()
plt.subplot(3, 1, 3)
plt.plot(u_real_control, label='u_real_control',linestyle='--')
plt.plot(u_control, label='u_control')
plt.xlabel('Time Step')
plt.ylabel('control_value')
plt.title('Control error')
plt.legend()
plt.tight_layout()
plt.show()
运行后可以看出效果非常好
6. 总结
本文介绍了神经网络直接逆控制的原理,并通过一个简单的 PyTorch 实现案例展示了其应用。直接逆控制通过训练神经网络模拟被控对象的逆动态特性,能够有效地实现对复杂系统的精确控制。尽管直接逆控制具有简单直接的优点,但它对训练数据的质量和神经网络的计算速度有一定要求。在未来的工作中,可以结合其他控制方法(如自适应控制或模糊控制)来进一步提高控制性能。我是橙色小博,关注我,一起在人工智能领域学习进步!