电机map程序完善

我们经常使用电机效率map图来直观地显示电机在不同转速和扭矩下的效率。
我们可以以此为参考对电机的结构进行微调,我需要它在低转速时有更高效率,或者说我需要在高转矩时有更好的性能等等。
同时,分析同一路谱数据在不同电机的map图中的平均效率,可以对比不同电机供应商的产品性能。

路谱数据为CAN分析仪采集的,某一时段的电机转矩、转速数据,路谱数据是随时间、工况、车型等不断变化的值。
电机效率map的原始数据来源于对电机在特定转速、转矩下效率的实验测试,属于和某机型绑定的恒定数据。
但此处注意,不同公司的台架平台下,可能会测出不同的map数据,所以要对不同电机进行性能比较,需要尽量在同样台架下获取数据。同时,需要知道获取的是电机效率还是系统效率。

当电机转速和转矩在同一方向时,表现的是驱动效率,当电机转速和转矩在相反方向时,表现的是发电效率。
以转速为横轴,以转矩为纵轴,那第一象限就是前进的驱动效率,第三象限就是反向的驱动效率,第四象限是前进的发电效率,第二象限是反向的发电效率。
理论上来说,电机第一象限和第三象限的map图谱是完全一样的,二四象限的图谱是完全一样的。但是为什么需要算四个象限呢,那是考虑到在一段路谱数据中,自然有正向转,有反向转,一起计算可以提高准确率。
主文件

close all;
clear;
clc;
%% 
global tolerance coefficient M2V_MCU_DMSpd M2V_MCU_DMTrq TX_mcuMotorSpeed TX_mcuOutTorque title_name
% M2V_MCU_DMSpd M2V_MCU_DMTrq为行走电机路谱;
% TX_mcuMotorSpeed TX_mcuOutTorque为泵用电机路谱;
load DATA_5T M2V_MCU_DMSpd M2V_MCU_DMTrq TX_mcuMotorSpeed TX_mcuOutTorque

tolerance = 3; % map表格各个档位的速度有可能会在正负1或正负3间切换。
coefficient = 100; % 效率值的系数,如果第三列效率是百分数,例如0.87,就乘以100,如果是87,那就乘以1。
title_name = 'xxx电机在xxx控制器厂商平台下的系统效率或电机效率的map图'; %标题请自己根据需要修改。
xlim_val = 3500; % 生成图的横坐标范围,行走电机为3500,泵用电机为3000
ylim_val = 2500; % 生成图的纵坐标范围, 行走电机为2500,泵用电机为1000
flag = 1; % 行走电机,flag为1,泵用电机,flag为0;
filename = '行走电机效率map.xlsx'; % 将表格放入本程序的同一个文件夹下,并在此填入表格名。
sheet1_name = '驱动效率'; % sheet1,有三列,转速,转矩,效率,第一行是列变量名称,第二行开始是数据。
sheet2_name = '发电效率'; % sheet2,同上。
value_eff = [30 50:5:80 81:2:90 91:1:94 95 95.7 96]; % MAP图显示的效率标签量。

speed_gears1 = [300, 600, 900, 1150, 1200, 1500, 1800, 2100, 2400, 2700, 3000, 3300, 3500]; % 驱动效率map的转速的档位值,从sheet1中提取。
speed_gears2 = [300, 600, 1200, 1500, 1800, 2100, 2400, 2700, 3000, 3300, 3500]; % 发电效率map的转速的档位值,从sheet2中提取。
% Data_Pre是xlsx的处理函数,生成包含三列数据的驱动效率map Motor_STE和包含三列数据的驱动效率map
% Generate_STE,以及两者的外特性曲线散点值。外特性曲线即为map在每个档位速度下的最大转矩值,在外特性曲线外的map图自动置为NAN,即在图中不显示。
[Motor_STE, Generate_STE, Motor_external, Gener_external] = Data_Pre(filename, sheet1_name, sheet2_name, speed_gears1, speed_gears2);
Motor_external(:,2) = Motor_external(:,2) + 70; % 可以给外特性曲线略微增加一些值,因为绘制外特性曲线的散点不一定囊括了所有的工作范围,实际电机的工作范围可能会远远超过,因此可以增加一些范围来使路谱所有数据被囊括其中。
% GtoM_TMcal为主功能函数,用来生成map图和路谱效率计算。
[EFF_Total_AVERAGE, EFF_MOT_AVERAGE, EFF_GEN_AVERAGE, STE_Road] = TMcal(Motor_STE, Generate_STE, Motor_external, Gener_external, xlim_val,ylim_val ,value_eff, flag);


function [Motor_STE, Generate_STE, Motor_external, Gener_external] = Data_Pre(filename, sheet1_name, sheet2_name, speed_gears1, speed_gears2)
global tolerance coefficient
% 读取表格,并将表格sheet生成变量opts
opts1 = detectImportOptions(filename, 'Sheet', sheet1_name);
opts2 = detectImportOptions(filename, 'Sheet', sheet2_name);
% 将sheet的每一列设定一变量名,防止表格抬头名称乱定义。
opts1.VariableNames = {'Var1','Var2','Var3'};
opts2.VariableNames = {'Var1','Var2','Var3'};
% 设置三列数据,数据格式为双精度。
opts1 = setvartype(opts1, {'Var1','Var2','Var3'}, 'double');
opts2 = setvartype(opts2, {'Var1','Var2','Var3'}, 'double');
Motor_data = readtable(filename, opts1);
Generate_data = readtable(filename, opts2);

Motor_data.Var3 = round(Motor_data.Var3 * coefficient, 3);
% 将抬头(第一行)去掉
Motor_STE = Motor_data{2:end, 1:3};
% Motor_data是驱动效率map(sheet1), Generate_data是发电效率map(sheet2).
Generate_data.Var3 = round(Generate_data.Var3 * 100, 3);
Generate_STE = Generate_data{2:end, 1:3};
% Generate_STE(:,2) = -Generate_STE(:,2); %
% 需要注意原发电效率map表格的转矩是否为负,若不为负,需要取相反数。
% 处理外特性曲线数据,is_max为1,那就找出同转速档位的最大转矩值,is_max为0,那就找出最小值。
is_max = 1;
Motor_external = external_character_cal(speed_gears1, tolerance, Motor_STE, is_max);
is_max = 0;
Gener_external = external_character_cal(speed_gears2, tolerance, Generate_STE, is_max);
% 如果需要,可以将修改后的数据写回Excel文件,尽量不要,一旦改了,就难改回来了。
% writetable(data, 'modified_table.xlsx');
end

function result = external_character_cal(speed_gears, tolerance, Motor_data, is_max)
speed = Motor_data(:, 1);
torque = Motor_data(:, 2);
result = zeros(length(speed_gears), 2);
for i = 1:length(speed_gears)
    % 找出当前档位的转速范围内的转矩
    idx = speed >= (speed_gears(i) - tolerance) & speed <= (speed_gears(i) + tolerance);
    torque_in_range = torque(idx);
    speed_in_range = speed(idx);
    
    % 找出最大转矩及其对应的转速
    if is_max == 1
        [max_torque, max_idx] = max(torque_in_range);
    else
        [max_torque, max_idx] = min(torque_in_range);
    end
    max_speed = speed_in_range(max_idx);
    
    % 存储结果,外特性曲线经过的 [各转速档位,各转速档位对应的最大转矩]
    result(i, :) = [max_speed, max_torque];
end
end

功能函数


% 第一列是转速,第二列是转矩,第三列是效率
function [EFF_Total_AVERAGE, EFF_MOT_AVERAGE, EFF_GEN_AVERAGE, STE_Road] = TMcal(Motor_5T, Generate_5T, Motor_outside, generate_outside, xlim_val, ylim_val, value_eff, flag)
global M2V_MCU_DMSpd M2V_MCU_DMTrq TX_mcuMotorSpeed TX_mcuOutTorque title_name
%% 电机电动效率曲面数据加载
    speed_eff_m = Motor_5T(:,1);
    torque_eff_m = Motor_5T(:,2);
    efficency_eff_m = Motor_5T(:,3);
% 电机电动效率曲面插值
    row = 400;
    col = row;
    [SPEED_M,TORQUE_M] = meshgrid(0:xlim_val/(row-1):xlim_val,0:max(max(torque_eff_m))/(col-1):max(max(torque_eff_m)));
    EFFICENCY_M = griddata(speed_eff_m,torque_eff_m,efficency_eff_m,SPEED_M,TORQUE_M); 
    EFFICENCY1_M = griddata(speed_eff_m,torque_eff_m,efficency_eff_m,SPEED_M,TORQUE_M,'V4'); 
    jm = find(isnan(EFFICENCY_M));
    EFFICENCY_M(jm) = EFFICENCY1_M(jm);
    
% 电机电动外特性曲线插值
    speed_ex_m = Motor_outside(:,1);
    torque_ex_m = Motor_outside(:,2);
    speed_q_m = [0:xlim_val/(row-1):xlim_val]; 
    torque_q_m = interp1(speed_ex_m,torque_ex_m,speed_q_m,'linear','extrap');

% 电机电动效率曲面沿外特性曲线剪切
    TRQ_RE_M = repmat(torque_q_m,col,1); 
    im = find(TORQUE_M > TRQ_RE_M);  %确定超出边界的格点下标
    EFFICENCY_M(im) = NaN;  %强制为非数

%% 电机发电效率曲面数据加载
    speed_eff_g = Generate_5T(:,1);
    torque_eff_g = Generate_5T(:,2);
    efficency_eff_g = Generate_5T(:,3);
    torque_eff_minus_g = torque_eff_g;   %这里的转矩数据列本身就为负。

% 电机发电效率曲面插值
    row = 400;
    col = row;
    [SPEED_G,TORQUE_G] = meshgrid(0:xlim_val/(row-1):xlim_val,0:min(min(torque_eff_minus_g))/(col-1):min(min(torque_eff_minus_g)));
    EFFICENCY_G = griddata(speed_eff_g,torque_eff_minus_g,efficency_eff_g,SPEED_G,TORQUE_G); 
    EFFICENCY1_G = griddata(speed_eff_g,torque_eff_minus_g,efficency_eff_g,SPEED_G,TORQUE_G,'V4'); 
    jg = find(isnan(EFFICENCY_G));
    EFFICENCY_G(jg) = EFFICENCY1_G(jg);

%% 电机发电外特性曲线
    speed_ex_g = generate_outside(:,1)';
    torque_ex_g = generate_outside(:,2)';
    torque_ex_minus_g = torque_ex_g;

% 电机发电外特性曲线插值
    speed_q_g = [0:xlim_val/(row-1):xlim_val];  %#ok<NBRAK>
    torque_q_g = interp1(speed_ex_g,torque_ex_minus_g,speed_q_g,'cubic','extrap');

% 电机发电效率曲面沿外特性曲线剪切
    TRQ_RE_G = repmat(torque_q_g,col,1); 
    ig = find(TORQUE_G < TRQ_RE_G);  %确定超出边界的格点下标
    EFFICENCY_G(ig) = NaN;  %强制为非数

%% 绘图
figure(1);

% 电机电动效率
    colormap(jet); % 色阶
    [c,h] = contourf(SPEED_M,TORQUE_M,EFFICENCY_M,value_eff);
    ylim([-ylim_val, ylim_val]);
    if flag == 1
        xlim([-xlim_val,xlim_val]);
    else
        xlim([0,xlim_val]);
    end
    hold on
    h = clabel(c,h,'labelspacing',160);
    set(h,'BackgroundColor',[1 1 6],...
        'EdgeColor',[7 7 7],...
        'Linewidth',1,...
        'Color','r');
    hold on
%   plot(speed_q_m,torque_q_m,'b','linewidth',2.5);
%   hold on

% 电机后退效率
if flag == 1 % 如果是泵用电机,那不需要第三象限
    colormap(jet); % 色阶
    [c,h] = contourf(-SPEED_M,-TORQUE_M,EFFICENCY_M,value_eff);
    hold on
    h = clabel(c,h,'labelspacing',160);
    set(h,'BackgroundColor',[1 1 6],...
        'EdgeColor',[7 7 7],...
        'Linewidth',1,...
        'Color','r');
%     hold on
%     plot(-speed_q_m,-torque_q_m,'b','linewidth',2.5);
%     hold on   
end
% 
% % 电机发电效率
    [c,h] = contourf(SPEED_G,TORQUE_G,EFFICENCY_G,value_eff);
    hold on
    h = clabel(c,h,'labelspacing',160);
    set(h,'BackgroundColor',[1 1 6],...
        'EdgeColor',[7 7 7],...
        'Linewidth',1,...
        'Color','r');
%     hold on
%     plot(speed_q_g,torque_q_g,'b','linewidth',2.5);
%     hold on

% 电机发电后退效率
if flag ==1 % 如果是泵用电机,那不需要第二象限
    [c,h] = contourf(-SPEED_G,-TORQUE_G,EFFICENCY_G,value_eff);
    hold on
    caxis([25 100]); % 设置颜色条的范围,比如0到100
    h = clabel(c,h,'labelspacing',160);
    set(h,'BackgroundColor',[1 1 6],...
        'EdgeColor',[7 7 7],...
        'Linewidth',1,...
        'Color','r');
%     hold on
%     plot(-speed_q_g,-torque_q_g,'b','linewidth',2.5);
%     hold on    
end

% 加载实测数据
   if flag == 1
       TmTor0 = M2V_MCU_DMTrq(:,end);
       TmSpd0 = M2V_MCU_DMSpd(:,end);
       index1 = [1,3]; % 行走电机map图为4个象限。
       index2 = [2,4];
   else 
       TmTor0 = TX_mcuOutTorque(:,end);
       TmSpd0 = TX_mcuMotorSpeed(:,end);
       index1 = [1]; % 泵用电机map图为一、四象限。
       index2 = [4];
   end
    TmSpd = TmSpd0;
    TmTor = TmTor0;
 
    scatter(TmSpd,TmTor,20,'o', 'MarkerEdgeColor','k','MarkerFaceColor','y');
    hold on 
    t1 = xlabel('speed [rpm]');
    t2 = ylabel('Torque [Nm]');    
    set([t1,t2],'FontSize',14);
    legend('Mot Efficiency Map','Mot WOT','Gen Efficiency Map','Gen WOT','location','southeast')
    title(title_name,'FontSize',14 )
    grid on;
    hold on;  

%% 计算平均效率
Ans_out1 = 0; Ans_input1 = 0;    
STE_Road = [];
for index = index1
    [TmSpd_index, TmTor_index] = Eff_data_pre(TmTor, TmSpd, index);
    if index == 1
        [Sum_Power_out, Sum_Power_input, CB_EFF_GEN] = Power_IO_cal(SPEED_M, TORQUE_M,EFFICENCY_M,TmSpd_index,TmTor_index);
    elseif index == 3
        [Sum_Power_out, Sum_Power_input, CB_EFF_GEN] = Power_IO_cal(-SPEED_M, -TORQUE_M,EFFICENCY_M,TmSpd_index,TmTor_index);
    end
    STE_Road = [STE_Road; [TmSpd_index, TmTor_index, CB_EFF_GEN]];
    Ans_out1 = Ans_out1 + Sum_Power_out;
    Ans_input1 = Ans_input1 + Sum_Power_input;
end
EFF_MOT_AVERAGE = Ans_out1 / Ans_input1;

Ans_out2 = 0; Ans_input2 = 0;    
for index = index2
    [TmSpd_index, TmTor_index] = Eff_data_pre(TmTor, TmSpd, index);
    if index == 2
        [Sum_Power_out, Sum_Power_input, CB_EFF_GEN] = Power_IO_cal(-SPEED_G, -TORQUE_G,EFFICENCY_G,TmSpd_index,TmTor_index);
    elseif index == 4
        [Sum_Power_out, Sum_Power_input, CB_EFF_GEN] = Power_IO_cal(SPEED_G, TORQUE_G,EFFICENCY_G,TmSpd_index,TmTor_index);
    end
    STE_Road = [STE_Road; [TmSpd_index, TmTor_index, CB_EFF_GEN]];
    Ans_out2 = Ans_out2 + Sum_Power_out;
    Ans_input2 = Ans_input2 + Sum_Power_input;
end
EFF_GEN_AVERAGE = Ans_out2 / Ans_input2;

%%
EFF_Total_AVERAGE = (Ans_out1 + Ans_out2) / (Ans_input1 + Ans_input2);

ax = gca;
xlen = ax.XLim;
ylen = ax.YLim;

% 计算右上角的位置
%%
xpos = xlen(2) - 0.03 * (xlen(2) - xlen(1)); % 在x轴方向留出一些空间
ypos = ylen(2) - 0.02 * (ylen(2) - ylen(1)); % 在y轴方向留出一些空间
text(xpos, ypos, ['电驱效率:',num2str(EFF_MOT_AVERAGE),'%'], 'HorizontalAlignment', 'right', 'VerticalAlignment', 'top');

xpos = xlen(2) - 0.03 * (xlen(2) - xlen(1)); % 在x轴方向留出一些空间
ypos = ylen(2) - 0.06 * (ylen(2) - ylen(1)); % 在y轴方向留出一些空间
text(xpos, ypos, ['发电效率:',num2str(EFF_GEN_AVERAGE),'%'], 'HorizontalAlignment', 'right', 'VerticalAlignment', 'top');

xpos = xlen(2) - 0.03 * (xlen(2) - xlen(1)); % 在x轴方向留出一些空间
ypos = ylen(2) - 0.10 * (ylen(2) - ylen(1)); % 在y轴方向留出一些空间

text(xpos, ypos, ['总效率:',num2str(EFF_Total_AVERAGE),'%'], 'HorizontalAlignment', 'right', 'VerticalAlignment', 'top');
set(gcf, 'Position',[100 100 1000 600]);
filename = [title_name , '.jpg'];
saveas(gcf,  filename);
end
% 计算输入总功率和输出总功率
function [Sum_Power_out, Sum_Power_input, CB_EFF_GEN] = Power_IO_cal(SPEED, TORQUE,EFFICENCY, TmSpd, TmTor)
    CB_EFF_GEN = griddata(SPEED, TORQUE, EFFICENCY, TmSpd, TmTor,'nearest'); % 根据实测数据查表得出效率值
    PowerGen_Output = TmSpd.*TmTor./9550;
    PowerGen_Input = PowerGen_Output./CB_EFF_GEN;
    Sum_Power_out = sum(PowerGen_Output(~isnan(PowerGen_Output)));
    Sum_Power_input = sum(PowerGen_Input(~isnan(PowerGen_Input)));
end

function [TmSpd_Mot2, TmTor_Mot2] = Eff_data_pre(TmTor, TmSpd, flag)
    % 第一象限
    TmTor_Mot1 = TmTor;
    TmSpd_Mot1 = TmSpd;
    switch flag
        case 1 
            INDEX_TRQNEG = find(TmTor <= 0);
            TmTor_Mot1(INDEX_TRQNEG) = [];
            TmSpd_Mot1(INDEX_TRQNEG) = [];
            INDEX_SPDZERO = find(TmSpd_Mot1 <= 0);
        case 2
            INDEX_TRQNEG = find(TmTor <= 0);
            TmTor_Mot1(INDEX_TRQNEG) = [];
            TmSpd_Mot1(INDEX_TRQNEG) = [];
            INDEX_SPDZERO = find(TmSpd_Mot1 >= 0);    
        case 3
            INDEX_TRQNEG = find(TmTor >= 0);
            TmTor_Mot1(INDEX_TRQNEG) = [];
            TmSpd_Mot1(INDEX_TRQNEG) = [];
            INDEX_SPDZERO = find(TmSpd_Mot1 >= 0);
        otherwise
            INDEX_TRQNEG = find(TmTor >= 0);
            TmTor_Mot1(INDEX_TRQNEG) = [];
            TmSpd_Mot1(INDEX_TRQNEG) = [];
            INDEX_SPDZERO = find(TmSpd_Mot1 <= 0);    
    end
    TmSpd_Mot2 = TmSpd_Mot1;
    TmTor_Mot2 = TmTor_Mot1;
    TmSpd_Mot2(INDEX_SPDZERO) = [];
    TmTor_Mot2(INDEX_SPDZERO) = [];
end

如果需要在Linux平台上运行上述程序,需要考虑以下几点进行完善: 1. 权限问题:在Linux系统中,非root用户无法占用1024以下的端口号,因此需要使用root权限运行服务器端程序,或者使用setcap命令对程序进行特殊权限设置。 2. IP地址问题:如果单片机和服务器不在同一局域网内,需要使用公网IP地址或者进行端口映射才能进行通信。 3. 串口通信问题:Linux系统中串口的文件名可能与Windows系统不同,需要根据实际情况进行修改。另外,还需要考虑串口权限的问题,需要将当前用户添加到dialout用户组中。 以下是一份在Linux平台上运行的示例代码: Python服务器端代码: ```python import socket HOST = '0.0.0.0' # 监听所有网卡的IP地址 PORT = 8888 # 服务器端口号 # 创建socket对象 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 绑定IP地址和端口号 s.bind((HOST, PORT)) # 监听端口号,等待客户端连接 s.listen(1) print('Waiting for connection...') while True: # 接收客户端连接 conn, addr = s.accept() print('Connected by', addr) while True: # 接收客户端发送的数据 data = conn.recv(1024) if not data: break # 解析接收到的数据 motor1_angle, motor2_angle = data.split(',') # 将角度转换为单片机控制命令,并通过串口发送给单片机 # ... # 关闭连接 conn.close() ``` 单片机端代码: ```c #include <SoftwareSerial.h> #define MOTOR1_PIN 6 #define MOTOR2_PIN 7 SoftwareSerial serial("/dev/ttyUSB0", "/dev/ttyUSB1"); // 串口通信对象 void setup() { pinMode(MOTOR1_PIN, OUTPUT); pinMode(MOTOR2_PIN, OUTPUT); serial.begin(9600); // 初始化串口通信 } void loop() { if (serial.available()) { String data = serial.readStringUntil('\n'); if (data.length() > 0) { // 解析接收到的数据 int motor1_angle = data.substring(0, data.indexOf(',')).toInt(); int motor2_angle = data.substring(data.indexOf(',') + 1).toInt(); // 将角度转换为电机控制命令 int motor1_pwm = map(motor1_angle, 0, 180, 0, 255); int motor2_pwm = map(motor2_angle, 0, 180, 0, 255); // 控制电机转动 analogWrite(MOTOR1_PIN, motor1_pwm); analogWrite(MOTOR2_PIN, motor2_pwm); } } } ``` 注意:以上示例代码仅供参考,具体实现还需要根据实际情况进行修改和完善
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值