Satu simulasi ode45 menghasilkan ratusan angka di workspace — dan tabel angka hampir tak pernah meyakinkan pembaca laporan. Grafik yang tepat menyaring ribuan angka menjadi satu pesan: sistem stabil atau tidak, fit berhasil atau gagal, parameter aman atau berbahaya. Chapter ini memetakan jenis hasil simulasi ke jenis grafik, lalu membangun empat pola yang paling sering dipakai dalam praktik: lintasan multi-seri, phase plane, dashboard subplot, dan heatmap sweep parameter. Cakupan kita:

  • tabel panduan memilih grafik sesuai pertanyaan pembaca
  • legend, anotasi text, dan pola headless dari course Visualisasi Data
  • dashboard subplot 2×2 dan heatmap contourf untuk hasil sweep
  • konsep animasi serta ekspor grafik untuk laporan

Memilih Grafik yang Tepat #

Aturannya: grafik menjawab pertanyaan, jadi tentukan pertanyaannya lebih dulu. “Kapan suhu mencapai target?” dan “di wilayah parameter mana sistem berbahaya?” menuntut grafik berbeda — memaksakan satu grafik untuk semua pertanyaan melahirkan kanvas 50 garis yang tak terbaca. Pemetaan yang kita pakai sepanjang chapter:

Jenis hasilPertanyaan pembacaGrafikFungsi MATLAB
lintasan waktu“kapan nilainya naik/turun?”line multi-seriplot, legend
state sistem orde dua“lewat jalur apa menuju ekuilibrium?”phase planeplot(x, dx)
sweep dua parameter“di wilayah mana sistem berperilaku buruk?”heatmapcontourf, imagesc
distribusi keluaran“seberapa menyebar hasilnya?”histogramhist
beberapa sinyal terkait“apa yang terjadi serentak?”dashboardsubplot

Semua contoh berikut memakai pola headless yang sudah kita kenal: figure('visible', 'off') membuat figur tanpa jendela, saveas menuliskan PNG, lalu delete menutup figur — sehingga skrip berjalan di server dan CI tanpa layar.

Lintasan Multi-Seri dengan Legend dan Anotasi #

Aturan pertama: dua skenario berarti dua seri plus legend; dan nilai kunci (angka akhir, puncak, batas) ditulis langsung di kanvas lewat text supaya pembaca tidak perlu memindai sumbu. Mekanismenya: ode45 dipanggil dalam loop dengan laju pendinginan r yang berbeda, tiap hasil digambar dengan hold on, dan sprintf menyusun teks anotasi dari data itu sendiri:

matlab
f1 = figure('visible', 'off');
r    = [0.03, 0.10];        % laju pendinginan (1/menit)
gaya = {'b-', 'r-'};
hold on;
Takhir = zeros(1, 2);
for i = 1:2
    pendingin = @(t, x) -r(i)*(x - 20);        % hukum pendinginan Newton
    [tt, xx] = ode45(pendingin, [0, 60], 90);  % suhu awal 90 deg C
    plot(tt, xx(:, 1), gaya{i});
    Takhir(i) = xx(end, 1);
end
legend('r = 0.03 /menit', 'r = 0.10 /menit');
xlabel('waktu (menit)');
ylabel('suhu (deg C)');
title('Pendinginan kopi: dua skenario');
text(40, Takhir(2) + 3, sprintf('r = 0.10: akhir %.1f deg C', Takhir(2)));
saveas(f1, 'pendinginan.png');
fprintf('Pendinginan: T akhir = %.1f (r = 0.03), %.1f (r = 0.10)\n', Takhir(1), Takhir(2));
fprintf('Grafik tersimpan: pendinginan.png\n');
delete(f1);

Output yang Dihasilkan:

text
Pendinginan: T akhir = 31.6 (r = 0.03), 20.2 (r = 0.10)
Grafik tersimpan: pendinginan.png

Mari kita bedah baris-baris kode di atas:

  1. Baris 1 (figure('visible', 'off')): figur dibuat tanpa jendela — pola headless; tanpa ini Octave di server mencampurkan keluaran gnuplot ke stdout.
  2. Baris 3 (gaya = {'b-', 'r-'}): cell array menyimpan gaya garis per skenario, diakses gaya{i} di dalam loop — pola yang sama untuk warna, marker, atau lebar garis.
  3. Baris 7-10 (ode45 dalam loop): satu anonymous function per skenario karena r(i) terikat tiap iterasi; hasil tiap simulasi langsung digambar, dan Takhir(i) menyimpan suhu akhir untuk anotasi.
  4. Baris 13 (legend(...)): label dipasangkan ke seri berurutan sesuai urutan panggilan plot.
  5. Baris 17 (text(40, Takhir(2) + 3, sprintf(...))): anotasi ditempatkan pada koordinat data (menit ke-40, 3 °C di atas kurva merah) dengan teks yang dibangun dari hasil simulasi — angka pada grafik dan angka di laporan tidak akan berbeda.

Grafik yang tersimpan: dua kurva turun dari 90 °C; kurva merah (r = 0.10) melandai mendekati 20 °C sebelum menit ke-40, kurva biru (r = 0.03) masih di 31.6 °C pada menit ke-60 — anotasi teks menempel di dekat ekor kurva merah.

Phase Plane: Potret Sistem Orde Dua #

Aturan kedua: sistem orde dua punya dua state (posisi dan kecepatan), dan memplot x terhadap dx/dt menghasilkan phase plane — potret jalur menuju ekuilibrium yang tak terlihat pada plot waktu biasa. Mekanismenya: ode45 mengembalikan state per kolom, jadi plot(xx(:,1), xx(:,2)) adalah kurva parametrik yang parameternya waktu; arah putaran mengikuti pertumbuhan t:

matlab
f2 = figure('visible', 'off');
mpd = @(t, x) [x(2); (100 - 2*x(2) - 100*x(1))];   % m = 1, c = 2, k = 100, F = 100
[tt, xx] = ode45(mpd, [0, 5], [0; 0]);
plot(xx(:, 1), xx(:, 2), 'b-');                    % lintasan di ruang state
hold on;
plot(1, 0, 'ro');                                  % titik ekuilibrium (F/k, 0)
text(1.03, 0.12, 'ekuilibrium');
xlabel('posisi x (m)');
ylabel('kecepatan v (m/s)');
title('Phase plane sistem underdamped');
saveas(f2, 'phase-plane.png');
fprintf('Grafik tersimpan: phase-plane.png\n');
delete(f2);

Output yang Dihasilkan:

text
Grafik tersimpan: phase-plane.png

Baris kuncinya: baris 5 memplot kolom 1 (posisi) terhadap kolom 2 (kecepatan) — sistem massa-pegas-damper underdamped persis dari Chapter Pemodelan Teknik; baris 7 menandai titik ekuilibrium (F/k, 0) = (1, 0). Grafik yang tersimpan: lintasan biru berputar mengelilingi titik merah dengan radius yang menyusut tiap putaran — satu putaran penuh adalah satu siklus osilasi, dan penyusutan radius adalah peluruhan damping. Overshoot pada plot waktu tampil di sini sebagai lingkaran yang menembus garis x = 1.

Dashboard subplot: Satu Layar, Empat Panel #

Aturan ketiga: simulasi berbasis kendali menghasilkan sinyal-sinyal saling terkait — state, error, sinyal kontrol, dan input — dan subplot menyatukannya dalam satu figur sehingga sebab-akibatnya terbaca sekilas. Mekanismenya: subplot(2, 2, k) membagi kanvas menjadi grid 2×2 lalu mengaktifkan panel ke-k; setiap panggilan plot/title setelahnya jatuh pada panel aktif:

matlab
f3 = figure('visible', 'off');
Kp = 25; Kd = 8;                                   % gain kendali PD
plant = @(t, x) [x(2); Kp*(1 - x(1)) - Kd*x(2)];  % x'' = u, u = Kp*e - Kd*v
[tt, xx] = ode45(plant, [0, 3], [0; 0]);
u = Kp*(1 - xx(:, 1)) - Kd*xx(:, 2);              % sinyal kontrol tercatat
subplot(2, 2, 1);
plot(tt, xx(:, 1), 'b-'); hold on; plot([0, 3], [1, 1], 'k--');
title('state: posisi x(t)');
subplot(2, 2, 2);
plot(tt, 1 - xx(:, 1), 'r-');
title('error: e(t) = 1 - x(t)');
subplot(2, 2, 3);
plot(tt, u, 'm-');
title('kontrol: u(t)');
subplot(2, 2, 4);
plot(tt, ones(size(tt)), 'k-');
title('input: r(t)');
saveas(f3, 'dashboard.png');
fprintf('Dashboard: |error| akhir = %.4f, puncak kontrol = %.1f\n', abs(1 - xx(end, 1)), max(u));
fprintf('Grafik tersimpan: dashboard.png\n');
delete(f3);

Output yang Dihasilkan:

text
Dashboard: |error| akhir = 0.0000, puncak kontrol = 25.0
Grafik tersimpan: dashboard.png

Baris-baris intinya: baris 3 mensimulasikan plant orde dua dengan kendali PD (u = Kp*e - Kd*v) yang dilipat langsung ke dalam persamaan; baris 5 merekonstruksi sinyal kontrol u(t) dari state — sinyal yang tidak diintegrasikan tetapi tetap tercatat; empat blok subplot lalu mengisi panel posisi (dengan garis putus-putus setpoint), error, kontrol, dan input. Grafik yang tersimpan: panel kiri-atas menunjukkan posisi mengejar garis putus 1 m dengan overshoot hanya 1.5% (damping rasio 0.8 dari kombinasi Kp, Kd); error meluruh ke nol (|error| akhir = 0.0000); kontrol melompat ke Kp = 25 di awal lalu meluruh; input konstan 1 — satu figur menjawab “apakah kendali bekerja” tanpa membuka empat jendela.

Heatmap Sweep Parameter #

Aturan keempat: menyapu dua parameter menghasilkan matriks hasil — terlalu besar untuk tabel, paling jelas sebagai warna. Mekanismenya: meshgrid membangun grid (c, k), formula overshoot dari Chapter Pemodelan Teknik mengisi matriks Z untuk seluruh sel sekaligus (vektoris, tanpa loop simulasi), lalu contourf dengan vektor level mengunci pita warna pada ambang yang kita pilih:

matlab
f4 = figure('visible', 'off');
cVec = 0.5:0.75:8;                                % damping (N.s/m)
kVec = 20:20:180;                                 % pegas (N/m)
[CG, KG] = meshgrid(cVec, kVec);
zeta = CG ./ (2*sqrt(KG));                        % rasio damping (m = 1)
Z = 100 * exp(-pi*zeta ./ sqrt(1 - zeta.^2));     % overshoot (%)
Z(zeta >= 1) = 0;                                 % tanpa osilasi, tanpa overshoot
contourf(CG, KG, Z, [10, 20, 30, 40, 50, 60, 70, 80]);
colorbar();
xlabel('damping c (N.s/m)');
ylabel('pegas k (N/m)');
title('Overshoot maksimum (%)');
saveas(f4, 'heatmap.png');
fprintf('Heatmap: overshoot %.1f%% s.d. %.1f%%\n', min(Z(:)), max(Z(:)));
fprintf('Grafik tersimpan: heatmap.png\n');
delete(f4);

Output yang Dihasilkan:

text
Heatmap: overshoot 0.2% s.d. 94.3%
Grafik tersimpan: heatmap.png

Baris 8 menghitung Z vektoris dari zeta di seluruh grid (dengan logical indexing Z(zeta >= 1) = 0 menihilkan wilayah tanpa osilasi), dan baris 10 meminta level eksplisit 10-80% sehingga pita warna punya arti yang sama antar-eksekusi. Grafik yang tersimpan: wilayah c kecil menyala dengan overshoot di atas 80%, warna mendingin ke arah c besar; colorbar mencantumkan nilai tiap pita. Sebagai alternatif, imagesc(cVec, kVec, Z) + colorbar() menampilkan sel warna mentah per grid tanpa garis level — keduanya tersedia di MATLAB dan Octave; contourf menang saat pembaca perlu membaca angka dari pita.

Animasi dan Ekspor ke Laporan #

Animasi menambah dimensi waktu sebagai gerak. Di MATLAB, polanya: gambar frame demi frame, tangkap dengan getframe, lalu putar ulang dengan movie:

matlab
% MATLAB: animasi lintasan — kumpulkan frame lalu putar
f = figure;
for k = 1:length(tt)
    plot(tt(1:k), xx(1:k), 'b-');        % gambar sampai titik ke-k
    xlim([0, tt(end)]); ylim([0, 2]);
    F(k) = getframe(f);                  % tangkap frame sebagai image
end
movie(f, F, 1);                          % putar rangkaian frame sekali

getframe mengembalikan tiap frame sebagai struct berisi data gambar; fungsi frame2im(F(k)) mengubahnya menjadi matriks gambar untuk diproses lebih lanjut, dan VideoWriter menuliskan rangkaian frame ke berkas MP4. Untuk animasi kilat di sesi interaktif, comet(x, y) menggambar lintasan yang tumbuh bertahap tanpa kita mengelola frame — comet tersedia di MATLAB maupun Octave.

Untuk laporan, ekspor dipilih sesuai target:

KebutuhanPerintahMATLABOctave
PNG cepatsaveas(gcf, 'hasil.png')adaada
PNG presisi, tepi rapatexportgraphics(gcf, 'hasil.png', 'Resolution', 150)R2020b+tidak ada
video MP4VideoWriter + getframeadaterbatas
animasi kilat interaktifcomet(x, y)adaada

Ringkasan #

Pemilihan grafik menentukan pesan: lintasan waktu multi-seri (plot + legend + anotasi text) menjawab “kapan dan seberapa jauh”, phase plane plot(x, dx) menjawab “lewat jalur apa menuju ekuilibrium”, heatmap contourf/imagesc menjawab “di wilayah parameter mana sistem bermasalah”, dan subplot menggabungkan state, error, kontrol, serta input menjadi satu dashboard. Animasi menambah dimensi waktu lewat getframe/movie (MATLAB) atau comet (kedua engine), sedangkan ekspor laporan berpaling pada saveas yang kompatibel di mana-mana. Berkas contoh.m pada bagian bawah halaman menghasilkan keempat grafik secara headless lalu membersihkan PNG sementaranya.