Skip to content

Commit 675446f

Browse files
committed
Add 3d realtim viz example
1 parent 4105dd1 commit 675446f

File tree

5 files changed

+405
-27
lines changed

5 files changed

+405
-27
lines changed

docs/src/examples.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,65 @@ default model, at a much better performance.
196196
197197
![Oscillating steering input response, simple system](assets/oscillating_steering_simple.png)
198198
199+
## Real-Time 3D Visualization
200+
201+
The `realtime_visualization.jl` example demonstrates how to create a custom simulation loop with real-time 3D visualization. This is useful for:
202+
- Watching system behavior as it evolves
203+
- Debugging dynamics issues visually
204+
- Creating demonstrations and videos
205+
- Better understanding system response
206+
207+
**Key Features:**
208+
- Observable-based updates for smooth animation
209+
- Proper sleep timing to maintain real-time speed
210+
- Interactive camera control during simulation
211+
- Configurable visualization frame rate and speed
212+
213+
**Usage:**
214+
```julia
215+
include("examples/realtime_visualization.jl")
216+
```
217+
218+
**How It Works:**
219+
220+
The example creates `Observable` objects for dynamic data (point positions, wing orientations, etc.) and updates them during the simulation loop:
221+
222+
```julia
223+
# Create observables
224+
point_positions = Observable([Point3f(p.pos_w) for p in sys_struct.points])
225+
226+
# In simulation loop:
227+
for step in 1:steps
228+
# Run simulation step
229+
next_step!(sam; ...)
230+
231+
# Update visualization
232+
if step % plot_interval == 0
233+
point_positions[] = [Point3f(p.pos_w) for p in sys_struct.points]
234+
sleep(0.001) # Allow Makie to process events
235+
end
236+
237+
# Sleep to maintain real-time pacing
238+
sleep(max(0.0, target_time - elapsed_time))
239+
end
240+
```
241+
242+
**Configuration:**
243+
- `realtime_factor`: Set to 2.0 for 2x speed, 0.5 for half speed, etc.
244+
- `plot_interval`: Update plot every N steps (higher = better performance)
245+
- `dt`: Simulation time step
246+
247+
**When to Use:**
248+
- Use **real-time visualization** when you want to observe behavior as it happens
249+
- Use **post-simulation plotting** (`plot(sys_struct, log)`) when you want to:
250+
- Run simulations faster than real-time
251+
- Analyze results in detail after completion
252+
- Create publication-quality plots
253+
254+
See: [`realtime_visualization.jl`](https://github.com/OpenSourceAWE/SymbolicAWEModels.jl/blob/main/examples/realtime_visualization.jl)
255+
199256
## Linearization
200-
The following example creates a nonlinear system model, finds a steady-state operating point,
257+
The following example creates a nonlinear system model, finds a steady-state operating point,
201258
linearizes the model around this operating point and creates bode plots from inputs (torques)
202259
to output (heading).
203260
```julia

examples/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
[deps]
2+
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
23
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
34
KiteUtils = "90980105-b163-44e5-ba9f-8b1c83bb0533"
5+
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
46
SymbolicAWEModels = "9c9a347c-5289-41db-a9b9-25ccc76c3360"

examples/realtime_visualization.jl

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
# Copyright (c) 2025 Bart van de Lint
2+
# SPDX-License-Identifier: MPL-2.0
3+
4+
"""
5+
Real-Time 3D Visualization Example
6+
7+
This example demonstrates how to create a custom simulation loop with real-time
8+
3D visualization using Makie's Observable system and the existing plot functions.
9+
10+
Key features:
11+
- Real-time 3D updates using Observables
12+
- Proper sleep timing to maintain real-time speed
13+
- Interactive camera control during simulation (hover, click-to-zoom)
14+
- Configurable visualization frame rate
15+
- Uses existing plot() functions for consistency
16+
"""
17+
18+
using GLMakie
19+
using SymbolicAWEModels
20+
using KiteUtils
21+
using LinearAlgebra
22+
using Printf
23+
24+
# ============================================================================
25+
# SIMULATION PARAMETERS
26+
# ============================================================================
27+
28+
dt = 0.05 # Time step [s]
29+
total_time = 20.0 # Total simulation time [s]
30+
vsm_interval = 3 # VSM update interval
31+
realtime_factor = 1.0 # 1.0 = realtime, 2.0 = 2x speed, 0.5 = half speed
32+
plot_interval = 1 # Update plot every N steps (1 = every step)
33+
vector_scale = 1.0 # Scale for wing orientation arrows
34+
35+
# Steering parameters
36+
steering_freq = 0.5 # Hz - full left-right cycle frequency
37+
steering_magnitude = 10.0 # Magnitude of steering input [Nm]
38+
bias = 0.2 # Steering bias
39+
40+
# ============================================================================
41+
# INITIALIZE MODEL
42+
# ============================================================================
43+
44+
println("Initializing model...")
45+
set_data_path("data")
46+
set = Settings("system.yaml")
47+
set.profile_law = 3
48+
sam = SymbolicAWEModel(set)
49+
init!(sam)
50+
find_steady_state!(sam)
51+
52+
sys_struct = sam.sys_struct
53+
steps = Int(round(total_time / dt))
54+
num_winches = length(sys_struct.winches)
55+
56+
# ============================================================================
57+
# CREATE OBSERVABLES AND PLOT SCENE
58+
# ============================================================================
59+
60+
println("Creating 3D visualization window with observables...")
61+
62+
# Create observables for dynamic visualization
63+
# Initialize with current system state
64+
segment_points_obs = Observable(Point3f[])
65+
point_positions_obs = Observable(Point3f[])
66+
wing_origins_obs = Observable(Point3f[])
67+
wing_directions_obs = Observable(Vec3f[])
68+
69+
# Initialize observables with current state
70+
update_plot_observables!(
71+
segment_points_obs, point_positions_obs,
72+
wing_origins_obs, wing_directions_obs,
73+
sys_struct; vector_scale
74+
)
75+
76+
# Create 3D scene using existing plot function with observables
77+
scene = plot(sys_struct;
78+
segment_points_obs,
79+
point_positions_obs,
80+
wing_origins_obs,
81+
wing_directions_obs,
82+
vector_scale,
83+
size=(1400, 900))
84+
85+
# Add text overlays for time and progress directly on the scene
86+
sim_time_text = Observable("Time: 0.00 s")
87+
progress_text = Observable("Progress: 0%")
88+
89+
# Add text labels to scene (top-left and top-right)
90+
text!(scene, sim_time_text, position = Point2f(20, 20), space = :pixel,
91+
fontsize = 24, color = :black, align = (:left, :top))
92+
text!(scene, progress_text, position = Point2f(1380, 20), space = :pixel,
93+
fontsize = 20, color = :black, align = (:right, :top))
94+
95+
# Display the scene (non-blocking)
96+
display(scene)
97+
98+
# ============================================================================
99+
# PREPARE CONTROL INPUTS
100+
# ============================================================================
101+
102+
println("Preparing control inputs...")
103+
set_values = zeros(Float64, steps, num_winches)
104+
105+
for step in 1:steps
106+
t = (step-1) * dt
107+
steering = steering_magnitude * cos(2π * steering_freq * t + bias)
108+
set_values[step, :] = [0.0, steering, -steering]
109+
end
110+
111+
# ============================================================================
112+
# RUN REAL-TIME SIMULATION
113+
# ============================================================================
114+
115+
println("\nStarting real-time simulation...")
116+
println(" Total time: $(total_time)s")
117+
println(" Time step: $(dt)s")
118+
println(" Realtime factor: $(realtime_factor)x")
119+
println(" Plot update interval: every $(plot_interval) step(s)")
120+
println("\nSimulation running... (you can interact with the 3D view)\n")
121+
122+
# Initialize state
123+
logger = SymbolicAWEModels.Logger(length(sys_struct.points), steps+1)
124+
sys_state = SysState(sam)
125+
sys_state.time = 0.0
126+
SymbolicAWEModels.log!(logger, sys_state)
127+
128+
steady_torque = SymbolicAWEModels.calc_steady_torque(sam)
129+
torque_damp = 0.9
130+
set_torques = similar(set_values)
131+
132+
# Simulation loop with real-time visualization
133+
start_time = time()
134+
for step in 1:steps
135+
global steady_torque # Declare that we're modifying the global variable
136+
t = step * dt
137+
target_elapsed = t / realtime_factor
138+
139+
# Calculate control torques
140+
steady_torque = torque_damp * steady_torque + (1-torque_damp) * SymbolicAWEModels.calc_steady_torque(sam)
141+
set_torques[step, :] = steady_torque .+ set_values[step, :]
142+
143+
# Simulation step
144+
try
145+
next_step!(sam; set_values=set_torques[step, :], dt, vsm_interval)
146+
catch e
147+
if e isa AssertionError
148+
@warn "Simulation crashed at t=$t"
149+
break
150+
else
151+
rethrow(e)
152+
end
153+
end
154+
155+
# Update system state and log
156+
SymbolicAWEModels.update_sys_state!(sys_state, sam)
157+
sys_state.time = t
158+
SymbolicAWEModels.log!(logger, sys_state)
159+
160+
# Update visualization
161+
if step % plot_interval == 0
162+
# Update observables from current system state
163+
update_plot_observables!(
164+
segment_points_obs, point_positions_obs,
165+
wing_origins_obs, wing_directions_obs,
166+
sys_struct; vector_scale
167+
)
168+
169+
# Update text overlays
170+
sim_time_text[] = @sprintf("Time: %.2f s", t)
171+
progress_text[] = @sprintf("Progress: %d%%", round(Int, 100 * step / steps))
172+
173+
# Force Makie to process events and update display
174+
sleep(0.001)
175+
end
176+
177+
# Sleep to maintain real-time pacing
178+
actual_elapsed = time() - start_time
179+
sleep_time = max(0.0, target_elapsed - actual_elapsed)
180+
sleep(sleep_time)
181+
182+
# Print progress every 10%
183+
if step % (steps ÷ 10) == 0
184+
@printf(" %.0f%% complete (t=%.1fs)\n", 100 * step / steps, t)
185+
end
186+
end
187+
188+
total_elapsed = time() - start_time
189+
println("\nSimulation complete!")
190+
println(" Total runtime: $(round(total_elapsed, digits=2))s")
191+
println(" Speedup: $(round(total_time / total_elapsed, digits=2))x realtime")
192+
193+
# ============================================================================
194+
# SAVE AND PLOT RESULTS
195+
# ============================================================================
196+
197+
println("\nSaving results...")
198+
mkpath(get_data_path())
199+
SymbolicAWEModels.save_log(logger, "tmp_realtime_run")
200+
lg = load_log("tmp_realtime_run")
201+
202+
println("Creating post-simulation plots...")
203+
fig_results = plot(sam.sys_struct, lg; plot_default=false, plot_heading=true, plot_aoa=true)
204+
display(fig_results)
205+
206+
println("\nDone! Close the windows to exit.")

0 commit comments

Comments
 (0)