19
19
from onshape_robotics_toolkit .models .document import Document
20
20
from onshape_robotics_toolkit .robot import Robot , RobotType
21
21
22
- N_DESIGN_TRAILS = 50
23
- N_PID_TRAILS = 150
22
+ N_DESIGN_TRAILS = 100
23
+ N_PID_TRAILS = 50
24
24
25
25
USE_MUJOCO_VIEWER = False
26
26
27
27
HEIGHT = 480
28
28
WIDTH = 640
29
29
30
30
FREQUENCY = 200
31
- USD_FRAME_RATE = 30
31
+ USD_FRAME_RATE = 25
32
32
dt = 1 / FREQUENCY
33
33
34
34
# Variable bounds (in mm and degrees)
35
- WHEEL_DIAMETER_BOUNDS = (100 , 200 )
36
- SPACER_HEIGHT_BOUNDS = (75 , 200 )
37
- ALPHA_BOUNDS = (30 , 55 )
35
+ WHEEL_DIAMETER_BOUNDS = (100 , 150 )
36
+ SPACER_HEIGHT_BOUNDS = (75 , 150 )
37
+ ALPHA_BOUNDS = (35 , 50 )
38
38
PLATE_BOUNDS = (1 , 30 )
39
39
40
- SIMULATION_DURATION = 200 # seconds to run each trial
40
+ LAMBDA_WEIGHT = 100.0
41
+ BETA_WEIGHT = 25.0
42
+
43
+ SIMULATION_DURATION = 120 # seconds to run each trial
41
44
VIBRATION_PENALTY = 1e-3
42
45
43
- TARGET_VALUE = 100 .0 # Exit control optimation if balanced for this long
46
+ TARGET_VALUE = 50 .0 # Exit control optimation if balanced for this long
44
47
PERTURBATION_INCREASE = 0.125 # Amount of Newtons to increase perturbation by each time
45
48
PERTURBATION_START = 5 # Time delay before perturbations begin
46
- PERTURBATION_REST = 7.5 # Time delay between perturbations
49
+ PERTURBATION_REST = 7 # Time delay between perturbations
47
50
48
51
MAX_ANGLE = np .deg2rad (60 )
49
52
MAX_DISTANCE_FROM_BALL = 0.3 # meters
54
57
KD = 2.4
55
58
FF = 0.2
56
59
57
- DERIVATIVE_FILTER_ALPHA = 0.2
60
+ DERIVATIVE_FILTER_ALPHA = 0.1
58
61
59
62
TORQUE_LIMIT_HIGH = 10.0
60
63
TORQUE_LIMIT_LOW = - 10.0
@@ -169,9 +172,9 @@ def apply_perturbation(data, count):
169
172
170
173
def find_best_pid_params (trial , model , data , viewer , variables , usd_output_dir ):
171
174
# Suggest values for the PID gains
172
- kp = trial .suggest_float ('kp' , low = 2 , high = 25.0 , step = 0.1 )
175
+ kp = trial .suggest_float ('kp' , low = 5 , high = 25.0 , step = 0.1 )
173
176
ki = trial .suggest_float ('ki' , low = 0.0 , high = 15.0 , step = 0.1 )
174
- kd = trial .suggest_float ('kd' , low = 0.0 , high = 1.0 , step = 0.001 )
177
+ kd = trial .suggest_float ('kd' , low = 0.0 , high = 1.0 , step = 0.01 )
175
178
ff = trial .suggest_float ('ff' , low = 0.01 , high = 1.01 , step = 0.05 )
176
179
177
180
LOGGER .info (f"KP: { kp } , KI: { ki } , KD: { kd } , FF: { ff } " )
@@ -208,10 +211,14 @@ def find_best_pid_params(trial, model, data, viewer, variables, usd_output_dir):
208
211
roll_pid .reset ()
209
212
pitch_pid .reset ()
210
213
214
+ # Initialize variables to track distance
215
+ cumulative_distance = 0.0
216
+ cumulative_vibration = 0.0
217
+ steps = 0
218
+
211
219
# Run the simulation
212
220
j = 0
213
221
214
- #while data.time < SIMULATION_DURATION and viewer.is_running():
215
222
while data .time < SIMULATION_DURATION :
216
223
mujoco .mj_step (model , data )
217
224
@@ -228,14 +235,35 @@ def find_best_pid_params(trial, model, data, viewer, variables, usd_output_dir):
228
235
if exit_condition (data ):
229
236
break
230
237
231
- viewer .sync ()
238
+ ball_pos = get_ball_pos (data )
239
+ distance = np .linalg .norm (np .array (ball_pos ))
240
+ cumulative_distance += distance
232
241
233
- # Combine performance metric with vibration penalty
234
- time_on_ball = data .time # TODO: Make this more accurate with contact detection
242
+ dtheta = get_dtheta (data )
243
+ cumulative_vibration += np .linalg .norm (dtheta )
244
+ steps += 1
245
+
246
+ if USE_MUJOCO_VIEWER :
247
+ viewer .sync ()
248
+ elif viewer .is_running ():
249
+ viewer .close ()
250
+
251
+ # Combine performance metric with vibration penalty and distance
252
+ time_on_ball = data .time # Time the ball stayed on top
253
+ average_distance = cumulative_distance / steps if steps > 0 else 0.0
254
+ average_vibration = cumulative_vibration / steps if steps > 0 else 0.0
255
+ objective_value = time_on_ball - LAMBDA_WEIGHT * average_distance - BETA_WEIGHT * average_vibration
256
+
257
+ LOGGER .info (
258
+ f"Time on Ball: { time_on_ball } , "
259
+ f"Average Distance: { average_distance } , "
260
+ f"Average Vibration: { average_vibration } , "
261
+ f"Objective: { objective_value } "
262
+ )
235
263
236
264
usd_exporter .save_scene (filetype = "usd" )
237
265
238
- return time_on_ball
266
+ return objective_value
239
267
240
268
def stop_when_target_reached (study , trial ):
241
269
if trial .value is not None and trial .value >= TARGET_VALUE :
@@ -346,6 +374,11 @@ def find_best_design_variables(trial):
346
374
if not USE_MUJOCO_VIEWER :
347
375
viewer .close ()
348
376
377
+ # Initialize variables to track distance
378
+ cumulative_distance = 0.0
379
+ cumulative_vibration = 0.0
380
+ steps = 0
381
+
349
382
#while data.time < SIMULATION_DURATION and viewer.is_running():
350
383
while data .time < SIMULATION_DURATION :
351
384
mujoco .mj_step (model , data )
@@ -373,15 +406,36 @@ def find_best_design_variables(trial):
373
406
if exit_condition (data ):
374
407
break
375
408
409
+ ball_pos = get_ball_pos (data )
410
+ distance = np .linalg .norm (np .array (ball_pos ))
411
+ cumulative_distance += distance
412
+
413
+ dtheta = get_dtheta (data )
414
+ cumulative_vibration += np .linalg .norm (dtheta )
415
+ steps += 1
376
416
377
417
if USE_MUJOCO_VIEWER :
378
418
viewer .sync ()
379
419
elif viewer .is_running ():
380
420
viewer .close ()
381
421
382
- objective_value = data .time
422
+ time_on_ball = data .time # Time the ball stayed on top
423
+ average_distance = cumulative_distance / steps if steps > 0 else 0.0
424
+ average_vibration = cumulative_vibration / steps if steps > 0 else 0.0
425
+
426
+ objective_value = time_on_ball - LAMBDA_WEIGHT * average_distance - BETA_WEIGHT * average_vibration
427
+
428
+ LOGGER .info (
429
+ f"Time on Ball: { time_on_ball } , "
430
+ f"Average Distance: { average_distance } , "
431
+ f"Average Vibration: { average_vibration } , "
432
+ f"Objective: { objective_value } "
433
+ )
434
+
435
+
436
+ if viewer .is_running ():
437
+ viewer .close ()
383
438
384
- viewer .close ()
385
439
usd_exporter .save_scene (filetype = "usd" )
386
440
387
441
return objective_value
@@ -390,14 +444,14 @@ def find_best_design_variables(trial):
390
444
if __name__ == "__main__" :
391
445
run_name = input ("Enter run name: " )
392
446
# Create output directory for this run
393
- output_dir = f" { run_name } "
447
+ output_dir = run_name
394
448
os .makedirs (output_dir , exist_ok = True )
395
449
os .makedirs (os .path .join (output_dir , "scenes" ), exist_ok = True ) # Create scenes subdirectory
396
450
# Update log file path
397
- LOGGER .set_file_name (f"{ output_dir } / { run_name } .log" )
451
+ LOGGER .set_file_name (os . path . join ( output_dir , f"{ run_name } .log" ) )
398
452
LOGGER .set_stream_level (LogLevel .INFO )
399
453
400
- client = Client ()
454
+ client = Client (env = ".env" )
401
455
doc = Document .from_url (
402
456
url = "https://cad.onshape.com/documents/01d73bbd0f243938a11fbb7c/w/20c6ecfe7711055ba2420fdc/e/833959fcd6ba649195a1e94c"
403
457
)
@@ -421,7 +475,7 @@ def find_best_design_variables(trial):
421
475
LOGGER .info (f" ff: { study .best_trial .user_attrs ['ff' ]} " )
422
476
423
477
# Save outputs in the run directory
424
- with open (f" { output_dir } / best_params.json" , "w" ) as f :
478
+ with open (os . path . join ( output_dir , " best_params.json") , "w" ) as f :
425
479
# Combine design parameters and PID values
426
480
all_params = {
427
481
** study .best_trial .params ,
@@ -434,21 +488,21 @@ def find_best_design_variables(trial):
434
488
}
435
489
json .dump (all_params , f )
436
490
437
- study .trials_dataframe ().to_csv (f" { output_dir } / data.csv" )
491
+ study .trials_dataframe ().to_csv (os . path . join ( output_dir , " data.csv") )
438
492
439
493
# Save visualization plots
440
494
contour_plot = optuna .visualization .plot_contour (study )
441
- plotly .io .write_html (contour_plot , f" { output_dir } / contour.html" )
495
+ plotly .io .write_html (contour_plot , os . path . join ( output_dir , " contour.html") )
442
496
443
497
optimization_history_plot = optuna .visualization .plot_optimization_history (study )
444
- plotly .io .write_html (optimization_history_plot , f" { output_dir } / optimization_history.html" )
498
+ plotly .io .write_html (optimization_history_plot , os . path . join ( output_dir , " optimization_history.html") )
445
499
446
500
hyperparameter_importance_plot = optuna .visualization .plot_param_importances (study )
447
- plotly .io .write_html (hyperparameter_importance_plot , f" { output_dir } / hyperparameter_importance.html" )
501
+ plotly .io .write_html (hyperparameter_importance_plot , os . path . join ( output_dir , " hyperparameter_importance.html") )
448
502
449
503
timeline_plot = optuna .visualization .plot_timeline (study )
450
- plotly .io .write_html (timeline_plot , f" { output_dir } / timeline.html" )
504
+ plotly .io .write_html (timeline_plot , os . path . join ( output_dir , " timeline.html") )
451
505
452
506
parallel_coordinate_plot = optuna .visualization .plot_parallel_coordinate (study )
453
- plotly .io .write_html (parallel_coordinate_plot , f" { output_dir } / parallel_coordinate.html" )
507
+ plotly .io .write_html (parallel_coordinate_plot , os . path . join ( output_dir , " parallel_coordinate.html") )
454
508
0 commit comments