Einleitung
MATLAB® ist eines der wichtigsten Werkzeuge für die numerische Berechnung, Datenanalyse und Simulation in den Ingenieur- und Naturwissenschaften. Neben der korrekten Umsetzung eines Algorithmus spielt dabei auch die Qualität des Programmcodes eine entscheidende Rolle. Gut strukturierter, effizienter und wartbarer Code erleichtert nicht nur die Fehlersuche und Weiterentwicklung, sondern verbessert häufig auch die Ausführungsgeschwindigkeit. In diesem Kurs lernen die Studierenden grundlegende und fortgeschrittene Programmiertechniken in MATLAB® kennen. Anhand praxisnaher Beispiele und Optimierungsaufgaben werden typische Programmierfehler identifiziert und bestehender Code schrittweise verbessert. Ziel ist es, moderne MATLAB®-Programme zu entwickeln, die übersichtlich, leistungsfähig und leicht wiederverwendbar sind.
Lernziele
Nach dem Kurs können die Teilnehmenden:
- effizienten und gut lesbaren MATLAB®-Code schreiben,
- Funktionen und Skripte sinnvoll einsetzen,
- Vektorisierung anstelle unnötiger Schleifen verwenden,
- Eingaben überprüfen,
- Code modular strukturieren,
- typische MATLAB®-Funktionen zur Datenverarbeitung anwenden,
- ihren Code dokumentieren und debuggen.
1. Programmierrichtlinien
- Analysieren Sie den nachfolgenden Quelltext unter Berücksichtigung der Programmierrichtlinien für MATLAB®.
- Optimieren Sie den Quelltext.
| Lesbarkeit.m
|
nLaenge = 5; % Verwenden Sie lesbare Variablennamen.
nBreite = 3;
nFlaeche = nLaenge * nBreite; % Rücken Sie Quelltext zur besseren Lesbarkeit ein.
fprintf('Fläche = %.2d\n', nFlaeche); % Verwenden Sie hilfreiche Kommentare.
|
2. Skripte und Funktionen
Ein Skript
- arbeitet im Workspace.
- ist leicht zu schreiben.
- ist schlecht wiederverwendbar.
radius = 3;
A = pi*radius^2;
Die Vorteile eine Funktion sind
- Wiederverwendbarkeit
- eigener Workspace
- einfach testbar
Wandeln Sie das Skript in die Funktion berechneKreis um und starten Sie diese mit der Funktion testBerechneKreis.
| berechneKreis.m
|
function [fA,fU] = berechneKreis(fRadius)
fA = pi*fRadius.^2;
fU = 2*pi*fRadius;
end
|
3. Vektorisierung
Der folgende Code berechnet mittels einer Schleife.
x=-10:0.1:10
for i=1:length(x)
y(i)=3*x(i)^2+2*x(i)-5;
end
- Ersetzen Sie die Schleife durch eine vektorisierte Berechnung.
- Messen Sie die Zeitersparnis mittels
tic und toc.
4. Logische Indizes
Der Quelltext sucht ineffizient alle Werte > 0.
x = randn(1000,1);
j=0;
for i=1:length(x)
if x>0
j=j+1;
xPos(j)=i;
end
end
- Optimieren Sie den Quelltext mit logischer Indizierung.
| LogischeIndizes.m
|
positive = x>0;
xPos = x(positive);
|
5. Vorallokation
Der Quelltext alloziert Speicher zur Laufzeit. Das ist ineffizient.
for i=1:10000
y(i)=i^2;
end
- Optimieren Sie den Quelltext mit Vorallokation.
- Messen Sie die Zeitersparnis mittels
tic und toc.
| Vorallokation.m
|
y=zeros(10000,1);
for i=1:10000
y(i)=i^2;
end
|
6. Eingaben prüfen
- Schreiben Sie eine Funktion
Wurzel, die die Wurzel berechnet.
- Prüfen Sie mit
arguments, dass der Übergabewert positiv ist.
| pruefeEingaben.m
|
function y = Wurzel(x)
arguments
x (1,1) double {mustBePositive}
end
y = sqrt(x);
end
|
= 7. Rauschen entfernen
x = x + 0.3*randn(size(x));
end
- Nutzen Sie die Funktionen
arguments und arguments, um das Rauschen zu entfernen.
- Prüfen Sie mit
arguments, dass der Übergabewert positiv ist.
| entferneRauschen.m
|
clear
clc
close all
%% Signal
fs = 1000;
t = 0:1/fs:2;
signal = sin(2*pi*20*t);
%% Rauschen
noiseSignal = signal + 0.3*randn(size(signal));
%% Moving Average
filtered = movmean(noiseSignal,15);
%% Darstellung
figure
subplot(3,1,1)
plot(t,signal)
title('Original')
grid on
subplot(3,1,2)
plot(t,noiseSignal)
title('Mit Rauschen')
grid on
subplot(3,1,3)
plot(t,filtered)
title('Gefiltert')
grid on
|
8. FTF eines Signals
Gegeben:
signal = randn(5000,1);
signal = signal + sin(2*pi*40*(0:4999)'/1000);
Erstellen Sie ein Programm mit folgenden Funktionen:
- main.m
- generateSignal()
- filterSignal()
- calculateFFT()
- plotResults()
| main.m
|
clear
clc
close all
fs = 1000;
signal = generateSignal(fs);
filtered = filterSignal(signal);
[f,A] = calculateFFT(filtered,fs);
plotResults(signal,filtered,f,A,fs);
|
| generateSignal.m
|
function signal = generateSignal(fs)
t = 0:1/fs:5;
signal = sin(2*pi*40*t) ...
+ 0.4*sin(2*pi*150*t);
signal = signal + 0.3*randn(size(signal));
end
|
| filterSignal.m
|
function y = filterSignal(x)
y = movmean(x,15);
end
|
| calculateFFT.m
|
function [f,A] = calculateFFT(signal,fs)
N = length(signal);
Y = fft(signal);
P2 = abs(Y)/N;
P1 = P2(1:N/2+1);
P1(2:end-1)=2*P1(2:end-1);
f = fs*(0:N/2)/N;
A = P1;
end
|
| plotResults.m
|
function plotResults(signal,filtered,f,A,fs)
t = (0:length(signal)-1)/fs;
figure
subplot(3,1,1)
plot(t,signal)
title('Originalsignal')
grid on
subplot(3,1,2)
plot(t,filtered)
title('Gefiltertes Signal')
grid on
subplot(3,1,3)
plot(f,A)
xlim([0 250])
title('FFT')
xlabel('Frequenz [Hz]')
ylabel('Amplitude')
grid on
|
Bestimmen Sie folgende Werte und stellen Sie diese dar
- Signalenergie berechnen
- Spitzenwert bestimmen
- Ereignisse oberhalb eines Schwellwerts markieren
- dominante Frequenz bestimmen
- Ergebnisse grafisch darstellen
| Signalparameter.m
|
%%Signalenergie
energy = sum(filtered.^2);
fprintf("Signalenergie = %.2f\n",energy)
%% Spitzenwert
peak = max(abs(filtered));
fprintf("Maximalwert = %.2f\n",peak)
%% Ereigniserkennung
threshold = 1.0;
eventIdx = abs(filtered) > threshold;
figure
plot(filtered)
hold on
plot(find(eventIdx),filtered(eventIdx),'ro')
grid on
title('Erkannte Ereignisse')
%% Dominante Frequenz bestimmen
[peakAmplitude,index] = max(A);
dominantFrequency = f(index);
fprintf("Dominante Frequenz = %.1f Hz\n",dominantFrequency)
|
9. Quelltext optimieren
Gegeben ist folgender Quelltext:
x = rand(1,100000);
for i = 1:length(x)
if x(i) < 0.5
y(i) = 0;
else
y(i) = sqrt(x(i));
end
end
- Optimieren Sie den Quelltext.
- Prüfen Sie mit
arguments, dass der Übergabewert positiv ist.
| Optimierung.m
|
x = rand(1,100000);
y = zeros(size(x));
idx = x >= 0.5;
y(idx) = sqrt(x(idx));
|
10. Anonyme Funktionen (@-Operator)
Gegeben ist folgender Quelltext:
clear
clc
x = 0:0.01:10;
for i = 1:length(x)
y(i) = sin(x(i)) + cos(x(i));
end
plot(x,y)
grid on
- Optimieren Sie den Code.
- Verwenden Sie den @-Operator.
- Definieren Sie eine anonyme Funktion.
- Der Code soll auch für andere Eingangsvektoren funktionieren.
| Optimierung.m
|
clear
clc
f = @(x) sin(x) + cos(x);
x = 0:0.01:10;
y = f(x);
plot(x,y)
grid on
|
11. Function Handle mehrfach verwenden
Gegeben ist folgender Quelltext:
x = linspace(0,2*pi,500);
y1 = sin(x);
y2 = sin(2*x);
y3 = sin(3*x);
plot(x,y1)
hold on
plot(x,y2)
plot(x,y3)
grid on
- Verwenden Sie einen Function Handle.
- Berechnen Sie beliebige Harmonische.
| FunctionHandle.m
|
wave = @(f,x) sin(f*x);
x = linspace(0,2*pi,500);
plot(x,wave(1,x))
hold on
plot(x,wave(2,x))
plot(x,wave(3,x))
grid on
|}
= 12. Mehrfaches Kopieren vermeiden =
Gegeben ist folgender Quelltext:
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">figure
subplot(2,2,1)
plot(x1,y1)
subplot(2,2,2)
plot(x2,y2)
subplot(2,2,3)
plot(x3,y3)
subplot(2,2,4)
plot(x4,y4)
- Optimieren Sie den Code mit einer Schleife.
| OptimierungMitSchleife.m
|
X = {x1,x2,x3,x4};
Y = {y1,y2,y3,y4};
figure
for k = 1:4
subplot(2,2,k)
plot(X{k},Y{k})
grid on
end
|}
= 13. Objektorientierung =
Gegeben ist folgender Quelltext:
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">radius = 5;
A = pi*radius^2;
U = 2*pi*radius;
fprintf("Fläche %.2f\n",A)
fprintf("Umfang %.2f\n",U)
Erstellen Sie eine Klasse
Circle
mit
| OptimierungMitSchleife.m
|
<syntaxhighlight lang="matlab" line style="border: none; background-color: #EFF1C1; font-size:small">classdef Circle
properties
Radius
end
methods
function obj = Circle(r)
obj.Radius = r;
end
function A = area(obj)
A = pi*obj.Radius^2;
end
function U = circumference(obj)
U = 2*pi*obj.Radius;
end
end
end
|
→ MATLAB® Befehlsübersicht
→ zurück zum Hauptartikel: BSE Modellierung und Simulation - SoSe26
|
|