在之前的无感FOC学习笔记中,提到了多种关于速度和位置的估算方法,比如SMO滑膜控制、HFI高频注入、磁链观测器等等。今天给大家介绍一种新的估算方式,利用人工神经网络ANNs来对位置和速度进行估算,主要的思路来自于下面这篇文章《ANN-based position and speed sensorless estimation for BLDC motors》,具体文章的地址如下:https://doi.org/10.1016/j.measurement.2021.110602
这篇文章主要介绍了一种基于人工神经网络的sensorless的位置和速度估算方法,文章提出的ANN网络采用的是多层感知机(MLP)的网络结构,具有三个层级:输入层、隐藏层、输出层:
输入层:1、当前时刻的三相终端电压,这包括A、B、C相三相电压,一共三个输入
2、下一个时刻的三相终端电压,同样也包括A、B、C相的电压,一共三个输入
3、时间差Δt:即两次采样之间的时间间隔,1个输入、
4、三相电压变化的特征(voltMul):通过当前时刻和下一个时刻的三相电压相乘来获得,对于每一个相(A、B、C)都有这样一个特征,这里也有三个输入。
所以输入层一个3+3+1+3=10个输入节点。
隐藏层:一共五个节点,使用tanh-sigmoid激活函数(g1(x)= -1 + 2/(1+exp(-2*x))),以建立输入和输出之间的非线性关系,限制输出范围在±1之间
输出层:一共2个节点,使用线性激活函数(g2(x)=x),允许输出值无限大,用于输出估计的角度的正弦和余弦分量
输入参数的获取:从电机获取的参数主要是终端相电压,这些电压信号通过信号处理(包括放大器和滤波器以及ADC进行预处理)来获得,同时还需要保存连续两个采样点之间的时间间隔,这个可以通过MCU的定时器来获取。上述提到的三相电压变化特征并不能直接从电机中获取,这是这篇文章中提出的一种特征增强的方式,通过Δt前后的两组三相电压值进行相乘的操作就得到了额外的三个输入参数。
文章中没有提供实际的代码,所以根据文章中提到的网络结构,我们尝试用代码来构建这个相对简单ANNs网络
首先用pytorch来进行模型训练:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
# 假设你有数据集 data 和对应的标签 labels
# data.shape = (num_samples, 10)
# labels.shape = (num_samples, 2)
# 将数据转换为 PyTorch 的 Tensor
data_tensor = torch.tensor(data, dtype=torch.float32)
labels_tensor = torch.tensor(labels, dtype=torch.float32)
# 创建 DataLoader
dataset = TensorDataset(data_tensor, labels_tensor)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
# 定义模型
class MLP(nn.Module):
def __init__(self):
super(MLP, self).__init__()
self.fc1 = nn.Linear(10, 64)
self.fc2 = nn.Linear(64, 32)
self.fc3 = nn.Linear(32, 2)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x
model = MLP()
# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模型
for epoch in range(100): # 循环遍历数据集多次
for inputs, targets in dataloader:
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, targets)
loss.backward()
optimizer.step()
# 保存模型
torch.save(model.state_dict(), 'model.pth')
然后将pth模型导出为ONNX格式
# 加载模型
model = MLP()
model.load_state_dict(torch.load('model.pth'))
model.eval()
# 导出到ONNX
dummy_input = torch.randn(1, 10, requires_grad=True) # 随机输入张量
output = model(dummy_input)
# 导出模型
torch.onnx.export(model, dummy_input, "model.onnx", verbose=True,
export_params=True, # 存储训练过的参数
opset_version=10, # ONNX版本
do_constant_folding=True, # 是否执行常量折叠优化
input_names=['input'], # 输入
output_names=['output']) # 输出
调用onnx runtime的API来加载模型并进行推理
#include <onnxruntime_c_api.h>
#include <iostream>
int main() {
OrtEnv* env;
OrtStatus* status;
// 初始化环境
OrtApi* api = OrtGetApiBase()->GetApi(ORT_API_VERSION);
ORT_ENFORCE(api->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "test", &env));
// 加载模型
OrtSessionOptions* session_options;
api->CreateSessionOptions(&session_options);
OrtSession* session;
api->CreateSession(env, "path/to/model.onnx", session_options, &session);
// 设置输入数据
const char* input_name = "input";
OrtAllocator* allocator;
api->GetAllocatorWithDefaultOptions(env, &allocator);
OrtValue* input_tensor;
api->AllocTensor(allocator, OrtMemTypeCPU, OrtTensorTypeInt32, 1, &input_name, &input_tensor);
int input_data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 你的输入数据
api->CopyToTensor(input_tensor, input_data, sizeof(input_data));
// 设置输出数据
const char* output_name = "output";
OrtValue* output_tensor;
api->AllocEmptyTensor(allocator, OrtTensorTypeFloat, 1, &output_name, &output_tensor);
// 执行模型
OrtRunOptions* run_options;
api->CreateRunOptions(&run_options);
api->RunSession(session, run_options, 1, &input_name, &input_tensor, 1, &output_name, &output_tensor);
// 获取输出数据
float* output_data;
size_t output_data_length;
api->GetTensorMutableDataAsFloat(output_tensor, &output_data);
api->GetTensorShapeElementCount(output_tensor, &output_data_length);
// 输出结果
std::cout << "Output: ";
for (size_t i = 0; i < output_data_length; ++i) {
std::cout << output_data[i] << " ";
}
std::cout << std::endl;
// 清理
api->ReleaseTensor(output_tensor);
api->ReleaseTensor(input_tensor);
api->ReleaseSession(session);
api->ReleaseSessionOptions(session_options);
api->ReleaseEnv(env);
return 0;
}
文章中最终实现的效果:在125~1500rpm转速范围内,位置误差为0.8电角度,速度误差为22rpm,这样一种结果,可以说位置的估计相比传统方法已经提升很大了 ,但是速度误差还是不如传统方法,还需要继续改进。
文章也和最近的相关研究结果做了对比,结果如下:
与传统方法(如 BEMF 零点交叉检测和基于 BEMF 观察器的改进)相比。所提出的方法将位置估计的平均误差减少了 95.1%,相对于 BEMF 观测器、终端电压传感和 SMO 的误差减少了 73.3%。
此外,在速度估算方面,该方法的平均误差减少了 26.7%,但与最佳传统方法相比,误差减少了 73.3%。但与最佳传统方法相比,误差增加了 340%。
与终端相电压衍生法等先进方法相比,EKF相电压求导法、EKF 算法、平滑算法、以及具有类似 ANN 拓扑的算法相比,所提出的方法将位置估计误差平均减少了 67.2%、误差减少了 20%。此外,在速度估计方面,它获得了平均误差增量为 144.4%,与最佳先进方法相比误差减少了 20%。
感兴趣的同学可以动手试试哦