handles.c

 
function ofdm_step_gui() % 创建主界面 fig = figure('Name', 'OFDM系统分步仿真', 'Position', [100 100 1200 800], ... 'NumberTitle', 'off', 'Resize', 'off'); % 创建控制面板 ctrl_panel = uipanel('Title', '控制面板', 'Position', [0.02 0.02 0.25 0.96]); % 创建结果展示区 results_panel = uipanel('Title', '仿真结果', 'Position', [0.28 0.02 0.7 0.96]); % 初始化参数存储结构 handles = struct(); % 添加参数输入控件 uicontrol(ctrl_panel, 'Style', 'text', 'String', '子载波数量:', ... 'Position', [20 700 100 20], 'HorizontalAlignment', 'left'); handles.num_sc = uicontrol(ctrl_panel, 'Style', 'edit', 'String', '64', ... 'Position', [130 700 80 25]); uicontrol(ctrl_panel, 'Style', 'text', 'String', '调制方式:', ... 'Position', [20 650 100 20], 'HorizontalAlignment', 'left'); handles.mod_type = uicontrol(ctrl_panel, 'Style', 'popupmenu', ... 'String', {'QPSK', '16QAM', 'BPSK'}, ... 'Position', [130 650 80 25], 'Value', 1); uicontrol(ctrl_panel, 'Style', 'text', 'String', '循环前缀长度:', ... 'Position', [20 600 100 20], 'HorizontalAlignment', 'left'); handles.cp_len = uicontrol(ctrl_panel, 'Style', 'edit', 'String', '16', ... 'Position', [130 600 80 25]); uicontrol(ctrl_panel, 'Style', 'text', 'String', '编码类型:', ... 'Position', [20 550 100 20], 'HorizontalAlignment', 'left'); handles.coding_type = uicontrol(ctrl_panel, 'Style', 'popupmenu', ... 'String', {'卷积编码', '无编码'}, ... 'Position', [130 550 80 25], 'Value', 1); uicontrol(ctrl_panel, 'Style', 'text', 'String', '交织深度:', ... 'Position', [20 500 100 20], 'HorizontalAlignment', 'left'); handles.intlvr_depth = uicontrol(ctrl_panel, 'Style', 'edit', 'String', '100', ... 'Position', [130 500 80 25]); uicontrol(ctrl_panel, 'Style', 'text', 'String', 'SNR(dB):', ... 'Position', [20 450 100 20], 'HorizontalAlignment', 'left'); handles.snr = uicontrol(ctrl_panel, 'Style', 'edit', 'String', '30', ... 'Position', [130 450 80 25]); % 创建步骤按钮 step_buttons = { '生成原始信号', @gen_data; '编码', @encode_data; '交织', @interleave_data; '调制', @modulate_data; 'IFFT变换', @ifft_transform; '加循环前缀', @add_cp; '上变频', @upconvert; '信道传输', @channel_transmission; '接收处理', @receive_process; '解调', @demodulate_data; '解交织', @deinterleave_data; '解码', @decode_data; '误码率分析', @ber_analysis; }; for i = 1:size(step_buttons, 1) uicontrol(ctrl_panel, 'Style', 'pushbutton', ... 'String', step_buttons{i,1}, ... 'Position', [30 400-30*i 180 30], ... 'Callback', step_buttons{i,2}, ... 'Tag', ['btn_' num2str(i)]); end % 创建结果展示区域 ax_positions = { [0.05 0.65 0.3 0.3], '原始信号'; [0.35 0.65 0.3 0.3], '编码后信号'; [0.65 0.65 0.3 0.3], '交织后信号'; [0.05 0.35 0.3 0.3], '调制星座图'; [0.35 0.35 0.3 0.3], 'OFDM时域信号'; [0.65 0.35 0.3 0.3], '加CP后频谱'; [0.05 0.05 0.3 0.3], '天线输出频谱'; [0.35 0.05 0.3 0.3], '接收端星座图'; [0.65 0.05 0.3 0.3], '误码率分析'; }; handles.axes = gobjects(size(ax_positions, 1), 1); for i = 1:size(ax_positions, 1) handles.axes(i) = axes(results_panel, 'Position', ax_positions{i,1}); title(handles.axes(i), ax_positions{i,2}); grid(handles.axes(i), 'on'); end % 添加状态文本 handles.status_text = uicontrol(results_panel, 'Style', 'text', ... 'String', '准备就绪', ... 'Position', [10 730 300 20], ... 'HorizontalAlignment', 'left', ... 'FontSize', 10); % 存储数据供回调使用 handles.data = struct(); % 存储中间数据 guidata(fig, handles); end %% 生成原始信号回调函数 function gen_data(hObject, ~) handles = guidata(hObject); % 更新状态 set(handles.status_text, 'String', '正在生成原始信号...'); drawnow; % 获取参数 N = str2double(get(handles.num_sc, 'String')); % 子载波数量 depth = str2double(get(handles.intlvr_depth, 'String')); % 交织深度 % 生成原始信号 (确保有足够数据) total_bits = max(10000, depth * ceil(10000/depth)); % 确保可被交织深度整除 data = randi([0 1], total_bits, 1); % 存储数据 handles.data.original = data; handles.data.total_bits = total_bits; % 显示结果 axes(handles.axes(1)); stem(data(1:min(40, length(data))), 'filled'); title(['原始二进制信号 (' num2str(length(data)) ' bits)']); ylim([-0.2 1.2]); xlabel('比特序号'); ylabel('幅度'); % 更新状态 set(handles.status_text, 'String', '原始信号已生成'); guidata(hObject, handles); end %% 编码回调函数 function encode_data(hObject, ~) handles = guidata(hObject); % 检查是否有原始数据 if ~isfield(handles.data, 'original') errordlg('请先生成原始信号!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行编码...'); drawnow; % 获取参数 coding_idx = get(handles.coding_type, 'Value'); % 编码类型索引 data = handles.data.original; % 编码 if coding_idx == 1 % 卷积编码 trellis = poly2trellis(7, [171 133]); coded_data = convenc(data, trellis); title_str = '卷积编码后'; else % 无编码 coded_data = data; title_str = '未编码数据'; end % 存储数据 handles.data.coded = coded_data; % 显示结果 axes(handles.axes(2)); stem(coded_data(1:min(80, length(coded_data))), 'filled'); title([title_str ' (' num2str(length(coded_data)) ' bits)']); ylim([-0.2 1.2]); xlabel('比特序号'); ylabel('幅度'); % 更新状态 set(handles.status_text, 'String', '编码已完成'); guidata(hObject, handles); end %% 交织回调函数 function interleave_data(hObject, ~) handles = guidata(hObject); % 检查是否有编码数据 if ~isfield(handles.data, 'coded') errordlg('请先进行编码!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行交织...'); drawnow; % 获取参数 depth = str2double(get(handles.intlvr_depth, 'String')); % 交织深度 coded_data = handles.data.coded; % 交织 rows = depth; cols = ceil(length(coded_data)/rows); % 补零使数据完整 padded_data = [coded_data; zeros(rows*cols - length(coded_data), 1)]; interleaved = reshape(reshape(padded_data, rows, cols)', [], 1); % 存储数据 handles.data.interleaved = interleaved; % 显示结果 axes(handles.axes(3)); stem(interleaved(1:min(80, length(interleaved))), 'filled'); title(['交织后 (' num2str(depth) '行交织器)']); ylim([-0.2 1.2]); xlabel('比特序号'); ylabel('幅度'); % 更新状态 set(handles.status_text, 'String', '交织已完成'); guidata(hObject, handles); end %% 调制回调函数 function modulate_data(hObject, ~) handles = guidata(hObject); % 检查是否有交织数据 if ~isfield(handles.data, 'interleaved') errordlg('请先进行交织!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行调制...'); drawnow; % 获取参数 mod_idx = get(handles.mod_type, 'Value'); % 调制类型索引 N = str2double(get(handles.num_sc, 'String')); % 子载波数量 interleaved = handles.data.interleaved; % 调制参数 mod_names = {'QPSK', '16QAM', 'BPSK'}; mod_orders = [4, 16, 2]; mod_str = mod_names{mod_idx}; M = mod_orders(mod_idx); bits_per_symbol = log2(M); % 串并转换 symbols_per_carrier = floor(length(interleaved)/(bits_per_symbol * N)); total_bits_needed = symbols_per_carrier * bits_per_symbol * N; % 处理多余数据 if length(interleaved) > total_bits_needed interleaved = interleaved(1:total_bits_needed); end % 重塑为并行流 parallel_data = reshape(interleaved, bits_per_symbol, []); parallel_data = parallel_data'; % 调制 symbol_input = bi2de(reshape(parallel_data, [], bits_per_symbol), 'left-msb'); switch mod_idx case 1 % QPSK mod_data = pskmod(symbol_input, M, pi/4, 'gray'); case 2 % 16QAM mod_data = qammod(symbol_input, M, 'gray', 'UnitAveragePower', true); case 3 % BPSK mod_data = pskmod(symbol_input, M, pi/2, 'gray'); end % 重塑为OFDM帧 mod_data = reshape(mod_data, [], N); % 存储数据 handles.data.modulated = mod_data; handles.data.M = M; handles.data.bits_per_symbol = bits_per_symbol; handles.data.symbols_per_carrier = symbols_per_carrier; % 显示结果 axes(handles.axes(4)); scatter(real(mod_data(:)), imag(mod_data(:)), 25, 'filled'); title([mod_str ' 调制星座图']); axis square; grid on; xlabel('同相分量'); ylabel('正交分量'); % 更新状态 set(handles.status_text, 'String', '调制已完成'); guidata(hObject, handles); end %% IFFT变换回调函数 function ifft_transform(hObject, ~) handles = guidata(hObject); % 检查是否有调制数据 if ~isfield(handles.data, 'modulated') errordlg('请先进行调制!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行IFFT变换...'); drawnow; % 获取参数 N = str2double(get(handles.num_sc, 'String')); % 子载波数量 mod_data = handles.data.modulated; % IFFT变换 ofdm_symbols = ifft(mod_data, N, 2); % 存储数据 handles.data.ofdm_symbols = ofdm_symbols; % 显示结果 axes(handles.axes(5)); plot(abs(ofdm_symbols(1, :))); title('OFDM符号 (时域)'); xlabel('样点序号'); ylabel('幅度'); % 更新状态 set(handles.status_text, 'String', 'IFFT变换已完成'); guidata(hObject, handles); end %% 加循环前缀回调函数 function add_cp(hObject, ~) handles = guidata(hObject); % 检查是否有OFDM符号数据 if ~isfield(handles.data, 'ofdm_symbols') errordlg('请先进行IFFT变换!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在添加循环前缀...'); drawnow; % 获取参数 cp_length = str2double(get(handles.cp_len, 'String')); % 循环前缀长度 ofdm_symbols = handles.data.ofdm_symbols; % 加循环前缀 cp = ofdm_symbols(:, end-cp_length+1:end); ofdm_cp = [cp, ofdm_symbols]; % 计算频谱 Fs = 1e6; % 采样率1MHz [pxx_base, f_base] = pwelch(ofdm_symbols(:), 512, 256, 512, Fs, 'centered'); [pxx_cp, f_cp] = pwelch(ofdm_cp(:), 512, 256, 512, Fs, 'centered'); % 存储数据 handles.data.ofdm_cp = ofdm_cp; handles.data.Fs = Fs; % 显示结果 axes(handles.axes(6)); plot(f_base/1e6, 10*log10(pxx_base), 'b'); hold on; plot(f_cp/1e6, 10*log10(pxx_cp), 'r'); title('加CP前后频谱对比'); xlabel('频率 (MHz)'); ylabel('功率 (dB)'); legend('无CP', '有CP'); grid on; hold off; % 更新状态 set(handles.status_text, 'String', '循环前缀已添加'); guidata(hObject, handles); end %% 上变频回调函数 function upconvert(hObject, ~) handles = guidata(hObject); % 检查是否有加CP后的数据 if ~isfield(handles.data, 'ofdm_cp') errordlg('请先添加循环前缀!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行上变频...'); drawnow; % 获取参数 Fs = handles.data.Fs; ofdm_cp = handles.data.ofdm_cp; % 上变频 carrier_freq = 2e9; % 2GHz载波 t = (0:length(ofdm_cp(:))-1)'/Fs; rf_signal = real(ofdm_cp(:)) .* cos(2*pi*carrier_freq*t) - ... imag(ofdm_cp(:)) .* sin(2*pi*carrier_freq*t); % 存储数据 handles.data.rf_signal = rf_signal; handles.data.carrier_freq = carrier_freq; % 显示频谱 axes(handles.axes(7)); [pxx_rf, f_rf] = pwelch(rf_signal, 1024, 512, 1024, Fs, 'centered'); plot(f_rf/1e6, 10*log10(pxx_rf)); title('天线输出频谱'); xlabel('频率 (MHz)'); ylabel('功率谱密度 (dB/Hz)'); grid on; % 更新状态 set(handles.status_text, 'String', '上变频已完成'); guidata(hObject, handles); end %% 信道传输回调函数 function channel_transmission(hObject, ~) handles = guidata(hObject); % 检查是否有RF信号 if ~isfield(handles.data, 'rf_signal') errordlg('请先进行上变频!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在模拟信道传输...'); drawnow; % 获取参数 snr = str2double(get(handles.snr, 'String')); % SNR rf_signal = handles.data.rf_signal; % 信道传输 (添加AWGN噪声) rx_signal = awgn(rf_signal, snr, 'measured'); % 存储数据 handles.data.rx_signal = rx_signal; % 更新状态 set(handles.status_text, 'String', '信道传输已完成'); guidata(hObject, handles); end %% 接收处理回调函数 function receive_process(hObject, ~) handles = guidata(hObject); % 检查是否有接收信号 if ~isfield(handles.data, 'rx_signal') errordlg('请先进行信道传输!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行接收处理...'); drawnow; % 获取参数 Fs = handles.data.Fs; carrier_freq = handles.data.carrier_freq; rx_signal = handles.data.rx_signal; % 接收处理 t = (0:length(rx_signal)-1)'/Fs; I = rx_signal .* cos(2*pi*carrier_freq*t) * 2; Q = -rx_signal .* sin(2*pi*carrier_freq*t) * 2; baseband = complex(I, Q); % 低通滤波 cutoff = Fs/2.5; [b, a] = butter(6, cutoff/(Fs/2)); filtered = filtfilt(b, a, baseband); % 存储数据 handles.data.filtered = filtered; % 显示接收端星座图 axes(handles.axes(8)); mod_data = handles.data.modulated; scatter(real(filtered(1:length(mod_data(:)))), ... imag(filtered(1:length(mod_data(:)))), 25, 'filled'); title('接收端星座图 (含噪声)'); axis square; grid on; xlabel('同相分量'); ylabel('正交分量'); % 更新状态 set(handles.status_text, 'String', '接收处理已完成'); guidata(hObject, handles); end %% 解调回调函数 function demodulate_data(hObject, ~) handles = guidata(hObject); % 检查是否有接收数据 if ~isfield(handles.data, 'filtered') errordlg('请先进行接收处理!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行解调...'); drawnow; % 获取参数 mod_idx = get(handles.mod_type, 'Value'); % 调制类型索引 M = handles.data.M; filtered = handles.data.filtered; mod_data = handles.data.modulated; % 解调 switch mod_idx case 1 % QPSK demod = pskdemod(filtered(1:length(mod_data(:))), M, pi/4, 'gray'); case 2 % 16QAM demod = qamdemod(filtered(1:length(mod_data(:))), M, 'gray', 'UnitAveragePower', true); case 3 % BPSK demod = pskdemod(filtered(1:length(mod_data(:))), M, pi/2, 'gray'); end % 符号到比特 bits_per_symbol = handles.data.bits_per_symbol; rx_bits = de2bi(demod, bits_per_symbol, 'left-msb'); rx_bits = rx_bits(:); % 存储数据 handles.data.demod_bits = rx_bits; % 更新状态 set(handles.status_text, 'String', '解调已完成'); guidata(hObject, handles); end %% 解交织回调函数 function deinterleave_data(hObject, ~) handles = guidata(hObject); % 检查是否有解调数据 if ~isfield(handles.data, 'demod_bits') errordlg('请先进行解调!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行解交织...'); drawnow; % 获取参数 depth = str2double(get(handles.intlvr_depth, 'String')); % 交织深度 rx_bits = handles.data.demod_bits; % 解交织 rows = depth; cols = ceil(length(rx_bits)/rows); padded_rx = [rx_bits; zeros(rows*cols - length(rx_bits), 1)]; deinterleaved = reshape(reshape(padded_rx, cols, rows)', [], 1); % 存储数据 handles.data.deinterleaved = deinterleaved; % 更新状态 set(handles.status_text, 'String', '解交织已完成'); guidata(hObject, handles); end %% 解码回调函数 function decode_data(hObject, ~) handles = guidata(hObject); % 检查是否有解交织数据 if ~isfield(handles.data, 'deinterleaved') errordlg('请先进行解交织!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行解码...'); drawnow; % 获取参数 coding_idx = get(handles.coding_type, 'Value'); % 编码类型索引 deinterleaved = handles.data.deinterleaved; % 解码 if coding_idx == 1 % 卷积编码 trellis = poly2trellis(7, [171 133]); decoded = vitdec(deinterleaved, trellis, 7, 'trunc', 'hard'); else % 无编码 decoded = deinterleaved; end % 存储数据 handles.data.decoded = decoded; % 更新状态 set(handles.status_text, 'String', '解码已完成'); guidata(hObject, handles); end %% 误码率分析回调函数 function ber_analysis(hObject, ~) handles = guidata(hObject); % 检查是否有原始数据和解码数据 if ~isfield(handles.data, 'original') || ~isfield(handles.data, 'decoded') errordlg('请先完成所有步骤!', '步骤错误'); return; end % 更新状态 set(handles.status_text, 'String', '正在进行误码率分析...'); drawnow; % 获取数据 original = handles.data.original; decoded = handles.data.decoded; % 确保长度匹配 len = min(length(original), length(decoded)); original = original(1:len); decoded = decoded(1:len); % 计算误码率 errors = sum(original ~= decoded); ber = errors / len; % 显示结果 axes(handles.axes(9)); bar(categorical({'BER'}), ber); ylim([0 0.5]); title(['误码率: ' num2str(ber*100) '% (' num2str(errors) '/' num2str(len) '错误)']); ylabel('误码率'); % 显示原始和解码数据对比 figure('Name', '原始与解码数据对比', 'Position', [200 200 800 400]); subplot(2,1,1); stem(original(1:min(50, len)), 'filled'); title('原始数据'); subplot(2,1,2); stem(decoded(1:min(50, len)), 'filled'); title('解码数据'); % 更新状态 set(handles.status_text, 'String', sprintf('误码率分析完成: BER = %.4f', ber)); guidata(hObject, handles); end 把这段代码修改为低一点误码率的正确完整的代码
06-22
% 飞机纵向飞行控制系统设计与仿真(波音737-800配置) clear; close all; clc; %% ======================== 1. 参数配置 ======================== params = struct(... 'aircraft', struct(... 'M_alpha', -1.2, 'M_q', -1.5, 'M_delta_e', -0.5, ... 'Z_alpha', -4.0, 'Z_q', -0.3, 'Z_delta_e', -0.2, ... 'Z_delta_th', 0.5, ... % 油门控制参数 'Xu', -0.05, 'Zu', -0.1, 'Xw', 0.1, 'Zw', -0.5), ... % 新增气动参数 'flight', struct(... 'V', 100, 'g', 9.81, 't_sim', linspace(0, 60, 600), ... 'rho', 1.225, 'S', 124.6, 'c', 4.17), ... % 新增空气动力学参数 'control', struct(... 'ref_pitch', 5, 'ref_speed', 100, ... 'Kp_pitch', 0.8, 'Ki_pitch', 0.15, 'Kd_pitch', 0.3, ... % 优化PID参数 'Kp_speed', 0.4, 'Ki_speed', 0.08, 'Kd_speed', 0.15, ... 'tau', 0.1), ... % 滤波器时间常数 'matlab_version', version('-release')); %% ======================== 2. 构建状态空间模型 ======================== V_flight = params.flight.V; g = params.flight.g; M_alpha = params.aircraft.M_alpha; M_q = params.aircraft.M_q; M_delta_e = params.aircraft.M_delta_e; Z_alpha = params.aircraft.Z_alpha; Z_q = params.aircraft.Z_q; Z_delta_e = params.aircraft.Z_delta_e; Z_delta_th = params.aircraft.Z_delta_th; Xu = params.aircraft.Xu; Zu = params.aircraft.Zu; Xw = params.aircraft.Xw; Zw = params.aircraft.Zw; % 状态变量: [u, w, q, theta]^T % u: 前进速度变化, w: 法向速度变化, q: 俯仰角速率, theta: 俯仰角 A = [Xu, Xw, 0, -g*cosd(0); Zu, Zw, V_flight, -g*sind(0); 0, 0, M_q, 0; 0, 0, 1, 0]; B = [0, Z_delta_th; Z_delta_e, 0; M_delta_e, 0; 0, 0]; C = [0 0 0 1; % 俯仰角输出 1 0 0 0]; % 速度输出 D = zeros(2,2); sys = ss(A, B, C, D); %% ======================== 3. PID控制器设计 ======================== fprintf('\nPID控制器参数:\n'); fprintf('俯仰角控制: Kp=%.2f, Ki=%.2f, Kd=%.2f\n', ... params.control.Kp_pitch, params.control.Ki_pitch, params.control.Kd_pitch); fprintf('速度控制: Kp=%.2f, Ki=%.2f, Kd=%.2f\n', ... params.control.Kp_speed, params.control.Ki_speed, params.control.Kd_speed); %% ======================== 4. 仿真与轨迹计算 ======================== t = params.flight.t_sim; dt = t(2) - t(1); % 初始化状态 x = [0; 0; 0; 0]; % [u; w; q; theta] x_closed = zeros(length(t), 4); u = zeros(length(t), 2); % [升降舵偏角; 油门] % PID控制器变量 integral_pitch = 0; integral_speed = 0; prev_error_pitch = 0; prev_error_speed = 0; prev_derivative_pitch = 0; prev_derivative_speed = 0; % 低通滤波器参数 alpha = dt / (params.control.tau + dt); for i = 1:length(t) % 当前输出 y = C * x; pitch = y(1); speed = V_flight + y(2); % 绝对速度 = 初始速度 + 速度变化 % 参考信号 ref_pitch = params.control.ref_pitch; ref_speed = params.control.ref_speed; % 误差计算 error_pitch = ref_pitch - pitch; error_speed = ref_speed - speed; % PID控制器计算(带滤波和抗饱和) % 俯仰角控制(使用升降舵) integral_pitch = integral_pitch + error_pitch * dt; derivative_pitch = (error_pitch - prev_error_pitch) / dt; % 应用低通滤波器 filtered_derivative_pitch = alpha * derivative_pitch + (1-alpha) * prev_derivative_pitch; delta_e = params.control.Kp_pitch * error_pitch + ... params.control.Ki_pitch * integral_pitch + ... params.control.Kd_pitch * filtered_derivative_pitch; % 速度控制(使用油门) integral_speed = integral_speed + error_speed * dt; derivative_speed = (error_speed - prev_error_speed) / dt; % 应用低通滤波器 filtered_derivative_speed = alpha * derivative_speed + (1-alpha) * prev_derivative_speed; delta_th = params.control.Kp_speed * error_speed + ... params.control.Ki_speed * integral_speed + ... params.control.Kd_speed * filtered_derivative_speed; % 更新误差和微分历史 prev_error_pitch = error_pitch; prev_error_speed = prev_error_speed; prev_derivative_pitch = filtered_derivative_pitch; prev_derivative_speed = filtered_derivative_speed; % 输入限幅和抗饱和处理 delta_e = max(min(delta_e, 25), -25); % 升降舵偏角限制在±25度 delta_th = max(min(delta_th, 1), 0); % 油门限制在0-1 % 积分抗饱和 if delta_e >= 25 || delta_e <= -25 integral_pitch = integral_pitch - error_pitch * dt; end if delta_th >= 1 || delta_th <= 0 integral_speed = integral_speed - error_speed * dt; end % 状态更新(使用ode45提高精度) [~, x_temp] = ode45(@(t,x) A*x + B*[delta_e; delta_th], [0 dt], x); x = x_temp(end, :)'; % 保存结果 x_closed(i, :) = x'; u(i, :) = [delta_e, delta_th]; end % 轨迹积分计算 gamma = atan2d(x_closed(:,2), V_flight + x_closed(:,1)); % 航迹角 V_total = sqrt((V_flight + x_closed(:,1)).^2 + x_closed(:,2).^2); % 总速度 % 位置计算(仅用于性能分析,不显示) x_pos = cumtrapz(t, V_total .* cosd(gamma)); z_pos = cumtrapz(t, V_total .* sind(gamma)); y_pos = zeros(size(t)); % 无侧向运动 %% ======================== 5. 创建主控制窗口 ======================== control_fig = figure('Position', [100, 100, 1000, 800], 'Name', '飞行控制面板'); set(control_fig, 'Color', [0.95 0.95 0.95]); % --- 创建滑块控件 --- uicontrol('Style', 'text', 'Position', [820 750 150 20],... 'String', '俯仰角参考值 ()', 'BackgroundColor', [0.95 0.95 0.95]); slider_pitch = uicontrol('Style', 'slider', 'Position', [820 720 150 20],... 'Min', -10, 'Max', 20, 'Value', params.control.ref_pitch,... 'Callback', @update_reference); uicontrol('Style', 'text', 'Position', [820 680 150 20],... 'String', '速度参考值 (m/s)', 'BackgroundColor', [0.95 0.95 0.95]); slider_speed = uicontrol('Style', 'slider', 'Position', [820 650 150 20],... 'Min', 80, 'Max', 150, 'Value', params.control.ref_speed,... 'Callback', @update_reference); % PID参数调整滑块 uicontrol('Style', 'text', 'Position', [820 550 150 20],... 'String', '俯仰角Kp', 'BackgroundColor', [0.95 0.95 0.95]); slider_Kp_pitch = uicontrol('Style', 'slider', 'Position', [820 520 150 20],... 'Min', 0, 'Max', 2, 'Value', params.control.Kp_pitch,... 'Callback', @update_pid); uicontrol('Style', 'text', 'Position', [820 480 150 20],... 'String', '速度Kp', 'BackgroundColor', [0.95 0.95 0.95]); slider_Kp_speed = uicontrol('Style', 'slider', 'Position', [820 450 150 20],... 'Min', 0, 'Max', 2, 'Value', params.control.Kp_speed,... 'Callback', @update_pid); % --- 使用3x2网格布局 --- % 高度变化 ax_height = subplot(3,2,1); grid on; hold on; title('高度变化'); xlabel('时间 (s)'); ylabel('高度 (m)'); h_height = plot(t, z_pos, 'r-', 'LineWidth', 1.5); % 俯仰角变化 ax_pitch = subplot(3,2,2); grid on; hold on; title('俯仰角变化'); xlabel('时间 (s)'); ylabel('俯仰角 ()'); h_pitch = plot(t, x_closed(:,4), 'g-', 'LineWidth', 1.5); % 第4个状态为俯仰角 h_ref_pitch = plot([t(1) t(end)], [params.control.ref_pitch params.control.ref_pitch], 'g--'); legend('实际值', '参考值'); % 攻角变化 ax_alpha = subplot(3,2,3); grid on; hold on; title('攻角变化'); xlabel('时间 (s)'); ylabel('攻角 ()'); h_alpha = plot(t, atan2d(x_closed(:,2), V_flight + x_closed(:,1)), 'm-', 'LineWidth', 1.5); % 速度变化 ax_speed = subplot(3,2,4); grid on; hold on; title('速度变化'); xlabel('时间 (s)'); ylabel('速度 (m/s)'); h_speed = plot(t, V_total, 'b-', 'LineWidth', 1.5); h_ref_speed = plot([t(1) t(end)], [params.control.ref_speed params.control.ref_speed], 'b--'); legend('实际值', '参考值'); % 控制输入 ax_elevator = subplot(3,2,5); grid on; hold on; title('控制输入'); xlabel('时间 (s)'); ylabel('值'); h_elevator = plot(t, u(:,1), 'k-', 'LineWidth', 1.5, 'DisplayName', '升降舵'); h_throttle = plot(t, u(:,2), 'c-', 'LineWidth', 1.5, 'DisplayName', '油门'); legend('Location', 'best'); % 创建导出Simulink PID模型按钮 uicontrol('Style', 'pushbutton', 'Position', [820 350 150 30],... 'String', '导出Simulink PID模型', 'Callback', @export_simulink_pid); % 创建导出完整Simulink模型按钮 uicontrol('Style', 'pushbutton', 'Position', [820 300 150 30],... 'String', '导出完整Simulink模型', 'Callback', @export_complete_model); % 保存图形句柄 handles = struct(... 'control_fig', control_fig, ... 'h_height', h_height, 'h_pitch', h_pitch, 'h_alpha', h_alpha, ... 'h_speed', h_speed, 'h_elevator', h_elevator, 'h_throttle', h_throttle, ... 'h_ref_pitch', h_ref_pitch, 'h_ref_speed', h_ref_speed, ... 'x_pos', x_pos, 'y_pos', y_pos, 'z_pos', z_pos, 't', t, ... 'x_closed', x_closed, 'u', u, ... 'params', params, ... 'A', A, 'B', B, 'C', C, 'D', D, ... % 保存系统矩阵 'slider_pitch', slider_pitch, 'slider_speed', slider_speed, ... 'slider_Kp_pitch', slider_Kp_pitch, 'slider_Kp_speed', slider_Kp_speed ... ); set(control_fig, 'UserData', handles); % 设置关闭函数 set(control_fig, 'CloseRequestFcn', @(src,~) close_simulation(src)); %% ======================== 6. 回调函数 ======================== function update_reference(src, ~) fig_handle = ancestor(src, 'figure'); handles = get(fig_handle, 'UserData'); % 获取新参考值 ref_pitch = get(handles.slider_pitch, 'Value'); ref_speed = get(handles.slider_speed, 'Value'); % 更新参考信号 t = handles.t; dt = t(2) - t(1); % 初始化状态 x = [0; 0; 0; 0]; % 初始化PID控制器变量 integral_pitch = 0; integral_speed = 0; prev_error_pitch = 0; prev_error_speed = 0; % 预分配存储 x_closed = zeros(length(t), 4); u = zeros(length(t), 2); for i = 1:length(t) % 当前输出 y = handles.C * x; pitch = y(1); speed = handles.params.flight.V + y(2); % 误差计算 error_pitch = ref_pitch - pitch; error_speed = ref_speed - speed; % PID控制器计算 integral_pitch = integral_pitch + error_pitch * dt; derivative_pitch = (error_pitch - prev_error_pitch) / dt; delta_e = handles.params.control.Kp_pitch * error_pitch + ... handles.params.control.Ki_pitch * integral_pitch + ... handles.params.control.Kd_pitch * derivative_pitch; integral_speed = integral_speed + error_speed * dt; derivative_speed = (error_speed - prev_error_speed) / dt; delta_th = handles.params.control.Kp_speed * error_speed + ... handles.params.control.Ki_speed * integral_speed + ... handles.params.control.Kd_speed * derivative_speed; % 更新误差 prev_error_pitch = error_pitch; prev_error_speed = error_speed; % 输入限幅 delta_e = max(min(delta_e, 25), -25); delta_th = max(min(delta_th, 1), 0); % 状态更新 dx = handles.A * x + handles.B * [delta_e; delta_th]; x = x + dx * dt; % 保存结果 x_closed(i, :) = x'; u(i, :) = [delta_e, delta_th]; end % 重新计算轨迹 gamma = atan2d(x_closed(:,2), handles.params.flight.V + x_closed(:,1)); V_total = sqrt((handles.params.flight.V + x_closed(:,1)).^2 + x_closed(:,2).^2); z_pos = cumtrapz(t, V_total .* sind(gamma)); x_pos = cumtrapz(t, V_total .* cosd(gamma)); % 更新图形数据 set(handles.h_height, 'YData', z_pos); set(handles.h_pitch, 'YData', x_closed(:,4)); set(handles.h_ref_pitch, 'YData', [ref_pitch, ref_pitch]); set(handles.h_alpha, 'YData', atan2d(x_closed(:,2), handles.params.flight.V + x_closed(:,1))); set(handles.h_speed, 'YData', V_total); set(handles.h_ref_speed, 'YData', [ref_speed, ref_speed]); set(handles.h_elevator, 'YData', u(:,1)); set(handles.h_throttle, 'YData', u(:,2)); % 更新存储数据 handles.x_closed = x_closed; handles.u = u; handles.x_pos = x_pos; handles.z_pos = z_pos; set(fig_handle, 'UserData', handles); fprintf('参考值更新: 俯仰角=%.1f°, 速度=%.1fm/s\n', ref_pitch, ref_speed); end function update_pid(src, ~) fig_handle = ancestor(src, 'figure'); handles = get(fig_handle, 'UserData'); % 获取新PID参数 Kp_pitch = get(handles.slider_Kp_pitch, 'Value'); Kp_speed = get(handles.slider_Kp_speed, 'Value'); % 更新PID参数 handles.params.control.Kp_pitch = Kp_pitch; handles.params.control.Kp_speed = Kp_speed; % 重新运行仿真 update_reference(handles.slider_pitch, []); fprintf('PID参数更新: Kp_pitch=%.2f, Kp_speed=%.2f\n', Kp_pitch, Kp_speed); end function export_simulink_pid(src, ~) fig_handle = ancestor(src, 'figure'); handles = get(fig_handle, 'UserData'); % 创建新的Simulink模型 model_name = 'Boeing737_PID_Controller'; new_system(model_name); open_system(model_name); % 添加PID控制器模块 - 使用正确的模块名称 pitch_pid_pos = [100, 100, 200, 150]; speed_pid_pos = [100, 200, 200, 250]; % 俯仰角PID控制器 pitch_pid = add_block('simulink/Continuous/PID Controller', [model_name '/Pitch_PID'], ... 'Position', pitch_pid_pos, ... 'P', num2str(handles.params.control.Kp_pitch), ... 'I', num2str(handles.params.control.Ki_pitch), ... 'D', num2str(handles.params.control.Kd_pitch), ... 'Name', '俯仰角PID'); % 速度PID控制器 speed_pid = add_block('simulink/Continuous/PID Controller', [model_name '/Speed_PID'], ... 'Position', speed_pid_pos, ... 'P', num2str(handles.params.control.Kp_speed), ... 'I', num2str(handles.params.control.Ki_speed), ... 'D', num2str(handles.params.control.Kd_speed), ... 'Name', '速度PID'); % 添加输入端口 pitch_ref = add_block('simulink/Sources/In1', [model_name '/Pitch_Ref'], ... 'Position', [50, 100, 80, 120], 'Name', '俯仰角参考'); speed_ref = add_block('simulink/Sources/In1', [model_name '/Speed_Ref'], ... 'Position', [50, 200, 80, 220], 'Name', '速度参考'); actual_pitch = add_block('simulink/Sources/In1', [model_name '/Actual_Pitch'], ... 'Position', [50, 150, 80, 170], 'Name', '实际俯仰角'); actual_speed = add_block('simulink/Sources/In1', [model_name '/Actual_Speed'], ... 'Position', [50, 250, 80, 270], 'Name', '实际速度'); % 添加输出端口 elevator_cmd = add_block('simulink/Sinks/Out1', [model_name '/Elevator_Cmd'], ... 'Position', [300, 100, 330, 120], 'Name', '升降舵指令'); throttle_cmd = add_block('simulink/Sinks/Out1', [model_name '/Throttle_Cmd'], ... 'Position', [300, 200, 330, 220], 'Name', '油门指令'); % 添加求和模块 sum_pitch = add_block('simulink/Math Operations/Sum', [model_name '/Sum_Pitch'], ... 'Position', [150, 100, 180, 130], 'IconShape', 'rectangular', ... 'Inputs', '|+-'); sum_speed = add_block('simulink/Math Operations/Sum', [model_name '/Sum_Speed'], ... 'Position', [150, 200, 180, 230], 'IconShape', 'rectangular', ... 'Inputs', '|+-'); % 连接模块 - 使用句柄而不是字符串名称 add_line(model_name, [getfullname(pitch_ref) '/1'], [getfullname(sum_pitch) '/1']); add_line(model_name, [getfullname(actual_pitch) '/1'], [getfullname(sum_pitch) '/2']); add_line(model_name, [getfullname(sum_pitch) '/1'], [getfullname(pitch_pid) '/1']); add_line(model_name, [getfullname(pitch_pid) '/1'], [getfullname(elevator_cmd) '/1']); add_line(model_name, [getfullname(speed_ref) '/1'], [getfullname(sum_speed) '/1']); add_line(model_name, [getfullname(actual_speed) '/1'], [getfullname(sum_speed) '/2']); add_line(model_name, [getfullname(sum_speed) '/1'], [getfullname(speed_pid) '/1']); add_line(model_name, [getfullname(speed_pid) '/1'], [getfullname(throttle_cmd) '/1']); % 保存并整理模型 save_system(model_name); set_param(model_name, 'ZoomFactor', 'FitSystem'); fprintf('Simulink PID模型已导出: %s.slx\n', model_name); fprintf('PID参数:\n'); fprintf(' 俯仰角控制: Kp=%.2f, Ki=%.2f, Kd=%.2f\n', ... handles.params.control.Kp_pitch, ... handles.params.control.Ki_pitch, ... handles.params.control.Kd_pitch); fprintf(' 速度控制: Kp=%.2f, Ki=%.2f, Kd=%.2f\n', ... handles.params.control.Kp_speed, ... handles.params.control.Ki_speed, ... handles.params.control.Kd_speed); end function export_complete_model(src, ~) fig_handle = ancestor(src, 'figure'); handles = get(fig_handle, 'UserData'); export_complete_simulink_model(handles); end function export_complete_simulink_model(handles) % 创建新的Simulink模型 model_name = 'Boeing737_Longitudinal_Control'; new_system(model_name); open_system(model_name); % 设置模型参数 set_param(model_name, 'Solver', 'ode4', 'FixedStep', '0.01', 'StopTime', '60'); %% 添加飞机模型(状态空间) aircraft_pos = [400, 100, 550, 250]; % 正确设置状态空间模型参数 aircraft_model = add_block('simulink/Continuous/State-Space', [model_name '/Aircraft_Model'], ... 'Position', aircraft_pos, ... 'A', mat2str(handles.A), ... 'B', mat2str(handles.B), ... 'C', mat2str(handles.C), ... 'D', mat2str(handles.D), ... 'X0', '[0;0;0;0]', ... 'Name', '飞机纵向模型'); %% 添加PID控制器 - 使用正确的模块名称 % 俯仰角PID pitch_pid_pos = [200, 100, 300, 150]; pitch_pid = add_block('simulink/Continuous/PID Controller', [model_name '/Pitch_PID'], ... 'Position', pitch_pid_pos, ... 'P', num2str(handles.params.control.Kp_pitch), ... 'I', num2str(handles.params.control.Ki_pitch), ... 'D', num2str(handles.params.control.Kd_pitch), ... 'N', num2str(1/handles.params.control.tau), ... 'Name', '俯仰角PID'); % 速度PID speed_pid_pos = [200, 200, 300, 250]; speed_pid = add_block('simulink/Continuous/PID Controller', [model_name '/Speed_PID'], ... 'Position', speed_pid_pos, ... 'P', num2str(handles.params.control.Kp_speed), ... 'I', num2str(handles.params.control.Ki_speed), ... 'D', num2str(handles.params.control.Kd_speed), ... 'N', num2str(1/handles.params.control.tau), ... 'Name', '速度PID'); %% 添加输入输出 % 参考信号 pitch_ref = add_block('simulink/Sources/Constant', [model_name '/Pitch_Ref'], ... 'Position', [50, 120, 80, 140], 'Value', num2str(handles.params.control.ref_pitch)); speed_ref = add_block('simulink/Sources/Constant', [model_name '/Speed_Ref'], ... 'Position', [50, 220, 80, 240], 'Value', num2str(handles.params.control.ref_speed)); % 输出显示 pitch_out = add_block('simulink/Sinks/Out1', [model_name '/Pitch_Out'], ... 'Position', [700, 120, 730, 140], 'Name', '俯仰角'); speed_out = add_block('simulink/Sinks/Out1', [model_name '/Speed_Out'], ... 'Position', [700, 170, 730, 190], 'Name', '速度'); elevator_out = add_block('simulink/Sinks/Out1', [model_name '/Elevator_Out'], ... 'Position', [700, 220, 730, 240], 'Name', '升降舵'); throttle_out = add_block('simulink/Sinks/Out1', [model_name '/Throttle_Out'], ... 'Position', [700, 270, 730, 290], 'Name', '油门'); % 添加示波器 scope = add_block('simulink/Sinks/Scope', [model_name '/Response_Scope'], ... 'Position', [600, 300, 650, 350]); %% 添加求和模块 sum_pitch = add_block('simulink/Math Operations/Sum', [model_name '/Sum_Pitch'], ... 'Position', [150, 120, 180, 140], 'IconShape', 'rectangular', 'Inputs', '|+-'); sum_speed = add_block('simulink/Math Operations/Sum', [model_name '/Sum_Speed'], ... 'Position', [150, 220, 180, 240], 'IconShape', 'rectangular', 'Inputs', '|+-'); %% 添加速度转换模块 speed_add = add_block('simulink/Math Operations/Add', [model_name '/Speed_Add'], ... 'Position', [500, 170, 530, 190], 'Name', '速度转换'); v0 = add_block('simulink/Sources/Constant', [model_name '/V0'], ... 'Position', [450, 160, 480, 180], 'Value', num2str(handles.params.flight.V)); %% 连接所有模块 - 使用getfullname获取完整路径 % 参考信号连接 add_line(model_name, [getfullname(pitch_ref) '/1'], [getfullname(sum_pitch) '/1']); add_line(model_name, [getfullname(speed_ref) '/1'], [getfullname(sum_speed) '/1']); % 反馈连接 add_line(model_name, [getfullname(aircraft_model) '/1'], [getfullname(sum_pitch) '/2']); add_line(model_name, [getfullname(aircraft_model) '/2'], [getfullname(speed_add) '/2']); % PID连接 add_line(model_name, [getfullname(sum_pitch) '/1'], [getfullname(pitch_pid) '/1']); add_line(model_name, [getfullname(sum_speed) '/1'], [getfullname(speed_pid) '/1']); % 控制输入连接 add_line(model_name, [getfullname(pitch_pid) '/1'], [getfullname(aircraft_model) '/1']); add_line(model_name, [getfullname(speed_pid) '/1'], [getfullname(aircraft_model) '/2']); % 输出连接 add_line(model_name, [getfullname(aircraft_model) '/1'], [getfullname(pitch_out) '/1']); add_line(model_name, [getfullname(speed_add) '/1'], [getfullname(speed_out) '/1']); add_line(model_name, [getfullname(pitch_pid) '/1'], [getfullname(elevator_out) '/1']); add_line(model_name, [getfullname(speed_pid) '/1'], [getfullname(throttle_out) '/1']); % 速度转换连接 add_line(model_name, [getfullname(v0) '/1'], [getfullname(speed_add) '/1']); % 示波器连接 add_line(model_name, [getfullname(pitch_ref) '/1'], [getfullname(scope) '/1']); add_line(model_name, [getfullname(speed_ref) '/1'], [getfullname(scope) '/2']); add_line(model_name, [getfullname(aircraft_model) '/1'], [getfullname(scope) '/3']); add_line(model_name, [getfullname(speed_add) '/1'], [getfullname(scope) '/4']); %% 添加模型注释 annotation_text = sprintf(['飞机纵向飞行控制系统\n', ... '机型: 波音737-800\n', ... '设计日期: %s\n', ... '状态变量: [u, w, q, θ]\n', ... '输入: [升降舵, 油门]'], datestr(now)); add_block('simulink/Note', [model_name '/Model_Info'], ... 'Position', [50, 300, 200, 350], 'Text', annotation_text); % 保存并整理模型 save_system(model_name); set_param(model_name, 'ZoomFactor', 'FitSystem'); fprintf('完整Simulink模型已导出: %s.slx\n', model_name); fprintf('包含飞机模型和PID控制器\n'); end function close_simulation(src, ~) % 获取图形句柄 fig_handle = ancestor(src, 'figure'); handles = get(fig_handle, 'UserData'); % 关闭所有相关图形 if ishandle(handles.control_fig) delete(handles.control_fig); end fprintf('仿真已关闭\n'); end 在matlabR2024a中出现 错误使用 LiveEditorEvaluationHelperE1200655152>export_simulink_pid (第 413 行) Simulink 对象名称无效: 'Boeing737_PID_Controller/俯仰角参考/1'。 计算 UIControl Callback 时出错。 错误使用 LiveEditorEvaluationHelperE1200655152>export_complete_simulink_model (第 523 行) Simulink 对象名称无效: 'Boeing737_Longitudinal_Control/Pitch_Ref/1'。 出错 LiveEditorEvaluationHelperE1200655152>export_complete_model (第 442) export_complete_simulink_model(handles); - 显示完整堆栈跟踪 计算 UIControl Callback 时出错。 >> 如何修改 完整呈现代码
06-16
% --- Executes just before GUI_pingtai is made visible.初始化函数 function GUI_pingtai_OpeningFcn(hObject, eventdata, handles, varargin) % This function has no output args, see OutputFcn. % hObject handle to figure 当前控件的句柄 % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % 是一个以GUI中所有控件的Tag属性为字段的结构体,每个字段的取值就是对应控件的句柄. %类似于C语言中指针,它是某个对象的唯一标识符,通过句柄就可以找到你需要的对象 % varargin command line arguments to GUI_pingtai (see VARARGIN) % Choose default command line output for GUI_pingtai handles.output = hObject; set(handles.save,'Enable','off'); set(handles.exit,'Enable','off'); set(handles.reset,'Enable','off'); set(handles.g1,'Visible','off'); set(handles.g2,'Visible','off'); set(handles.slider4,'Enable','off'); set(handles.slider5,'Enable','off'); set(handles.m1,'Enable','off'); set(handles.m2,'Enable','off'); set(handles.m3,'Enable','off'); set(handles.m4,'Enable','off'); set(handles.m5,'Enable','off'); set(handles.m6,'Enable','off'); set(handles.m7,'Enable','off'); set(handles.m8,'Enable','off'); set(handles.m9,'Enable','off'); set(handles.p2,'Enable','off'); set(handles.p3,'Enable','off'); set(handles.p4,'Enable','off'); % Update handles structure guidata(hObject, handles); % UIWAIT makes GUI_pingtai wait for user response (see UIRESUME) % uiwait(handles.figure1);这个代码意思
06-12
1. 【编写思路】:响应用户输入,简要阐述修改代码的思路。 2. 【编写步骤及编写代码】: a. 列明编写步骤,阐述用户可操作的编写方法。 b. 直接在原代码片段上进行编辑后输出,代码要满足需求、可正确运行。 c. 并在注释中标明改动点。 3. 【代码调用示例】:给出正确调用和运行代码的输入输出示例,跟代码片段一起输出,并且增加注释说明,必要时给出代码运行的依赖和运行方式。 a. 简单代码给出输入输出示例。 b. 复杂代码不给输出示例。 4. 【代码解释】:概括解释给出的代码和调用示例,重点对修改点进行解释说明,要求语言通顺易懂,详略得当,格式清晰。 5. 【注意事项和其他补充说明】:如其他修改思路等。你回答得结构必须按找这样输出,function varargout = QIKONGMIANJI(varargin) gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... 'gui_Singleton', gui_Singleton, ... 'gui_OpeningFcn', @QIKONGMIANJI_OpeningFcn, ... 'gui_OutputFcn', @QIKONGMIANJI_OutputFcn, ... 'gui_LayoutFcn', [] , ... 'gui_Callback', []); if nargin && ischar(varargin{1}) gui_State.gui_Callback = str2func(varargin{1}); end if nargout [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); else gui_mainfcn(gui_State, varargin{:}); end function QIKONGMIANJI_OpeningFcn(hObject, eventdata, handles, varargin) handles.output = hObject; % 初始化轮廓线句柄 handles.contourLine = []; handles.contourPoints = []; % 存储描点数据 guidata(hObject, handles); function varargout = QIKONGMIANJI_OutputFcn(hObject, eventdata, handles) varargout{1} = handles.output; % --- 灰度化按钮 function pushbutton3_Callback(hObject, eventdata, handles) if ~isfield(handles, 'originalImage') errordlg('请先加载图像!', '错误'); return; end originalImage = handles.originalImage; if size(originalImage, 3) == 3 grayImage = rgb2gray(originalImage); else grayImage = originalImage; end axes(handles.axes2); imshow(grayImage, []); title('灰度化后的图像'); set(gca, 'XTick', [], 'YTick', []); handles.grayImage = grayImage; guidata(hObject, handles); % --- 二值化按钮 function pushbutton4_Callback(hObject, eventdata, handles) if ~isfield(handles, 'grayImage') errordlg('请先进行图像灰度化处理!', '错误'); return; end grayImage = handles.grayImage; binaryImage = imbinarize(grayImage); axes(handles.axes3); imshow(binaryImage); title('二值化后的图像'); set(gca, 'XTick', [], 'YTick', []); handles.binaryImage = binaryImage; guidata(hObject, handles); function edit1_Callback(hObject, eventdata, handles) function edit1_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- 描点提取轮廓按钮 (只描点不计算) function pushbutton5_Callback(hObject, eventdata, handles) % 检查必要数据是否存在 if ~isfield(handles, 'binaryImage') || ~isfield(handles, 'scale') errordlg('请先完成图像二值化和比例尺标定!', '错误'); return; end % 准备图像显示 axes(handles.axes3); imshow(handles.binaryImage); title('点击提取轮廓 - 按Enter结束'); % 删除旧的轮廓线 if isfield(handles, 'contourLine') % 检查contourLine是否是有效的图形句柄 if all(ishandle(handles.contourLine)) delete(handles.contourLine); end handles = rmfield(handles, 'contourLine'); end % 用户交互描点 try [x, y] = ginput(); catch return; % 用户取消操作 end % 检查是否有点被选取 if isempty(x) return; end % 保存描点数据 handles.contourPoints = [x, y]; % 存储描点坐标 % 绘制轮廓点(不闭合) hold on; handles.contourLine = plot(x, y, 'ro', 'MarkerSize', 5, 'MarkerFaceColor', 'r'); title('已描点 - 请点击"提取完成"按钮'); hold off; % 保存数据 guidata(hObject, handles); % --- 提取完成按钮 (计算面积并显示结果) function pushbutton6_Callback(hObject, eventdata, handles) % 检查是否有描点数据 if ~isfield(handles, 'contourPoints') || isempty(handles.contourPoints) errordlg('请先使用"描点提取轮廓"按钮进行描点!', '错误'); return; end % 获取描点数据 points = handles.contourPoints; x = points(:,1); y = points(:,2); % 闭合轮廓 x(end+1) = x(1); y(end+1) = y(1); % 在axes3中绘制完整轮廓 axes(handles.axes3); hold on; % 删除之前的点 if isfield(handles, 'contourLine') && all(ishandle(handles.contourLine)) delete(handles.contourLine); end % 绘制闭合轮廓 handles.contourLine = plot(x, y, 'r-', 'LineWidth', 2); hold off; title('提取的轮廓'); % 计算面积 mask = poly2mask(x, y, size(handles.binaryImage, 1), size(handles.binaryImage, 2)); pixelArea = sum(mask(:)); actualArea = pixelArea * (handles.scale)^2; % 显示结果 set(handles.edit1, 'String', sprintf('%.2f', actualArea)); msgbox(sprintf('气孔面积: %.2f μm&sup2;', actualArea), '计算结果'); % 在axes4中显示带有轮廓的图像 axes(handles.axes4); % 显示原始二值图像 imshow(handles.binaryImage); hold on; % 绘制轮廓 plot(x, y, 'r-', 'LineWidth', 2); % 标记轮廓点 plot(points(:,1), points(:,2), 'ro', 'MarkerSize', 5, 'MarkerFaceColor', 'r'); hold off; title('气孔轮廓提取结果'); set(gca, 'XTick', [], 'YTick', []); % 保存数据 handles.actualArea = actualArea; guidata(hObject, handles); % --- 清除轮廓按钮 function pushbutton7_Callback(hObject, eventdata, handles) % 清除轮廓线 if isfield(handles, 'contourLine') % 检查contourLine是否是有效的图形句柄 if all(ishandle(handles.contourLine)) delete(handles.contourLine); end handles = rmfield(handles, 'contourLine'); end % 清除轮廓数据 if isfield(handles, 'contourPoints') handles = rmfield(handles, 'contourPoints'); end if isfield(handles, 'actualArea') handles = rmfield(handles, 'actualArea'); end % 重置显示 if isfield(handles, 'binaryImage') axes(handles.axes3); imshow(handles.binaryImage); title('二值化后的图像'); axes(handles.axes4); cla; % 清除axes4的内容 set(gca, 'XTick', [], 'YTick', []); end set(handles.edit1, 'String', ''); guidata(hObject, handles); % --- 加载图像按钮 function pushbutton1_Callback(hObject, eventdata, handles) [filename, pathname] = uigetfile(... {'*.jpg;*.jpeg;*.png;*.bmp;*.tif;*.tiff', 'Image Files (*.jpg, *.png, *.bmp, *.tif)'},... '选择涂层图像'); if isequal(filename, 0) return; end imgPath = fullfile(pathname, filename); handles.originalImage = imread(imgPath); axes(handles.axes1); imshow(handles.originalImage); title('原始涂层图像 - 请绘制比例尺线段'); hold off; msgbox({'请按以下步骤标定比例尺:', ... '1. 在图像上点击并拖动绘制一条线段', ... '2. 线段应代表已知的实际长度', ... '3. 绘制完成后输入该线段的实际长度(μm)'}, ... '比例尺标定说明', 'help'); hLine = imline(handles.axes1); hLine.setColor('r'); position = wait(hLine); dx = diff(position(:,1)); dy = diff(position(:,2)); pixelLength = sqrt(dx^2 + dy^2); prompt = {'输入该线段代表的实际长度 (μm):'}; dlgtitle = '比例尺标定'; dims = [1 35]; definput = {'100'}; answer = inputdlg(prompt, dlgtitle, dims, definput); if isempty(answer) delete(hLine); return; end try realLength = str2double(answer{1}); if isnan(realLength) || realLength <= 0 error('请输入正数作为实际长度'); end handles.scale = realLength / pixelLength; axes(handles.axes1); hold on; plot(position(:,1), position(:,2), 'r-', 'LineWidth', 2); text(mean(position(:,1)), mean(position(:,2)), ... sprintf('%.2f μm', realLength), ... 'Color', 'r', 'FontSize', 12, 'FontWeight', 'bold'); hold off; guidata(hObject, handles); msgbox(sprintf('比例尺标定成功! 比例: %.4f μm/像素', handles.scale), ... '标定完成', 'help'); catch ME delete(hLine); errordlg(['错误: ' ME.message], '标定失败'); end % --- 重新标定按钮 function pushbutton2_Callback(hObject, eventdata, handles) if ~isfield(handles, 'originalImage') errordlg('请先加载图像!', '错误'); return; end if ~strcmp(questdlg('确定要重新标定比例尺吗?', '重新标定', '是', '否', '否'), '是') return; end if isfield(handles, 'scale') handles = rmfield(handles, 'scale'); end cla(handles.axes1); imshow(handles.originalImage, 'Parent', handles.axes1); title(handles.axes1, '原始涂层图像 - 请绘制比例尺线段'); msgbox({'请按以下步骤标定比例尺:', ... '1. 在图像上点击并拖动绘制一条线段', ... '2. 线段应代表已知的实际长度', ... '3. 绘制完成后输入该线段的实际长度(μm)'}, ... '比例尺标定说明', 'help'); hLine = imline(handles.axes1); hLine.setColor('r'); position = wait(hLine); dx = diff(position(:,1)); dy = diff(position(:,2)); pixelLength = sqrt(dx^2 + dy^2); prompt = {'输入该线段代表的实际长度 (μm):'}; dlgtitle = '比例尺标定'; dims = [1 35]; definput = {'100'}; answer = inputdlg(prompt, dlgtitle, dims, definput); if isempty(answer) delete(hLine); return; end try realLength = str2double(answer{1}); if isnan(realLength) || realLength <= 0 error('请输入正数作为实际长度'); end handles.scale = realLength / pixelLength; axes(handles.axes1); hold on; plot(position(:,1), position(:,2), 'r-', 'LineWidth', 2); text(mean(position(:,1)), mean(position(:,2)), ... sprintf('%.2f μm', realLength), ... 'Color', 'r', 'FontSize', 12, 'FontWeight', 'bold'); hold off; guidata(hObject, handles); msgbox(sprintf('比例尺标定成功! 比例: %.4f μm/像素', handles.scale), ... '标定完成', 'help'); catch ME delete(hLine); errordlg(['错误: ' ME.message], '标定失败'); end % --- 其他未使用按钮的空回调 function pushbutton8_Callback(hObject, eventdata, handles) function pushbutton9_Callback(hObject, eventdata, handles) function pushbutton10_Callback(hObject, eventdata, handles) 上述代码确实实现了气孔面积的提取,但是有一些瑕疵,我想通过点击pushbutton5按钮只对axes3坐标系中的图片进行描点提取轮廓,然后边点击边显示已经描过的点,然后再点击pushbutton6按钮即可提取完成并同时将提取轮廓的图片显示在axes4中,而不是点击enter键提取完成,请帮我是实现这个功能,并提供完整的代码给我 这是我得问题
06-26
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值