From 555d795aa60cf579810a84b5b27994399f5f4ef3 Mon Sep 17 00:00:00 2001 From: Amandine Date: Tue, 11 Jul 2023 14:01:51 -0400 Subject: [PATCH 1/8] Resisting load --- resisting_current_calibration_load.py | 120 ++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 resisting_current_calibration_load.py diff --git a/resisting_current_calibration_load.py b/resisting_current_calibration_load.py new file mode 100644 index 0000000..cea1949 --- /dev/null +++ b/resisting_current_calibration_load.py @@ -0,0 +1,120 @@ +""" +This script is used to sample the current corresponding to the resisting torque of the motor at different velocities. +""" +import json +import matplotlib.pyplot as plt +import numpy as np +import time + +from scipy.optimize import curve_fit + +from ergocycleS2M.motor_control.enums import DirectionMode +from ergocycleS2M.motor_control.motor_controller import MotorController + +# Initialisation +motor = MotorController() +motor.set_direction(DirectionMode.REVERSE) + + +def calibration(instruction, nb_turns=5): + """ + This function is used to sample the current corresponding to the resisting torque of the motor at different + cadences. + + Parameters + ---------- + instruction + The cadence instruction in rpm. + nb_turns + The number of turns to sample the data at the desired cadence. + """ + if instruction > 0: + motor.set_direction(DirectionMode.FORWARD) + else: + motor.set_direction(DirectionMode.REVERSE) + motor.cadence_control(instruction) + + print(f"Waiting to stabilize at {instruction} rpm...") + + # Wait for to reach the instruction + while not (instruction - 1 < motor.get_cadence() < instruction + 1): + pass + + t0 = time.time() + t1 = time.time() + + # Wait for 1 second to stabilize at the instructed velocity (can't be done with a time.sleep() because of the + # watchdog thread) + while t1 - t0 < 3.0: + t1 = time.time() + + print(f"Stabilized at {instruction} rpm.") + + t0 = time.time() + t1 = time.time() + + # Save the data as frequently as possible during nb_turns + iq_measured = [] + vel_estimate = [] + while t1 - t0 < nb_turns / (abs(instruction) / 60): + t1 = time.time() + iq_measured.append(motor.axis.motor.current_control.Iq_measured) + vel_estimate.append(motor.axis.encoder.vel_estimate) + print(np.mean(iq_measured), "*/-", np.std(iq_measured), "A") + print(np.mean(vel_estimate), "*/-", np.std(vel_estimate), "tr/s") + return np.mean(iq_measured), np.mean(vel_estimate) + + +if __name__ == "__main__": + intensities = [] + velocities = [] + for ins in range(-60, 1, 5): + if ins == 0: + average_iq_measured, average_vel_estimate = calibration(1, 1) + intensities.append(average_iq_measured) + velocities.append(average_vel_estimate) + else: + average_iq_measured, average_vel_estimate = calibration(ins) + intensities.append(average_iq_measured) + velocities.append(average_vel_estimate) + + def lost_current(vel_estimate, resisting_current_proportional, resisting_current_constant): + """ + The current corresponding to the resisting torque of the motor is modeled as a linear function of the velocity. + + Parameters + ---------- + vel_estimate: + The velocity of the motor. + resisting_current_proportional: + The proportional coefficient. + resisting_current_constant: + The constant coefficient. + + Returns + ------- + + """ + return np.sign(vel_estimate) * ( + resisting_current_proportional * np.abs(vel_estimate) + resisting_current_constant + ) + + popt = curve_fit(lost_current, np.asarray(velocities), np.asarray(intensities), p0=[0.01, 0.5]) + + motor.stop() + + plt.plot(velocities, intensities) + plt.plot(velocities, lost_current(velocities, *popt[0])) + plt.xlabel("Motor velocity (tr/s)") + plt.ylabel("Resisting current (A)") + plt.show() + + data = { + "velocities": velocities, + "intensities": intensities, + } + + # Writing to .json + json_object = json.dumps(data, indent=4) + with open("./ergocycleS2M/parameters/resisting_current_load_h1_e7.json", "w") as outfile: + outfile.write(json_object) From 98ce47a9b480b2ab49fef31276e26dbc866fa559 Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 29 Jun 2023 12:48:53 -0400 Subject: [PATCH 2/8] Resisting torque --- .../resisting_current_load_h1_b10.json | 46 +++++++++++++++++ .../resisting_current_load_h2_b10.json | 46 +++++++++++++++++ .../resisting_current_load_h3_b10.json | 46 +++++++++++++++++ .../resisting_current_load_h4_b10.json | 46 +++++++++++++++++ .../resisting_current_load_h5_b10.json | 46 +++++++++++++++++ .../resisting_current_load_hh_b1.json | 46 +++++++++++++++++ .../resisting_current_load_hh_b10.json | 49 +++++++++++++++++++ .../resisting_current_load_hh_b2.json | 46 +++++++++++++++++ .../resisting_current_load_hh_b3.json | 46 +++++++++++++++++ .../resisting_current_load_hh_b4.json | 46 +++++++++++++++++ .../resisting_current_load_hh_b6.json | 46 +++++++++++++++++ .../resisting_current_load_hh_b8.json | 46 +++++++++++++++++ .../resisting_current_load_hl_b10.json | 46 +++++++++++++++++ computation.py | 46 +++++++++++++++++ .../motor_control/motor_controller.py | 5 +- ergocycleS2M/parameters/gains.json | 4 +- .../parameters/hardware_and_security.json | 4 +- resisting_current_calibration_load.py | 30 +++++++----- 18 files changed, 672 insertions(+), 18 deletions(-) create mode 100644 calibration_files/resisting_current_load_h1_b10.json create mode 100644 calibration_files/resisting_current_load_h2_b10.json create mode 100644 calibration_files/resisting_current_load_h3_b10.json create mode 100644 calibration_files/resisting_current_load_h4_b10.json create mode 100644 calibration_files/resisting_current_load_h5_b10.json create mode 100644 calibration_files/resisting_current_load_hh_b1.json create mode 100644 calibration_files/resisting_current_load_hh_b10.json create mode 100644 calibration_files/resisting_current_load_hh_b2.json create mode 100644 calibration_files/resisting_current_load_hh_b3.json create mode 100644 calibration_files/resisting_current_load_hh_b4.json create mode 100644 calibration_files/resisting_current_load_hh_b6.json create mode 100644 calibration_files/resisting_current_load_hh_b8.json create mode 100644 calibration_files/resisting_current_load_hl_b10.json create mode 100644 computation.py diff --git a/calibration_files/resisting_current_load_h1_b10.json b/calibration_files/resisting_current_load_h1_b10.json new file mode 100644 index 0000000..cff9e9a --- /dev/null +++ b/calibration_files/resisting_current_load_h1_b10.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4122814094470955, + -6.824549608836408, + -10.234620627551287, + -13.646749013946947, + -17.05942871776043, + -20.47294103480792, + -23.884709681738556, + -27.29780505830115, + -30.712880698990137, + -34.12034771387118, + -37.53569106310214, + -40.941674348115434 + ], + "intensities": [ + 0.27811992627046433, + 0.388563680619895, + 0.45507799347401007, + 0.5016333329285085, + 0.5966743872537427, + 0.6681222593390906, + 0.790767386877535, + 1.013019271262357, + 1.0644758806845416, + 1.603867497285763, + 1.3974308929603743, + 2.079223339843453 + ], + "velocities_std": [ + 0.15779928107970184, + 0.23911234153327357, + 0.25111059899911664, + 0.1865149787760561, + 0.184596646626383, + 0.2098365288570435, + 0.26097255544231124, + 0.27930969495042535, + 0.2706046597893147, + 0.29650572466626124, + 0.3155743993285291, + 0.45500660602874476 + ], + "a": -0.1532295821802699, + "b": -2.211950144909677 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_h2_b10.json b/calibration_files/resisting_current_load_h2_b10.json new file mode 100644 index 0000000..a936043 --- /dev/null +++ b/calibration_files/resisting_current_load_h2_b10.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4111452608211943, + -6.825293417432621, + -10.235405597564753, + -13.647771651953521, + -17.05809889568907, + -20.47276139032273, + -23.889240205287933, + -27.297842556174228, + -30.705112979657052, + -34.121111774840294, + -37.53102253614659, + -40.95016839513179 + ], + "intensities": [ + 0.40170171422509265, + 0.5083155407521315, + 0.7779980280844396, + 0.7614725392556392, + 0.6804280244965135, + 0.7338791749995764, + 0.7875363870536305, + 1.0023330315831753, + 1.073053893052075, + 1.5545523143599156, + 1.3764593297525105, + 2.0715453281924354 + ], + "velocities_std": [ + 0.1474620473416586, + 0.2167729543916627, + 0.2191314669079122, + 0.17031136339655675, + 0.17271796475737805, + 0.20051687826393055, + 0.23440110075564624, + 0.29359038632232787, + 0.276277034038277, + 0.31643478695779736, + 0.3076143770996864, + 0.4511988398237746 + ], + "a": -0.13262303350016438, + "b": -2.1496761211300517 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_h3_b10.json b/calibration_files/resisting_current_load_h3_b10.json new file mode 100644 index 0000000..b9565e9 --- /dev/null +++ b/calibration_files/resisting_current_load_h3_b10.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.41018774289602, + -6.823433210298306, + -10.234794812519294, + -13.64791581906606, + -17.058045084923585, + -20.473977611860292, + -23.880214842856006, + -27.295889228313012, + -30.708536142268585, + -34.12319047718797, + -37.53119543541517, + -40.94779457177021 + ], + "intensities": [ + 0.2793413917745369, + 0.39268204670722306, + 0.3800897954528682, + 0.3995978906149249, + 0.48482392463512625, + 0.6180903599356785, + 0.7642747612350458, + 1.0129095765194842, + 1.0724695311894832, + 1.5526896493044486, + 1.4297274064381924, + 2.0822678325835455 + ], + "velocities_std": [ + 0.14831164873764094, + 0.2910761208852303, + 0.25337764837599736, + 0.17227498255130333, + 0.16684259827660206, + 0.19785991729136046, + 0.23763040620386328, + 0.27761032471639535, + 0.2726816812997153, + 0.298285186474567, + 0.29722746402149747, + 0.44066767628796616 + ], + "a": -0.1475475392584593, + "b": -2.10758354417158 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_h4_b10.json b/calibration_files/resisting_current_load_h4_b10.json new file mode 100644 index 0000000..4795d95 --- /dev/null +++ b/calibration_files/resisting_current_load_h4_b10.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4102991025779503, + -6.823662862681027, + -10.235723641995877, + -13.649005398066432, + -17.061077590376122, + -20.475918812676852, + -23.884393839043476, + -27.299844849302097, + -30.71013031718303, + -34.1239041440288, + -37.53120730547922, + -40.95739669967116 + ], + "intensities": [ + 0.2788703943729869, + 0.36197982614478963, + 0.3501246342433477, + 0.37773427900047823, + 0.45729311672473827, + 0.6182559949787423, + 0.7711423931390106, + 1.0536233688430825, + 1.0747427176400006, + 1.6478858368781624, + 1.4527284180632292, + 2.174604301027509 + ], + "velocities_std": [ + 0.14245671888134204, + 0.27772532940937766, + 0.2561044394037885, + 0.18146919523564362, + 0.17002981438554657, + 0.1856615597223832, + 0.2329605819678024, + 0.27319433278391103, + 0.2611852419452288, + 0.30279233953456075, + 0.3061778355059749, + 0.4259669877063793 + ], + "a": -0.18172197236468093, + "b": -2.4441038535479733 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_h5_b10.json b/calibration_files/resisting_current_load_h5_b10.json new file mode 100644 index 0000000..cd93a7a --- /dev/null +++ b/calibration_files/resisting_current_load_h5_b10.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4112367586011705, + -6.823847296121482, + -10.235175874183142, + -13.649853736407136, + -17.061385709458804, + -20.473021919436192, + -23.885489336940168, + -27.297403816845357, + -30.71061489292601, + -34.12809728038714, + -37.52393759806682, + -39.85482250659659 + ], + "intensities": [ + 0.30026767391172127, + 0.408398466131638, + 0.439372552884401, + 0.50103067072354, + 0.6214635672786373, + 0.7016135360705785, + 0.8222165507292368, + 1.034315063276005, + 1.1221861223527645, + 1.65475170272647, + 1.4504351004209117, + 2.157566047270202 + ], + "velocities_std": [ + 0.165461591936041, + 0.2793111478843145, + 0.2746668111608924, + 0.20132443176068454, + 0.18905529027564466, + 0.21422786028489924, + 0.24446205059791629, + 0.28758592488594525, + 0.2738639155716095, + 0.30086315074819003, + 0.3172034935871232, + 0.3451476644715911 + ], + "a": -0.20529709140653754, + "b": -2.5189724044568917 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_hh_b1.json b/calibration_files/resisting_current_load_hh_b1.json new file mode 100644 index 0000000..6663548 --- /dev/null +++ b/calibration_files/resisting_current_load_hh_b1.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4120407462220586, + -6.825516923704728, + -10.238383447830685, + -13.64796277595554, + -17.064630128195486, + -20.476294325933317, + -23.88442265500885, + -27.30425639813553, + -30.715214906702542, + -34.1235301245941, + -37.53436877080029, + -40.94614917954288 + ], + "intensities": [ + 0.20134716162186858, + 0.1702650221351979, + 0.20586088855711746, + 0.26082112876762414, + 0.34875508223431334, + 0.519981824014429, + 0.5900712039616689, + 0.9144900400963688, + 1.009164603613374, + 1.518491570633324, + 1.3814229696799087, + 1.9581028206099105 + ], + "velocities_std": [ + 0.23798876502463326, + 0.1133226039405681, + 0.10196502625161918, + 0.11067510954684111, + 0.1207289251362517, + 0.13436317626303587, + 0.13875809498665231, + 0.16727527052278335, + 0.13259369373451754, + 0.1849650389184444, + 0.1643218331395464, + 0.40623457670842267 + ], + "a": -0.03380779858882961, + "b": -0.8872093238163651 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_hh_b10.json b/calibration_files/resisting_current_load_hh_b10.json new file mode 100644 index 0000000..3334d15 --- /dev/null +++ b/calibration_files/resisting_current_load_hh_b10.json @@ -0,0 +1,49 @@ +{ + "velocities": [ + -3.4131318421539003, + -6.825291518567895, + -10.235453080804414, + -13.647720409250189, + -17.059243895968454, + -20.473035534966563, + -23.885975200013778, + -27.299204483423267, + -30.70959353775562, + -34.12389353978015, + -37.539720093584044, + -40.31614969667087, + -43.05072026543655 + ], + "intensities": [ + 0.3189242567582075, + 0.6439382942516751, + 0.6333561570994258, + 0.6723625093811607, + 0.6534935524053814, + 0.7224324734239292, + 0.8116124338671254, + 1.0532807705888074, + 1.1240853340444623, + 1.6428424185752302, + 1.4422977533628334, + 2.2712644428290885, + 2.564576979914713 + ], + "velocities_std": [ + 0.15890002927971816, + 0.2919853668120776, + 0.2588566578487991, + 0.19979624041730495, + 0.1888732226614335, + 0.20505021959167202, + 0.2419878419254406, + 0.2878692375460531, + 0.2923843259419282, + 0.3190076362138282, + 0.3273148411130423, + 0.38783138509707044, + 0.39911529950743857 + ], + "a": -0.19483370030561692, + "b": -2.517670315420316 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_hh_b2.json b/calibration_files/resisting_current_load_hh_b2.json new file mode 100644 index 0000000..40814e2 --- /dev/null +++ b/calibration_files/resisting_current_load_hh_b2.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4126778307158743, + -6.823725593082668, + -10.23750268395414, + -13.650760427263458, + -17.06491538017027, + -20.477396532146432, + -23.88639135840322, + -27.303663151284002, + -30.707168174368594, + -34.13344826309183, + -37.544105193827626, + -40.942522948940855 + ], + "intensities": [ + 0.20018342983745457, + 0.17955567669673272, + 0.2134997427687467, + 0.2655619622139137, + 0.3476182566914793, + 0.5210104978328366, + 0.5617469793859872, + 0.8717163503416356, + 1.036202590148378, + 1.560986389624314, + 1.3818801642761775, + 1.9686330620998214 + ], + "velocities_std": [ + 0.22083991379312792, + 0.10508787921939915, + 0.10989697305512404, + 0.11519432771257837, + 0.13071221460865895, + 0.14486091445928367, + 0.13364660430486577, + 0.19317633038210588, + 0.1534370840317438, + 0.17940358090886516, + 0.18660607191495165, + 0.3513860430631813 + ], + "a": -0.04115635809031276, + "b": -0.8785364704740983 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_hh_b3.json b/calibration_files/resisting_current_load_hh_b3.json new file mode 100644 index 0000000..435df9d --- /dev/null +++ b/calibration_files/resisting_current_load_hh_b3.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4123896628864365, + -6.823475531269763, + -10.235010744590234, + -13.648227169566894, + -17.061631189090633, + -20.47760564930501, + -23.886034983079075, + -27.30167534775249, + -30.70592108100009, + -34.12458760693233, + -37.539284611921, + -40.94120913344914 + ], + "intensities": [ + 0.18504869366732601, + 0.18040004561274464, + 0.21048279980126627, + 0.2716968499998153, + 0.3574053716576325, + 0.527519439307864, + 0.6197824425410143, + 0.9201933799278003, + 1.0457696392739992, + 1.6170590382037173, + 1.3789959725356447, + 2.009044110931159 + ], + "velocities_std": [ + 0.1627553117521026, + 0.10472010730253321, + 0.1120183647242364, + 0.12617343248040824, + 0.14355871092305986, + 0.15036449578869204, + 0.15631255571472757, + 0.19754824337495822, + 0.17948322897881377, + 0.19088344955464104, + 0.20264665390783718, + 0.38415466304259477 + ], + "a": -0.04892395040994347, + "b": -1.0157003238159605 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_hh_b4.json b/calibration_files/resisting_current_load_hh_b4.json new file mode 100644 index 0000000..d59e315 --- /dev/null +++ b/calibration_files/resisting_current_load_hh_b4.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4121695082027976, + -6.824554932625013, + -10.237850779499658, + -13.6509354846196, + -17.06336093161059, + -20.471121755769413, + -23.88501364798773, + -27.30433403749416, + -30.7207879063268, + -34.121877469140095, + -37.53141209960297, + -40.9489961419481 + ], + "intensities": [ + 0.18311243328323315, + 0.187533658879728, + 0.21236315566535074, + 0.28105213186169564, + 0.3638344459012182, + 0.5335937085713144, + 0.6389214948229889, + 0.9284447624411939, + 1.0432386334844175, + 1.560147466793445, + 1.4120693849335735, + 2.075535626001884 + ], + "velocities_std": [ + 0.14489564803913885, + 0.12160559736002967, + 0.11792007625558881, + 0.1391748512997074, + 0.1437118621134034, + 0.15086001839185512, + 0.18820911950823035, + 0.21776715638227392, + 0.2049390704876942, + 0.227025297906265, + 0.2073145073516913, + 0.36962054875896033 + ], + "a": -0.05651973552639991, + "b": -1.0875613214864253 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_hh_b6.json b/calibration_files/resisting_current_load_hh_b6.json new file mode 100644 index 0000000..5e942be --- /dev/null +++ b/calibration_files/resisting_current_load_hh_b6.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4124769054438486, + -6.825273832449391, + -10.237663557592567, + -13.650054780278111, + -17.061859206410613, + -20.474653689169614, + -23.887637180609374, + -27.30001380348773, + -30.713970353620937, + -34.12433227930313, + -37.53718291705637, + -40.94872058013526 + ], + "intensities": [ + 0.20722168177758726, + 0.20328868232157235, + 0.2384488114258002, + 0.3013598439045397, + 0.39776098987596525, + 0.542224024924887, + 0.663479777329414, + 0.9671075741813547, + 1.0678230460936728, + 1.6448125676914274, + 1.4322365713063254, + 2.094336799602215 + ], + "velocities_std": [ + 0.13692546070039555, + 0.12679425223290602, + 0.1476552712374031, + 0.15523431730900583, + 0.18242578102937793, + 0.19299044985143615, + 0.17760162727004347, + 0.1931767949559949, + 0.1707174088035144, + 0.1876724911736178, + 0.1987595743580701, + 0.33865624253706494 + ], + "a": -0.0851339004401859, + "b": -1.3342928731568007 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_hh_b8.json b/calibration_files/resisting_current_load_hh_b8.json new file mode 100644 index 0000000..9887d12 --- /dev/null +++ b/calibration_files/resisting_current_load_hh_b8.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.4122947257249763, + -6.825006860481938, + -10.238273648260948, + -13.651301161413352, + -17.063185445069447, + -20.475610864104105, + -23.8888449746979, + -27.300071102317013, + -30.71372027157043, + -34.12789496733372, + -37.54037633430527, + -40.955089894315165 + ], + "intensities": [ + 0.2208736286540598, + 0.22807247964490554, + 0.30704565826013497, + 0.3211250403209993, + 0.39397329761149125, + 0.5693886055632087, + 0.6937546112360465, + 0.9903805238413066, + 1.0729209878044244, + 1.562140967389249, + 1.3685302540860678, + 2.0224120512064334 + ], + "velocities_std": [ + 0.13571116224010635, + 0.14283492009058515, + 0.21004419096417162, + 0.19164749313483445, + 0.1559303600236407, + 0.14901243153649268, + 0.1689725634084108, + 0.2060416304565691, + 0.19381139269424097, + 0.22386149754375761, + 0.22413923670366878, + 0.3640467352272724 + ], + "a": -0.1223547649293402, + "b": -1.6190083488141043 +} \ No newline at end of file diff --git a/calibration_files/resisting_current_load_hl_b10.json b/calibration_files/resisting_current_load_hl_b10.json new file mode 100644 index 0000000..3784667 --- /dev/null +++ b/calibration_files/resisting_current_load_hl_b10.json @@ -0,0 +1,46 @@ +{ + "velocities": [ + -3.409737442179061, + -6.823610077178679, + -10.234296717346671, + -13.64679238220741, + -17.059681444479814, + -20.470556000633948, + -23.88306303353799, + -27.29311261272856, + -30.70529648460968, + -34.11653378821919, + -37.52937806346429, + -40.940233886266455 + ], + "intensities": [ + 0.295462438367951, + 0.3489281184096797, + 0.3443942732068211, + 0.35699830471635124, + 0.42093845958635534, + 0.5775719373560323, + 0.7297574205610777, + 0.9339640778047322, + 1.043738798065894, + 1.5122314393157943, + 1.38686032572716, + 2.013714849584207 + ], + "velocities_std": [ + 0.17045843404589545, + 0.2562122376281941, + 0.23419495371700577, + 0.15068923269466547, + 0.15185582579687895, + 0.1923588947242439, + 0.25801002799615497, + 0.25066234787152547, + 0.2514374076224203, + 0.28587503682487897, + 0.31268509674467615, + 0.4406270796421223 + ], + "a": -0.06913469565407133, + "b": -2.035780172578649 +} \ No newline at end of file diff --git a/computation.py b/computation.py new file mode 100644 index 0000000..27cea0a --- /dev/null +++ b/computation.py @@ -0,0 +1,46 @@ +import json +import matplotlib.pyplot as plt +import numpy as np +from scipy.optimize import curve_fit + +a=[] +b=[] +h = "h" +for bike in ["10", "8", "6", "4", "3" , "2", "1"]: + with open(f"./calibration_files/resisting_current_load_h{h}_b{bike}.json", "r") as f: + data = json.load(f) + a.append(data["a"]) + b.append(data["b"]) + +def find(pig, prop, cst): + return prop * pig + cst + +pig = 1/np.asarray([11, 15, 19, 25, 28, 32, 36]) + +popt = curve_fit(find, pig, b) + + +plt.plot(pig, b) +plt.plot(pig, find(pig, *popt[0])) +plt.show() + +a=[] +b=[] +bike = "10" +for h in ["h", "5", "4", "3", "2", "l"]: + with open(f"./calibration_files/resisting_current_load_h{h}_b{bike}.json", "r") as f: + data = json.load(f) + a.append(data["a"]) + b.append(data["b"]) + +def find(ht, prop, cst): + return prop * ht + cst + +ht = np.asarray([6, 5, 4, 3, 2, 0]) + +popt = curve_fit(find, ht, b) + + +plt.plot(ht, b) +plt.plot(ht, find(ht, *popt[0])) +plt.show() \ No newline at end of file diff --git a/ergocycleS2M/motor_control/motor_controller.py b/ergocycleS2M/motor_control/motor_controller.py index 6bda8a5..d710a47 100644 --- a/ergocycleS2M/motor_control/motor_controller.py +++ b/ergocycleS2M/motor_control/motor_controller.py @@ -75,7 +75,10 @@ def __init__( self.odrive_board = odrive.find_any() # The following line has been written to simplify the occurrences of `self.odrive_board.axis0` in the code and # if the motor happened to be wired on axis 1, it would be easier to change it. + self.odrive_board.clear_errors() self.axis = self.odrive_board.axis0 + self._gains_path = gains_path + self.gains_configuration(False) print("Odrive found") self._watchdog_is_ready = self.config_watchdog(enable_watchdog) @@ -84,8 +87,6 @@ def __init__( self._direction = DirectionMode.FORWARD self.previous_control_mode = ControlMode.STOP - self._gains_path = gains_path - if file_path: self.file_path = file_path else: diff --git a/ergocycleS2M/parameters/gains.json b/ergocycleS2M/parameters/gains.json index 076b783..458c937 100644 --- a/ergocycleS2M/parameters/gains.json +++ b/ergocycleS2M/parameters/gains.json @@ -1,6 +1,6 @@ { "pos_gain": 1.0, - "k_vel_gain": 0.02, + "k_vel_gain": 0.01, "k_vel_integrator_gain": 0.1, "bandwidth": 100.0 -} \ No newline at end of file +} diff --git a/ergocycleS2M/parameters/hardware_and_security.json b/ergocycleS2M/parameters/hardware_and_security.json index 4320524..7ea7c56 100644 --- a/ergocycleS2M/parameters/hardware_and_security.json +++ b/ergocycleS2M/parameters/hardware_and_security.json @@ -22,7 +22,7 @@ "resistance_calib_max_voltage": 20.0, "requested_current_range": 25.0, "current_control_bandwidth": 100.0, - "current_lim": 10.416666666666666, + "current_lim": 12.5, "torque_lim": 38.75160393469862, "power_lim": 267.8318593193551, "watchdog_timeout": 0.3, @@ -33,4 +33,4 @@ "maximal_cadence_stop": 31, "resisting_current_proportional": 0.012109769054928311, "resisting_current_constant": 0.38324651511638025 -} \ No newline at end of file +} diff --git a/resisting_current_calibration_load.py b/resisting_current_calibration_load.py index cea1949..bebe52d 100644 --- a/resisting_current_calibration_load.py +++ b/resisting_current_calibration_load.py @@ -1,5 +1,6 @@ """ This script is used to sample the current corresponding to the resisting torque of the motor at different velocities. +60 psi """ import json import matplotlib.pyplot as plt @@ -62,21 +63,20 @@ def calibration(instruction, nb_turns=5): vel_estimate.append(motor.axis.encoder.vel_estimate) print(np.mean(iq_measured), "*/-", np.std(iq_measured), "A") print(np.mean(vel_estimate), "*/-", np.std(vel_estimate), "tr/s") - return np.mean(iq_measured), np.mean(vel_estimate) + return np.mean(iq_measured), np.std(iq_measured), np.mean(vel_estimate), np.std(vel_estimate) if __name__ == "__main__": intensities = [] velocities = [] - for ins in range(-60, 1, 5): - if ins == 0: - average_iq_measured, average_vel_estimate = calibration(1, 1) - intensities.append(average_iq_measured) - velocities.append(average_vel_estimate) - else: - average_iq_measured, average_vel_estimate = calibration(ins) - intensities.append(average_iq_measured) - velocities.append(average_vel_estimate) + intensities_std = [] + velocities_std = [] + for ins in range(5, 61, 5): + average_iq_measured, i_std, average_vel_estimate, v_std = calibration(ins) + intensities.append(average_iq_measured) + velocities.append(average_vel_estimate) + intensities_std.append(i_std) + velocities_std.append(v_std) def lost_current(vel_estimate, resisting_current_proportional, resisting_current_constant): """ @@ -112,9 +112,15 @@ def lost_current(vel_estimate, resisting_current_proportional, resisting_current data = { "velocities": velocities, "intensities": intensities, + "velocities_std": velocities_std, + "intensities": intensities_std, + "a" : popt[0][0], + "b" : popt[0][1] } + print(data["a"], data["b"]) + # Writing to .json json_object = json.dumps(data, indent=4) - with open("./ergocycleS2M/parameters/resisting_current_load_h1_e7.json", "w") as outfile: - outfile.write(json_object) + with open("./calibration_files/resisting_current_load_h3_b10.json", "w") as outfile: + outfile.write(json_object) \ No newline at end of file From 9545f721f03eea80d2eb017caaeeaf72b1527f80 Mon Sep 17 00:00:00 2001 From: Amandine Date: Wed, 12 Jul 2023 10:50:57 -0400 Subject: [PATCH 3/8] Gear and hometrainer --- computation.py | 33 +++++---- computation_2.py | 68 +++++++++++++++++++ ergocycleS2M/application/application.py | 21 ++++-- ergocycleS2M/data_processing/load.py | 22 +++++- ergocycleS2M/data_processing/save.py | 10 +++ ergocycleS2M/gui/ergocycle_gui.py | 34 +++++++++- ergocycleS2M/gui/ergocycle_gui.ui | 63 ++++++++++++++++- ergocycleS2M/gui/gui.py | 28 +++++++- ergocycleS2M/motor_control/mock_controller.py | 18 ++++- .../motor_control/motor_computations.py | 56 +++++++++++---- .../motor_control/motor_controller.py | 60 ++++++++-------- .../parameters/hardware_and_security.json | 10 ++- 12 files changed, 353 insertions(+), 70 deletions(-) create mode 100644 computation_2.py diff --git a/computation.py b/computation.py index 27cea0a..108ff28 100644 --- a/computation.py +++ b/computation.py @@ -3,44 +3,49 @@ import numpy as np from scipy.optimize import curve_fit -a=[] -b=[] +a = [] +b = [] h = "h" -for bike in ["10", "8", "6", "4", "3" , "2", "1"]: +for bike in ["10", "8", "6", "4", "3", "2", "1"]: with open(f"./calibration_files/resisting_current_load_h{h}_b{bike}.json", "r") as f: data = json.load(f) a.append(data["a"]) b.append(data["b"]) + def find(pig, prop, cst): - return prop * pig + cst + return prop / pig + cst + -pig = 1/np.asarray([11, 15, 19, 25, 28, 32, 36]) +pig = np.asarray([11, 15, 19, 25, 28, 32, 36]) -popt = curve_fit(find, pig, b) +popt = curve_fit(find, pig, a) -plt.plot(pig, b) +plt.plot(pig, a) plt.plot(pig, find(pig, *popt[0])) +print(popt[0]) plt.show() -a=[] -b=[] +a = [] +b = [] bike = "10" -for h in ["h", "5", "4", "3", "2", "l"]: +for h in ["h", "5", "4", "3", "2", "1", "l"]: with open(f"./calibration_files/resisting_current_load_h{h}_b{bike}.json", "r") as f: data = json.load(f) a.append(data["a"]) b.append(data["b"]) + def find(ht, prop, cst): return prop * ht + cst -ht = np.asarray([6, 5, 4, 3, 2, 0]) -popt = curve_fit(find, ht, b) +ht = np.asarray([6, 5, 4, 3, 2, 1, 0]) +popt = curve_fit(find, ht, a) +print(popt[0]) -plt.plot(ht, b) +plt.plot(ht, a) plt.plot(ht, find(ht, *popt[0])) -plt.show() \ No newline at end of file +plt.show() diff --git a/computation_2.py b/computation_2.py new file mode 100644 index 0000000..5b8e0e3 --- /dev/null +++ b/computation_2.py @@ -0,0 +1,68 @@ +import json +import matplotlib.pyplot as plt +import numpy as np + +from scipy.optimize import curve_fit + +resisting_current_proportional = [] +resisting_current_constant = [] +x = np.zeros((2, 12)) +h = { + "h": 6, + "5": 5, + "4": 4, + "3": 3, + "2": 2, + # "1": 1, + "l": 0, +} +bike = {"10": 11, "8": 15, "6": 19, "4": 25, "3": 28, "2": 32, "1": 36} +for ht in h.keys(): + if ht == "h": + for bk in bike.keys(): + with open(f"./calibration_files/resisting_current_load_h{ht}_b{bk}.json", "r") as f: + data = json.load(f) + resisting_current_proportional.append(data["a"]) + resisting_current_constant.append(data["b"]) + x[0, len(resisting_current_proportional) - 1] = h[ht] + x[1, len(resisting_current_proportional) - 1] = bike[bk] + + else: + with open(f"./calibration_files/resisting_current_load_h{ht}_b10.json", "r") as f: + data = json.load(f) + resisting_current_proportional.append(data["a"]) + resisting_current_constant.append(data["b"]) + x[0, len(resisting_current_proportional) - 1] = h[ht] + x[1, len(resisting_current_proportional) - 1] = bike["10"] + + +def find_pig(x, coeff_pig, coeff_ht, coeff_cst): + return coeff_pig / x[1] + coeff_ht * x[0] + coeff_cst + + +popt_constant = curve_fit(find_pig, x, resisting_current_constant, p0=[-2.53, -0.023, 0.0]) +popt_proportional = curve_fit(find_pig, x, resisting_current_proportional, p0=[-2.53, -0.023, 0.0]) +print(popt_constant[0]) + +plt.plot(x[0, :], resisting_current_constant, "o") +plt.plot(x[0, :], find_pig(x, *popt_constant[0]), "o") +plt.show() + +plt.plot(x[1, :], resisting_current_constant, "o") +plt.plot(x[1, :], find_pig(x, *popt_constant[0]), "o") +plt.show() + +with open("./ergocycleS2M/parameters/hardware_and_security.json", "r") as f: + hardware_and_security = json.load(f) + +hardware_and_security["resisting_current_cst_gear"] = popt_constant[0][0] +hardware_and_security["resisting_current_cst_hometrainer"] = popt_constant[0][1] +hardware_and_security["resisting_current_cst_constant"] = popt_constant[0][2] +hardware_and_security["resisting_current_prop_gear"] = popt_proportional[0][0] +hardware_and_security["resisting_current_prop_hometrainer"] = popt_proportional[0][1] +hardware_and_security["resisting_current_prop_constant"] = popt_proportional[0][2] + +# Writing to .json +json_object = json.dumps(hardware_and_security, indent=4) +with open("./ergocycleS2M/parameters/hardware_and_security.json", "w") as outfile: + outfile.write(json_object) diff --git a/ergocycleS2M/application/application.py b/ergocycleS2M/application/application.py index c84f577..15d835b 100644 --- a/ergocycleS2M/application/application.py +++ b/ergocycleS2M/application/application.py @@ -7,7 +7,7 @@ import sys import time -from ctypes import c_bool, c_double, c_long, c_wchar_p +from ctypes import c_bool, c_double, c_int, c_long, c_wchar_p from PyQt5 import QtWidgets from ergocycleS2M.data_processing.save import DataSaver @@ -101,6 +101,9 @@ def __init__(self, save_period: float = 10): # Shared memory # Security self.run = mp.Manager().Value(c_bool, True) + # Hardware + self.gear = mp.Manager().Value(c_int, 1) + self.hometrainer = mp.Manager().Value(c_int, 0) # Control self.zero_position = mp.Manager().Value(c_bool, 0.0) self.queue_instructions = mp.Manager().Queue() @@ -178,19 +181,23 @@ def motor_control_loop(self): # because of the resisting torque. # Furthermore, it allows to stop the pedals by reducing the torque if the user has stopped. if control_mode == ControlMode.TORQUE_CONTROL: - self.instruction.value = motor.torque_control(self.spin_box.value, self.ramp_instruction.value) + self.instruction.value = motor.torque_control( + self.spin_box.value, self.ramp_instruction.value, self.gear.value, self.hometrainer.value + ) # The concentric power control mode is based on the torque control mode, but the torque input is # calculated from the current cadence (torque_input = f(power / cadence, resiting torque)). elif control_mode == ControlMode.CONCENTRIC_POWER_CONTROL: self.instruction.value = motor.concentric_power_control( - self.spin_box.value, self.ramp_instruction.value + self.spin_box.value, self.ramp_instruction.value, self.gear.value, self.hometrainer.value ) # The linear control mode is based on the torque control mode, but the torque input is calculated from # the current cadence (torque_input = f(linear_coeff * cadence, resiting torque)). elif control_mode == ControlMode.LINEAR_CONTROL: - self.instruction.value = motor.linear_control(self.spin_box.value, self.ramp_instruction.value) + self.instruction.value = motor.linear_control( + self.spin_box.value, self.ramp_instruction.value, self.gear.value, self.hometrainer.value + ) # The concentric power control mode is based on the cadence control mode, but the cadence input is # calculated from the current torque (cadence_input = f(power / torque, resiting torque)). @@ -236,6 +243,8 @@ def gui(self): app = QtWidgets.QApplication(sys.argv) gui = ErgocycleGUI( run=self.run, + gear=self.gear, + hometrainer=self.hometrainer, zero_position=self.zero_position, queue_instructions=self.queue_instructions, training_mode=self.training_mode, @@ -279,10 +288,14 @@ def data_saver(self): i += 1 file_name = f"{file_name}_{i}" + print("Saving") + saving_widget = DataSaver( file_path=file_name, run=self.run, saving=self.saving, + gear=self.gear, + hometrainer=self.hometrainer, spin_box=self.spin_box, instruction=self.instruction, ramp_instruction=self.ramp_instruction, diff --git a/ergocycleS2M/data_processing/load.py b/ergocycleS2M/data_processing/load.py index 4013ca8..bf25ea4 100644 --- a/ergocycleS2M/data_processing/load.py +++ b/ergocycleS2M/data_processing/load.py @@ -74,6 +74,8 @@ def compute_data( vel_estimate: np.ndarray, turns: np.ndarray, iq_measured: np.ndarray, + gear: np.ndarray, + hometrainer: np.ndarray, ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Computes the data of interest from raw data saved. @@ -104,9 +106,11 @@ def compute_data( for i in range(nb_points): cadence[i] = motor_object.compute_cadence(vel_estimate[i]) angle[i] = motor_object.compute_angle(turns[i]) - user_torque[i] = motor_object.compute_user_torque(iq_measured[i], vel_estimate[i]) + user_torque[i] = motor_object.compute_user_torque(iq_measured[i], vel_estimate[i], gear[i], hometrainer[i]) user_power[i] = motor_object.compute_user_power(user_torque[i], cadence[i]) - resisting_torque[i] = motor_object.compute_resisting_torque(iq_measured[i], vel_estimate[i]) + resisting_torque[i] = motor_object.compute_resisting_torque( + iq_measured[i], vel_estimate[i], gear[i], hometrainer[i] + ) motor_torque[i] = motor_object.compute_motor_torque(iq_measured[i]) return user_torque, cadence, angle, user_power, resisting_torque, motor_torque @@ -151,7 +155,13 @@ def interpolate_data(data: dict, frequency: float = 10) -> dict: data_interpolated["lap"][j] = 0.0 # For the other keys, which are strings, instructions or errors, we take the nearest value. + if "gear" not in data.keys(): + data["gear"] = np.zeros(len(data["time"])) + if "hometrainer" not in data.keys(): + data["hometrainer"] = np.zeros(len(data["time"])) nearest_keys = [ + "gear", + "hometrainer", "state", "control_mode", "direction", @@ -268,7 +278,13 @@ def read(data_path: str, sample_frequency: float, window_length: int) -> dict: data_interpolated["user_power"], data_interpolated["resisting_torque"], data_interpolated["motor_torque"], - ) = compute_data(data_interpolated["vel_estimate"], data_interpolated["turns"], data_interpolated["iq_measured"]) + ) = compute_data( + data_interpolated["vel_estimate"], + data_interpolated["turns"], + data_interpolated["iq_measured"], + data_interpolated["gear"], + data_interpolated["hometrainer"], + ) smoothed_data = smooth_data(data_interpolated, window_length) return smoothed_data diff --git a/ergocycleS2M/data_processing/save.py b/ergocycleS2M/data_processing/save.py index a550492..39802d0 100644 --- a/ergocycleS2M/data_processing/save.py +++ b/ergocycleS2M/data_processing/save.py @@ -34,6 +34,8 @@ def save(data_dict, data_path): def save_data_to_file( file_path: str, time: float = None, + gear: int = 0, + hometrainer: int = 0, spin_box: float = None, instruction: float = None, ramp_instruction: float = None, @@ -111,6 +113,8 @@ def save_data_to_file( data = { "time": time, "spin_box": spin_box, + "gear": gear, + "hometrainer": hometrainer, "instruction": instruction, "ramp_instruction": ramp_instruction, "comments": comment, @@ -141,6 +145,8 @@ def __init__( file_path: str, run: mp.Value, saving: mp.Value, + gear: mp.Value, + hometrainer: mp.Value, spin_box: mp.Value, instruction: mp.Value, ramp_instruction: mp.Value, @@ -172,6 +178,8 @@ def __init__( # Data self.file_path = file_path + self.gear = gear + self.hometrainer = hometrainer self.spin_box = spin_box self.instruction = instruction self.ramp_instruction = ramp_instruction @@ -229,6 +237,8 @@ def save_data_to_file(self): data = { "time": time.time() - self.start_time, "motor_time": self.motor_time.value, + "gear": self.gear.value, + "hometrainer": self.hometrainer.value, "spin_box": self.spin_box.value, "instruction": self.instruction.value, "ramp_instruction": self.ramp_instruction.value, diff --git a/ergocycleS2M/gui/ergocycle_gui.py b/ergocycleS2M/gui/ergocycle_gui.py index 9550984..de064ea 100644 --- a/ergocycleS2M/gui/ergocycle_gui.py +++ b/ergocycleS2M/gui/ergocycle_gui.py @@ -14,7 +14,7 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(1060, 714) + MainWindow.resize(1060, 769) MainWindow.setMinimumSize(QtCore.QSize(0, 0)) MainWindow.setMaximumSize(QtCore.QSize(1060, 16777215)) self.centralwidget = QtWidgets.QWidget(MainWindow) @@ -150,6 +150,36 @@ def setupUi(self, MainWindow): self.command_verticalLayout.addWidget(self.line_4) self.command_horizontalLayout = QtWidgets.QHBoxLayout() self.command_horizontalLayout.setObjectName("command_horizontalLayout") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.gear_label = QtWidgets.QLabel(self.centralwidget) + font = QtGui.QFont() + font.setPointSize(8) + self.gear_label.setFont(font) + self.gear_label.setObjectName("gear_label") + self.verticalLayout.addWidget(self.gear_label) + self.gear_spinBox = QtWidgets.QSpinBox(self.centralwidget) + self.gear_spinBox.setMaximumSize(QtCore.QSize(50, 16777215)) + self.gear_spinBox.setObjectName("gear_spinBox") + self.verticalLayout.addWidget(self.gear_spinBox) + self.hometrainer_label = QtWidgets.QLabel(self.centralwidget) + font = QtGui.QFont() + font.setPointSize(8) + self.hometrainer_label.setFont(font) + self.hometrainer_label.setObjectName("hometrainer_label") + self.verticalLayout.addWidget(self.hometrainer_label) + self.hometrainer_spinBox = QtWidgets.QSpinBox(self.centralwidget) + self.hometrainer_spinBox.setMaximumSize(QtCore.QSize(50, 16777215)) + self.hometrainer_spinBox.setObjectName("hometrainer_spinBox") + self.verticalLayout.addWidget(self.hometrainer_spinBox) + self.command_horizontalLayout.addLayout(self.verticalLayout) + self.line_5 = QtWidgets.QFrame(self.centralwidget) + self.line_5.setLineWidth(0) + self.line_5.setMidLineWidth(20) + self.line_5.setFrameShape(QtWidgets.QFrame.VLine) + self.line_5.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_5.setObjectName("line_5") + self.command_horizontalLayout.addWidget(self.line_5) self.verticalLayout_8 = QtWidgets.QVBoxLayout() self.verticalLayout_8.setObjectName("verticalLayout_8") self.label = QtWidgets.QLabel(self.centralwidget) @@ -434,6 +464,8 @@ def retranslateUi(self, MainWindow): self.average_power_label.setText(_translate("MainWindow", "Average power")) self.average_power_display.setText(_translate("MainWindow", "TextLabel")) self.emergency_pushButton.setText(_translate("MainWindow", "EMERGENCY STOP")) + self.gear_label.setText(_translate("MainWindow", "Gear")) + self.hometrainer_label.setText(_translate("MainWindow", "Home trainer")) self.label.setText(_translate("MainWindow", "Reset the angle and turns")) self.angle_reset_pushButton.setText(_translate("MainWindow", "0 ° and 0 tr")) self.stop_pushButton.setText(_translate("MainWindow", "Stop")) diff --git a/ergocycleS2M/gui/ergocycle_gui.ui b/ergocycleS2M/gui/ergocycle_gui.ui index df618e5..57107c9 100644 --- a/ergocycleS2M/gui/ergocycle_gui.ui +++ b/ergocycleS2M/gui/ergocycle_gui.ui @@ -7,7 +7,7 @@ 0 0 1060 - 714 + 769 @@ -344,6 +344,67 @@ + + + + + + + 8 + + + + Gear + + + + + + + + 50 + 16777215 + + + + + + + + + 8 + + + + Home trainer + + + + + + + + 50 + 16777215 + + + + + + + + + + 0 + + + 20 + + + Qt::Vertical + + + diff --git a/ergocycleS2M/gui/gui.py b/ergocycleS2M/gui/gui.py index 6d2b050..24bfe8f 100644 --- a/ergocycleS2M/gui/gui.py +++ b/ergocycleS2M/gui/gui.py @@ -24,10 +24,11 @@ ) - class ErgocycleGUI(QtWidgets.QMainWindow): def __init__( self, + gear: mp.Manager().Value, + hometrainer: mp.Manager().Value, run: mp.Manager().Value, zero_position: mp.Manager().Value, queue_instructions: mp.Queue, @@ -64,6 +65,9 @@ def __init__( # Shared memory # Security self.run = run + # Hardware + self.gear = gear + self.hometrainer = hometrainer # Control self.zero_position = zero_position self.queue_instruction = queue_instructions @@ -104,6 +108,19 @@ def __init__( ) self._color_grey = QtGui.QColor(220, 220, 220) + # Hardware + self.gui_gear = 0 + self.ui.gear_spinBox.valueChanged.connect(self._update_gear) + self.ui.gear_spinBox.setValue(0) + self.ui.gear_spinBox.setSingleStep(1) + self.ui.gear_spinBox.setRange(0, 10) + self.gui_hometrainer = 0 + self.ui.hometrainer_spinBox.valueChanged.connect(self._update_hometrainer) + self.ui.hometrainer_spinBox.setValue(0) + self.ui.hometrainer_spinBox.setSingleStep(1) + self.ui.hometrainer_spinBox.setRange(0, 6) + self.ui.hometrainer_spinBox.setEnabled(False) + # Control self.motor_started = False self._gui_control_mode = GUIControlMode.POWER @@ -208,6 +225,13 @@ def _check_text(self, text): self.ui.save_lineEdit.setText(new_text) self.ui.save_lineEdit.setCursorPosition(cursor_pos - 1) + def _update_gear(self): + self.gear.value = self.gui_gear = self.ui.gear_spinBox.value() + self.ui.hometrainer_spinBox.setEnabled(self.gear.value != 0) + + def _update_hometrainer(self): + self.hometrainer.value = self.gui_hometrainer = self.ui.hometrainer_spinBox.value() + def _update_instruction_display_on_training_mode_change(self): """ Update the display accordingly to the training mode. @@ -615,7 +639,7 @@ def loop(self): turns = self.turns.value vel_estimate = self.vel_estimate.value - user_torque = self.motor_computations.compute_user_torque(i_measured, vel_estimate) + user_torque = self.motor_computations.compute_user_torque(i_measured, vel_estimate, self.gui_gear, self.gui_hometrainer) cadence = self.motor_computations.compute_cadence(vel_estimate) user_power = self.motor_computations.compute_user_power(user_torque, cadence) angle = self.motor_computations.compute_angle(turns) diff --git a/ergocycleS2M/motor_control/mock_controller.py b/ergocycleS2M/motor_control/mock_controller.py index 2ae99fb..ba65f2c 100644 --- a/ergocycleS2M/motor_control/mock_controller.py +++ b/ergocycleS2M/motor_control/mock_controller.py @@ -260,6 +260,8 @@ def torque_control( self, user_torque: float = 0.0, torque_ramp_rate: float = 2.0, + gear: int = 0, + hometrainer: int = 0, resisting_torque: float = None, control_mode: ControlMode = ControlMode.TORQUE_CONTROL, ): @@ -311,7 +313,12 @@ def torque_control( return motor_torque # Nm at the pedals def concentric_power_control( - self, power: float = 0.0, torque_ramp_rate: float = 2.0, resisting_torque: float = None + self, + power: float = 0.0, + torque_ramp_rate: float = 2.0, + gear: int = 0, + hometrainer: int = 0, + resisting_torque: float = None, ): """ Ensure a constant power at the pedals. In concentric mode, the power is positive when the user is pedaling and @@ -373,7 +380,14 @@ def eccentric_power_control(self, power: float = 0.0, cadence_ramp_rate: float = cadence = min(abs(power / torque) / 2 / np.pi * 60, cadence_max) return self.cadence_control(cadence, cadence_ramp_rate, ControlMode.ECCENTRIC_POWER_CONTROL) - def linear_control(self, linear_coeff: float = 0.0, torque_ramp_rate: float = 2.0, resisting_torque: float = None): + def linear_control( + self, + linear_coeff: float = 0.0, + torque_ramp_rate: float = 2.0, + gear: int = 0, + hometrainer: int = 0, + resisting_torque: float = None, + ): """ Produce a torque proportional to the user's cadence. diff --git a/ergocycleS2M/motor_control/motor_computations.py b/ergocycleS2M/motor_control/motor_computations.py index 96e2ce6..651192a 100644 --- a/ergocycleS2M/motor_control/motor_computations.py +++ b/ergocycleS2M/motor_control/motor_computations.py @@ -23,6 +23,30 @@ def __init__(self, hardware_and_security_path: str = json_path): self.resisting_current_proportional = self.hardware_and_security["resisting_current_proportional"] self.resisting_current_constant = self.hardware_and_security["resisting_current_constant"] + self.prop_gear = self.hardware_and_security["resisting_current_prop_gear"] + self.prop_hometrainer = self.hardware_and_security["resisting_current_prop_hometrainer"] + self.prop_cst = self.hardware_and_security["resisting_current_cst_constant"] + self.cst_gear = self.hardware_and_security["resisting_current_cst_gear"] + self.cst_hometrainer = self.hardware_and_security["resisting_current_cst_hometrainer"] + self.cst_cst = self.hardware_and_security["resisting_current_cst_constant"] + + self.gears = [0, 11, 13, 15, 17, 19, 22, 25, 28, 32, 36] + + def compute_resisting_current_coefficients(self, gear: int = 0, hometrainer: int = 0): + """ """ + if gear == 0: + return ( + self.hardware_and_security["resisting_current_proportional"], + self.hardware_and_security["resisting_current_constant"], + ) + else: + resisting_current_proportional = ( + self.prop_gear / self.gears[gear] + self.prop_hometrainer * hometrainer + self.prop_cst + ) + resisting_current_constant = ( + self.cst_gear / self.gears[gear] + self.cst_hometrainer * hometrainer + self.cst_cst + ) + return resisting_current_proportional, resisting_current_constant @staticmethod def compute_angle(turns: float) -> float: @@ -58,11 +82,12 @@ def compute_cadence(self, vel_estimate: float) -> float: return -vel_estimate * self.reduction_ratio * 60 def compute_resisting_torque_for_positive_velocity(self, vel_estimate: float) -> float: - return np.sign(vel_estimate) * ( - self.resisting_current_proportional * abs(vel_estimate) + self.resisting_current_constant - ) + resisting_current_proportional, resisting_current_constant = self.compute_resisting_current_coefficients() + return np.sign(vel_estimate) * (resisting_current_proportional * abs(vel_estimate) + resisting_current_constant) - def compute_resisting_current(self, i_measured: float, vel_estimate: float) -> float: + def compute_resisting_current( + self, i_measured: float, vel_estimate: float, gear: int = 0, hometrainer: int = 0 + ) -> float: """ Returns the current corresponding to the resisting torque. @@ -78,16 +103,19 @@ def compute_resisting_current(self, i_measured: float, vel_estimate: float) -> f resisting_current : float The current corresponding to the resisting torque. """ + _, resisting_current_constant = self.compute_resisting_current_coefficients(gear, hometrainer) if vel_estimate != 0.0: resisting_current = self.compute_resisting_torque_for_positive_velocity(vel_estimate) else: # As the motor is not moving, we consider that all the current under the resisting_current_constant is # dissipated in the motor, the rest corresponds to the user torque. This is not what actually happens but - # this choice has been made, in case of the study of a static movement it has to be adapted. - resisting_current = -np.sign(i_measured) * min(self.resisting_current_constant, abs(i_measured)) + # this choice has been made, in case of the study of resisting_current_proportional static movement it has to be adapted. + resisting_current = -np.sign(i_measured) * min(resisting_current_constant, abs(i_measured)) return resisting_current - def compute_resisting_torque(self, i_measured: float, vel_estimate: float) -> float: + def compute_resisting_torque( + self, i_measured: float, vel_estimate: float, gear: int = 0, hometrainer: int = 0 + ) -> float: """ Returns the resisting torque. @@ -104,13 +132,13 @@ def compute_resisting_torque(self, i_measured: float, vel_estimate: float) -> fl resisting_torque : float The resisting torque due to solid frictions in Nm at the pedals. """ - return self.torque_constant * self.compute_resisting_current(i_measured, vel_estimate) / self.reduction_ratio + return ( + self.torque_constant + * self.compute_resisting_current(i_measured, vel_estimate, gear, hometrainer) + / self.reduction_ratio + ) - def compute_user_torque( - self, - i_measured: float, - vel_estimate: float, - ) -> float: + def compute_user_torque(self, i_measured: float, vel_estimate: float, gear: int = 0, hometrainer: int = 0) -> float: """ Returns the measured user torque (the resisting torque is subtracted from the motor torque). @@ -127,7 +155,7 @@ def compute_user_torque( The measured user torque in Nm at the pedals. """ return ( - -self.compute_resisting_torque(i_measured, vel_estimate) + -self.compute_resisting_torque(i_measured, vel_estimate, gear, hometrainer) - self.torque_constant * i_measured / self.reduction_ratio ) diff --git a/ergocycleS2M/motor_control/motor_controller.py b/ergocycleS2M/motor_control/motor_controller.py index d710a47..c6f6f3e 100644 --- a/ergocycleS2M/motor_control/motor_controller.py +++ b/ergocycleS2M/motor_control/motor_controller.py @@ -296,15 +296,11 @@ def gains_configuration( # For position and cadence control if k_vel_gain is not None: self.axis.controller.config.vel_gain = ( - k_vel_gain - * self.axis.motor.config.torque_constant - * self.axis.encoder.config.cpr + k_vel_gain * self.axis.motor.config.torque_constant * self.axis.encoder.config.cpr ) if k_vel_integrator_gain is not None: self.axis.controller.config.vel_integrator_gain = ( - k_vel_integrator_gain - * self.axis.motor.config.torque_constant - * self.axis.encoder.config.cpr + k_vel_integrator_gain * self.axis.motor.config.torque_constant * self.axis.encoder.config.cpr ) # For position, cadence and torque control if current_gain is not None: @@ -340,9 +336,7 @@ def hardware_and_security_configuration(self) -> None: self.axis.controller.config.vel_limit = ( self.hardware_and_security["pedals_cadence_limit"] / self.reduction_ratio / 60 ) # tr/s - self.axis.controller.config.vel_limit_tolerance = self.hardware_and_security[ - "vel_limit_tolerance" - ] # tr/s + self.axis.controller.config.vel_limit_tolerance = self.hardware_and_security["vel_limit_tolerance"] # tr/s # Encoder self.axis.encoder.config.mode = self.hardware_and_security["mode"] # Mode of the encoder @@ -354,19 +348,11 @@ def hardware_and_security_configuration(self) -> None: self.axis.motor.config.pole_pairs = self.hardware_and_security["pole_pairs"] self.axis.motor.config.torque_constant = self.hardware_and_security["torque_constant"] self.axis.motor.config.calibration_current = self.hardware_and_security["calibration_current"] - self.axis.motor.config.resistance_calib_max_voltage = self.hardware_and_security[ - "resistance_calib_max_voltage" - ] - self.axis.motor.config.requested_current_range = self.hardware_and_security[ - "requested_current_range" - ] - self.axis.motor.config.current_control_bandwidth = self.hardware_and_security[ - "current_control_bandwidth" - ] + self.axis.motor.config.resistance_calib_max_voltage = self.hardware_and_security["resistance_calib_max_voltage"] + self.axis.motor.config.requested_current_range = self.hardware_and_security["requested_current_range"] + self.axis.motor.config.current_control_bandwidth = self.hardware_and_security["current_control_bandwidth"] self.axis.motor.config.current_lim = self.hardware_and_security["current_lim"] - self.axis.motor.config.torque_lim = ( - self.hardware_and_security["torque_lim"] * self.reduction_ratio - ) + self.axis.motor.config.torque_lim = self.hardware_and_security["torque_lim"] * self.reduction_ratio # cadence and acceleration limits self.axis.trap_traj.config.vel_limit = self.axis.controller.config.vel_limit @@ -525,6 +511,8 @@ def torque_control( self, user_torque: float = 0.0, torque_ramp_rate: float = 2.0, + gear: int = 0, + hometrainer: int = 0, resisting_torque: float = None, control_mode: ControlMode = ControlMode.TORQUE_CONTROL, ) -> float: @@ -572,7 +560,7 @@ def torque_control( if resisting_torque is None: resisting_torque = self.compute_resisting_torque( - self.axis.motor.current_control.Iq_measured, vel_estimate + self.axis.motor.current_control.Iq_measured, vel_estimate, gear, hometrainer ) if user_torque == 0.0: @@ -604,7 +592,12 @@ def torque_control( return self.axis.controller.torque_setpoint / self.reduction_ratio # Nm at the pedals def concentric_power_control( - self, power: float = 0.0, torque_ramp_rate: float = 2.0, resisting_torque: float = None + self, + power: float = 0.0, + torque_ramp_rate: float = 2.0, + gear: int = 0, + hometrainer: int = 0, + resisting_torque: float = None, ) -> float: """ Ensure a constant power at the pedals. In concentric mode, the power is positive when the user is pedaling and @@ -627,11 +620,15 @@ def concentric_power_control( """ cadence = abs(self.axis.encoder.vel_estimate * self.reduction_ratio * 2 * np.pi) # rad/s if cadence == 0: - return self.torque_control(0.0, torque_ramp_rate, resisting_torque, ControlMode.CONCENTRIC_POWER_CONTROL) + return self.torque_control( + 0.0, torque_ramp_rate, gear, hometrainer, resisting_torque, ControlMode.CONCENTRIC_POWER_CONTROL + ) else: return self.torque_control( min(abs(power) / cadence, self.hardware_and_security["torque_lim"]), torque_ramp_rate, + gear, + hometrainer, resisting_torque, ControlMode.CONCENTRIC_POWER_CONTROL, ) @@ -669,7 +666,12 @@ def eccentric_power_control( return self.cadence_control(cadence, cadence_ramp_rate, ControlMode.ECCENTRIC_POWER_CONTROL) def linear_control( - self, linear_coeff: float = 0.0, torque_ramp_rate: float = 2.0, resisting_torque: float = None + self, + linear_coeff: float = 0.0, + torque_ramp_rate: float = 2.0, + gear: int = 0, + hometrainer: int = 0, + resisting_torque: float = None, ) -> float: """ Produce a torque proportional to the user's cadence. @@ -692,6 +694,8 @@ def linear_control( return self.torque_control( min(cadence * abs(linear_coeff), self.hardware_and_security["torque_lim"]), torque_ramp_rate, + gear, + hometrainer, resisting_torque, ControlMode.LINEAR_CONTROL, ) @@ -817,13 +821,15 @@ def get_motor_torque(self) -> float: """ return self.compute_motor_torque(self.axis.motor.current_control.Iq_measured) - def get_resisting_torque(self) -> float: + def get_resisting_torque(self, gear=0, hometrainer=0) -> float: """ Returns the resisting torque. """ return self.compute_resisting_torque( self.axis.motor.current_control.Iq_measured, self.axis.encoder.vel_estimate, + gear, + hometrainer, ) def get_user_torque(self) -> float: @@ -934,4 +940,4 @@ def minimal_save_data_to_file( "can_error": self.odrive_board.can.error, } - save(data, file_path) \ No newline at end of file + save(data, file_path) diff --git a/ergocycleS2M/parameters/hardware_and_security.json b/ergocycleS2M/parameters/hardware_and_security.json index 7ea7c56..687c899 100644 --- a/ergocycleS2M/parameters/hardware_and_security.json +++ b/ergocycleS2M/parameters/hardware_and_security.json @@ -32,5 +32,11 @@ "torque_ramp_rate_lim": 5.5, "maximal_cadence_stop": 31, "resisting_current_proportional": 0.012109769054928311, - "resisting_current_constant": 0.38324651511638025 -} + "resisting_current_constant": 0.38324651511638025, + "resisting_current_cst_gear": -26.865106709394023, + "resisting_current_cst_hometrainer": -0.07514872658063802, + "resisting_current_cst_constant": 0.4242290168121604, + "resisting_current_prop_gear": -2.912417074524715, + "resisting_current_prop_hometrainer": -0.02090332448016754, + "resisting_current_prop_constant": 0.18166617458193188 +} \ No newline at end of file From 43844a883b4dc7a9a6d334298c8af444046da98c Mon Sep 17 00:00:00 2001 From: Amandine Date: Wed, 12 Jul 2023 12:13:42 -0400 Subject: [PATCH 4/8] Ibus_hard_max --- ergocycleS2M/motor_control/motor_controller.py | 1 + ergocycleS2M/parameters/write_to_json.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ergocycleS2M/motor_control/motor_controller.py b/ergocycleS2M/motor_control/motor_controller.py index c6f6f3e..ffb6ac1 100644 --- a/ergocycleS2M/motor_control/motor_controller.py +++ b/ergocycleS2M/motor_control/motor_controller.py @@ -344,6 +344,7 @@ def hardware_and_security_configuration(self) -> None: self.axis.encoder.config.calib_scan_distance = self.hardware_and_security["calib_scan_distance"] # Motor + self.odrive_board.motor.config.Ibus_hard_max = self.hardware_and_security["Ibus_hard_max"] self.axis.motor.config.motor_type = self.hardware_and_security["motor_type"] self.axis.motor.config.pole_pairs = self.hardware_and_security["pole_pairs"] self.axis.motor.config.torque_constant = self.hardware_and_security["torque_constant"] diff --git a/ergocycleS2M/parameters/write_to_json.py b/ergocycleS2M/parameters/write_to_json.py index 3088c92..793f736 100644 --- a/ergocycleS2M/parameters/write_to_json.py +++ b/ergocycleS2M/parameters/write_to_json.py @@ -6,7 +6,7 @@ with open("hardware_and_security.json", "r") as hardware_and_security_file: hardware_and_security = json.load(hardware_and_security_file) -current_lim = 500 / 48 # 500W / 48V # Not sure of this value +current_lim = 60 # A # Calculated with the `torque_constant_computation.py` script # 0.1053225104205947 (calib done by Amandine at the beginning of her internship) # 0.09084625098244366 (calib done by Amandine and Kevin at the end of her internship with greater loads) @@ -56,6 +56,7 @@ "resistance_calib_max_voltage": 20.0, # Not sure of this value "requested_current_range": 25.0, # > current_lim + current_lim_margin but as little as possible "current_control_bandwidth": 100.0, # Not sure of this value + "I_bus_hard_max": 600 / 48, # 600W / 48V "current_lim": current_lim, "torque_lim": torque_lim, "power_lim": torque_lim * cadence_lim * 2 * np.pi / 60, # Motor power limit in W From 348491f57e5fb745d9b1cde080063a4b66c9937c Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 29 Jun 2023 16:05:11 -0400 Subject: [PATCH 5/8] Debug --- Test.bio | Bin 0 -> 44104 bytes ergocycleS2M/application/application.py | 5 ++- .../motor_control/motor_controller.py | 36 +++++++++--------- .../parameters/hardware_and_security.json | 9 +++-- ergocycleS2M/parameters/write_to_json.py | 8 ++++ main.py | 2 +- 6 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 Test.bio diff --git a/Test.bio b/Test.bio new file mode 100644 index 0000000000000000000000000000000000000000..6a1cbd75d4b927093778da52cc1faef72b8fbbb7 GIT binary patch literal 44104 zcmeI*eQXnD90%}?t*}?P4Pgx2=l~Ur8AxJgqPRk*BU^xGZw4aiYI_;Q^~G!VLI|<) zHYBGmp$vl|G-?35n2dygf`l%UfFLphjDR$&GEl*dEr^4_jCbuQ-QBNdzygkb@4tI` zZSS7@Qd+s^Yp*3w? zd~5})~q z*ua@D8_g%@-QBx;=%_a=T#b%11ddMj_k_`05%}(fd{Q-Asby_K?};aOuS*p6D$ZviVfURAb(rg5BkSv+lm#s{Aifkq_LIGvXshDN>b zW6+3X8s~S`hN02$*QcNn$uxZH=Q`*CWn0u1(1>Ij7x&Ez6E(ib`3*E8na1TS{^>I@ z4gK=`E1(g{G_GW93llYVN1XtTNT$(QRv(7O-rftK5h*m3M9QmsezQZcK@2G0UVd>< zG*r_>Te6f_uvwn+D$L{`Jl7RB1i7v;YMnty8?nm%gwhbCz4!znpQabGcbrl0e9#nF z?s4l;m)MnxV0P*;rNAgEJsoHK_gS7DZMilMRK!g0pd!hCQaN->>|>EYf3`GN*K=e1 zPo^|Uwlvp!e5cBP+~cN_MMRbBH5WYM$;qjF@0$33q%ss4^hX_4Jj@ce%TM4gm9<~D zD>S7FDV1NYWjXxEkyI9_V)#L2)*1exGwaV1R&Fp3m3Ve?E3F;|R)#37Y;{YltZ%9i129rBF{Q7T(_qqy%K-*p zq+nvRj2;?HhW-%10E`Sw=P4BpX6l6>0R~{CU=#;dyJ#>oIx+zUV5DG1*7Z1OFtg_N z01UuL!Hg+taFl!=`#ZcjpaLuz}ARlF2=d4v0-EJPSY7!i7CdBfPe`C-89&e#Dk03!qAKBW}e zhzPT9ULU{!j1h z838Z=BLm}`uA;&GG4*+X0T?Nmw7l24X)xUfF(lzBN%S({DJjiu@Ci-Cq_;Qr93l)N zj1&!1(%R(O_oGZU-2Ek%F0|?$gj None: except fibre.libfibre.ObjectLostError: pass self.odrive_board = odrive.find_any() - # The following line has been written to simplify the occurrences of `self.odrive_board.axis0` in the code and + # The following line has been written to simplify the occurrences of `self.odrive_board.axis1` in the code and # if the motor happened to be wired on axis 1, it would be easier to change it. - self.axis = self.odrive_board.axis0 + self.axis = self.odrive_board.axis1 try: self.odrive_board.reboot() except fibre.libfibre.ObjectLostError: pass self.odrive_board = odrive.find_any() - # The following line has been written to simplify the occurrences of `self.odrive_board.axis0` in the code and + # The following line has been written to simplify the occurrences of `self.odrive_board.axis1` in the code and # if the motor happened to be wired on axis 1, it would be easier to change it. - self.axis = self.odrive_board.axis0 + self.axis = self.odrive_board.axis1 def save_configuration(self) -> None: """ @@ -129,17 +129,17 @@ def save_configuration(self) -> None: except fibre.libfibre.ObjectLostError: pass self.odrive_board = odrive.find_any() - # The following line has been written to simplify the occurrences of `self.odrive_board.axis0` in the code and + # The following line has been written to simplify the occurrences of `self.odrive_board.axis1` in the code and # if the motor happened to be wired on axis 1, it would be easier to change it. - self.axis = self.odrive_board.axis0 + self.axis = self.odrive_board.axis1 try: self.odrive_board.reboot() except fibre.libfibre.ObjectLostError: pass self.odrive_board = odrive.find_any() - # The following line has been written to simplify the occurrences of `self.odrive_board.axis0` in the code and + # The following line has been written to simplify the occurrences of `self.odrive_board.axis1` in the code and # if the motor happened to be wired on axis 1, it would be easier to change it. - self.axis = self.odrive_board.axis0 + self.axis = self.odrive_board.axis1 def config_watchdog(self, enable_watchdog: bool, watchdog_timeout: float = None) -> bool: """ @@ -323,9 +323,9 @@ def hardware_and_security_configuration(self) -> None: "dc_bus_overvoltage_ramp_start" ] self.odrive_board.config.dc_bus_overvoltage_ramp_end = self.hardware_and_security["dc_bus_overvoltage_ramp_end"] - self.odrive_board.config.gpio9_mode = self.hardware_and_security["gpio9_mode"] - self.odrive_board.config.gpio10_mode = self.hardware_and_security["gpio10_mode"] - self.odrive_board.config.gpio11_mode = self.hardware_and_security["gpio11_mode"] + self.odrive_board.config.gpio12_mode = self.hardware_and_security["gpio9_mode"] + self.odrive_board.config.gpio13_mode = self.hardware_and_security["gpio10_mode"] + self.odrive_board.config.gpio14_mode = self.hardware_and_security["gpio11_mode"] self.odrive_board.config.max_regen_current = self.hardware_and_security["max_regen_current"] self.odrive_board.config.dc_max_positive_current = self.hardware_and_security["dc_max_positive_current"] self.odrive_board.config.dc_max_negative_current = self.hardware_and_security["dc_max_negative_current"] @@ -344,7 +344,9 @@ def hardware_and_security_configuration(self) -> None: self.axis.encoder.config.calib_scan_distance = self.hardware_and_security["calib_scan_distance"] # Motor - self.odrive_board.motor.config.Ibus_hard_max = self.hardware_and_security["Ibus_hard_max"] + print("here",self.axis.motor.config.I_bus_hard_max) + self.axis.motor.config.requested_current_range = 35.0 + self.axis.motor.config.I_bus_hard_max = self.hardware_and_security["I_bus_hard_max"] self.axis.motor.config.motor_type = self.hardware_and_security["motor_type"] self.axis.motor.config.pole_pairs = self.hardware_and_security["pole_pairs"] self.axis.motor.config.torque_constant = self.hardware_and_security["torque_constant"] @@ -941,4 +943,4 @@ def minimal_save_data_to_file( "can_error": self.odrive_board.can.error, } - save(data, file_path) + save(data, file_path) \ No newline at end of file diff --git a/ergocycleS2M/parameters/hardware_and_security.json b/ergocycleS2M/parameters/hardware_and_security.json index 687c899..35edf09 100644 --- a/ergocycleS2M/parameters/hardware_and_security.json +++ b/ergocycleS2M/parameters/hardware_and_security.json @@ -22,9 +22,10 @@ "resistance_calib_max_voltage": 20.0, "requested_current_range": 25.0, "current_control_bandwidth": 100.0, - "current_lim": 12.5, - "torque_lim": 38.75160393469862, - "power_lim": 267.8318593193551, + "I_bus_hard_max": 12.5, + "current_lim": 30, + "torque_lim": 223.20923866386406, + "power_lim": 1542.7115096794855, "watchdog_timeout": 0.3, "watchdog_feed_time": 0.01, "reduction_ratio": 0.02442002442002442, @@ -39,4 +40,4 @@ "resisting_current_prop_gear": -2.912417074524715, "resisting_current_prop_hometrainer": -0.02090332448016754, "resisting_current_prop_constant": 0.18166617458193188 -} \ No newline at end of file +} diff --git a/ergocycleS2M/parameters/write_to_json.py b/ergocycleS2M/parameters/write_to_json.py index 793f736..047f8d4 100644 --- a/ergocycleS2M/parameters/write_to_json.py +++ b/ergocycleS2M/parameters/write_to_json.py @@ -71,6 +71,14 @@ # `resisting_current_calibration.py` script (it runs a calibration on the motor that lasts several minutes) "resisting_current_proportional": hardware_and_security["resisting_current_proportional"], "resisting_current_constant": hardware_and_security["resisting_current_constant"], + "resisting_current_proportional": hardware_and_security["resisting_current_proportional"], + "resisting_current_constant": hardware_and_security["resisting_current_constant"], + "resisting_current_cst_gear": hardware_and_security["resisting_current_cst_gear"], + "resisting_current_cst_hometrainer": hardware_and_security["resisting_current_cst_hometrainer"], + "resisting_current_cst_constant": hardware_and_security["resisting_current_cst_constant"], + "resisting_current_prop_gear": hardware_and_security["resisting_current_prop_gear"], + "resisting_current_prop_hometrainer": hardware_and_security["resisting_current_prop_hometrainer"], + "resisting_current_prop_constant": hardware_and_security["resisting_current_prop_constant"] } # Serializing json diff --git a/main.py b/main.py index 210aaa1..2685de7 100644 --- a/main.py +++ b/main.py @@ -6,4 +6,4 @@ if __name__ == "__main__": save_period = 0.1 # seconds app = Application(save_period=save_period) - app.start() + app.start() \ No newline at end of file From b4d2428a9abbb72efe26efe0ca8d5e37ff3f6f16 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 30 Jun 2023 11:40:45 -0400 Subject: [PATCH 6/8] Gear and hometrainer --- ergocycleS2M/application/application.py | 5 +---- ergocycleS2M/gui/gui.py | 2 +- ergocycleS2M/motor_control/motor_computations.py | 11 ++++++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/ergocycleS2M/application/application.py b/ergocycleS2M/application/application.py index a1feb80..023cbad 100644 --- a/ergocycleS2M/application/application.py +++ b/ergocycleS2M/application/application.py @@ -102,7 +102,7 @@ def __init__(self, save_period: float = 10): # Security self.run = mp.Manager().Value(c_bool, True) # Hardware - self.gear = mp.Manager().Value(c_int, 1) + self.gear = mp.Manager().Value(c_int, 0) self.hometrainer = mp.Manager().Value(c_int, 0) # Control self.zero_position = mp.Manager().Value(c_bool, 0.0) @@ -218,9 +218,6 @@ def motor_control_loop(self): self.stopping.value = not motor.stopped() # Data - i = motor.axis.motor.current_control.Iq_measured - if i != 0: - print(motor.axis.controller.electrical_power / (i**2)) self.i_measured.value = motor.get_iq_measured() self.turns.value = motor.get_turns() self.vel_estimate.value = motor.get_vel_estimate() diff --git a/ergocycleS2M/gui/gui.py b/ergocycleS2M/gui/gui.py index 24bfe8f..668e8ee 100644 --- a/ergocycleS2M/gui/gui.py +++ b/ergocycleS2M/gui/gui.py @@ -682,4 +682,4 @@ def loop(self): self.spin_box_array[-1] = self.spin_box.value else: self.spin_box_array = None - self._plot_update() + self._plot_update() \ No newline at end of file diff --git a/ergocycleS2M/motor_control/motor_computations.py b/ergocycleS2M/motor_control/motor_computations.py index 651192a..99008ee 100644 --- a/ergocycleS2M/motor_control/motor_computations.py +++ b/ergocycleS2M/motor_control/motor_computations.py @@ -46,7 +46,7 @@ def compute_resisting_current_coefficients(self, gear: int = 0, hometrainer: int resisting_current_constant = ( self.cst_gear / self.gears[gear] + self.cst_hometrainer * hometrainer + self.cst_cst ) - return resisting_current_proportional, resisting_current_constant + return - resisting_current_proportional, - resisting_current_constant @staticmethod def compute_angle(turns: float) -> float: @@ -81,8 +81,9 @@ def compute_cadence(self, vel_estimate: float) -> float: """ return -vel_estimate * self.reduction_ratio * 60 - def compute_resisting_torque_for_positive_velocity(self, vel_estimate: float) -> float: - resisting_current_proportional, resisting_current_constant = self.compute_resisting_current_coefficients() + def compute_resisting_torque_for_positive_velocity(self, vel_estimate: float, gear, hometrainer) -> float: + resisting_current_proportional, resisting_current_constant = self.compute_resisting_current_coefficients(gear, hometrainer) + print(resisting_current_proportional, resisting_current_constant) return np.sign(vel_estimate) * (resisting_current_proportional * abs(vel_estimate) + resisting_current_constant) def compute_resisting_current( @@ -105,7 +106,7 @@ def compute_resisting_current( """ _, resisting_current_constant = self.compute_resisting_current_coefficients(gear, hometrainer) if vel_estimate != 0.0: - resisting_current = self.compute_resisting_torque_for_positive_velocity(vel_estimate) + resisting_current = self.compute_resisting_torque_for_positive_velocity(vel_estimate, gear, hometrainer) else: # As the motor is not moving, we consider that all the current under the resisting_current_constant is # dissipated in the motor, the rest corresponds to the user torque. This is not what actually happens but @@ -192,4 +193,4 @@ def compute_user_power(user_torque: float, cadence: float) -> float: user_power : float The user power in W. """ - return user_torque * cadence * 2 * np.pi / 60 + return user_torque * cadence * 2 * np.pi / 60 \ No newline at end of file From 5cbf44b3a53a22d75b7e9e3bd13ed758957fbae4 Mon Sep 17 00:00:00 2001 From: Amandine Date: Thu, 13 Jul 2023 10:14:21 -0400 Subject: [PATCH 7/8] Minus --- Test.bio | Bin 44104 -> 0 bytes .../resisting_current_load_h1_b10.json | 4 ++-- .../resisting_current_load_h2_b10.json | 4 ++-- .../resisting_current_load_h3_b10.json | 4 ++-- .../resisting_current_load_h4_b10.json | 4 ++-- .../resisting_current_load_h5_b10.json | 4 ++-- .../resisting_current_load_hh_b1.json | 4 ++-- .../resisting_current_load_hh_b10.json | 4 ++-- .../resisting_current_load_hh_b2.json | 4 ++-- .../resisting_current_load_hh_b3.json | 4 ++-- .../resisting_current_load_hh_b4.json | 4 ++-- .../resisting_current_load_hh_b6.json | 4 ++-- .../resisting_current_load_hh_b8.json | 4 ++-- .../resisting_current_load_hl_b10.json | 4 ++-- .../motor_control/motor_computations.py | 8 ++++---- .../parameters/hardware_and_security.json | 14 +++++++------- resisting_current_calibration_load.py | 6 +++--- 17 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 Test.bio diff --git a/Test.bio b/Test.bio deleted file mode 100644 index 6a1cbd75d4b927093778da52cc1faef72b8fbbb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44104 zcmeI*eQXnD90%}?t*}?P4Pgx2=l~Ur8AxJgqPRk*BU^xGZw4aiYI_;Q^~G!VLI|<) zHYBGmp$vl|G-?35n2dygf`l%UfFLphjDR$&GEl*dEr^4_jCbuQ-QBNdzygkb@4tI` zZSS7@Qd+s^Yp*3w? zd~5})~q z*ua@D8_g%@-QBx;=%_a=T#b%11ddMj_k_`05%}(fd{Q-Asby_K?};aOuS*p6D$ZviVfURAb(rg5BkSv+lm#s{Aifkq_LIGvXshDN>b zW6+3X8s~S`hN02$*QcNn$uxZH=Q`*CWn0u1(1>Ij7x&Ez6E(ib`3*E8na1TS{^>I@ z4gK=`E1(g{G_GW93llYVN1XtTNT$(QRv(7O-rftK5h*m3M9QmsezQZcK@2G0UVd>< zG*r_>Te6f_uvwn+D$L{`Jl7RB1i7v;YMnty8?nm%gwhbCz4!znpQabGcbrl0e9#nF z?s4l;m)MnxV0P*;rNAgEJsoHK_gS7DZMilMRK!g0pd!hCQaN->>|>EYf3`GN*K=e1 zPo^|Uwlvp!e5cBP+~cN_MMRbBH5WYM$;qjF@0$33q%ss4^hX_4Jj@ce%TM4gm9<~D zD>S7FDV1NYWjXxEkyI9_V)#L2)*1exGwaV1R&Fp3m3Ve?E3F;|R)#37Y;{YltZ%9i129rBF{Q7T(_qqy%K-*p zq+nvRj2;?HhW-%10E`Sw=P4BpX6l6>0R~{CU=#;dyJ#>oIx+zUV5DG1*7Z1OFtg_N z01UuL!Hg+taFl!=`#ZcjpaLuz}ARlF2=d4v0-EJPSY7!i7CdBfPe`C-89&e#Dk03!qAKBW}e zhzPT9ULU{!j1h z838Z=BLm}`uA;&GG4*+X0T?Nmw7l24X)xUfF(lzBN%S({DJjiu@Ci-Cq_;Qr93l)N zj1&!1(%R(O_oGZU-2Ek%F0|?$gj float: @@ -193,4 +193,4 @@ def compute_user_power(user_torque: float, cadence: float) -> float: user_power : float The user power in W. """ - return user_torque * cadence * 2 * np.pi / 60 \ No newline at end of file + return user_torque * cadence * 2 * np.pi / 60 diff --git a/ergocycleS2M/parameters/hardware_and_security.json b/ergocycleS2M/parameters/hardware_and_security.json index 35edf09..f2628b1 100644 --- a/ergocycleS2M/parameters/hardware_and_security.json +++ b/ergocycleS2M/parameters/hardware_and_security.json @@ -34,10 +34,10 @@ "maximal_cadence_stop": 31, "resisting_current_proportional": 0.012109769054928311, "resisting_current_constant": 0.38324651511638025, - "resisting_current_cst_gear": -26.865106709394023, - "resisting_current_cst_hometrainer": -0.07514872658063802, - "resisting_current_cst_constant": 0.4242290168121604, - "resisting_current_prop_gear": -2.912417074524715, - "resisting_current_prop_hometrainer": -0.02090332448016754, - "resisting_current_prop_constant": 0.18166617458193188 -} + "resisting_current_cst_gear": 26.86510681095839, + "resisting_current_cst_hometrainer": 0.07514872888652697, + "resisting_current_cst_constant": -0.4242290344268693, + "resisting_current_prop_gear": 2.9124170635770343, + "resisting_current_prop_hometrainer": 0.020903324289955783, + "resisting_current_prop_constant": -0.18166617303562008 +} \ No newline at end of file diff --git a/resisting_current_calibration_load.py b/resisting_current_calibration_load.py index bebe52d..b65944b 100644 --- a/resisting_current_calibration_load.py +++ b/resisting_current_calibration_load.py @@ -114,8 +114,8 @@ def lost_current(vel_estimate, resisting_current_proportional, resisting_current "intensities": intensities, "velocities_std": velocities_std, "intensities": intensities_std, - "a" : popt[0][0], - "b" : popt[0][1] + "a" : -popt[0][0], + "b" : -popt[0][1] } print(data["a"], data["b"]) @@ -123,4 +123,4 @@ def lost_current(vel_estimate, resisting_current_proportional, resisting_current # Writing to .json json_object = json.dumps(data, indent=4) with open("./calibration_files/resisting_current_load_h3_b10.json", "w") as outfile: - outfile.write(json_object) \ No newline at end of file + outfile.write(json_object) From d8f86dd4815c83c18eb3f234fb571c5974b3331d Mon Sep 17 00:00:00 2001 From: Lucie Poinsignon Date: Wed, 16 Apr 2025 14:20:47 -0400 Subject: [PATCH 8/8] Power pedal control + Additional user window + Isometric mode --- XP/preXP_970.bio | Bin 4038432 -> 0 bytes ergocycleS2M/application/application.py | 38 +++++- ergocycleS2M/data_processing/save.py | 6 +- ergocycleS2M/gui/enums.py | 1 + ergocycleS2M/gui/gui.py | 81 +++++++++++- ergocycleS2M/gui/gui2.py | 120 ++++++++++++++++++ ergocycleS2M/motor_control/enums.py | 1 + .../motor_control/motor_controller.py | 79 +++++++++++- .../parameters/hardware_and_security.json | 16 +-- 9 files changed, 326 insertions(+), 16 deletions(-) delete mode 100644 XP/preXP_970.bio create mode 100644 ergocycleS2M/gui/gui2.py diff --git a/XP/preXP_970.bio b/XP/preXP_970.bio deleted file mode 100644 index 186ea500114154cf8efc1f4129cb8851b0a56205..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4038432 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 UJ25Z${#(FQVWN-u|AqPg0N&-UMgRZ+ diff --git a/ergocycleS2M/application/application.py b/ergocycleS2M/application/application.py index 023cbad..d4546d2 100644 --- a/ergocycleS2M/application/application.py +++ b/ergocycleS2M/application/application.py @@ -17,6 +17,9 @@ from ergocycleS2M.motor_control.motor_controller import MotorController # from ergocycleS2M.motor_control.mock_controller import MockController +# Import the feedback window (assuming you saved it in a module) +from ergocycleS2M.gui.gui2 import AverageCadenceFeedbackWindow + class Application: """ @@ -135,6 +138,8 @@ def __init__(self, save_period: float = 10): self.motor_error = mp.Manager().Value(c_long, 0) self.sensorless_estimator_error = mp.Manager().Value(c_long, 0) self.can_error = mp.Manager().Value(c_long, 0) + #self.update_period = 0.1, + #self.plot_window_size = 5, # Create the processes self.gui_process = mp.Process(name="GUI process", target=self.gui, daemon=True) @@ -160,6 +165,8 @@ def motor_control_loop(self): motor = MotorController(enable_watchdog=True, external_watchdog=False) motor.zero_position_calibration() is_cadence_control = False + current_mode = None + current_target = None while self.run.value: control_mode = motor.get_control_mode() @@ -169,10 +176,18 @@ def motor_control_loop(self): self.zero_position.value = False if not self.stopping.value: - # Adapt the control of the motor accordingly to the current cadence and torque try: - control_mode, direction = self.queue_instructions.get_nowait() + instruction = self.queue_instructions.get_nowait() + if len(instruction) == 2: + control_mode, direction = instruction + target_angle = None + elif len(instruction) == 3: + control_mode, direction, target_angle = instruction + else: + continue motor.set_direction(direction) + current_mode = control_mode + current_target = target_angle is_cadence_control = False except Exception: pass @@ -212,6 +227,10 @@ def motor_control_loop(self): motor.cadence_control(self.spin_box.value, self.ramp_instruction.value) is_cadence_control = True + elif current_mode == ControlMode.ISOMETRIC_CONTROL and current_target is not None: + motor.isometric_control(current_target, self.ramp_instruction.value) + current_mode = ControlMode.STOP + else: motor.stopping(cadence_ramp_rate=self.ramp_instruction.value) if abs(motor.get_cadence()) < 10.0: @@ -234,6 +253,7 @@ def motor_control_loop(self): self.can_error.value = motor.get_can_error() self.motor_time.value = time.time() + self.user_torque.value = motor.get_user_torque() def gui(self): """ @@ -241,6 +261,7 @@ def gui(self): update the control of the motor in the shared memory. """ app = QtWidgets.QApplication(sys.argv) + # Create the main Ergocycle GUI window. gui = ErgocycleGUI( run=self.run, gear=self.gear, @@ -267,8 +288,20 @@ def gui(self): motor_error=self.motor_error, sensorless_estimator_error=self.sensorless_estimator_error, can_error=self.can_error, + user_torque=self.user_torque, + #update_period=self.plot_window_size, + #plot_window_size=self.plot_window_size, ) + + # Create the Average Cadence Feedback Window. + feedback_window = AverageCadenceFeedbackWindow() + + # Attach the feedback window instance to the main GUI. + gui.feedback_window = feedback_window + + # Show both windows. gui.show() + feedback_window.show() app.exec() def data_saver(self): @@ -316,6 +349,7 @@ def data_saver(self): motor_error=self.motor_error, sensorless_estimator_error=self.sensorless_estimator_error, can_error=self.can_error, + user_torque=self.user_torque, save_period=self.save_period, motor_time=self.motor_time, ) diff --git a/ergocycleS2M/data_processing/save.py b/ergocycleS2M/data_processing/save.py index 39802d0..f776c0f 100644 --- a/ergocycleS2M/data_processing/save.py +++ b/ergocycleS2M/data_processing/save.py @@ -167,8 +167,9 @@ def __init__( motor_error: mp.Value, sensorless_estimator_error: mp.Value, can_error: mp.Value, + user_torque, motor_time: mp.Value, - save_period: float = 0.1, + save_period: float = 0.01, ): super().__init__(parent=None) self.save_period = save_period @@ -201,6 +202,7 @@ def __init__( self.motor_error = motor_error self.sensorless_estimator_error = sensorless_estimator_error self.can_error = can_error + self.user_torque = user_torque self.motor_time = motor_time @@ -259,6 +261,8 @@ def save_data_to_file(self): "motor_error": self.motor_error.value, "sensorless_estimator_error": self.sensorless_estimator_error.value, "can_error": self.can_error.value, + "user_torque": self.user_torque.value, + } save(data, self.file_path) diff --git a/ergocycleS2M/gui/enums.py b/ergocycleS2M/gui/enums.py index 3a8344d..e103dbd 100644 --- a/ergocycleS2M/gui/enums.py +++ b/ergocycleS2M/gui/enums.py @@ -24,6 +24,7 @@ class GUIControlMode(StrEnum): CADENCE = "Cadence" TORQUE = "Torque" POWER = "Power" + ISOMETRIC = "Isometric" class StopwatchStates(Enum): diff --git a/ergocycleS2M/gui/gui.py b/ergocycleS2M/gui/gui.py index 668e8ee..d60c9f9 100644 --- a/ergocycleS2M/gui/gui.py +++ b/ergocycleS2M/gui/gui.py @@ -23,6 +23,9 @@ traduce_error, ) +from ergocycleS2M.gui.gui2 import AverageCadenceFeedbackWindow + + class ErgocycleGUI(QtWidgets.QMainWindow): def __init__( @@ -52,8 +55,9 @@ def __init__( motor_error: mp.Manager().Value, sensorless_estimator_error: mp.Manager().Value, can_error: mp.Manager().Value, + user_torque: mp.Manager().Value, update_period: float = 0.1, - plot_window_size: int = 10, + plot_window_size: int = 5, ): super(ErgocycleGUI, self).__init__(parent=None) self.ui = Ui_MainWindow() @@ -87,6 +91,7 @@ def __init__( self.i_measured = i_measured self.turns = turns self.vel_estimate = vel_estimate + self.user_torque = user_torque # Errors self.error = error self.axis_error = axis_error @@ -178,6 +183,7 @@ def __init__( self.ui.torque_horizontalLayout.insertWidget(0, self.plot_torque) self.plot_window_size = plot_window_size self.size_arrays = int(self.plot_window_size * 1 / update_period) # 20 seconds + self.size_1s = int(1 / update_period) self.time_array = np.linspace(-self.plot_window_size, 0, self.size_arrays) self.cadence_array = np.zeros(self.size_arrays) self.torque_array = np.zeros(self.size_arrays) @@ -250,6 +256,7 @@ def _update_instruction_display_on_training_mode_change(self): GUIControlMode.CADENCE.value, GUIControlMode.LINEAR.value, GUIControlMode.TORQUE.value, + GUIControlMode.ISOMETRIC.value ] ) self.ui.direction_comboBox.setCurrentText(DirectionMode.FORWARD.value) @@ -270,9 +277,9 @@ def _update_instruction_display_on_control_mode_change(self): Update the display accordingly to the control mode. """ self._set_instruction_to_0() - power_step = 10 + power_step = 5 vel_step = 10 - vel_ramp = 30 + vel_ramp = 10 torque_step = 1 torque_ramp = 5 linear_step = 0.1 @@ -346,6 +353,18 @@ def _update_instruction_display_on_control_mode_change(self): self.ui.units_label.setText("N.m") self.ui.ramp_label.setText("Torque ramp") self.ui.acceleration_units_label.setText("N.m/s") + + elif control_mode == GUIControlMode.ISOMETRIC.value: + self.ui.instruction_spinBox.setRange(0, 360) + self.ui.instruction_spinBox.setSingleStep(10) + self.ui.instruction_spinBox.setValue(90) + self.ui.instruction_label.setText("Angle cible") + self.ui.units_label.setText("°") + self.ui.acceleration_spinBox.setRange(0, self.motor_computations.hardware_and_security["pedals_accel_lim"] - 1) + self.ui.acceleration_spinBox.setSingleStep(vel_step) + self.ui.acceleration_spinBox.setValue(vel_ramp) + self.ui.ramp_label.setText("Rampe de cadence") + self.ui.acceleration_units_label.setText("rpm/s") else: raise NotImplementedError(f"{control_mode} control has not been implemented yet.") @@ -422,6 +441,15 @@ def _control_update(self): self._get_sign(self.ui.direction_comboBox.currentText()) * self.ui.instruction_spinBox.value() ) self.queue_instruction.put_nowait((ControlMode.TORQUE_CONTROL, self.ui.direction_comboBox.currentText())) + + elif self._gui_control_mode == GUIControlMode.ISOMETRIC: + target_angle = self.ui.instruction_spinBox.value() + self.spin_box.value = target_angle + self.queue_instruction.put_nowait(( + ControlMode.ISOMETRIC_CONTROL, + self.ui.direction_comboBox.currentText(), + target_angle + )) else: raise NotImplementedError(f"{self._gui_control_mode} control has not been implemented yet.") @@ -640,6 +668,7 @@ def loop(self): vel_estimate = self.vel_estimate.value user_torque = self.motor_computations.compute_user_torque(i_measured, vel_estimate, self.gui_gear, self.gui_hometrainer) + self.user_torque.value = user_torque cadence = self.motor_computations.compute_cadence(vel_estimate) user_power = self.motor_computations.compute_user_power(user_torque, cadence) angle = self.motor_computations.compute_angle(turns) @@ -682,4 +711,48 @@ def loop(self): self.spin_box_array[-1] = self.spin_box.value else: self.spin_box_array = None - self._plot_update() \ No newline at end of file + self._plot_update() + + + average_cadence = float(np.mean(self.cadence_array[-self.size_1s:])) + + + # Tell the feedback window about the new average + self.feedback_window.update_value(average_cadence) + + # Then force its bar-plot redraw + self.feedback_window.update_plot() + + + def keyPressEvent(self, event): + # Only modify the power target if we're in POWER mode. + if self._gui_control_mode == GUIControlMode.POWER.value: + # Get the step from the spin box settings (or set it explicitly) + + + power_step = self.ui.instruction_spinBox.singleStep() + + if event.key() == QtCore.Qt.Key_A: + # Increase power target + new_value = self.ui.instruction_spinBox.value() + power_step + # Clamp to the maximum allowed value + new_value = min(new_value, self.ui.instruction_spinBox.maximum()) + self.ui.instruction_spinBox.setValue(new_value) + # Simulate pressing the update button by calling _control_update + self._control_update() + event.accept() + return + + elif event.key() == QtCore.Qt.Key_B: + # Decrease power target + new_value = self.ui.instruction_spinBox.value() - power_step + # Clamp to the minimum allowed value + new_value = max(new_value, self.ui.instruction_spinBox.minimum()) + self.ui.instruction_spinBox.setValue(new_value) + # Simulate pressing the update button by calling _control_update + self._control_update() + event.accept() + return + + # For all other keys, call the base implementation. + super(ErgocycleGUI, self).keyPressEvent(event) diff --git a/ergocycleS2M/gui/gui2.py b/ergocycleS2M/gui/gui2.py new file mode 100644 index 0000000..a85a521 --- /dev/null +++ b/ergocycleS2M/gui/gui2.py @@ -0,0 +1,120 @@ +import sys +import time +import numpy as np +import matplotlib.pyplot as plt + +from PyQt5 import QtWidgets, QtCore +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas + + +class AverageCadenceFeedbackWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + super(AverageCadenceFeedbackWindow, self).__init__(parent) + self.setWindowTitle("Average Cadence Feedback") + self.setWindowFlags(self.windowFlags() | QtCore.Qt.Window) + self.resize(1000, 1000) + + # Central widget and layout + central_widget = QtWidgets.QWidget(self) + self.setCentralWidget(central_widget) + main_layout = QtWidgets.QVBoxLayout(central_widget) + + # Create Matplotlib figure and canvas for the bar plot + self.figure = plt.Figure(figsize=(5, 5)) + self.canvas = FigureCanvas(self.figure) + + # Add the canvas with a stretch factor of 9 (90% of the space) + main_layout.addWidget(self.canvas, stretch=9) + + # Add a spacer widget with a stretch factor of 1 (10% of the space) + spacer = QtWidgets.QWidget() + spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + main_layout.addWidget(spacer, stretch=1) + + # Create a form layout for the edit boxes (min/max y, target, tolerance) + form_layout = QtWidgets.QFormLayout() + + self.min_y_edit = QtWidgets.QLineEdit("30") + self.max_y_edit = QtWidgets.QLineEdit("80") + self.target_edit = QtWidgets.QLineEdit("60") + self.tolerance_edit = QtWidgets.QLineEdit("5") + + form_layout.addRow("Min Y limit:", self.min_y_edit) + form_layout.addRow("Max Y limit:", self.max_y_edit) + form_layout.addRow("Target (rpm):", self.target_edit) + form_layout.addRow("Tolerance (%):", self.tolerance_edit) + main_layout.addLayout(form_layout) + + # Initialize current average cadence value and out-of-range tracking + self.average_cadence = 0.0 #todo: supprimer + self.out_of_range_start = None + + # Timer to update the plot periodically + #self.update_timer = QtCore.QTimer(self) + #self.update_timer.timeout.connect(self.update_plot) + #self.update_timer.start(100) # update every 100 ms + + def update_value(self, average_cadence: float): + """ + Update the average cadence value. + In your main loop, call: + + feedback_window.update_value(np.mean(self.cadence_array)) + + to supply the current average cadence. + """ + self.average_cadence = average_cadence + + def update_plot(self): + """ + Update the bar plot with the current average cadence. + The bar color is set to: + - Green: if within the tolerance bounds, + - Orange: if outside tolerance for less than 5 seconds, + - Red: if outside tolerance for 5 seconds or more. + """ + # Get current parameters from the edit boxes; if conversion fails, skip update + try: + min_y = float(self.min_y_edit.text()) + max_y = float(self.max_y_edit.text()) + target = float(self.target_edit.text()) + tolerance_pct = float(self.tolerance_edit.text()) + except ValueError: + return + + # Compute tolerance bounds (tolerance as a percentage of the target) + lower_bound = target * (1 - tolerance_pct / 100) + upper_bound = target * (1 + tolerance_pct / 100) + + # Determine the bar color based on the average cadence + if lower_bound <= self.average_cadence <= upper_bound: + color = 'green' + self.out_of_range_start = None # Reset timer if back in tolerance + else: + if self.out_of_range_start is None: + self.out_of_range_start = time.time() + elapsed = time.time() - self.out_of_range_start + # Orange if out-of-range for less than 5 seconds; red otherwise. + color = 'orange' if elapsed < 5 else 'red' + + # Clear the figure and create a new subplot. + self.figure.clf() + ax = self.figure.add_subplot(111) + ax.set_ylim(min_y, max_y) + + # Draw a single bar at x=0 with height equal to the average cadence. + ax.bar(0, self.average_cadence, color=color, width=0.5) + ax.set_xticks([]) # Hide x-axis ticks + + # Draw horizontal lines for target and tolerance bounds. + ax.axhline(target, color='black', linestyle='--', label='Target') + ax.axhline(lower_bound, color='blue', linestyle='--', label='Target - tol') + ax.axhline(upper_bound, color='blue', linestyle='--', label='Target + tol') + #ax.legend(loc='upper right') + + # Optionally add a text label showing the average cadence. + ax.text(0, self.average_cadence, f"{self.average_cadence:.1f} rpm", + ha='center', va='bottom') + + # Refresh the canvas. + self.canvas.draw() diff --git a/ergocycleS2M/motor_control/enums.py b/ergocycleS2M/motor_control/enums.py index 1f75666..8e33e22 100644 --- a/ergocycleS2M/motor_control/enums.py +++ b/ergocycleS2M/motor_control/enums.py @@ -17,6 +17,7 @@ class ControlMode(StrEnum): TORQUE_CONTROL = "Torque control" CONCENTRIC_POWER_CONTROL = "Concentric power control" ECCENTRIC_POWER_CONTROL = "Eccentric power control" + ISOMETRIC_CONTROL = "Isometric control" # Thanks to the StrEnum, we can use the following syntax: diff --git a/ergocycleS2M/motor_control/motor_controller.py b/ergocycleS2M/motor_control/motor_controller.py index 2628318..5c21692 100644 --- a/ergocycleS2M/motor_control/motor_controller.py +++ b/ergocycleS2M/motor_control/motor_controller.py @@ -344,7 +344,7 @@ def hardware_and_security_configuration(self) -> None: self.axis.encoder.config.calib_scan_distance = self.hardware_and_security["calib_scan_distance"] # Motor - print("here",self.axis.motor.config.I_bus_hard_max) + print("here", self.axis.motor.config.I_bus_hard_max) self.axis.motor.config.requested_current_range = 35.0 self.axis.motor.config.I_bus_hard_max = self.hardware_and_security["I_bus_hard_max"] self.axis.motor.config.motor_type = self.hardware_and_security["motor_type"] @@ -703,6 +703,83 @@ def linear_control( ControlMode.LINEAR_CONTROL, ) + def isometric_control( + self, + target_angle: float = 0.0, + cadence_ramp_rate: float = 5.0, + control_mode: ControlMode = ControlMode.ISOMETRIC_CONTROL, + ) -> None: + + + if not (-180.0 <= target_angle <= 360.0): + raise ValueError(f"Angle must be in [-180, 360], got {target_angle}.") + + # Compute target position in turns relative to calibration + target_turns = -self.get_sign() *target_angle / 360.0 / self.reduction_ratio + target_pos = self._relative_pos + target_turns # absolute encoder pos + + if self._control_mode != ControlMode.POSITION_CONTROL: + self.stop() + self.axis.controller.config.control_mode = CONTROL_MODE_POSITION_CONTROL + self.axis.controller.config.input_mode = INPUT_MODE_TRAP_TRAJ + self.axis.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL + + self.axis.controller.input_pos = target_pos + self._control_mode = ControlMode.POSITION_CONTROL + + + """ + Control the crank to maintain an isometric position. + If deviation > 5°, slowly move at 5 rpm toward the target. + """ + + + + + """ + current_angle = self.get_angle() + error = target_angle - current_angle # signed error + + + current_turn = -(self.axis.encoder.pos_estimate - self._relative_pos) * self.reduction_ratio + print(f"Current turn: {current_turn:.2f}") + + + + + + print(f"Current angle: {current_angle:.2f}°, Target angle: {target_angle:.2f}°, Error: {error:.2f}°") + + if abs(error) > 5: + # Move in the direction of the error at 5 rpm + direction = np.sign(error) # 1 if target ahead, -1 if behind + velocity_rpm = 5 * direction + else: + # No motion needed + velocity_rpm = 0 + + print(velocity_rpm) + + # Convert to motor units + velocity_tr_per_s = velocity_rpm / 60 / self.reduction_ratio + ramp_rate_tr_per_s2 = cadence_ramp_rate / 60 / self.reduction_ratio + + # Apply velocity control + if self._control_mode not in control_modes_based_on_cadence: + self.stopping() + self.stopped() + self.axis.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL + self.axis.controller.config.input_mode = INPUT_MODE_VEL_RAMP + self.axis.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL + + self.axis.controller.config.vel_ramp_rate = ramp_rate_tr_per_s2 + self.axis.controller.input_vel = -self.get_sign() * velocity_tr_per_s + self._control_mode = control_mode + + return error""" + + + def stopping( self, cadence_ramp_rate: float = 30, diff --git a/ergocycleS2M/parameters/hardware_and_security.json b/ergocycleS2M/parameters/hardware_and_security.json index f2628b1..2a9eab8 100644 --- a/ergocycleS2M/parameters/hardware_and_security.json +++ b/ergocycleS2M/parameters/hardware_and_security.json @@ -32,12 +32,12 @@ "pedals_accel_lim": 31, "torque_ramp_rate_lim": 5.5, "maximal_cadence_stop": 31, - "resisting_current_proportional": 0.012109769054928311, - "resisting_current_constant": 0.38324651511638025, - "resisting_current_cst_gear": 26.86510681095839, - "resisting_current_cst_hometrainer": 0.07514872888652697, - "resisting_current_cst_constant": -0.4242290344268693, - "resisting_current_prop_gear": 2.9124170635770343, - "resisting_current_prop_hometrainer": 0.020903324289955783, - "resisting_current_prop_constant": -0.18166617303562008 + "resisting_current_proportional": 0.004312443183247655, + "resisting_current_constant": 0.3251453534100984, + "resisting_current_cst_gear": 26.865107332479884, + "resisting_current_cst_hometrainer": 0.0751487381089588, + "resisting_current_cst_constant": -0.4242291122064278, + "resisting_current_prop_gear": 2.912417063523588, + "resisting_current_prop_hometrainer": 0.020903324203691103, + "resisting_current_prop_constant": -0.18166617262949072 } \ No newline at end of file