Merge commit '0952958af7685646cdbf2a4907c6e2ae59d4d352'
This commit is contained in:
21
analysis/2026042802-1/2026042802-1_analysis_summary.json
Normal file
21
analysis/2026042802-1/2026042802-1_analysis_summary.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"pcap_path": "D:\\Dev\\Codes\\rvbust-code\\FlyingShotPkg_3.15_VDA\\Rvbust\\uttc-20260428\\2026042802-1.pcap",
|
||||
"all_command_count": 1788,
|
||||
"trigger_count": 17,
|
||||
"existing_runtime_actual_send_exists": true,
|
||||
"existing_runtime_actual_send_has_io_columns": false,
|
||||
"existing_shot_events_exists": true,
|
||||
"pcap_specific_combined_export_preexisting": false,
|
||||
"average_max_error_deg": 4.241583591714439,
|
||||
"max_error_deg": 11.130744251720401,
|
||||
"average_rms_error_deg": 2.5406136706026206,
|
||||
"max_error_axis_counter": {
|
||||
"J6": 9,
|
||||
"J4": 5,
|
||||
"J5": 1,
|
||||
"J2": 1,
|
||||
"J1": 1
|
||||
},
|
||||
"order_only_addr_mismatch_count": 14,
|
||||
"real_addr_mismatch_count": 0
|
||||
}
|
||||
1789
analysis/2026042802-1/2026042802-1_j519_actual_send_all_with_io.csv
Normal file
1789
analysis/2026042802-1/2026042802-1_j519_actual_send_all_with_io.csv
Normal file
File diff suppressed because it is too large
Load Diff
1790
analysis/2026042802-1/2026042802-1_j519_status_feedback_all.csv
Normal file
1790
analysis/2026042802-1/2026042802-1_j519_status_feedback_all.csv
Normal file
File diff suppressed because it is too large
Load Diff
18
analysis/2026042802-1/2026042802-1_j519_trigger_frames.csv
Normal file
18
analysis/2026042802-1/2026042802-1_j519_trigger_frames.csv
Normal file
@@ -0,0 +1,18 @@
|
||||
frame_number,time_relative_s,sequence,last_data,write_io_type,write_io_index,write_io_mask,write_io_value,io_addrs,j1_deg,j2_deg,j3_deg,j4_deg,j5_deg,j6_deg,ext1_deg,ext2_deg,ext3_deg
|
||||
1955,5.888271,1381124,0,2,1,10,10,"[2, 4]",45.8632698059082,2.9724788665771484,-13.609341621398926,-2.1851141452789307,7.582361221313477,5.701517581939697,0.0,0.0,0.0
|
||||
2151,6.480252,1381198,0,2,1,14,14,"[2, 3, 4]",56.34336471557617,11.66723918914795,-6.600923538208008,-68.1976089477539,6.058437347412109,71.5392837524414,0.0,0.0,0.0
|
||||
2223,6.704225,1381226,0,2,1,14,14,"[2, 3, 4]",53.50831985473633,7.412222862243652,-9.629739761352539,-32.30571365356445,12.488306045532227,30.826189041137695,0.0,0.0,0.0
|
||||
2336,7.048266,1381269,0,2,1,10,10,"[2, 4]",44.433006286621094,-2.1156294345855713,-18.39777374267578,4.8371992111206055,33.99372482299805,-20.26228904724121,0.0,0.0,0.0
|
||||
2477,7.464289,1381321,0,2,1,10,10,"[2, 4]",65.84967041015625,5.695003032684326,-14.563636779785156,-28.69342613220215,27.678611755371094,46.437156677246094,0.0,0.0,0.0
|
||||
2665,8.024277,1381391,0,2,1,12,12,"[3, 4]",60.847530364990234,24.47777557373047,-5.617043972015381,34.8044319152832,5.786255359649658,-31.2253475189209,0.0,0.0,0.0
|
||||
2811,8.472317,1381447,0,2,1,12,12,"[3, 4]",70.8250503540039,10.95259952545166,-13.136046409606934,-34.68459701538086,14.022420883178711,38.90604019165039,0.0,0.0,0.0
|
||||
2908,8.760361,1381483,0,2,1,10,10,"[2, 4]",69.40516662597656,-20.066781997680664,-8.152935981750488,-63.93235397338867,6.775912284851074,70.37761688232422,0.0,0.0,0.0
|
||||
3112,9.384332,1381561,0,2,1,10,10,"[2, 4]",73.90592956542969,-8.016124725341797,-9.002426147460938,-131.3327178955078,15.526206970214844,142.41668701171875,0.0,0.0,0.0
|
||||
3170,9.560358,1381583,0,2,1,10,10,"[2, 4]",76.72688293457031,-16.518726348876953,-7.15147066116333,-127.53003692626953,18.53788948059082,138.7897491455078,0.0,0.0,0.0
|
||||
3350,10.104322,1381651,0,2,1,10,10,"[2, 4]",83.71662139892578,-13.186573028564453,-13.738808631896973,-72.70317840576172,26.66758918762207,91.98423767089844,0.0,0.0,0.0
|
||||
3470,10.472333,1381697,0,2,1,10,10,"[2, 4]",59.78955841064453,-2.7094168663024902,-8.933828353881836,-108.24857330322266,21.177492141723633,114.14110565185547,0.0,0.0,0.0
|
||||
3627,10.944371,1381756,0,2,1,12,12,"[3, 4]",86.41329193115234,4.266173362731934,-19.15987777709961,-52.24028015136719,29.853723526000977,55.86907196044922,0.0,0.0,0.0
|
||||
3710,11.192339,1381787,0,2,1,10,10,"[2, 4]",110.7523193359375,0.8751887679100037,-34.756534576416016,-48.14332580566406,45.0865592956543,87.97444152832031,0.0,0.0,0.0
|
||||
3796,11.456361,1381820,0,2,1,10,10,"[2, 4]",111.79177856445312,-6.225440502166748,-31.958988189697266,-53.68573760986328,44.76150894165039,89.33454895019531,0.0,0.0,0.0
|
||||
3870,11.680362,1381848,0,2,1,10,10,"[2, 4]",117.87474060058594,-19.607177734375,-30.329103469848633,-56.600250244140625,49.08655548095703,90.32424926757812,0.0,0.0,0.0
|
||||
4138,12.496365,1381950,0,2,1,12,12,"[3, 4]",59.93589401245117,-21.102140426635742,-9.684239387512207,70.64311981201172,32.941307067871094,-63.34855651855469,0.0,0.0,0.0
|
||||
|
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"pcap_path": "D:\\Dev\\Codes\\rvbust-code\\FlyingShotPkg_3.15_VDA\\Rvbust\\uttc-20260428\\2026042802-1.pcap",
|
||||
"command_count": 1788,
|
||||
"trigger_count": 17,
|
||||
"command_minus_paired_status_sequence_counter": {
|
||||
"8": 17
|
||||
},
|
||||
"paired_status_average_max_error_deg": 6.469219290127501,
|
||||
"paired_status_max_error_deg": 16.123934474475888,
|
||||
"paired_status_max_error_axis_counter": {
|
||||
"J1": 2,
|
||||
"J6": 9,
|
||||
"J2": 4,
|
||||
"J4": 2
|
||||
},
|
||||
"best_status_average_max_error_deg": 0.14953970473465206,
|
||||
"best_status_max_error_deg": 0.5696919293327056,
|
||||
"best_status_max_error_axis_counter": {
|
||||
"J6": 10,
|
||||
"J1": 3,
|
||||
"J5": 1,
|
||||
"J2": 2,
|
||||
"J4": 1
|
||||
},
|
||||
"best_status_delta_from_paired_cycles_counter": {
|
||||
"10": 8,
|
||||
"9": 9
|
||||
},
|
||||
"best_status_delta_from_trigger_sequence_counter": {
|
||||
"2": 8,
|
||||
"1": 9
|
||||
},
|
||||
"best_status_time_after_trigger_ms_min": 71.83799999999962,
|
||||
"best_status_time_after_trigger_ms_max": 79.96399999999859,
|
||||
"best_status_time_after_trigger_ms_avg": 75.6613529411763,
|
||||
"search_window_cycles": 20
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
trigger_no,waypoint_index,trigger_command_sequence,trigger_command_frame,trigger_command_time_relative_s,trigger_current_status_sequence,trigger_current_status_frame,trigger_current_status_time_relative_s,command_leads_status_cycles,trigger_current_status_max_error_axis,trigger_current_status_max_error_deg,trigger_current_status_rms_error_deg,best_status_sequence,best_status_frame,best_status_time_relative_s,best_status_delay_from_current_status_cycles,best_status_delay_from_trigger_command_cycles,best_status_delay_from_trigger_command_ms,best_status_max_error_axis,best_status_max_error_deg,best_status_rms_error_deg
|
||||
1,1,1381124,1955,5.888271,1381116,1954,5.888166,8,J1,6.588241610414407,3.8225443630072706,1381126,1981,5.968124,10,2,79.85300000000083,J6,0.20758988641565335,0.12239685259209541
|
||||
2,2,1381198,2151,6.480252,1381190,2150,6.480138,8,J1,2.33531536749085,1.3827543088729335,1381199,2172,6.552164,9,1,71.9120000000002,J1,0.08213390997131853,0.04616054814443023
|
||||
3,3,1381226,2223,6.704225,1381218,2222,6.704166,8,J6,16.123934474475888,8.852376616648156,1381228,2248,6.784154,10,2,79.92899999999992,J6,0.5696919293327056,0.3239642953120798
|
||||
4,4,1381269,2336,7.048266,1381261,2335,7.048148,8,J6,4.901882807288516,3.2440556235708993,1381271,2362,7.128175,10,2,79.90899999999979,J6,0.09470430997406254,0.06580474324148583
|
||||
5,5,1381321,2477,7.464289,1381313,2476,7.464178,8,J6,9.21943820204428,4.621321121191458,1381323,2502,7.544122,10,2,79.83299999999983,J5,0.056622412878706285,0.03357478517689402
|
||||
6,6,1381391,2665,8.024277,1381383,2664,8.024156,8,J6,6.1464605154096255,3.0478405356855083,1381392,2687,8.096181,9,1,71.90399999999997,J6,0.17697427883735983,0.08563125303054821
|
||||
7,7,1381447,2811,8.472317,1381439,2810,8.472208,8,J6,13.15171783052326,7.78202379046887,1381448,2834,8.54419,9,1,71.87300000000008,J6,0.30658691011310424,0.18210669390591905
|
||||
8,8,1381483,2908,8.760361,1381475,2907,8.760245,8,J6,7.436279407197546,5.127041386597523,1381484,2929,8.832199,9,1,71.83799999999962,J2,0.06074767130727565,0.02750705896232285
|
||||
9,9,1381561,3112,9.384332,1381553,3111,9.384204,8,J6,5.585605293050492,3.277206017832995,1381562,3134,9.456205,9,1,71.87300000000008,J4,0.15225994654176134,0.089150370994325
|
||||
10,10,1381583,3170,9.560358,1381575,3169,9.56025,8,J2,4.165077087081428,2.128983861449047,1381585,3196,9.640214,10,2,79.85599999999948,J6,0.0734395372110157,0.043285948566531826
|
||||
11,11,1381651,3350,10.104322,1381643,3349,10.104215,8,J2,1.8309533977318093,1.169340011162825,1381652,3373,10.17622,9,1,71.8980000000009,J6,0.07766831894582538,0.04246508921571141
|
||||
12,12,1381697,3470,10.472333,1381689,3469,10.472191,8,J4,4.822353276363401,2.8611252897135864,1381698,3493,10.544283,9,1,71.94999999999929,J1,0.021077280093038553,0.014935517911187784
|
||||
13,13,1381756,3627,10.944371,1381748,3626,10.944251,8,J4,8.152507963730145,5.168659196117862,1381757,3651,11.016226,9,1,71.85499999999934,J1,0.14554304541260876,0.0699602863618738
|
||||
14,14,1381787,3710,11.192339,1381779,3709,11.19225,8,J6,10.40441920064211,5.679810575214033,1381789,3735,11.272261,10,2,79.92199999999983,J6,0.11945317484617135,0.05835950688086092
|
||||
15,15,1381820,3796,11.456361,1381812,3795,11.456253,8,J2,2.6161077284098373,1.5911754635072362,1381822,3821,11.536287,10,2,79.92600000000039,J2,0.14980253942005772,0.08716897465096986
|
||||
16,16,1381848,3870,11.680362,1381840,3869,11.680263,8,J2,4.682473812334727,2.2067399623329567,1381850,3895,11.760326,10,2,79.96399999999859,J6,0.17295643126782068,0.10419421346699378
|
||||
17,18,1381950,4138,12.496365,1381942,4137,12.496271,8,J6,1.8139599579791934,1.1536991630766555,1381951,4159,12.568313,9,1,71.94799999999901,J6,0.07492339792059965,0.03834497543010648
|
||||
|
@@ -0,0 +1,18 @@
|
||||
trigger_no,waypoint_index,best_sample_order,best_frame_number,best_sequence,best_time_relative_s,teach_j1_deg,teach_j2_deg,teach_j3_deg,teach_j4_deg,teach_j5_deg,teach_j6_deg,offset_6_frame_number,offset_6_sequence,offset_6_time_relative_s,offset_6_max_error_axis,offset_6_max_error_deg,offset_6_rms_error_deg,offset_6_delta_from_best_ms,offset_6_j1_actual_deg,offset_6_diff_j1_deg,offset_6_j2_actual_deg,offset_6_diff_j2_deg,offset_6_j3_actual_deg,offset_6_diff_j3_deg,offset_6_j4_actual_deg,offset_6_diff_j4_deg,offset_6_j5_actual_deg,offset_6_diff_j5_deg,offset_6_j6_actual_deg,offset_6_diff_j6_deg,offset_7_frame_number,offset_7_sequence,offset_7_time_relative_s,offset_7_max_error_axis,offset_7_max_error_deg,offset_7_rms_error_deg,offset_7_delta_from_best_ms,offset_7_j1_actual_deg,offset_7_diff_j1_deg,offset_7_j2_actual_deg,offset_7_diff_j2_deg,offset_7_j3_actual_deg,offset_7_diff_j3_deg,offset_7_j4_actual_deg,offset_7_diff_j4_deg,offset_7_j5_actual_deg,offset_7_diff_j5_deg,offset_7_j6_actual_deg,offset_7_diff_j6_deg,offset_8_frame_number,offset_8_sequence,offset_8_time_relative_s,offset_8_max_error_axis,offset_8_max_error_deg,offset_8_rms_error_deg,offset_8_delta_from_best_ms,offset_8_j1_actual_deg,offset_8_diff_j1_deg,offset_8_j2_actual_deg,offset_8_diff_j2_deg,offset_8_j3_actual_deg,offset_8_diff_j3_deg,offset_8_j4_actual_deg,offset_8_diff_j4_deg,offset_8_j5_actual_deg,offset_8_diff_j5_deg,offset_8_j6_actual_deg,offset_8_diff_j6_deg,best_of_6_7_8_offset
|
||||
1,1,730,1941,1381118,5.840207,48.886810269468405,2.1989850886957285,-11.021017368511105,0.4102097980549552,6.248381265333557,2.294990756284542,1955,1381124,5.888271,J6,3.4065268256551553,2.468438977201345,48.06399999999922,45.8632698059082,-3.0235404635602023,2.9724788665771484,0.7734937778814199,-13.609341621398926,-2.588324252887821,-2.1851141452789307,-2.5953239433338857,7.582361221313477,1.3339799559799195,5.701517581939697,3.4065268256551553,1958,1381125,5.896184,J6,4.180931828249149,2.9355776062802286,55.9769999999995,45.449710845947266,-3.43709942352114,3.117816925048828,0.9188318363530996,-13.963269233703613,-2.9422518651925085,-2.845515012741089,-3.255724810796044,7.768581867218018,1.5202006018844605,6.475922584533691,4.180931828249149,1961,1381126,5.904272,J6,5.0045521476156045,3.418446646752538,64.06499999999937,45.06731414794922,-3.8194961215191867,3.2660434246063232,1.0670583359105947,-14.290565490722656,-3.2695481222115514,-3.562589168548584,-3.972798966603539,7.9421586990356445,1.6937774337020874,7.2995429039001465,5.0045521476156045,6
|
||||
2,2,804,2135,1381192,6.432175,55.34775509527405,11.807039833001637,-7.0090952672806885,-71.01433145543973,6.012065051914967,74.24953191606797,2151,1381198,6.480252,J4,2.8167225076858244,1.6562461612538633,48.07700000000015,56.34336471557617,0.995609620302119,11.66723918914795,-0.13980064385368784,-6.600923538208008,0.40817172907268073,-68.1976089477539,2.8167225076858244,6.058437347412109,0.04637229549714217,71.5392837524414,-2.7102481636265594,2153,1381199,6.488246,J4,3.526759739131137,2.060948429836345,56.071000000000204,56.451515197753906,1.1037601024798533,11.618118286132812,-0.18892154686882456,-6.580313682556152,0.4287815847245362,-67.4875717163086,3.526759739131137,6.09804105758667,0.08597600567170272,70.84339141845703,-3.4061404976109344,2155,1381200,6.496271,J4,4.301478977412387,2.5033932349312007,64.09600000000015,56.54535675048828,1.1976016552142283,11.561552047729492,-0.24548778527214488,-6.571277618408203,0.4378176488724854,-66.71285247802734,4.301478977412387,6.147788047790527,0.13572299587556014,70.078857421875,-4.170674494192966,6
|
||||
3,3,832,2207,1381220,6.656264,55.109808014787404,8.759497374223619,-8.518216825284897,-41.11725046606459,10.108488114686867,41.956933292858096,2223,1381226,6.704225,J6,11.130744251720401,5.955607695749779,47.96099999999992,53.50831985473633,-1.6014881600510762,7.412222862243652,-1.3472745119799665,-9.629739761352539,-1.111522936067642,-32.30571365356445,8.811536812500137,12.488306045532227,2.379817930845359,30.826189041137695,-11.130744251720401,2227,1381227,6.712268,J6,13.165517124645206,7.03533795629365,56.00399999999972,53.18336486816406,-1.9264431466233418,7.154932975769043,-1.6045643984545759,-9.842713356018066,-1.3244965307331693,-30.750762939453125,10.366487526611465,12.961709976196289,2.8532218615094216,28.79141616821289,-13.165517124645206,2229,1381228,6.720286,J6,15.212756428234073,8.119717107623766,64.02199999999958,52.84844207763672,-2.2613659371506856,6.892857074737549,-1.86664029948607,-10.059968948364258,-1.5417521230793607,-29.200178146362305,11.917072319702285,13.449018478393555,3.340530363706687,26.744176864624023,-15.212756428234073,6
|
||||
4,4,875,2321,1381263,7.000291,43.65359310453334,-1.629659559507137,-17.715753611915318,5.995208633582219,32.17171770646655,-22.573973337684023,2336,1381269,7.048266,J6,2.3116842904428125,1.3731583485126422,47.9750000000001,44.433006286621094,0.779413182087751,-2.1156294345855713,-0.48596987507843425,-18.39777374267578,-0.6820201307604634,4.8371992111206055,-1.158009422461613,33.99372482299805,1.8220071165314948,-20.26228904724121,2.3116842904428125,2339,1381270,7.056293,J6,3.083185831580508,1.7437179916275436,56.00200000000033,44.65972137451172,1.006128269978376,-2.1617355346679688,-0.5320759751608317,-18.490602493286133,-0.774848881370815,4.406780242919922,-1.5884283906622967,34.24979019165039,2.0780724851838386,-19.490787506103516,3.083185831580508,2341,1381271,7.064278,J6,3.9520155587533594,2.15807761350517,63.987000000000016,44.91068649291992,1.2570933883865791,-2.196718215942383,-0.5670586564352458,-18.575654983520508,-0.85990137160519,3.9164717197418213,-2.0787369138403973,34.48752212524414,2.3158044187775886,-18.621957778930664,3.9520155587533594,6
|
||||
5,5,927,2457,1381315,7.416267,64.58244475717193,4.262979315506351,-15.669217122643433,-29.952927185666542,29.850439933020308,45.62652743544272,2477,1381321,7.464289,J5,2.171828177649214,1.404704060246151,48.021999999999565,65.84967041015625,1.2672256529843224,5.695003032684326,1.4320237171779748,-14.563636779785156,1.1055803428582767,-28.69342613220215,1.259501053464394,27.678611755371094,-2.171828177649214,46.437156677246094,0.810629241803376,2479,1381322,7.472265,J5,2.585320380408003,1.6670103454750829,55.99799999999977,66.01153564453125,1.4290908873593224,5.957343578338623,1.6943642628322717,-14.353693008422852,1.3155241142205814,-28.232824325561523,1.720102860105019,27.265119552612305,-2.585320380408003,46.24872589111328,0.6221984556705635,2483,1381323,7.480284,J5,3.0049218208376907,1.9507458917658702,64.01699999999977,66.15470886230469,1.57226410513276,6.221103191375732,1.958123875869381,-14.140877723693848,1.5283393989495853,-27.70670509338379,2.2462220922827534,26.845518112182617,-3.0049218208376907,45.96609115600586,0.33956372056314166,6
|
||||
6,6,997,2649,1381385,7.976283,60.47948252708421,23.068781981390185,-5.01126420129949,36.10685486878251,5.525681179628412,-31.3025636495649,2665,1381391,8.024277,J2,1.408993592340284,0.8424031169595625,47.99400000000009,60.847530364990234,0.36804783790602613,24.47777557373047,1.408993592340284,-5.617043972015381,-0.6057797707158912,34.8044319152832,-1.3024229534993097,5.786255359649658,0.260574180021246,-31.2253475189209,0.07721613064400046,2667,1381392,8.032258,J4,1.7701696881184503,1.0669192783996628,55.975000000000996,60.95240783691406,0.47292530982985426,24.69621467590332,1.6274326945131357,-5.75187873840332,-0.7406145371038306,34.33668518066406,-1.7701696881184503,5.885775089263916,0.3600939096355038,-30.920429229736328,0.38213441982857077,2669,1381393,8.040307,J4,2.303903056038372,1.3309750271252634,64.02400000000074,61.06847381591797,0.5889912888337605,24.908491134643555,1.83970915325337,-5.895160675048828,-0.8838964737493384,33.80295181274414,-2.303903056038372,5.999467372894287,0.4737861932658749,-30.538488388061523,0.7640752615033755,6
|
||||
7,7,1053,2795,1381441,8.424315,70.47583707168602,16.39384887825908,-13.456948007467595,-27.892318852946243,14.53566195089614,31.71193282686115,2811,1381447,8.472317,J6,7.1941073647892395,4.618545262978315,48.00200000000032,70.8250503540039,0.3492132823178906,10.95259952545166,-5.4412493528074215,-13.136046409606934,0.3209015978606615,-34.68459701538086,-6.7922781624346165,14.022420883178711,-0.5132410677174288,38.90604019165039,7.1941073647892395,2814,1381448,8.480253,J6,8.24868042387127,5.331515658278243,55.93799999999938,70.84622192382812,0.37038485214210937,9.979106903076172,-6.41474197518291,-13.042067527770996,0.414880479696599,-35.67851257324219,-7.786193720295945,13.882524490356445,-0.6531374605396945,39.96061325073242,8.24868042387127,2817,1381449,8.488291,J6,9.275558780804865,6.035874346469871,63.976000000000255,70.85871887207031,0.38288180038429687,8.986706733703613,-7.407142144555468,-12.9381742477417,0.5187737597258959,-36.64576721191406,-8.75344835896782,13.72922134399414,-0.8064406069019991,40.987491607666016,9.275558780804865,6
|
||||
8,8,1089,2892,1381477,8.71231,69.58246408878419,-17.10571341532583,-8.710589696831251,-58.47569441890704,7.63059003611043,64.43773280685575,2908,1381483,8.760361,J6,5.93988407546847,3.533132924843701,48.05099999999918,69.40516662597656,-0.1772974628076298,-20.066781997680664,-2.9610685823548337,-8.152935981750488,0.5576537150807628,-63.93235397338867,-5.456659554481632,6.775912284851074,-0.8546777512593557,70.37761688232422,5.93988407546847,2910,1381484,8.768294,J6,6.922168621366907,4.090127751125079,55.9839999999987,69.39288330078125,-0.1895807880029423,-20.37982177734375,-3.2741083620179197,-8.094615936279297,0.6159737605519542,-64.83366394042969,-6.357969521522648,6.682157039642334,-0.948432996468096,71.35990142822266,6.922168621366907,2912,1381485,8.77637,J6,7.916950115507532,4.649086823187336,64.05999999999956,69.38455200195312,-0.1979120868310673,-20.654869079589844,-3.5491556642640134,-8.043557167053223,0.6670325297780284,-65.74622344970703,-7.270529030799992,6.598722457885742,-1.0318675782246878,72.35468292236328,7.916950115507532,6
|
||||
9,9,1167,3096,1381555,9.336375,73.57125874861414,-6.42984524964374,-9.628579811400881,-128.80864727564332,14.671082817606491,140.00204816414424,3112,1381561,9.384332,J4,2.5240706198644887,1.630593251762862,47.95700000000025,73.90592956542969,0.3346708168155459,-8.016124725341797,-1.5862794756980572,-9.002426147460938,0.6261536639399434,-131.3327178955078,-2.5240706198644887,15.526206970214844,0.8551241526083526,142.41668701171875,2.4146388475745084,3114,1381562,9.392355,J4,2.830116152091051,1.8576181251506447,55.97999999999992,73.96361541748047,0.39235666886632714,-8.363268852233887,-1.933423602590147,-8.884164810180664,0.7444150012202169,-131.63876342773438,-2.830116152091051,15.65822696685791,0.987144149251419,142.69912719726562,2.6970790331213834,3118,1381563,9.400331,J4,3.1013106100988637,2.0726473342415157,63.955999999999236,74.02384948730469,0.4525907386905459,-8.728874206542969,-2.299028956899229,-8.76374626159668,0.8648335498042012,-131.9099578857422,-3.1013106100988637,15.788768768310547,1.1176859507040557,142.9464569091797,2.944408745035446,6
|
||||
10,10,1189,3154,1381577,9.512325,75.56938603377543,-14.67930590788221,-7.294156094303152,-130.92303342701462,17.688361072687904,141.89350789658602,3170,1381583,9.560358,J4,3.3929965007450846,2.1059862266772753,48.033000000000214,76.72688293457031,1.1574969007948823,-16.518726348876953,-1.8394204409947434,-7.15147066116333,0.14268543313982196,-127.53003692626953,3.3929965007450846,18.53788948059082,0.8495284079029162,138.7897491455078,-3.103758751078203,3172,1381584,9.568328,J4,4.172522257581022,2.562681778316925,56.00299999999869,76.96759033203125,1.3982042982558198,-16.79741096496582,-2.1181050570836106,-7.157340049743652,0.1368160445594997,-126.7505111694336,4.172522257581022,18.698274612426758,1.0099135397388537,138.08377075195312,-3.8097371446328907,3175,1381585,9.576354,J4,4.999006937756803,3.042227585289095,64.02899999999967,77.21700286865234,1.6476168348769136,-17.061315536499023,-2.3820096286168138,-7.1720428466796875,0.12211324762346454,-125.92402648925781,4.999006937756803,18.86079978942871,1.1724387167408068,137.33689880371094,-4.556609092875078,6
|
||||
11,11,1257,3334,1381645,10.056378,86.0934977330502,-14.498333177585573,-13.681510793859989,-69.8682955440411,26.742681901805224,88.9994136192388,3350,1381651,10.104322,J6,2.9848240516596434,2.0134925384056306,47.94399999999932,83.71662139892578,-2.376876334124418,-13.186573028564453,1.3117601490211204,-13.738808631896973,-0.05729783803698396,-72.70317840576172,-2.834882861720615,26.66758918762207,-0.07509271418315322,91.98423767089844,2.9848240516596434,3352,1381652,10.112375,J6,3.668104996483862,2.4582215610180698,55.99699999999963,83.25711822509766,-2.836379507952543,-12.97336483001709,1.5249683475684837,-13.721805572509766,-0.04029477864977693,-73.39217376708984,-3.52387822304874,26.632198333740234,-0.11048356806498916,92.66751861572266,3.668104996483862,3355,1381653,10.120334,J6,4.397696366112768,2.932320329432031,63.955999999999236,82.77881622314453,-3.314681509905668,-12.759296417236328,1.7390367603492454,-13.69810676574707,-0.016595971887081618,-74.13638305664062,-4.2680875125995215,26.590784072875977,-0.15189782892924697,93.39710998535156,4.397696366112768,6
|
||||
12,12,1303,3455,1381691,10.424364,61.720732564877665,-4.2327893098442155,-9.784423185760874,-108.38408270751574,22.16077221865394,118.14206389103131,3470,1381697,10.472333,J6,4.000958239175844,1.9902461373621054,47.96900000000015,59.78955841064453,-1.9311741542331333,-2.7094168663024902,1.5233724435417253,-8.933828353881836,0.8505948318790377,-108.24857330322266,0.13550940429308866,21.177492141723633,-0.9832800769303063,114.14110565185547,-4.000958239175844,3474,1381698,10.480323,J6,4.9290969720860005,2.3936680954994465,55.95899999999965,59.577754974365234,-2.14297759051243,-2.4748787879943848,1.7579105218498308,-8.810709953308105,0.9737132324527682,-107.96444702148438,0.4196356860313699,21.03158187866211,-1.1291903399918297,113.21296691894531,-4.9290969720860005,3476,1381699,10.488357,J6,5.929425036050844,2.82736270797146,63.99299999999997,59.389400482177734,-2.33133208269993,-2.241248369216919,1.9915409406272966,-8.690967559814453,1.0934556259464205,-107.61585998535156,0.7682227221641824,20.888513565063477,-1.2722586535904625,112.21263885498047,-5.929425036050844,6
|
||||
13,13,1362,3611,1381750,10.896324,79.75708299218505,4.6402310571176475,-15.311185536748695,-56.03531247084017,26.13313788666632,52.152257172101606,3627,1381756,10.944371,J1,6.656208938967296,4.109282348845555,48.047000000000395,86.41329193115234,6.656208938967296,4.266173362731934,-0.3740576943857139,-19.15987777709961,-3.848692240350914,-52.24028015136719,3.7950323194729805,29.853723526000977,3.720585639334658,55.86907196044922,3.7168147883476124,3629,1381757,10.952347,J1,7.722973770510265,4.798701518313248,56.02299999999971,87.48005676269531,7.722973770510265,4.181386947631836,-0.45884410948581156,-19.803678512573242,-4.492492975824547,-51.80745315551758,4.22785931532259,30.479778289794922,4.346640403128603,56.773921966552734,4.621664794451128,3633,1381758,10.960362,J1,8.793827957521984,5.501947903898078,64.03800000000004,88.55091094970703,8.793827957521984,4.09064245223999,-0.5495886048776573,-20.455745697021484,-5.144560160272789,-51.41057586669922,4.624736604140949,31.11459732055664,4.981459433890322,57.749176025390625,5.596918853289019,6
|
||||
14,14,1393,3694,1381781,11.144347,108.95555081237923,1.3707600427061273,-33.59442519685133,-48.36736100282485,43.963403989432074,85.03926111958742,3710,1381787,11.192339,J6,2.93518040873289,1.5679827735552678,47.9920000000007,110.7523193359375,1.7967685235582707,0.8751887679100037,-0.4955712747961236,-34.756534576416016,-1.1621093795646829,-48.14332580566406,0.2240351971607879,45.0865592956543,1.1231553062222233,87.97444152832031,2.93518040873289,3712,1381788,11.20037,J6,3.224395496623515,1.7217974200989363,56.02299999999971,110.92666625976562,1.9711154473863957,0.7935436964035034,-0.5772163463026239,-34.86042404174805,-1.265998844896714,-48.102813720703125,0.2645472821217254,45.1879768371582,1.2245728477261295,88.26365661621094,3.224395496623515,3714,1381789,11.208359,J6,3.4549634287524214,1.844232474600388,64.01199999999996,111.0645751953125,2.1090243829332707,0.7117024660110474,-0.6590575766950799,-34.93739700317383,-1.3429718063224954,-48.06473159790039,0.3026294049244598,45.2645149230957,1.3011109336636295,88.49422454833984,3.4549634287524214,6
|
||||
15,15,1426,3780,1381814,11.408361,110.58484797608095,-3.860899593758653,-32.39640711653168,-51.112590875369015,44.2291595765054,87.62399978413751,3796,1381820,11.456361,J4,2.573146734494266,1.686689503314435,48.00000000000004,111.79177856445312,1.206930588372174,-6.225440502166748,-2.364540908408095,-31.958988189697266,0.4374189268344111,-53.68573760986328,-2.573146734494266,44.76150894165039,0.532349365144988,89.33454895019531,1.7105491660578025,3798,1381821,11.464349,J4,3.0742606260958283,2.037357874022524,55.98800000000104,112.08367919921875,1.498831223137799,-6.6951823234558105,-2.8342827296971573,-31.910240173339844,0.48616694319183296,-54.186851501464844,-3.0742606260958283,44.907752990722656,0.6785934142172536,89.73998260498047,2.1159828208429587,3801,1381822,11.472359,J4,3.579433355588016,2.3980591339233284,63.998000000001554,112.39252471923828,1.8076767431573302,-7.177155494689941,-3.316255900931288,-31.86791229248047,0.528494824051208,-54.69202423095703,-3.579433355588016,45.06621170043945,0.8370521239340505,90.16300964355469,2.5390098594171775,6
|
||||
16,16,1454,3854,1381842,11.63236,118.09595224767921,-17.37638727211256,-31.06900084212659,-59.56053843778568,48.73257576059714,94.13477131163891,3870,1381848,11.680362,J6,3.8105220440607894,2.197782779718695,48.00200000000032,117.87474060058594,-0.2212116470932699,-19.607177734375,-2.2307904622624406,-30.329103469848633,0.7398973722779587,-56.600250244140625,2.960288193645056,49.08655548095703,0.353979720359888,90.32424926757812,-3.8105220440607894,3872,1381849,11.688367,J6,4.756826365349852,2.710409629634467,56.00699999999925,117.7159423828125,-0.3800098648667074,-19.938819885253906,-2.562432613141347,-30.167478561401367,0.9015222807252243,-55.84638595581055,3.714152481975134,49.0964241027832,0.3638483421860599,89.37794494628906,-4.756826365349852,3874,1381850,11.696354,J6,5.783033335564696,3.263710825457299,63.99399999999922,117.52371978759766,-0.5722324600815512,-20.255823135375977,-2.879435863263417,-29.996501922607422,1.0724989195191696,-55.02521514892578,4.5353232888598995,49.092247009277344,0.3596712486802005,88.35173797607422,-5.783033335564696,6
|
||||
17,18,1556,4121,1381944,12.448375,60.28248244838438,-22.938080568038327,-10.333247998560786,77.49164224770549,35.58337770883919,-69.66898071584893,4138,1381950,12.496365,J4,6.848522435693766,4.03614029285736,47.99000000000042,59.93589401245117,-0.34658843593320654,-21.102140426635742,1.8359401414025847,-9.684239387512207,0.6490086110485791,70.64311981201172,-6.848522435693766,32.941307067871094,-2.642070640968093,-63.34855651855469,6.320424197294244,4140,1381951,12.504369,J4,8.296535741357829,4.890437505980655,55.9940000000001,59.902915954589844,-0.37956649379453467,-20.689186096191406,2.2488944718469206,-9.52907943725586,0.8041685613049268,69.19510650634766,-8.296535741357829,32.343406677246094,-3.239971031593093,-62.032222747802734,7.636757968046197,4142,1381952,12.512395,J4,9.82779337075236,5.794164211715548,64.0199999999993,59.8752326965332,-0.4072497518511753,-20.24807357788086,2.6900069901574675,-9.361802101135254,0.9714458974255322,67.66384887695312,-9.82779337075236,31.704130172729492,-3.8792475361096947,-60.64377975463867,9.02520096121026,6
|
||||
|
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"rows": 17,
|
||||
"best_offset_win_counts": {
|
||||
"6": 17,
|
||||
"7": 0,
|
||||
"8": 0
|
||||
},
|
||||
"average_max_error_deg": {
|
||||
"6": 4.241583591714439,
|
||||
"7": 5.068092118561757,
|
||||
"8": 5.9316839578472145
|
||||
},
|
||||
"average_rms_error_deg": {
|
||||
"6": 2.5406136706026206,
|
||||
"7": 3.021296972079456,
|
||||
"8": 3.5201758351699857
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
trigger_no,waypoint_index,trigger_frame_number,trigger_time_relative_s,trigger_sequence,paired_status_frame_number,paired_status_time_relative_s,paired_status_sequence,paired_status_timestamp,paired_status_to_trigger_sequence_delta,paired_status_to_trigger_time_ms,teach_j1_deg,teach_j2_deg,teach_j3_deg,teach_j4_deg,teach_j5_deg,teach_j6_deg,paired_status_j1_actual_deg,paired_status_diff_j1_deg,paired_status_j2_actual_deg,paired_status_diff_j2_deg,paired_status_j3_actual_deg,paired_status_diff_j3_deg,paired_status_j4_actual_deg,paired_status_diff_j4_deg,paired_status_j5_actual_deg,paired_status_diff_j5_deg,paired_status_j6_actual_deg,paired_status_diff_j6_deg,paired_status_max_error_axis,paired_status_max_error_deg,paired_status_rms_error_deg,best_status_frame_number,best_status_time_relative_s,best_status_sequence,best_status_timestamp,best_status_delta_from_paired_cycles,best_status_delta_from_trigger_sequence,best_status_time_after_trigger_ms,best_status_j1_actual_deg,best_status_diff_j1_deg,best_status_j2_actual_deg,best_status_diff_j2_deg,best_status_j3_actual_deg,best_status_diff_j3_deg,best_status_j4_actual_deg,best_status_diff_j4_deg,best_status_j5_actual_deg,best_status_diff_j5_deg,best_status_j6_actual_deg,best_status_diff_j6_deg,best_status_max_error_axis,best_status_max_error_deg,best_status_rms_error_deg
|
||||
1,1,1955,5.888271,1381124,1954,5.888166,1381116,624368380,-8,-0.10499999999957765,48.886810269468405,2.1989850886957285,-11.021017368511105,0.4102097980549552,6.248381265333557,2.294990756284542,55.47505187988281,6.588241610414407,1.2506256103515625,-0.948359478344166,-5.400295734405518,5.620721634105587,0.5259769558906555,0.1157671578357003,3.3612561225891113,-2.8871251427444458,0.44421958923339844,-1.8507711670511435,J1,6.588241610414407,3.8225443630072706,1981,5.968124,1381126,624368460,10,2,79.85300000000083,48.76339340209961,-0.12341686736879609,2.256481647491455,0.05749655879572657,-11.157909393310547,-0.13689202479944207,0.37860527634620667,-0.031604521708748556,6.3406662940979,0.0922850287643433,2.5025806427001953,0.20758988641565335,J6,0.20758988641565335,0.12239685259209541
|
||||
2,2,2151,6.480252,1381198,2150,6.480138,1381190,624368972,-8,-0.11399999999994748,55.34775509527405,11.807039833001637,-7.0090952672806885,-71.01433145543973,6.012065051914967,74.24953191606797,53.0124397277832,-2.33531536749085,11.506516456604004,-0.30052337639763316,-8.466276168823242,-1.4571809015425536,-69.63382720947266,1.3805042459670744,6.426647663116455,0.41458261120148787,72.93537902832031,-1.3141528877476532,J1,2.33531536749085,1.3827543088729335,2172,6.552164,1381199,624369044,9,1,71.9120000000002,55.265621185302734,-0.08213390997131853,11.803675651550293,-0.003364181451344095,-7.050373554229736,-0.04127828694904778,-71.06609344482422,-0.05176198938448806,6.026607990264893,0.014542938349925372,74.28738403320312,0.03785211713515935,J1,0.08213390997131853,0.04616054814443023
|
||||
3,3,2223,6.704225,1381226,2222,6.704166,1381218,624369196,-8,-0.059000000000253294,55.109808014787404,8.759497374223619,-8.518216825284897,-41.11725046606459,10.108488114686867,41.956933292858096,56.5781364440918,1.4683284293043926,10.494372367858887,1.7348749936352679,-7.114619255065918,1.4035975702189791,-55.112239837646484,-13.994989371581894,7.423236846923828,-2.6852512677630394,58.080867767333984,16.123934474475888,J6,16.123934474475888,8.852376616648156,2248,6.784154,1381228,624369276,10,2,79.92899999999992,55.00252151489258,-0.10728649989482619,8.69005012512207,-0.06944724910154854,-8.577027320861816,-0.05881049557691931,-40.59286117553711,0.5243892905274805,10.210430145263672,0.1019420305768044,41.38724136352539,-0.5696919293327056,J6,0.5696919293327056,0.3239642953120798
|
||||
4,4,2336,7.048266,1381269,2335,7.048148,1381261,624369540,-8,-0.11799999999961841,43.65359310453334,-1.629659559507137,-17.715753611915318,5.995208633582219,32.17171770646655,-22.573973337684023,44.479610443115234,0.8260173385818916,0.07192186266183853,1.7015814221689756,-15.996956825256348,1.7187967866589702,2.40028715133667,-3.5949214822455486,27.737812042236328,-4.433905664230224,-17.672090530395508,4.901882807288516,J6,4.901882807288516,3.2440556235708993,2362,7.128175,1381271,624369620,10,2,79.90899999999979,43.72715759277344,0.07356448824009476,-1.6386029720306396,-0.008943412523502614,-17.75617218017578,-0.04041856826046342,5.952699661254883,-0.04250897232733575,32.2616081237793,0.08989041731274483,-22.47926902770996,0.09470430997406254,J6,0.09470430997406254,0.06580474324148583
|
||||
5,5,2477,7.464289,1381321,2476,7.464178,1381313,624369956,-8,-0.11099999999952814,64.58244475717193,4.262979315506351,-15.669217122643433,-29.952927185666542,29.850439933020308,45.62652743544272,60.830230712890625,-3.7522140442813026,1.8808640241622925,-2.382115291344059,-17.297021865844727,-1.6278047432012936,-26.684900283813477,3.268026901853066,33.02191925048828,3.1714793174679734,36.40708923339844,-9.21943820204428,J6,9.21943820204428,4.621321121191458,2502,7.544122,1381323,624370036,10,2,79.83299999999983,64.5761947631836,-0.006249993988333813,4.283629417419434,0.020650101913082253,-15.615406036376953,0.05381108626647979,-29.94143295288086,0.011494232785683067,29.7938175201416,-0.056622412878706285,45.61851119995117,-0.008016235491545842,J5,0.056622412878706285,0.03357478517689402
|
||||
6,6,2665,8.024277,1381391,2664,8.024156,1381383,624370516,-8,-0.12100000000003774,60.47948252708421,23.068781981390185,-5.01126420129949,36.10685486878251,5.525681179628412,-31.3025636495649,60.89759063720703,0.418108110122823,20.716407775878906,-2.3523742055112784,-4.913214206695557,0.09804999460393304,32.72084045410156,-3.3860144146809503,6.405356407165527,0.8796752275371151,-25.156103134155273,6.1464605154096255,J6,6.1464605154096255,3.0478405356855083,2687,8.096181,1381392,624370588,9,1,71.90399999999997,60.54315185546875,0.06366932838454176,22.99761962890625,-0.07116235248393465,-5.037043571472168,-0.025779370172678284,36.056732177734375,-0.050122691048137824,5.545217037200928,0.019535857572515525,-31.12558937072754,0.17697427883735983,J6,0.17697427883735983,0.08563125303054821
|
||||
7,7,2811,8.472317,1381447,2810,8.472208,1381439,624370964,-8,-0.10900000000013677,70.47583707168602,16.39384887825908,-13.456948007467595,-27.892318852946243,14.53566195089614,31.71193282686115,69.13842010498047,-1.3374169667055469,22.202428817749023,5.808579939489942,-12.907807350158691,0.5491406573089037,-15.469873428344727,12.422445424601516,14.040997505187988,-0.4946644457081515,18.56021499633789,-13.15171783052326,J6,13.15171783052326,7.78202379046887,2834,8.54419,1381448,624371036,9,1,71.87300000000008,70.3967514038086,-0.07908566787742188,16.456680297851562,0.06283141959248084,-13.330730438232422,0.12621756923517324,-27.611600875854492,0.2807179770917507,14.542427062988281,0.006765112092141479,31.405345916748047,-0.30658691011310424,J6,0.30658691011310424,0.18210669390591905
|
||||
8,8,2908,8.760361,1381483,2907,8.760245,1381475,624371252,-8,-0.11600000000022703,69.58246408878419,-17.10571341532583,-8.710589696831251,-58.47569441890704,7.63059003611043,64.43773280685575,70.10379791259766,0.5213338238134639,-10.075156211853027,7.030557203472803,-9.98978042602539,-1.2791907291941396,-51.5866584777832,6.889035941123836,9.534638404846191,1.9040483687357614,57.0014533996582,-7.436279407197546,J6,7.436279407197546,5.127041386597523,2929,8.832199,1381484,624371324,9,1,71.83799999999962,69.59716033935547,0.014696250571276437,-17.044965744018555,0.06074767130727565,-8.703485488891602,0.007104207939649498,-58.473426818847656,0.0022676000593833123,7.6463942527771,0.015804216666669646,64.45584869384766,0.01811588699190736,J2,0.06074767130727565,0.02750705896232285
|
||||
9,9,3112,9.384332,1381561,3111,9.384204,1381553,624371876,-8,-0.128000000000128,73.57125874861414,-6.42984524964374,-9.628579811400881,-128.80864727564332,14.671082817606491,140.00204816414424,72.96144104003906,-0.6098177085750791,-6.3001484870910645,0.12969676255267526,-10.035088539123535,-0.40650872772265423,-123.29313659667969,5.515510678963636,13.165532112121582,-1.505550705484909,134.41644287109375,-5.585605293050492,J6,5.585605293050492,3.277206017832995,3134,9.456205,1381562,624371948,9,1,71.87300000000008,73.55095672607422,-0.02030202253992286,-6.441007614135742,-0.011162364492002474,-9.614203453063965,0.014376358336916084,-128.65638732910156,0.15225994654176134,14.635379791259766,-0.03570302634672551,139.8520965576172,-0.14995160652705408,J4,0.15225994654176134,0.089150370994325
|
||||
10,10,3170,9.560358,1381583,3169,9.56025,1381575,624372052,-8,-0.10800000000088517,75.56938603377543,-14.67930590788221,-7.294156094303152,-130.92303342701462,17.688361072687904,141.89350789658602,74.36400604248047,-1.2053799912949614,-10.514228820800781,4.165077087081428,-8.236283302307129,-0.9421272080039769,-132.6507110595703,-1.7276776325556966,16.38176155090332,-1.3065995217845838,143.5712890625,1.6777811659139843,J2,4.165077087081428,2.128983861449047,3196,9.640214,1381585,624372132,10,2,79.85599999999948,75.62290954589844,0.05352351212300732,-14.662798881530762,0.016507026351447962,-7.297325611114502,-0.003169516811349915,-130.88812255859375,0.03491086842086588,17.726865768432617,0.03850469574471305,141.820068359375,-0.0734395372110157,J6,0.0734395372110157,0.043285948566531826
|
||||
11,11,3350,10.104322,1381651,3349,10.104215,1381643,624372596,-8,-0.10699999999985721,86.0934977330502,-14.498333177585573,-13.681510793859989,-69.8682955440411,26.742681901805224,88.9994136192388,87.81878662109375,1.7252888880435506,-16.329286575317383,-1.8309533977318093,-13.073507308959961,0.6080034849000278,-70.97254180908203,-1.1042462650409277,26.366661071777344,-0.3760208300278798,89.37981414794922,0.3804005287104246,J2,1.8309533977318093,1.169340011162825,3373,10.17622,1381652,624372668,9,1,71.8980000000009,86.11857604980469,0.025078316754488128,-14.53605842590332,-0.03772524831774682,-13.651655197143555,0.029855596716434007,-69.8272933959961,0.04100214804500979,26.72992706298828,-0.012754838816942282,88.92174530029297,-0.07766831894582538,J6,0.07766831894582538,0.04246508921571141
|
||||
12,12,3470,10.472333,1381697,3469,10.472191,1381689,624372964,-8,-0.14200000000030855,61.720732564877665,-4.2327893098442155,-9.784423185760874,-108.38408270751574,22.16077221865394,118.14206389103131,65.91849517822266,4.197762613344992,-6.331206798553467,-2.0984174887092513,-11.008773803710938,-1.2243506179500638,-103.56172943115234,4.822353276363401,23.532543182373047,1.3717709637191078,117.46694946289062,-0.675114428140688,J4,4.822353276363401,2.8611252897135864,3493,10.544283,1381698,624373036,9,1,71.94999999999929,61.7418098449707,0.021077280093038553,-4.212831497192383,0.019957812651832718,-9.789913177490234,-0.00548999172936071,-108.36312103271484,0.020961674800901164,22.163673400878906,0.002901182224967158,118.13783264160156,-0.0042312494297505054,J1,0.021077280093038553,0.014935517911187784
|
||||
13,13,3627,10.944371,1381756,3626,10.944251,1381748,624373436,-8,-0.12000000000078614,79.75708299218505,4.6402310571176475,-15.311185536748695,-56.03531247084017,26.13313788666632,52.152257172101606,72.04009246826172,-7.716990523923329,4.5515007972717285,-0.08873025984591898,-11.385177612304688,3.926007924444008,-64.18782043457031,-8.152507963730145,22.412456512451172,-3.7206813742151468,54.39056396484375,2.2383067927421436,J4,8.152507963730145,5.168659196117862,3651,11.016226,1381757,624373508,9,1,71.85499999999934,79.90262603759766,0.14554304541260876,4.62429666519165,-0.015934391925997105,-15.370849609375,-0.05966407262630469,-55.99199676513672,0.043315705703449225,26.182939529418945,0.04980164275262666,52.15596008300781,0.003702910906206114,J1,0.14554304541260876,0.0699602863618738
|
||||
14,14,3710,11.192339,1381787,3709,11.19225,1381779,624373684,-8,-0.08900000000089392,108.95555081237923,1.3707600427061273,-33.59442519685133,-48.36736100282485,43.963403989432074,85.03926111958742,102.20675659179688,-6.748794220582354,2.5045764446258545,1.1338164019197272,-29.148040771484375,4.446384425366958,-48.76271438598633,-0.3953533831614777,39.65629959106445,-4.3071043983676205,74.63484191894531,-10.40441920064211,J6,10.40441920064211,5.679810575214033,3735,11.272261,1381789,624373764,10,2,79.92199999999983,108.94917297363281,-0.006377838746416842,1.3447811603546143,-0.025978882351513022,-33.561458587646484,0.032966609204848396,-48.376487731933594,-0.009126729108743348,44.02882766723633,0.06542367780425451,85.1587142944336,0.11945317484617135,J6,0.11945317484617135,0.05835950688086092
|
||||
15,15,3796,11.456361,1381820,3795,11.456253,1381812,624373948,-8,-0.10799999999910881,110.58484797608095,-3.860899593758653,-32.39640711653168,-51.112590875369015,44.2291595765054,87.62399978413751,110.58193969726562,-0.002908278815326071,-1.244791865348816,2.6161077284098373,-33.74542999267578,-1.3490228761441045,-48.585723876953125,2.5268669984158905,44.58126449584961,0.35210491934420673,87.75857543945312,0.134575655315615,J2,2.6161077284098373,1.5911754635072362,3821,11.536287,1381822,624374028,10,2,79.92600000000039,110.67292022705078,0.08807225096983018,-4.010702133178711,-0.14980253942005772,-32.37457275390625,0.021834362625426706,-51.22603225708008,-0.11344138171106266,44.249629974365234,0.020470397859831735,87.66435241699219,0.040352632854677495,J2,0.14980253942005772,0.08716897465096986
|
||||
16,16,3870,11.680362,1381848,3869,11.680263,1381840,624374172,-8,-0.09900000000051534,118.09595224767921,-17.37638727211256,-31.06900084212659,-59.56053843778568,48.73257576059714,94.13477131163891,115.98970031738281,-2.106251930296395,-12.693913459777832,4.682473812334727,-31.563575744628906,-0.49457490250231473,-59.300933837890625,0.2596045998950558,47.15116500854492,-1.5814107520522214,94.34331512451172,0.20854381287280432,J2,4.682473812334727,2.2067399623329567,3895,11.760326,1381850,624374252,10,2,79.96399999999859,118.06391906738281,-0.0320331802963949,-17.536325454711914,-0.15993818259935466,-31.00341796875,0.06558287337659152,-59.507286071777344,0.05325236600833705,48.77106475830078,0.038488997703638006,93.9618148803711,-0.17295643126782068,J6,0.17295643126782068,0.10419421346699378
|
||||
17,18,4138,12.496365,1381950,4137,12.496271,1381942,624374988,-8,-0.09400000000070463,60.28248244838438,-22.938080568038327,-10.333247998560786,77.49164224770549,35.58337770883919,-69.66898071584893,61.48033142089844,1.197848972514059,-23.1823787689209,-0.2442982008825716,-10.346473693847656,-0.01322569528687012,79.24906921386719,1.7574269661617024,35.91868591308594,0.33530820424675056,-71.48294067382812,-1.8139599579791934,J6,1.8139599579791934,1.1536991630766555,4159,12.568313,1381951,624375060,9,1,71.94799999999901,60.27695846557617,-0.00552398280820654,-22.918088912963867,0.019991655074459658,-10.347146987915039,-0.013898989354252933,77.54158782958984,0.0499455818843586,35.57386016845703,-0.009517540382155687,-69.74390411376953,-0.07492339792059965,J6,0.07492339792059965,0.03834497543010648
|
||||
|
@@ -0,0 +1,18 @@
|
||||
trigger_no,waypoint_index,frame_number,sequence,time_relative_s,write_io_value,io_addrs,config_addr,max_error_axis,max_error_deg,rms_error_deg,j1_actual_deg,j1_teach_deg,diff_j1_deg,j2_actual_deg,j2_teach_deg,diff_j2_deg,j3_actual_deg,j3_teach_deg,diff_j3_deg,j4_actual_deg,j4_teach_deg,diff_j4_deg,j5_actual_deg,j5_teach_deg,diff_j5_deg,j6_actual_deg,j6_teach_deg,diff_j6_deg
|
||||
1,1,1955,1381124,5.888271,10,"[2, 4]","[2, 4]",J6,3.4065268256551553,2.468438977201345,45.8632698059082,48.886810269468405,-3.0235404635602023,2.9724788665771484,2.1989850886957285,0.7734937778814199,-13.609341621398926,-11.021017368511105,-2.588324252887821,-2.1851141452789307,0.4102097980549552,-2.5953239433338857,7.582361221313477,6.248381265333557,1.3339799559799195,5.701517581939697,2.294990756284542,3.4065268256551553
|
||||
2,2,2151,1381198,6.480252,14,"[2, 3, 4]","[3, 4, 2]",J4,2.8167225076858244,1.6562461612538633,56.34336471557617,55.34775509527405,0.995609620302119,11.66723918914795,11.807039833001637,-0.13980064385368784,-6.600923538208008,-7.0090952672806885,0.40817172907268073,-68.1976089477539,-71.01433145543973,2.8167225076858244,6.058437347412109,6.012065051914967,0.04637229549714217,71.5392837524414,74.24953191606797,-2.7102481636265594
|
||||
3,3,2223,1381226,6.704225,14,"[2, 3, 4]","[3, 4, 2]",J6,11.130744251720401,5.955607695749779,53.50831985473633,55.109808014787404,-1.6014881600510762,7.412222862243652,8.759497374223619,-1.3472745119799665,-9.629739761352539,-8.518216825284897,-1.111522936067642,-32.30571365356445,-41.11725046606459,8.811536812500137,12.488306045532227,10.108488114686867,2.379817930845359,30.826189041137695,41.956933292858096,-11.130744251720401
|
||||
4,4,2336,1381269,7.048266,10,"[2, 4]","[4, 2]",J6,2.3116842904428125,1.3731583485126422,44.433006286621094,43.65359310453334,0.779413182087751,-2.1156294345855713,-1.629659559507137,-0.48596987507843425,-18.39777374267578,-17.715753611915318,-0.6820201307604634,4.8371992111206055,5.995208633582219,-1.158009422461613,33.99372482299805,32.17171770646655,1.8220071165314948,-20.26228904724121,-22.573973337684023,2.3116842904428125
|
||||
5,5,2477,1381321,7.464289,10,"[2, 4]","[4, 2]",J5,2.171828177649214,1.404704060246151,65.84967041015625,64.58244475717193,1.2672256529843224,5.695003032684326,4.262979315506351,1.4320237171779748,-14.563636779785156,-15.669217122643433,1.1055803428582767,-28.69342613220215,-29.952927185666542,1.259501053464394,27.678611755371094,29.850439933020308,-2.171828177649214,46.437156677246094,45.62652743544272,0.810629241803376
|
||||
6,6,2665,1381391,8.024277,12,"[3, 4]","[3, 4]",J2,1.408993592340284,0.8424031169595625,60.847530364990234,60.47948252708421,0.36804783790602613,24.47777557373047,23.068781981390185,1.408993592340284,-5.617043972015381,-5.01126420129949,-0.6057797707158912,34.8044319152832,36.10685486878251,-1.3024229534993097,5.786255359649658,5.525681179628412,0.260574180021246,-31.2253475189209,-31.3025636495649,0.07721613064400046
|
||||
7,7,2811,1381447,8.472317,12,"[3, 4]","[3, 4]",J6,7.1941073647892395,4.618545262978315,70.8250503540039,70.47583707168602,0.3492132823178906,10.95259952545166,16.39384887825908,-5.4412493528074215,-13.136046409606934,-13.456948007467595,0.3209015978606615,-34.68459701538086,-27.892318852946243,-6.7922781624346165,14.022420883178711,14.53566195089614,-0.5132410677174288,38.90604019165039,31.71193282686115,7.1941073647892395
|
||||
8,8,2908,1381483,8.760361,10,"[2, 4]","[4, 2]",J6,5.93988407546847,3.533132924843701,69.40516662597656,69.58246408878419,-0.1772974628076298,-20.066781997680664,-17.10571341532583,-2.9610685823548337,-8.152935981750488,-8.710589696831251,0.5576537150807628,-63.93235397338867,-58.47569441890704,-5.456659554481632,6.775912284851074,7.63059003611043,-0.8546777512593557,70.37761688232422,64.43773280685575,5.93988407546847
|
||||
9,9,3112,1381561,9.384332,10,"[2, 4]","[4, 2]",J4,2.5240706198644887,1.630593251762862,73.90592956542969,73.57125874861414,0.3346708168155459,-8.016124725341797,-6.42984524964374,-1.5862794756980572,-9.002426147460938,-9.628579811400881,0.6261536639399434,-131.3327178955078,-128.80864727564332,-2.5240706198644887,15.526206970214844,14.671082817606491,0.8551241526083526,142.41668701171875,140.00204816414424,2.4146388475745084
|
||||
10,10,3170,1381583,9.560358,10,"[2, 4]","[4, 2]",J4,3.3929965007450846,2.1059862266772753,76.72688293457031,75.56938603377543,1.1574969007948823,-16.518726348876953,-14.67930590788221,-1.8394204409947434,-7.15147066116333,-7.294156094303152,0.14268543313982196,-127.53003692626953,-130.92303342701462,3.3929965007450846,18.53788948059082,17.688361072687904,0.8495284079029162,138.7897491455078,141.89350789658602,-3.103758751078203
|
||||
11,11,3350,1381651,10.104322,10,"[2, 4]","[4, 2]",J6,2.9848240516596434,2.0134925384056306,83.71662139892578,86.0934977330502,-2.376876334124418,-13.186573028564453,-14.498333177585573,1.3117601490211204,-13.738808631896973,-13.681510793859989,-0.05729783803698396,-72.70317840576172,-69.8682955440411,-2.834882861720615,26.66758918762207,26.742681901805224,-0.07509271418315322,91.98423767089844,88.9994136192388,2.9848240516596434
|
||||
12,12,3470,1381697,10.472333,10,"[2, 4]","[4, 2]",J6,4.000958239175844,1.9902461373621054,59.78955841064453,61.720732564877665,-1.9311741542331333,-2.7094168663024902,-4.2327893098442155,1.5233724435417253,-8.933828353881836,-9.784423185760874,0.8505948318790377,-108.24857330322266,-108.38408270751574,0.13550940429308866,21.177492141723633,22.16077221865394,-0.9832800769303063,114.14110565185547,118.14206389103131,-4.000958239175844
|
||||
13,13,3627,1381756,10.944371,12,"[3, 4]","[4, 3]",J1,6.656208938967296,4.109282348845555,86.41329193115234,79.75708299218505,6.656208938967296,4.266173362731934,4.6402310571176475,-0.3740576943857139,-19.15987777709961,-15.311185536748695,-3.848692240350914,-52.24028015136719,-56.03531247084017,3.7950323194729805,29.853723526000977,26.13313788666632,3.720585639334658,55.86907196044922,52.152257172101606,3.7168147883476124
|
||||
14,14,3710,1381787,11.192339,10,"[2, 4]","[4, 2]",J6,2.93518040873289,1.5679827735552678,110.7523193359375,108.95555081237923,1.7967685235582707,0.8751887679100037,1.3707600427061273,-0.4955712747961236,-34.756534576416016,-33.59442519685133,-1.1621093795646829,-48.14332580566406,-48.36736100282485,0.2240351971607879,45.0865592956543,43.963403989432074,1.1231553062222233,87.97444152832031,85.03926111958742,2.93518040873289
|
||||
15,15,3796,1381820,11.456361,10,"[2, 4]","[4, 2]",J4,2.573146734494266,1.686689503314435,111.79177856445312,110.58484797608095,1.206930588372174,-6.225440502166748,-3.860899593758653,-2.364540908408095,-31.958988189697266,-32.39640711653168,0.4374189268344111,-53.68573760986328,-51.112590875369015,-2.573146734494266,44.76150894165039,44.2291595765054,0.532349365144988,89.33454895019531,87.62399978413751,1.7105491660578025
|
||||
16,16,3870,1381848,11.680362,10,"[2, 4]","[4, 2]",J6,3.8105220440607894,2.197782779718695,117.87474060058594,118.09595224767921,-0.2212116470932699,-19.607177734375,-17.37638727211256,-2.2307904622624406,-30.329103469848633,-31.06900084212659,0.7398973722779587,-56.600250244140625,-59.56053843778568,2.960288193645056,49.08655548095703,48.73257576059714,0.353979720359888,90.32424926757812,94.13477131163891,-3.8105220440607894
|
||||
17,18,4138,1381950,12.496365,12,"[3, 4]","[4, 3]",J4,6.848522435693766,4.03614029285736,59.93589401245117,60.28248244838438,-0.34658843593320654,-21.102140426635742,-22.938080568038327,1.8359401414025847,-9.684239387512207,-10.333247998560786,0.6490086110485791,70.64311981201172,77.49164224770549,-6.848522435693766,32.941307067871094,35.58337770883919,-2.642070640968093,-63.34855651855469,-69.66898071584893,6.320424197294244
|
||||
|
363
analysis/analyze_2026042802_1_status_feedback_vs_teach_points.py
Normal file
363
analysis/analyze_2026042802_1_status_feedback_vs_teach_points.py
Normal file
@@ -0,0 +1,363 @@
|
||||
#!/usr/bin/env python3
|
||||
"""提取 2026042802-1 抓包中的 60015 状态反馈,并和 UTTC_MS11 示教点对比。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import json
|
||||
import math
|
||||
import struct
|
||||
import subprocess
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_PCAP = REPO_ROOT.parent / "Rvbust" / "uttc-20260428" / "2026042802-1.pcap"
|
||||
DEFAULT_TSHARK = Path(r"D:\Zyx\Downloads\WiresharkPortable32\App\Wireshark\tshark.exe")
|
||||
OUTPUT_DIR = REPO_ROOT / "analysis" / "2026042802-1"
|
||||
CONFIG_PATH = REPO_ROOT / "Config" / "RobotConfig.json"
|
||||
SEARCH_WINDOW_CYCLES = 20
|
||||
|
||||
|
||||
def be_u32(data: bytes, offset: int) -> int:
|
||||
"""按大端读取 4 字节无符号整数。"""
|
||||
return struct.unpack(">I", data[offset : offset + 4])[0]
|
||||
|
||||
|
||||
def be_u16(data: bytes, offset: int) -> int:
|
||||
"""按大端读取 2 字节无符号整数。"""
|
||||
return struct.unpack(">H", data[offset : offset + 2])[0]
|
||||
|
||||
|
||||
def be_f32(data: bytes, offset: int) -> float:
|
||||
"""按大端读取 4 字节浮点数。"""
|
||||
return struct.unpack(">f", data[offset : offset + 4])[0]
|
||||
|
||||
|
||||
def load_udp_rows(pcap: Path, tshark: Path) -> list[list[str]]:
|
||||
"""提取 UDP 60015 原始字段,后续按方向和长度拆分命令与状态。"""
|
||||
command = [
|
||||
str(tshark),
|
||||
"-r",
|
||||
str(pcap),
|
||||
"-Y",
|
||||
"udp.port==60015",
|
||||
"-T",
|
||||
"fields",
|
||||
"-e",
|
||||
"frame.number",
|
||||
"-e",
|
||||
"frame.time_relative",
|
||||
"-e",
|
||||
"ip.src",
|
||||
"-e",
|
||||
"ip.dst",
|
||||
"-e",
|
||||
"udp.payload",
|
||||
]
|
||||
output = subprocess.check_output(command, text=True, encoding="utf-8", errors="ignore")
|
||||
rows: list[list[str]] = []
|
||||
for line in output.splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
parts = line.split("\t")
|
||||
if len(parts) >= 5:
|
||||
rows.append(parts[:5])
|
||||
return rows
|
||||
|
||||
|
||||
def decode_command_records(rows: list[list[str]], client_ip: str, robot_ip: str) -> list[dict]:
|
||||
"""把 64B J519 命令帧解码成结构化记录。"""
|
||||
records: list[dict] = []
|
||||
for frame_no, time_rel, ip_src, ip_dst, payload_hex in rows:
|
||||
if ip_src != client_ip or ip_dst != robot_ip:
|
||||
continue
|
||||
|
||||
payload = bytes.fromhex(payload_hex)
|
||||
if len(payload) != 64:
|
||||
continue
|
||||
|
||||
records.append(
|
||||
{
|
||||
"frame_number": int(frame_no),
|
||||
"time_relative_s": float(time_rel),
|
||||
"sequence": be_u32(payload, 0x08),
|
||||
"write_io_value": be_u16(payload, 0x18),
|
||||
"j1_deg": be_f32(payload, 0x1C),
|
||||
"j2_deg": be_f32(payload, 0x20),
|
||||
"j3_deg": be_f32(payload, 0x24),
|
||||
"j4_deg": be_f32(payload, 0x28),
|
||||
"j5_deg": be_f32(payload, 0x2C),
|
||||
"j6_deg": be_f32(payload, 0x30),
|
||||
}
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
def decode_status_records(rows: list[list[str]], client_ip: str, robot_ip: str) -> list[dict]:
|
||||
"""把 132B J519 状态帧按运行时代码同口径解码。"""
|
||||
records: list[dict] = []
|
||||
for frame_no, time_rel, ip_src, ip_dst, payload_hex in rows:
|
||||
if ip_src != robot_ip or ip_dst != client_ip:
|
||||
continue
|
||||
|
||||
payload = bytes.fromhex(payload_hex)
|
||||
if len(payload) != 132:
|
||||
continue
|
||||
|
||||
status = payload[0x0C]
|
||||
joints = [be_f32(payload, 0x3C + index * 4) for index in range(6)]
|
||||
pose = [be_f32(payload, 0x18 + index * 4) for index in range(6)]
|
||||
records.append(
|
||||
{
|
||||
"frame_number": int(frame_no),
|
||||
"time_relative_s": float(time_rel),
|
||||
"sequence": be_u32(payload, 0x08),
|
||||
"status": status,
|
||||
"accepts_command": bool(status & 0b0001),
|
||||
"received_command": bool(status & 0b0010),
|
||||
"system_ready": bool(status & 0b0100),
|
||||
"robot_in_motion": bool(status & 0b1000),
|
||||
"read_io_value": be_u16(payload, 0x12),
|
||||
"timestamp": be_u32(payload, 0x14),
|
||||
"pose_x_mm": pose[0],
|
||||
"pose_y_mm": pose[1],
|
||||
"pose_z_mm": pose[2],
|
||||
"pose_w_deg": pose[3],
|
||||
"pose_p_deg": pose[4],
|
||||
"pose_r_deg": pose[5],
|
||||
"j1_deg": joints[0],
|
||||
"j2_deg": joints[1],
|
||||
"j3_deg": joints[2],
|
||||
"j4_deg": joints[3],
|
||||
"j5_deg": joints[4],
|
||||
"j6_deg": joints[5],
|
||||
}
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
def pick_trigger_first_high_frames(records: list[dict]) -> list[dict]:
|
||||
"""由于 io_keep_cycles=2,只保留每组高电平脉冲的第一帧。"""
|
||||
trigger_frames: list[dict] = []
|
||||
previous_high = False
|
||||
for record in records:
|
||||
current_high = record["write_io_value"] > 0
|
||||
if current_high and not previous_high:
|
||||
trigger_frames.append(record)
|
||||
previous_high = current_high
|
||||
return trigger_frames
|
||||
|
||||
|
||||
def load_uttc_ms11_config() -> dict:
|
||||
"""读取 UTTC_MS11 的示教点和触发配置。"""
|
||||
config = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
|
||||
return config["flying_shots"]["UTTC_MS11"]
|
||||
|
||||
|
||||
def build_diff_row(prefix: str, actual_deg: list[float], teach_deg: list[float], row: dict) -> tuple[float, float, str]:
|
||||
"""向结果行写入逐轴误差,并返回聚合误差。"""
|
||||
diffs = [actual_deg[index] - teach_deg[index] for index in range(6)]
|
||||
abs_diffs = [abs(value) for value in diffs]
|
||||
max_error = max(abs_diffs)
|
||||
max_error_axis = f"J{abs_diffs.index(max_error) + 1}"
|
||||
rms_error = math.sqrt(sum(value * value for value in diffs) / 6.0)
|
||||
|
||||
for joint_index in range(6):
|
||||
joint_no = joint_index + 1
|
||||
row[f"{prefix}_j{joint_no}_actual_deg"] = actual_deg[joint_index]
|
||||
row[f"{prefix}_diff_j{joint_no}_deg"] = diffs[joint_index]
|
||||
|
||||
row[f"{prefix}_max_error_axis"] = max_error_axis
|
||||
row[f"{prefix}_max_error_deg"] = max_error
|
||||
row[f"{prefix}_rms_error_deg"] = rms_error
|
||||
return max_error, rms_error, max_error_axis
|
||||
|
||||
|
||||
def build_trigger_status_rows(trigger_frames: list[dict], status_records: list[dict], shot_config: dict) -> list[dict]:
|
||||
"""按触发顺序对齐命令帧、当前状态帧以及最接近示教点的反馈状态帧。"""
|
||||
rows: list[dict] = []
|
||||
trigger_waypoint_indices = [index for index, flag in enumerate(shot_config["shot_flags"]) if flag]
|
||||
status_by_sequence = {record["sequence"]: record for record in status_records}
|
||||
status_sequence_set = set(status_by_sequence)
|
||||
|
||||
for trigger_no, (trigger_frame, waypoint_index) in enumerate(zip(trigger_frames, trigger_waypoint_indices), start=1):
|
||||
teach_deg = [math.degrees(value) for value in shot_config["traj_waypoints"][waypoint_index]]
|
||||
current_status_sequence = trigger_frame["sequence"] - 8
|
||||
current_status = status_by_sequence[current_status_sequence]
|
||||
|
||||
row = {
|
||||
"trigger_no": trigger_no,
|
||||
"waypoint_index": waypoint_index,
|
||||
"trigger_frame_number": trigger_frame["frame_number"],
|
||||
"trigger_time_relative_s": trigger_frame["time_relative_s"],
|
||||
"trigger_sequence": trigger_frame["sequence"],
|
||||
"paired_status_frame_number": current_status["frame_number"],
|
||||
"paired_status_time_relative_s": current_status["time_relative_s"],
|
||||
"paired_status_sequence": current_status["sequence"],
|
||||
"paired_status_timestamp": current_status["timestamp"],
|
||||
"paired_status_to_trigger_sequence_delta": current_status["sequence"] - trigger_frame["sequence"],
|
||||
"paired_status_to_trigger_time_ms": (current_status["time_relative_s"] - trigger_frame["time_relative_s"]) * 1000.0,
|
||||
}
|
||||
|
||||
for joint_index in range(6):
|
||||
joint_no = joint_index + 1
|
||||
row[f"teach_j{joint_no}_deg"] = teach_deg[joint_index]
|
||||
|
||||
build_diff_row(
|
||||
"paired_status",
|
||||
[current_status[f"j{joint_no}_deg"] for joint_no in range(1, 7)],
|
||||
teach_deg,
|
||||
row,
|
||||
)
|
||||
|
||||
best_candidate = None
|
||||
for delta_cycles in range(-SEARCH_WINDOW_CYCLES, SEARCH_WINDOW_CYCLES + 1):
|
||||
candidate_sequence = current_status_sequence + delta_cycles
|
||||
if candidate_sequence not in status_sequence_set:
|
||||
continue
|
||||
|
||||
candidate = status_by_sequence[candidate_sequence]
|
||||
diffs = [candidate[f"j{joint_no}_deg"] - teach_deg[joint_no - 1] for joint_no in range(1, 7)]
|
||||
rms_error = math.sqrt(sum(value * value for value in diffs) / 6.0)
|
||||
max_error = max(abs(value) for value in diffs)
|
||||
score = (rms_error, max_error, abs(delta_cycles))
|
||||
if best_candidate is None or score < best_candidate["score"]:
|
||||
best_candidate = {
|
||||
"score": score,
|
||||
"delta_cycles": delta_cycles,
|
||||
"record": candidate,
|
||||
}
|
||||
|
||||
if best_candidate is None:
|
||||
raise RuntimeError(f"Trigger {trigger_no} 未找到候选状态帧。")
|
||||
|
||||
best_status = best_candidate["record"]
|
||||
row["best_status_frame_number"] = best_status["frame_number"]
|
||||
row["best_status_time_relative_s"] = best_status["time_relative_s"]
|
||||
row["best_status_sequence"] = best_status["sequence"]
|
||||
row["best_status_timestamp"] = best_status["timestamp"]
|
||||
row["best_status_delta_from_paired_cycles"] = best_candidate["delta_cycles"]
|
||||
row["best_status_delta_from_trigger_sequence"] = best_status["sequence"] - trigger_frame["sequence"]
|
||||
row["best_status_time_after_trigger_ms"] = (best_status["time_relative_s"] - trigger_frame["time_relative_s"]) * 1000.0
|
||||
|
||||
build_diff_row(
|
||||
"best_status",
|
||||
[best_status[f"j{joint_no}_deg"] for joint_no in range(1, 7)],
|
||||
teach_deg,
|
||||
row,
|
||||
)
|
||||
|
||||
rows.append(row)
|
||||
|
||||
return rows
|
||||
|
||||
|
||||
def write_csv(path: Path, rows: list[dict]) -> None:
|
||||
"""把分析结果落成 UTF-8 CSV。"""
|
||||
if not rows:
|
||||
raise ValueError(f"No rows to write: {path}")
|
||||
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=list(rows[0].keys()))
|
||||
writer.writeheader()
|
||||
writer.writerows(rows)
|
||||
|
||||
|
||||
def build_summary(command_records: list[dict], trigger_status_rows: list[dict]) -> dict:
|
||||
"""汇总命令序列偏移和状态反馈误差分布。"""
|
||||
sequence_offsets = [row["trigger_sequence"] - row["paired_status_sequence"] for row in trigger_status_rows]
|
||||
best_paired_cycle_offsets = [row["best_status_delta_from_paired_cycles"] for row in trigger_status_rows]
|
||||
best_trigger_sequence_offsets = [row["best_status_delta_from_trigger_sequence"] for row in trigger_status_rows]
|
||||
best_time_offsets = [row["best_status_time_after_trigger_ms"] for row in trigger_status_rows]
|
||||
paired_max_errors = [row["paired_status_max_error_deg"] for row in trigger_status_rows]
|
||||
best_max_errors = [row["best_status_max_error_deg"] for row in trigger_status_rows]
|
||||
paired_axes = Counter(row["paired_status_max_error_axis"] for row in trigger_status_rows)
|
||||
best_axes = Counter(row["best_status_max_error_axis"] for row in trigger_status_rows)
|
||||
|
||||
sequence_offset_counter = Counter()
|
||||
trigger_frames = pick_trigger_first_high_frames(command_records)
|
||||
status_pairs = {row["trigger_frame_number"]: row for row in trigger_status_rows}
|
||||
for trigger_frame in trigger_frames:
|
||||
sequence_offset_counter[trigger_frame["sequence"] - status_pairs[trigger_frame["frame_number"]]["paired_status_sequence"]] += 1
|
||||
|
||||
return {
|
||||
"pcap_path": str(DEFAULT_PCAP),
|
||||
"command_count": len(command_records),
|
||||
"trigger_count": len(trigger_status_rows),
|
||||
"command_minus_paired_status_sequence_counter": dict(sequence_offset_counter),
|
||||
"paired_status_average_max_error_deg": sum(paired_max_errors) / len(paired_max_errors),
|
||||
"paired_status_max_error_deg": max(paired_max_errors),
|
||||
"paired_status_max_error_axis_counter": dict(paired_axes),
|
||||
"best_status_average_max_error_deg": sum(best_max_errors) / len(best_max_errors),
|
||||
"best_status_max_error_deg": max(best_max_errors),
|
||||
"best_status_max_error_axis_counter": dict(best_axes),
|
||||
"best_status_delta_from_paired_cycles_counter": dict(Counter(best_paired_cycle_offsets)),
|
||||
"best_status_delta_from_trigger_sequence_counter": dict(Counter(best_trigger_sequence_offsets)),
|
||||
"best_status_time_after_trigger_ms_min": min(best_time_offsets),
|
||||
"best_status_time_after_trigger_ms_max": max(best_time_offsets),
|
||||
"best_status_time_after_trigger_ms_avg": sum(best_time_offsets) / len(best_time_offsets),
|
||||
"search_window_cycles": SEARCH_WINDOW_CYCLES,
|
||||
}
|
||||
|
||||
|
||||
def build_manual_compare_rows(trigger_status_rows: list[dict]) -> list[dict]:
|
||||
"""整理成便于人工逐点核对的三时刻对照表。"""
|
||||
rows: list[dict] = []
|
||||
for row in trigger_status_rows:
|
||||
rows.append(
|
||||
{
|
||||
"trigger_no": row["trigger_no"],
|
||||
"waypoint_index": row["waypoint_index"],
|
||||
"trigger_command_sequence": row["trigger_sequence"],
|
||||
"trigger_command_frame": row["trigger_frame_number"],
|
||||
"trigger_command_time_relative_s": row["trigger_time_relative_s"],
|
||||
"trigger_current_status_sequence": row["paired_status_sequence"],
|
||||
"trigger_current_status_frame": row["paired_status_frame_number"],
|
||||
"trigger_current_status_time_relative_s": row["paired_status_time_relative_s"],
|
||||
"command_leads_status_cycles": row["trigger_sequence"] - row["paired_status_sequence"],
|
||||
"trigger_current_status_max_error_axis": row["paired_status_max_error_axis"],
|
||||
"trigger_current_status_max_error_deg": row["paired_status_max_error_deg"],
|
||||
"trigger_current_status_rms_error_deg": row["paired_status_rms_error_deg"],
|
||||
"best_status_sequence": row["best_status_sequence"],
|
||||
"best_status_frame": row["best_status_frame_number"],
|
||||
"best_status_time_relative_s": row["best_status_time_relative_s"],
|
||||
"best_status_delay_from_current_status_cycles": row["best_status_delta_from_paired_cycles"],
|
||||
"best_status_delay_from_trigger_command_cycles": row["best_status_delta_from_trigger_sequence"],
|
||||
"best_status_delay_from_trigger_command_ms": row["best_status_time_after_trigger_ms"],
|
||||
"best_status_max_error_axis": row["best_status_max_error_axis"],
|
||||
"best_status_max_error_deg": row["best_status_max_error_deg"],
|
||||
"best_status_rms_error_deg": row["best_status_rms_error_deg"],
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""执行状态反馈提取、触发对齐和摘要落盘。"""
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
rows = load_udp_rows(DEFAULT_PCAP, DEFAULT_TSHARK)
|
||||
command_records = decode_command_records(rows, client_ip="192.168.10.10", robot_ip="192.168.10.11")
|
||||
status_records = decode_status_records(rows, client_ip="192.168.10.10", robot_ip="192.168.10.11")
|
||||
trigger_frames = pick_trigger_first_high_frames(command_records)
|
||||
shot_config = load_uttc_ms11_config()
|
||||
trigger_status_rows = build_trigger_status_rows(trigger_frames, status_records, shot_config)
|
||||
manual_compare_rows = build_manual_compare_rows(trigger_status_rows)
|
||||
summary = build_summary(command_records, trigger_status_rows)
|
||||
|
||||
write_csv(OUTPUT_DIR / "2026042802-1_j519_status_feedback_all.csv", status_records)
|
||||
write_csv(OUTPUT_DIR / "2026042802-1_trigger_status_feedback_vs_teach_points.csv", trigger_status_rows)
|
||||
write_csv(OUTPUT_DIR / "2026042802-1_trigger_manual_compare.csv", manual_compare_rows)
|
||||
(OUTPUT_DIR / "2026042802-1_status_feedback_summary.json").write_text(
|
||||
json.dumps(summary, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
250
analysis/analyze_2026042802_1_trigger_vs_teach_points.py
Normal file
250
analysis/analyze_2026042802_1_trigger_vs_teach_points.py
Normal file
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env python3
|
||||
"""提取 2026042802-1 抓包中的真实 J519 发包,并对比 UTTC_MS11 示教点。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import json
|
||||
import math
|
||||
import struct
|
||||
import subprocess
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_PCAP = REPO_ROOT.parent / "Rvbust" / "uttc-20260428" / "2026042802-1.pcap"
|
||||
DEFAULT_TSHARK = Path(r"D:\Zyx\Downloads\WiresharkPortable32\App\Wireshark\tshark.exe")
|
||||
OUTPUT_DIR = REPO_ROOT / "analysis" / "2026042802-1"
|
||||
CONFIG_PATH = REPO_ROOT / "Config" / "RobotConfig.json"
|
||||
RUNTIME_DATA_DIR = REPO_ROOT / "Config" / "Data" / "UTTC_MS11"
|
||||
|
||||
|
||||
def be_u32(data: bytes, offset: int) -> int:
|
||||
"""按大端读取 4 字节无符号整数。"""
|
||||
return struct.unpack(">I", data[offset : offset + 4])[0]
|
||||
|
||||
|
||||
def be_u16(data: bytes, offset: int) -> int:
|
||||
"""按大端读取 2 字节无符号整数。"""
|
||||
return struct.unpack(">H", data[offset : offset + 2])[0]
|
||||
|
||||
|
||||
def be_f32(data: bytes, offset: int) -> float:
|
||||
"""按大端读取 4 字节浮点数。"""
|
||||
return struct.unpack(">f", data[offset : offset + 4])[0]
|
||||
|
||||
|
||||
def load_j519_command_rows(pcap: Path, tshark: Path) -> list[list[str]]:
|
||||
"""只提取 UDP 60015 的原始字段,后续再按 IP 方向筛选真实下发命令。"""
|
||||
command = [
|
||||
str(tshark),
|
||||
"-r",
|
||||
str(pcap),
|
||||
"-Y",
|
||||
"udp.port==60015",
|
||||
"-T",
|
||||
"fields",
|
||||
"-e",
|
||||
"frame.number",
|
||||
"-e",
|
||||
"frame.time_relative",
|
||||
"-e",
|
||||
"ip.src",
|
||||
"-e",
|
||||
"udp.srcport",
|
||||
"-e",
|
||||
"ip.dst",
|
||||
"-e",
|
||||
"udp.dstport",
|
||||
"-e",
|
||||
"udp.payload",
|
||||
]
|
||||
output = subprocess.check_output(command, text=True, encoding="utf-8", errors="ignore")
|
||||
rows: list[list[str]] = []
|
||||
for line in output.splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
parts = line.split("\t")
|
||||
if len(parts) >= 7:
|
||||
rows.append(parts[:7])
|
||||
return rows
|
||||
|
||||
|
||||
def decode_command_records(rows: list[list[str]], client_ip: str, robot_ip: str) -> list[dict]:
|
||||
"""把抓包中的 64B J519 命令帧解码成带 IO 信息的结构化记录。"""
|
||||
records: list[dict] = []
|
||||
for frame_no, time_rel, ip_src, _udp_src, ip_dst, _udp_dst, payload_hex in rows:
|
||||
if ip_src != client_ip or ip_dst != robot_ip:
|
||||
continue
|
||||
|
||||
payload = bytes.fromhex(payload_hex)
|
||||
if len(payload) != 64:
|
||||
continue
|
||||
|
||||
io_value = be_u16(payload, 0x18)
|
||||
io_addrs = [bit + 1 for bit in range(16) if io_value & (1 << bit)]
|
||||
targets = [be_f32(payload, 0x1C + index * 4) for index in range(9)]
|
||||
records.append(
|
||||
{
|
||||
"frame_number": int(frame_no),
|
||||
"time_relative_s": float(time_rel),
|
||||
"sequence": be_u32(payload, 0x08),
|
||||
"last_data": payload[0x0C],
|
||||
"write_io_type": payload[0x13],
|
||||
"write_io_index": be_u16(payload, 0x14),
|
||||
"write_io_mask": be_u16(payload, 0x16),
|
||||
"write_io_value": io_value,
|
||||
"io_addrs": io_addrs,
|
||||
"j1_deg": targets[0],
|
||||
"j2_deg": targets[1],
|
||||
"j3_deg": targets[2],
|
||||
"j4_deg": targets[3],
|
||||
"j5_deg": targets[4],
|
||||
"j6_deg": targets[5],
|
||||
"ext1_deg": targets[6],
|
||||
"ext2_deg": targets[7],
|
||||
"ext3_deg": targets[8],
|
||||
}
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
def pick_trigger_first_high_frames(records: list[dict]) -> list[dict]:
|
||||
"""由于 io_keep_cycles=2,只记录每组高电平脉冲的第一帧。"""
|
||||
trigger_frames: list[dict] = []
|
||||
previous_high = False
|
||||
for record in records:
|
||||
current_high = record["write_io_value"] > 0
|
||||
if current_high and not previous_high:
|
||||
trigger_frames.append(record)
|
||||
previous_high = current_high
|
||||
return trigger_frames
|
||||
|
||||
|
||||
def load_uttc_ms11_config() -> dict:
|
||||
"""读取 UTTC_MS11 的示教点和触发配置。"""
|
||||
config = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
|
||||
return config["flying_shots"]["UTTC_MS11"]
|
||||
|
||||
|
||||
def build_trigger_vs_teach_rows(trigger_frames: list[dict], shot_config: dict) -> list[dict]:
|
||||
"""按 shot_flags 为 true 的 waypoint 顺序,对齐抓包触发帧和示教点。"""
|
||||
rows: list[dict] = []
|
||||
trigger_waypoint_indices = [index for index, flag in enumerate(shot_config["shot_flags"]) if flag]
|
||||
|
||||
for trigger_no, (frame, waypoint_index) in enumerate(zip(trigger_frames, trigger_waypoint_indices), start=1):
|
||||
teach_rad = shot_config["traj_waypoints"][waypoint_index]
|
||||
teach_deg = [math.degrees(value) for value in teach_rad]
|
||||
actual_deg = [frame[f"j{joint_index}_deg"] for joint_index in range(1, 7)]
|
||||
diffs = [actual_deg[index] - teach_deg[index] for index in range(6)]
|
||||
abs_diffs = [abs(value) for value in diffs]
|
||||
max_error = max(abs_diffs)
|
||||
max_error_axis = f"J{abs_diffs.index(max_error) + 1}"
|
||||
rms_error = math.sqrt(sum(value * value for value in diffs) / 6.0)
|
||||
|
||||
row = {
|
||||
"trigger_no": trigger_no,
|
||||
"waypoint_index": waypoint_index,
|
||||
"frame_number": frame["frame_number"],
|
||||
"sequence": frame["sequence"],
|
||||
"time_relative_s": frame["time_relative_s"],
|
||||
"write_io_value": frame["write_io_value"],
|
||||
"io_addrs": frame["io_addrs"],
|
||||
"config_addr": shot_config["addr"][waypoint_index],
|
||||
"max_error_axis": max_error_axis,
|
||||
"max_error_deg": max_error,
|
||||
"rms_error_deg": rms_error,
|
||||
}
|
||||
|
||||
for joint_index in range(6):
|
||||
joint_no = joint_index + 1
|
||||
row[f"j{joint_no}_actual_deg"] = actual_deg[joint_index]
|
||||
row[f"j{joint_no}_teach_deg"] = teach_deg[joint_index]
|
||||
row[f"diff_j{joint_no}_deg"] = diffs[joint_index]
|
||||
|
||||
rows.append(row)
|
||||
|
||||
return rows
|
||||
|
||||
|
||||
def write_csv(path: Path, rows: list[dict]) -> None:
|
||||
"""把分析结果落成 UTF-8 CSV,便于后续继续筛选和画图。"""
|
||||
if not rows:
|
||||
raise ValueError(f"No rows to write: {path}")
|
||||
|
||||
serializable_rows: list[dict] = []
|
||||
for row in rows:
|
||||
serializable_row: dict = {}
|
||||
for key, value in row.items():
|
||||
if isinstance(value, list):
|
||||
serializable_row[key] = json.dumps(value, ensure_ascii=False)
|
||||
else:
|
||||
serializable_row[key] = value
|
||||
serializable_rows.append(serializable_row)
|
||||
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=list(serializable_rows[0].keys()))
|
||||
writer.writeheader()
|
||||
writer.writerows(serializable_rows)
|
||||
|
||||
|
||||
def build_summary(records: list[dict], trigger_rows: list[dict]) -> dict:
|
||||
"""汇总本次分析关心的导出状态和误差统计。"""
|
||||
max_errors = [float(row["max_error_deg"]) for row in trigger_rows]
|
||||
rms_errors = [float(row["rms_error_deg"]) for row in trigger_rows]
|
||||
axis_counter = Counter(str(row["max_error_axis"]) for row in trigger_rows)
|
||||
|
||||
order_only_addr_mismatch = 0
|
||||
real_addr_mismatch = 0
|
||||
for row in trigger_rows:
|
||||
io_addrs = list(row["io_addrs"])
|
||||
config_addr = list(row["config_addr"])
|
||||
if io_addrs != config_addr:
|
||||
if sorted(io_addrs) == sorted(config_addr):
|
||||
order_only_addr_mismatch += 1
|
||||
else:
|
||||
real_addr_mismatch += 1
|
||||
|
||||
return {
|
||||
"pcap_path": str(DEFAULT_PCAP),
|
||||
"all_command_count": len(records),
|
||||
"trigger_count": len(trigger_rows),
|
||||
"existing_runtime_actual_send_exists": (RUNTIME_DATA_DIR / "ActualSendJointTraj.txt").exists(),
|
||||
"existing_runtime_actual_send_has_io_columns": False,
|
||||
"existing_shot_events_exists": (RUNTIME_DATA_DIR / "ShotEvents.json").exists(),
|
||||
"pcap_specific_combined_export_preexisting": False,
|
||||
"average_max_error_deg": sum(max_errors) / len(max_errors),
|
||||
"max_error_deg": max(max_errors),
|
||||
"average_rms_error_deg": sum(rms_errors) / len(rms_errors),
|
||||
"max_error_axis_counter": dict(axis_counter),
|
||||
"order_only_addr_mismatch_count": order_only_addr_mismatch,
|
||||
"real_addr_mismatch_count": real_addr_mismatch,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""执行抓包提取、示教点对齐、CSV 导出和摘要落盘。"""
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
rows = load_j519_command_rows(DEFAULT_PCAP, DEFAULT_TSHARK)
|
||||
records = decode_command_records(rows, client_ip="192.168.10.10", robot_ip="192.168.10.11")
|
||||
trigger_frames = pick_trigger_first_high_frames(records)
|
||||
shot_config = load_uttc_ms11_config()
|
||||
trigger_rows = build_trigger_vs_teach_rows(trigger_frames, shot_config)
|
||||
summary = build_summary(records, trigger_rows)
|
||||
|
||||
write_csv(OUTPUT_DIR / "2026042802-1_j519_actual_send_all_with_io.csv", records)
|
||||
write_csv(OUTPUT_DIR / "2026042802-1_j519_trigger_frames.csv", trigger_frames)
|
||||
write_csv(OUTPUT_DIR / "2026042802-1_trigger_vs_teach_points.csv", trigger_rows)
|
||||
(OUTPUT_DIR / "2026042802-1_analysis_summary.json").write_text(
|
||||
json.dumps(summary, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
137
analysis/build_trigger_offset_compare_6_7_8.py
Normal file
137
analysis/build_trigger_offset_compare_6_7_8.py
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
"""生成 2026042802-1 抓包中触发偏移 6/7/8 周期的对照表。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
OUTPUT_DIR = REPO_ROOT / "analysis" / "2026042802-1"
|
||||
CONFIG_PATH = REPO_ROOT / "Config" / "RobotConfig.json"
|
||||
ACTUAL_SEND_CSV = OUTPUT_DIR / "2026042802-1_j519_actual_send_all_with_io.csv"
|
||||
TRIGGER_CSV = OUTPUT_DIR / "2026042802-1_j519_trigger_frames.csv"
|
||||
OUTPUT_CSV = OUTPUT_DIR / "2026042802-1_trigger_offset_6_7_8_compare.csv"
|
||||
OUTPUT_JSON = OUTPUT_DIR / "2026042802-1_trigger_offset_6_7_8_summary.json"
|
||||
|
||||
|
||||
def load_rows(path: Path) -> list[dict]:
|
||||
with path.open(encoding="utf-8") as handle:
|
||||
return list(csv.DictReader(handle))
|
||||
|
||||
|
||||
def to_float_list(record: dict, prefix: str = "j") -> list[float]:
|
||||
return [float(record[f"{prefix}{index}_deg"]) for index in range(1, 7)]
|
||||
|
||||
|
||||
def compute_diff_metrics(actual_deg: list[float], teach_deg: list[float]) -> tuple[list[float], float, float, str]:
|
||||
diffs = [actual_deg[index] - teach_deg[index] for index in range(6)]
|
||||
abs_diffs = [abs(value) for value in diffs]
|
||||
max_error = max(abs_diffs)
|
||||
rms_error = math.sqrt(sum(value * value for value in diffs) / 6.0)
|
||||
max_axis = f"J{abs_diffs.index(max_error) + 1}"
|
||||
return diffs, max_error, rms_error, max_axis
|
||||
|
||||
|
||||
def main() -> None:
|
||||
actual_rows = load_rows(ACTUAL_SEND_CSV)
|
||||
trigger_rows = load_rows(TRIGGER_CSV)
|
||||
config = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))["flying_shots"]["UTTC_MS11"]
|
||||
trigger_waypoint_indices = [index for index, flag in enumerate(config["shot_flags"]) if flag]
|
||||
|
||||
actual_index_by_frame = {int(row["frame_number"]): row for row in actual_rows}
|
||||
actual_order_by_frame = {int(row["frame_number"]): idx for idx, row in enumerate(actual_rows)}
|
||||
|
||||
output_rows: list[dict] = []
|
||||
offset_win_counts = {6: 0, 7: 0, 8: 0}
|
||||
|
||||
for trigger_no, (trigger_row, waypoint_index) in enumerate(zip(trigger_rows, trigger_waypoint_indices), start=1):
|
||||
trigger_frame = int(trigger_row["frame_number"])
|
||||
trigger_order = actual_order_by_frame[trigger_frame]
|
||||
teach_deg = [math.degrees(value) for value in config["traj_waypoints"][waypoint_index]]
|
||||
|
||||
window_start = max(0, trigger_order - 20)
|
||||
window_end = min(len(actual_rows) - 1, trigger_order + 20)
|
||||
best_order = trigger_order
|
||||
best_rms = float("inf")
|
||||
best_max = float("inf")
|
||||
|
||||
for candidate_order in range(window_start, window_end + 1):
|
||||
candidate = actual_rows[candidate_order]
|
||||
_, max_error, rms_error, _ = compute_diff_metrics(to_float_list(candidate), teach_deg)
|
||||
score = (rms_error, max_error, abs(candidate_order - trigger_order))
|
||||
if (best_rms, best_max, abs(best_order - trigger_order)) > score:
|
||||
best_order = candidate_order
|
||||
best_rms = rms_error
|
||||
best_max = max_error
|
||||
|
||||
row = {
|
||||
"trigger_no": trigger_no,
|
||||
"waypoint_index": waypoint_index,
|
||||
"best_sample_order": best_order,
|
||||
"best_frame_number": int(actual_rows[best_order]["frame_number"]),
|
||||
"best_sequence": int(actual_rows[best_order]["sequence"]),
|
||||
"best_time_relative_s": float(actual_rows[best_order]["time_relative_s"]),
|
||||
}
|
||||
|
||||
for joint_index in range(6):
|
||||
row[f"teach_j{joint_index + 1}_deg"] = teach_deg[joint_index]
|
||||
|
||||
for offset in (6, 7, 8):
|
||||
target_order = min(len(actual_rows) - 1, best_order + offset)
|
||||
target = actual_rows[target_order]
|
||||
actual_deg = to_float_list(target)
|
||||
diffs, max_error, rms_error, max_axis = compute_diff_metrics(actual_deg, teach_deg)
|
||||
|
||||
row[f"offset_{offset}_frame_number"] = int(target["frame_number"])
|
||||
row[f"offset_{offset}_sequence"] = int(target["sequence"])
|
||||
row[f"offset_{offset}_time_relative_s"] = float(target["time_relative_s"])
|
||||
row[f"offset_{offset}_max_error_axis"] = max_axis
|
||||
row[f"offset_{offset}_max_error_deg"] = max_error
|
||||
row[f"offset_{offset}_rms_error_deg"] = rms_error
|
||||
row[f"offset_{offset}_delta_from_best_ms"] = (
|
||||
float(target["time_relative_s"]) - float(actual_rows[best_order]["time_relative_s"])
|
||||
) * 1000.0
|
||||
for joint_index in range(6):
|
||||
joint_no = joint_index + 1
|
||||
row[f"offset_{offset}_j{joint_no}_actual_deg"] = actual_deg[joint_index]
|
||||
row[f"offset_{offset}_diff_j{joint_no}_deg"] = diffs[joint_index]
|
||||
|
||||
best_offset = min(
|
||||
(6, 7, 8),
|
||||
key=lambda offset: (
|
||||
row[f"offset_{offset}_rms_error_deg"],
|
||||
row[f"offset_{offset}_max_error_deg"],
|
||||
),
|
||||
)
|
||||
row["best_of_6_7_8_offset"] = best_offset
|
||||
offset_win_counts[best_offset] += 1
|
||||
output_rows.append(row)
|
||||
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
with OUTPUT_CSV.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=list(output_rows[0].keys()))
|
||||
writer.writeheader()
|
||||
writer.writerows(output_rows)
|
||||
|
||||
summary = {
|
||||
"rows": len(output_rows),
|
||||
"best_offset_win_counts": offset_win_counts,
|
||||
"average_max_error_deg": {
|
||||
str(offset): sum(row[f"offset_{offset}_max_error_deg"] for row in output_rows) / len(output_rows)
|
||||
for offset in (6, 7, 8)
|
||||
},
|
||||
"average_rms_error_deg": {
|
||||
str(offset): sum(row[f"offset_{offset}_rms_error_deg"] for row in output_rows) / len(output_rows)
|
||||
for offset in (6, 7, 8)
|
||||
},
|
||||
}
|
||||
OUTPUT_JSON.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
461
analysis/calc_jointdetail_acceleration.py
Normal file
461
analysis/calc_jointdetail_acceleration.py
Normal file
@@ -0,0 +1,461 @@
|
||||
#!/usr/bin/env python3
|
||||
"""计算 JointDetialTraj 类文件的速度 / 加速度 / 跃度峰值,并与当前生效轴限位对比。
|
||||
|
||||
输入格式:
|
||||
time joint1 joint2 ... jointN
|
||||
|
||||
本脚本采用的规则:
|
||||
1. 将轨迹按离散时间采样点读取,允许时间轴非等间隔。
|
||||
2. 自动推断角度单位:
|
||||
- 任一关节绝对值超过 2*pi*1.5,则按 degree 处理
|
||||
- 否则按 radian 处理
|
||||
3. 使用后向差分计算导数:
|
||||
v_i = (q_i - q_{i-1}) / dt_i
|
||||
a_i = (v_i - v_{i-1}) / dt_i
|
||||
j_i = (a_i - a_{i-1}) / dt_i
|
||||
4. 所有导数量统一换算成 rad 基单位,再与当前生效的机器人限值比较。
|
||||
|
||||
当前生效限值来源:
|
||||
.robot limit.velocity
|
||||
.robot limit.acceleration * RobotConfig.robot.acc_limit
|
||||
.robot limit.jerk * RobotConfig.robot.jerk_limit
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
AUTO_DEG_THRESHOLD = 2.0 * math.pi * 1.5
|
||||
DEFAULT_VELOCITY_LIMITS = [6.45, 5.41, 7.15, 9.59, 9.51, 17.45]
|
||||
DEFAULT_ACCELERATION_LIMITS = [26.90, 22.54, 29.81, 39.99, 39.63, 72.72]
|
||||
DEFAULT_JERK_LIMITS = [224.22, 187.86, 248.46, 333.30, 330.27, 606.01]
|
||||
DEFAULT_JOINT_NAMES = [f"Joint{index}" for index in range(1, 7)]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class JointLimit:
|
||||
name: str
|
||||
velocity: float
|
||||
acceleration: float
|
||||
jerk: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PeakMetric:
|
||||
joint_name: str
|
||||
axis_index: int
|
||||
window_start: float
|
||||
window_end: float
|
||||
row_number: int
|
||||
metric_native: float
|
||||
metric_rad: float
|
||||
effective_limit_rad: float
|
||||
|
||||
@property
|
||||
def ratio_vs_limit(self) -> float:
|
||||
return abs(self.metric_rad) / self.effective_limit_rad
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EffectiveLimits:
|
||||
joints: list[JointLimit]
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Calculate velocity / acceleration / jerk peaks from JointDetialTraj.txt and compare with built-in effective robot limits."
|
||||
)
|
||||
parser.add_argument("joint_detail", type=Path, help="Path to JointDetialTraj.txt")
|
||||
parser.add_argument(
|
||||
"--limit-csv",
|
||||
type=Path,
|
||||
default=None,
|
||||
help="Optional CSV file with columns: Joint,Velocity,Acceleration,Jerk . If omitted, use built-in 1/1 effective limits.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--unit",
|
||||
choices=("auto", "rad", "deg"),
|
||||
default="auto",
|
||||
help="Input joint-angle unit. Default: auto.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def resolve_path(path: Path) -> Path:
|
||||
return path if path.is_absolute() else (Path.cwd() / path).resolve()
|
||||
|
||||
|
||||
def read_joint_rows(path: Path) -> list[list[float]]:
|
||||
rows: list[list[float]] = []
|
||||
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line:
|
||||
continue
|
||||
rows.append([float(part) for part in line.split()])
|
||||
|
||||
if len(rows) < 4:
|
||||
raise ValueError(f"{path} must contain at least 4 rows to calculate jerk.")
|
||||
|
||||
width = len(rows[0])
|
||||
if width < 3:
|
||||
raise ValueError(f"{path} must contain time + at least 2 joint columns.")
|
||||
|
||||
for index, row in enumerate(rows, start=1):
|
||||
if len(row) != width:
|
||||
raise ValueError(f"{path} line {index} has inconsistent column count.")
|
||||
|
||||
return rows
|
||||
|
||||
|
||||
def trim_rows_to_limit_count(rows: list[list[float]], limit_count: int) -> tuple[list[list[float]], str | None]:
|
||||
joint_count = len(rows[0]) - 1
|
||||
if joint_count == limit_count:
|
||||
return rows, None
|
||||
|
||||
if joint_count < limit_count:
|
||||
raise ValueError(f"Joint column count ({joint_count}) is smaller than robot limit count ({limit_count}).")
|
||||
|
||||
trimmed_rows = [row[: limit_count + 1] for row in rows]
|
||||
ignored_joint_count = joint_count - limit_count
|
||||
trim_note = (
|
||||
f"ignored_joint_columns={ignored_joint_count} "
|
||||
f"(using first {limit_count} joints out of {joint_count}; trailing columns treated as external axes/placeholders)"
|
||||
)
|
||||
return trimmed_rows, trim_note
|
||||
|
||||
|
||||
def infer_unit(rows: Iterable[list[float]], requested_unit: str) -> str:
|
||||
if requested_unit != "auto":
|
||||
return requested_unit
|
||||
|
||||
max_abs_joint = max(abs(value) for row in rows for value in row[1:])
|
||||
return "deg" if max_abs_joint > AUTO_DEG_THRESHOLD else "rad"
|
||||
|
||||
|
||||
def read_limit_csv(path: Path) -> list[JointLimit]:
|
||||
rows = [line.strip() for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]
|
||||
if len(rows) < 2:
|
||||
raise ValueError(f"{path} must contain a header and at least one data row.")
|
||||
|
||||
header = [part.strip().lower() for part in rows[0].split(",")]
|
||||
expected = ["joint", "velocity", "acceleration", "jerk"]
|
||||
if header != expected:
|
||||
raise ValueError(f"{path} header must be: Joint,Velocity,Acceleration,Jerk")
|
||||
|
||||
limits: list[JointLimit] = []
|
||||
for row_index, row in enumerate(rows[1:], start=2):
|
||||
parts = [part.strip() for part in row.split(",")]
|
||||
if len(parts) != 4:
|
||||
raise ValueError(f"{path} line {row_index} must contain 4 columns.")
|
||||
limits.append(
|
||||
JointLimit(
|
||||
name=parts[0],
|
||||
velocity=float(parts[1]),
|
||||
acceleration=float(parts[2]),
|
||||
jerk=float(parts[3]),
|
||||
)
|
||||
)
|
||||
return limits
|
||||
|
||||
|
||||
def load_effective_limits(limit_csv_path: Path | None) -> EffectiveLimits:
|
||||
if limit_csv_path is not None:
|
||||
limits = read_limit_csv(resolve_path(limit_csv_path))
|
||||
else:
|
||||
limits = [
|
||||
JointLimit(
|
||||
name=name,
|
||||
velocity=velocity,
|
||||
acceleration=acceleration,
|
||||
jerk=jerk,
|
||||
)
|
||||
for name, velocity, acceleration, jerk in zip(
|
||||
DEFAULT_JOINT_NAMES,
|
||||
DEFAULT_VELOCITY_LIMITS,
|
||||
DEFAULT_ACCELERATION_LIMITS,
|
||||
DEFAULT_JERK_LIMITS,
|
||||
strict=True,
|
||||
)
|
||||
]
|
||||
|
||||
return EffectiveLimits(joints=limits)
|
||||
|
||||
|
||||
def to_radians(value: float, unit: str) -> float:
|
||||
return math.radians(value) if unit == "deg" else value
|
||||
|
||||
|
||||
def to_native_from_rad(value: float, unit: str) -> float:
|
||||
return math.degrees(value) if unit == "deg" else value
|
||||
|
||||
|
||||
def calculate_velocity_peaks(rows: list[list[float]], unit: str, limits: list[JointLimit]) -> list[PeakMetric]:
|
||||
joint_count = len(rows[0]) - 1
|
||||
if joint_count != len(limits):
|
||||
raise ValueError(f"Joint column count ({joint_count}) does not match robot limit count ({len(limits)}).")
|
||||
|
||||
peaks: list[PeakMetric | None] = [None] * joint_count
|
||||
|
||||
for row_index in range(1, len(rows)):
|
||||
previous = rows[row_index - 1]
|
||||
current = rows[row_index]
|
||||
dt = current[0] - previous[0]
|
||||
if dt <= 0.0:
|
||||
raise ValueError(f"Non-positive dt at line {row_index + 1}: {dt}")
|
||||
|
||||
for joint_index in range(joint_count):
|
||||
dq_native = current[joint_index + 1] - previous[joint_index + 1]
|
||||
dq_rad = to_radians(dq_native, unit)
|
||||
velocity_rad = dq_rad / dt
|
||||
velocity_native = to_native_from_rad(velocity_rad, unit)
|
||||
|
||||
candidate = PeakMetric(
|
||||
joint_name=limits[joint_index].name,
|
||||
axis_index=joint_index + 1,
|
||||
window_start=previous[0],
|
||||
window_end=current[0],
|
||||
row_number=row_index + 1,
|
||||
metric_native=velocity_native,
|
||||
metric_rad=velocity_rad,
|
||||
effective_limit_rad=limits[joint_index].velocity,
|
||||
)
|
||||
|
||||
current_peak = peaks[joint_index]
|
||||
if current_peak is None or abs(candidate.metric_rad) > abs(current_peak.metric_rad):
|
||||
peaks[joint_index] = candidate
|
||||
|
||||
return [peak for peak in peaks if peak is not None]
|
||||
|
||||
|
||||
def calculate_acceleration_peaks(rows: list[list[float]], unit: str, limits: list[JointLimit]) -> list[PeakMetric]:
|
||||
joint_count = len(rows[0]) - 1
|
||||
if joint_count != len(limits):
|
||||
raise ValueError(f"Joint column count ({joint_count}) does not match robot limit count ({len(limits)}).")
|
||||
|
||||
velocities_rad: list[list[float]] = []
|
||||
velocity_windows: list[tuple[float, float, int]] = []
|
||||
|
||||
for row_index in range(1, len(rows)):
|
||||
previous = rows[row_index - 1]
|
||||
current = rows[row_index]
|
||||
dt = current[0] - previous[0]
|
||||
if dt <= 0.0:
|
||||
raise ValueError(f"Non-positive dt at line {row_index + 1}: {dt}")
|
||||
|
||||
velocity_row = []
|
||||
for joint_index in range(joint_count):
|
||||
dq_native = current[joint_index + 1] - previous[joint_index + 1]
|
||||
dq_rad = to_radians(dq_native, unit)
|
||||
velocity_row.append(dq_rad / dt)
|
||||
|
||||
velocities_rad.append(velocity_row)
|
||||
velocity_windows.append((previous[0], current[0], row_index + 1))
|
||||
|
||||
peaks: list[PeakMetric | None] = [None] * joint_count
|
||||
|
||||
for velocity_index in range(1, len(velocities_rad)):
|
||||
dt = velocity_windows[velocity_index][1] - velocity_windows[velocity_index][0]
|
||||
for joint_index in range(joint_count):
|
||||
acceleration_rad = (velocities_rad[velocity_index][joint_index] - velocities_rad[velocity_index - 1][joint_index]) / dt
|
||||
acceleration_native = to_native_from_rad(acceleration_rad, unit)
|
||||
|
||||
candidate = PeakMetric(
|
||||
joint_name=limits[joint_index].name,
|
||||
axis_index=joint_index + 1,
|
||||
window_start=velocity_windows[velocity_index][0],
|
||||
window_end=velocity_windows[velocity_index][1],
|
||||
row_number=velocity_windows[velocity_index][2],
|
||||
metric_native=acceleration_native,
|
||||
metric_rad=acceleration_rad,
|
||||
effective_limit_rad=limits[joint_index].acceleration,
|
||||
)
|
||||
|
||||
current_peak = peaks[joint_index]
|
||||
if current_peak is None or abs(candidate.metric_rad) > abs(current_peak.metric_rad):
|
||||
peaks[joint_index] = candidate
|
||||
|
||||
return [peak for peak in peaks if peak is not None]
|
||||
|
||||
|
||||
def calculate_jerk_peaks(rows: list[list[float]], unit: str, limits: list[JointLimit]) -> list[PeakMetric]:
|
||||
joint_count = len(rows[0]) - 1
|
||||
if joint_count != len(limits):
|
||||
raise ValueError(f"Joint column count ({joint_count}) does not match robot limit count ({len(limits)}).")
|
||||
|
||||
velocities_rad: list[list[float]] = []
|
||||
velocity_windows: list[tuple[float, float, int]] = []
|
||||
for row_index in range(1, len(rows)):
|
||||
previous = rows[row_index - 1]
|
||||
current = rows[row_index]
|
||||
dt = current[0] - previous[0]
|
||||
if dt <= 0.0:
|
||||
raise ValueError(f"Non-positive dt at line {row_index + 1}: {dt}")
|
||||
|
||||
velocity_row = []
|
||||
for joint_index in range(joint_count):
|
||||
dq_native = current[joint_index + 1] - previous[joint_index + 1]
|
||||
dq_rad = to_radians(dq_native, unit)
|
||||
velocity_row.append(dq_rad / dt)
|
||||
|
||||
velocities_rad.append(velocity_row)
|
||||
velocity_windows.append((previous[0], current[0], row_index + 1))
|
||||
|
||||
accelerations_rad: list[list[float]] = []
|
||||
acceleration_windows: list[tuple[float, float, int]] = []
|
||||
for velocity_index in range(1, len(velocities_rad)):
|
||||
dt = velocity_windows[velocity_index][1] - velocity_windows[velocity_index][0]
|
||||
acceleration_row = []
|
||||
for joint_index in range(joint_count):
|
||||
acceleration_row.append((velocities_rad[velocity_index][joint_index] - velocities_rad[velocity_index - 1][joint_index]) / dt)
|
||||
accelerations_rad.append(acceleration_row)
|
||||
acceleration_windows.append((velocity_windows[velocity_index][0], velocity_windows[velocity_index][1], velocity_windows[velocity_index][2]))
|
||||
|
||||
peaks: list[PeakMetric | None] = [None] * joint_count
|
||||
|
||||
for acceleration_index in range(1, len(accelerations_rad)):
|
||||
dt = acceleration_windows[acceleration_index][1] - acceleration_windows[acceleration_index][0]
|
||||
for joint_index in range(joint_count):
|
||||
jerk_rad = (accelerations_rad[acceleration_index][joint_index] - accelerations_rad[acceleration_index - 1][joint_index]) / dt
|
||||
jerk_native = to_native_from_rad(jerk_rad, unit)
|
||||
|
||||
candidate = PeakMetric(
|
||||
joint_name=limits[joint_index].name,
|
||||
axis_index=joint_index + 1,
|
||||
window_start=acceleration_windows[acceleration_index][0],
|
||||
window_end=acceleration_windows[acceleration_index][1],
|
||||
row_number=acceleration_windows[acceleration_index][2],
|
||||
metric_native=jerk_native,
|
||||
metric_rad=jerk_rad,
|
||||
effective_limit_rad=limits[joint_index].jerk,
|
||||
)
|
||||
|
||||
current_peak = peaks[joint_index]
|
||||
if current_peak is None or abs(candidate.metric_rad) > abs(current_peak.metric_rad):
|
||||
peaks[joint_index] = candidate
|
||||
|
||||
return [peak for peak in peaks if peak is not None]
|
||||
|
||||
|
||||
def format_table(peaks: list[PeakMetric], native_unit: str, rad_unit: str, limit_header: str) -> str:
|
||||
lines = [
|
||||
f"{'Joint':<8} {'Window(s)':<20} {'Line':>6} {'Peak(' + native_unit + ')':>18} {'Peak(' + rad_unit + ')':>18} {limit_header:>20} {'Ratio':>10}",
|
||||
"-" * 108,
|
||||
]
|
||||
for peak in peaks:
|
||||
lines.append(
|
||||
f"{peak.joint_name:<8} "
|
||||
f"{peak.window_start:>7.6f}->{peak.window_end:<10.6f} "
|
||||
f"{peak.row_number:>6} "
|
||||
f"{peak.metric_native:>18.6f} "
|
||||
f"{peak.metric_rad:>18.6f} "
|
||||
f"{peak.effective_limit_rad:>20.6f} "
|
||||
f"{peak.ratio_vs_limit:>10.4f}"
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def print_metric_section(title: str, peaks: list[PeakMetric], unit: str, native_suffix: str, rad_suffix: str, limit_header: str) -> None:
|
||||
print(build_metric_section(title, peaks, unit, native_suffix, rad_suffix, limit_header))
|
||||
|
||||
|
||||
def build_metric_section(title: str, peaks: list[PeakMetric], unit: str, native_suffix: str, rad_suffix: str, limit_header: str) -> str:
|
||||
native_unit = f"deg/{native_suffix}" if unit == "deg" else f"rad/{native_suffix}"
|
||||
rad_unit = f"rad/{rad_suffix}"
|
||||
lines = [title, format_table(peaks, native_unit, rad_unit, limit_header)]
|
||||
worst = max(peaks, key=lambda item: item.ratio_vs_limit)
|
||||
metric_key = title.lower().replace(" ", "_")
|
||||
lines.append(
|
||||
f"worst_{metric_key}="
|
||||
f"{worst.joint_name}, window={worst.window_start:.6f}->{worst.window_end:.6f}, "
|
||||
f"peak_rad={worst.metric_rad:.6f}, limit_rad={worst.effective_limit_rad:.6f}, "
|
||||
f"ratio={worst.ratio_vs_limit:.4f}"
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_report_text(
|
||||
joint_detail_path: Path,
|
||||
rows: list[list[float]],
|
||||
unit: str,
|
||||
max_abs_joint: float,
|
||||
limit_source_text: str,
|
||||
trim_note: str | None,
|
||||
velocity_peaks: list[PeakMetric],
|
||||
acceleration_peaks: list[PeakMetric],
|
||||
jerk_peaks: list[PeakMetric],
|
||||
) -> str:
|
||||
lines = [
|
||||
f"joint_detail={joint_detail_path}",
|
||||
limit_source_text,
|
||||
f"row_count={len(rows)}",
|
||||
f"joint_count={len(rows[0]) - 1}",
|
||||
f"inferred_unit={unit}",
|
||||
f"max_abs_joint_value={max_abs_joint:.6f}",
|
||||
]
|
||||
if trim_note is not None:
|
||||
lines.append(trim_note)
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
build_metric_section("Velocity Peaks", velocity_peaks, unit, "s", "s", "VelLimit(rad/s)"),
|
||||
"",
|
||||
build_metric_section("Acceleration Peaks", acceleration_peaks, unit, "s^2", "s^2", "AccLimit(rad/s^2)"),
|
||||
"",
|
||||
build_metric_section("Jerk Peaks", jerk_peaks, unit, "s^3", "s^3", "JerkLimit(rad/s^3)"),
|
||||
"",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
joint_detail_path = resolve_path(args.joint_detail)
|
||||
rows = read_joint_rows(joint_detail_path)
|
||||
limits_info = load_effective_limits(args.limit_csv)
|
||||
rows, trim_note = trim_rows_to_limit_count(rows, len(limits_info.joints))
|
||||
unit = infer_unit(rows, args.unit)
|
||||
|
||||
velocity_peaks = calculate_velocity_peaks(rows, unit, limits_info.joints)
|
||||
acceleration_peaks = calculate_acceleration_peaks(rows, unit, limits_info.joints)
|
||||
jerk_peaks = calculate_jerk_peaks(rows, unit, limits_info.joints)
|
||||
|
||||
max_abs_joint = max(abs(value) for row in rows for value in row[1:])
|
||||
|
||||
if args.limit_csv is None:
|
||||
limit_source_text = "limit_source=built-in fixed effective limits (acc_limit=1, jerk_limit=1)"
|
||||
else:
|
||||
limit_source_text = f"limit_source_csv={resolve_path(args.limit_csv)}"
|
||||
|
||||
report_text = build_report_text(
|
||||
joint_detail_path=joint_detail_path,
|
||||
rows=rows,
|
||||
unit=unit,
|
||||
max_abs_joint=max_abs_joint,
|
||||
limit_source_text=limit_source_text,
|
||||
trim_note=trim_note,
|
||||
velocity_peaks=velocity_peaks,
|
||||
acceleration_peaks=acceleration_peaks,
|
||||
jerk_peaks=jerk_peaks,
|
||||
)
|
||||
|
||||
print(report_text, end="")
|
||||
|
||||
output_path = joint_detail_path.with_suffix(".analysis.txt")
|
||||
try:
|
||||
output_path.write_text(report_text, encoding="utf-8")
|
||||
print(f"saved_report={output_path}")
|
||||
except PermissionError as error:
|
||||
print(f"save_report_failed={output_path}")
|
||||
print(f"save_report_error={error}")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
199
analysis/fanuc_10010_dissector.lua
Normal file
199
analysis/fanuc_10010_dissector.lua
Normal file
@@ -0,0 +1,199 @@
|
||||
local plugin_info = {
|
||||
version = "1.1.0",
|
||||
author = "OpenAI Codex",
|
||||
description = "FANUC TCP 10010 状态帧解析器",
|
||||
}
|
||||
|
||||
set_plugin_info(plugin_info)
|
||||
|
||||
local fanuc10010_proto = Proto("fanuc10010_state_frame", "FANUC 10010 State Frame")
|
||||
|
||||
local FRAME_LENGTH = 90
|
||||
local HEADER_LENGTH = 3
|
||||
local TRAILER_LENGTH = 3
|
||||
local LENGTH_OFFSET = 3
|
||||
local MESSAGE_ID_OFFSET = 7
|
||||
local POSE_OFFSET = 11
|
||||
local POSE_COUNT = 6
|
||||
local JOINT_OFFSET = 35
|
||||
local JOINT_COUNT = 9
|
||||
local TAIL_OFFSET = 71
|
||||
local TAIL_COUNT = 4
|
||||
|
||||
local POSE_NAMES = { "X", "Y", "Z", "W", "P", "R" }
|
||||
local JOINT_NAMES = { "J1", "J2", "J3", "J4", "J5", "J6", "Ext1", "Ext2", "Ext3" }
|
||||
local HEADER_MAGIC = "doz"
|
||||
local TRAILER_MAGIC = "zod"
|
||||
|
||||
local fields = {
|
||||
frame = ProtoField.bytes("fanuc10010.frame", "原始状态帧"),
|
||||
header = ProtoField.string("fanuc10010.header", "帧头 Magic"),
|
||||
declared_length = ProtoField.uint32("fanuc10010.length", "声明长度", base.DEC),
|
||||
message_id = ProtoField.uint32("fanuc10010.message_id", "消息号", base.DEC),
|
||||
pose = ProtoField.none("fanuc10010.pose", "笛卡尔位姿"),
|
||||
joints = ProtoField.none("fanuc10010.joints", "关节与扩展轴"),
|
||||
joint_degrees = ProtoField.none("fanuc10010.joint_degrees", "Joint Degrees"),
|
||||
tail = ProtoField.none("fanuc10010.tail", "尾部状态字"),
|
||||
trailer = ProtoField.string("fanuc10010.trailer", "帧尾 Magic"),
|
||||
expert_bad_length = ProtoField.string("fanuc10010.expert.bad_length", "长度异常"),
|
||||
expert_bad_magic = ProtoField.string("fanuc10010.expert.bad_magic", "Magic 异常"),
|
||||
}
|
||||
|
||||
for index = 1, POSE_COUNT do
|
||||
fields["pose_" .. index] = ProtoField.float(
|
||||
"fanuc10010.pose_" .. index,
|
||||
"Pose " .. POSE_NAMES[index])
|
||||
end
|
||||
|
||||
for index = 1, JOINT_COUNT do
|
||||
fields["joint_" .. index] = ProtoField.float(
|
||||
"fanuc10010.joint_" .. index,
|
||||
JOINT_NAMES[index] .. " (raw)")
|
||||
end
|
||||
|
||||
for index = 1, 6 do
|
||||
fields["joint_deg_" .. index] = ProtoField.float(
|
||||
"fanuc10010.joint_deg_" .. index,
|
||||
JOINT_NAMES[index] .. " (deg)")
|
||||
end
|
||||
|
||||
for index = 1, TAIL_COUNT do
|
||||
fields["tail_" .. index] = ProtoField.uint32(
|
||||
"fanuc10010.tail_" .. index,
|
||||
"Tail[" .. (index - 1) .. "]",
|
||||
base.DEC)
|
||||
end
|
||||
|
||||
fanuc10010_proto.fields = fields
|
||||
|
||||
local function read_f32_be(tvb, offset)
|
||||
return tvb(offset, 4):tvb():range(0, 4):float()
|
||||
end
|
||||
|
||||
local function read_u32_be(tvb, offset)
|
||||
return tvb(offset, 4):uint()
|
||||
end
|
||||
|
||||
local function read_ascii(tvb, offset, length)
|
||||
return tvb(offset, length):string()
|
||||
end
|
||||
|
||||
local function radians_to_degrees(value)
|
||||
return value * 180.0 / math.pi
|
||||
end
|
||||
|
||||
local function add_error(subtree, range, field, message)
|
||||
local item = subtree:add(field, range, message)
|
||||
item:add_expert_info(PI_MALFORMED, PI_ERROR, message)
|
||||
end
|
||||
|
||||
local function dissect_single_frame(tvb, pinfo, tree, frame_offset)
|
||||
local remaining = tvb:len() - frame_offset
|
||||
local candidate_length = math.min(remaining, FRAME_LENGTH)
|
||||
local frame_range = tvb(frame_offset, candidate_length)
|
||||
local subtree = tree:add(fanuc10010_proto, frame_range, "FANUC 10010 状态帧")
|
||||
subtree:add(fields.frame, frame_range)
|
||||
|
||||
if remaining < FRAME_LENGTH then
|
||||
add_error(subtree, frame_range, fields.expert_bad_length, "剩余字节不足 90B,无法组成完整状态帧")
|
||||
return remaining
|
||||
end
|
||||
|
||||
local header = read_ascii(tvb, frame_offset, HEADER_LENGTH)
|
||||
local declared_length = read_u32_be(tvb, frame_offset + LENGTH_OFFSET)
|
||||
local message_id = read_u32_be(tvb, frame_offset + MESSAGE_ID_OFFSET)
|
||||
local trailer = read_ascii(tvb, frame_offset + FRAME_LENGTH - TRAILER_LENGTH, TRAILER_LENGTH)
|
||||
|
||||
subtree:add(fields.header, tvb(frame_offset, HEADER_LENGTH), header)
|
||||
subtree:add(fields.declared_length, tvb(frame_offset + LENGTH_OFFSET, 4), declared_length)
|
||||
subtree:add(fields.message_id, tvb(frame_offset + MESSAGE_ID_OFFSET, 4), message_id)
|
||||
|
||||
local pose_tree = subtree:add(fields.pose, tvb(frame_offset + POSE_OFFSET, POSE_COUNT * 4))
|
||||
local pose_values = {}
|
||||
for index = 1, POSE_COUNT do
|
||||
local field_offset = frame_offset + POSE_OFFSET + ((index - 1) * 4)
|
||||
local value = read_f32_be(tvb, field_offset)
|
||||
pose_values[index] = value
|
||||
pose_tree:add(fields["pose_" .. index], tvb(field_offset, 4), value)
|
||||
end
|
||||
|
||||
local joint_tree = subtree:add(fields.joints, tvb(frame_offset + JOINT_OFFSET, JOINT_COUNT * 4))
|
||||
local joint_values = {}
|
||||
for index = 1, JOINT_COUNT do
|
||||
local field_offset = frame_offset + JOINT_OFFSET + ((index - 1) * 4)
|
||||
local value = read_f32_be(tvb, field_offset)
|
||||
joint_values[index] = value
|
||||
joint_tree:add(fields["joint_" .. index], tvb(field_offset, 4), value)
|
||||
end
|
||||
|
||||
-- 单独保留一组角度显示,便于对照原始关节弧度值。
|
||||
local joint_degree_tree = subtree:add(fields.joint_degrees, tvb(frame_offset + JOINT_OFFSET, 6 * 4))
|
||||
for index = 1, 6 do
|
||||
local field_offset = frame_offset + JOINT_OFFSET + ((index - 1) * 4)
|
||||
joint_degree_tree:add(
|
||||
fields["joint_deg_" .. index],
|
||||
tvb(field_offset, 4),
|
||||
radians_to_degrees(joint_values[index]))
|
||||
end
|
||||
|
||||
local tail_tree = subtree:add(fields.tail, tvb(frame_offset + TAIL_OFFSET, TAIL_COUNT * 4))
|
||||
local tail_values = {}
|
||||
for index = 1, TAIL_COUNT do
|
||||
local field_offset = frame_offset + TAIL_OFFSET + ((index - 1) * 4)
|
||||
local value = read_u32_be(tvb, field_offset)
|
||||
tail_values[index] = value
|
||||
tail_tree:add(fields["tail_" .. index], tvb(field_offset, 4), value)
|
||||
end
|
||||
|
||||
subtree:add(fields.trailer, tvb(frame_offset + FRAME_LENGTH - TRAILER_LENGTH, TRAILER_LENGTH), trailer)
|
||||
|
||||
if header ~= HEADER_MAGIC then
|
||||
add_error(subtree, tvb(frame_offset, HEADER_LENGTH), fields.expert_bad_magic, "帧头不是 doz")
|
||||
end
|
||||
|
||||
if trailer ~= TRAILER_MAGIC then
|
||||
add_error(subtree, tvb(frame_offset + FRAME_LENGTH - TRAILER_LENGTH, TRAILER_LENGTH), fields.expert_bad_magic, "帧尾不是 zod")
|
||||
end
|
||||
|
||||
if declared_length ~= FRAME_LENGTH then
|
||||
add_error(subtree, tvb(frame_offset + LENGTH_OFFSET, 4), fields.expert_bad_length, "长度字段不是 90")
|
||||
end
|
||||
|
||||
local summary = string.format(
|
||||
"MsgId=%u X=%.3f Y=%.3f Z=%.3f J1=%.6f rad / %.3f deg J2=%.6f rad / %.3f deg Tail=[%u,%u,%u,%u]",
|
||||
message_id,
|
||||
pose_values[1], pose_values[2], pose_values[3],
|
||||
joint_values[1], radians_to_degrees(joint_values[1]),
|
||||
joint_values[2], radians_to_degrees(joint_values[2]),
|
||||
tail_values[1], tail_values[2], tail_values[3], tail_values[4])
|
||||
|
||||
subtree:set_text("FANUC 10010 状态帧, " .. summary)
|
||||
pinfo.cols.info:append(" | " .. summary)
|
||||
|
||||
return FRAME_LENGTH
|
||||
end
|
||||
|
||||
function fanuc10010_proto.dissector(tvb, pinfo, tree)
|
||||
if tvb:len() == 0 then
|
||||
return 0
|
||||
end
|
||||
|
||||
pinfo.cols.protocol = "FANUC10010"
|
||||
|
||||
local offset = 0
|
||||
while offset < tvb:len() do
|
||||
local consumed = dissect_single_frame(tvb, pinfo, tree, offset)
|
||||
if consumed <= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
offset = offset + consumed
|
||||
if consumed < FRAME_LENGTH then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return tvb:len()
|
||||
end
|
||||
|
||||
DissectorTable.get("tcp.port"):add(10010, fanuc10010_proto)
|
||||
200
analysis/read_fanuc_allowable_limits.py
Normal file
200
analysis/read_fanuc_allowable_limits.py
Normal file
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/env python3
|
||||
"""读取 FANUC J519 允许速度/加速度/jerk 上限表。
|
||||
|
||||
本脚本只做 UDP packet type=3 查询,不启动运行时服务,也不写入机器人参数。
|
||||
响应中的 limit 值保留为控制器原始单位:速度 deg/s、加速度 deg/s^2、jerk deg/s^3。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
LIMIT_TYPES = {
|
||||
0: ("velocity", "deg/s"),
|
||||
1: ("acceleration", "deg/s^2"),
|
||||
2: ("jerk", "deg/s^3"),
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LimitTable:
|
||||
"""保存单个轴、单种限制类型的 20 档 no-payload / max-payload 上限表。"""
|
||||
|
||||
axis: int
|
||||
limit_type: int
|
||||
vmax_mm_s: int
|
||||
intermediate_check_s: int
|
||||
no_payload: tuple[float, ...]
|
||||
max_payload: tuple[float, ...]
|
||||
|
||||
|
||||
def pack_request(axis: int, limit_type: int) -> bytes:
|
||||
"""按手册 Table 5 封装允许上限表查询请求。"""
|
||||
|
||||
return struct.pack(">IIII", 3, 1, axis, limit_type)
|
||||
|
||||
|
||||
def parse_response(packet: bytes, expected_axis: int, expected_type: int) -> LimitTable:
|
||||
"""按手册 Table 6 解析允许上限表响应。"""
|
||||
|
||||
if len(packet) != 184:
|
||||
raise ValueError(f"响应长度应为 184 字节,实际为 {len(packet)} 字节。")
|
||||
|
||||
packet_type, version, axis, limit_type, vmax, check_time = struct.unpack_from(">IIIIII", packet, 0)
|
||||
if packet_type != 3 or version != 1:
|
||||
raise ValueError(f"响应头不正确:packet_type={packet_type}, version={version}。")
|
||||
if axis != expected_axis or limit_type != expected_type:
|
||||
raise ValueError(
|
||||
f"响应与请求不匹配:期望 axis={expected_axis}, type={expected_type},"
|
||||
f"实际 axis={axis}, type={limit_type}。"
|
||||
)
|
||||
|
||||
values = struct.unpack_from(">40f", packet, 24)
|
||||
return LimitTable(
|
||||
axis=axis,
|
||||
limit_type=limit_type,
|
||||
vmax_mm_s=vmax,
|
||||
intermediate_check_s=check_time,
|
||||
no_payload=tuple(values[:20]),
|
||||
max_payload=tuple(values[20:]),
|
||||
)
|
||||
|
||||
|
||||
def query_table(sock: socket.socket, robot_ip: str, port: int, axis: int, limit_type: int) -> LimitTable:
|
||||
"""发送单个查询请求并等待对应响应。"""
|
||||
|
||||
sock.sendto(pack_request(axis, limit_type), (robot_ip, port))
|
||||
packet, address = sock.recvfrom(2048)
|
||||
if address[0] != robot_ip:
|
||||
raise ValueError(f"收到非目标机器人响应:{address[0]}:{address[1]}。")
|
||||
return parse_response(packet, axis, limit_type)
|
||||
|
||||
|
||||
def iter_requested_tables(
|
||||
robot_ip: str,
|
||||
port: int,
|
||||
timeout_s: float,
|
||||
axes: range,
|
||||
limit_types: tuple[int, ...],
|
||||
) -> list[LimitTable]:
|
||||
"""按轴和限制类型顺序读取所有上限表。"""
|
||||
|
||||
tables: list[LimitTable] = []
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
|
||||
sock.settimeout(timeout_s)
|
||||
for axis in axes:
|
||||
for limit_type in limit_types:
|
||||
tables.append(query_table(sock, robot_ip, port, axis, limit_type))
|
||||
return tables
|
||||
|
||||
|
||||
def write_csv(path: Path, tables: list[LimitTable]) -> None:
|
||||
"""把 20 档速度区间和两套 payload 表写成扁平 CSV。"""
|
||||
|
||||
with path.open("w", newline="", encoding="utf-8") as file:
|
||||
writer = csv.writer(file)
|
||||
writer.writerow(
|
||||
[
|
||||
"axis",
|
||||
"limit_type",
|
||||
"unit",
|
||||
"vmax_mm_s",
|
||||
"intermediate_check_s",
|
||||
"speed_bin_index",
|
||||
"speed_bin_upper_mm_s",
|
||||
"no_payload_limit",
|
||||
"max_payload_limit",
|
||||
]
|
||||
)
|
||||
for table in tables:
|
||||
limit_name, unit = LIMIT_TYPES[table.limit_type]
|
||||
for index, (no_payload, max_payload) in enumerate(zip(table.no_payload, table.max_payload), start=1):
|
||||
writer.writerow(
|
||||
[
|
||||
table.axis,
|
||||
limit_name,
|
||||
unit,
|
||||
table.vmax_mm_s,
|
||||
table.intermediate_check_s,
|
||||
index,
|
||||
table.vmax_mm_s * index / 20.0,
|
||||
f"{no_payload:.9g}",
|
||||
f"{max_payload:.9g}",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def print_summary(tables: list[LimitTable]) -> None:
|
||||
"""打印每张表的首末档,便于现场快速确认返回值是否正常。"""
|
||||
|
||||
for table in tables:
|
||||
limit_name, unit = LIMIT_TYPES[table.limit_type]
|
||||
print(
|
||||
f"axis={table.axis} type={limit_name:<12} unit={unit:<8} "
|
||||
f"vmax={table.vmax_mm_s}mm/s check={table.intermediate_check_s}s "
|
||||
f"no_payload[1]={table.no_payload[0]:.6g} no_payload[20]={table.no_payload[-1]:.6g} "
|
||||
f"max_payload[1]={table.max_payload[0]:.6g} max_payload[20]={table.max_payload[-1]:.6g}"
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
"""解析命令行参数。"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Read FANUC J519 packet type=3 allowable upper limit tables.",
|
||||
)
|
||||
parser.add_argument("robot_ip", help="机器人控制柜 IP,例如 192.168.10.11。")
|
||||
parser.add_argument("--port", type=int, default=60015, help="J519 UDP 端口,默认 60015。")
|
||||
parser.add_argument("--timeout", type=float, default=2.0, help="单次响应超时秒数,默认 2.0。")
|
||||
parser.add_argument("--axis-start", type=int, default=1, help="起始轴号,默认 1。")
|
||||
parser.add_argument("--axis-end", type=int, default=9, help="结束轴号,默认 9。")
|
||||
parser.add_argument(
|
||||
"--types",
|
||||
default="0,1,2",
|
||||
help="限制类型列表:0=velocity, 1=acceleration, 2=jerk;默认 0,1,2。",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--csv",
|
||||
type=Path,
|
||||
default=Path("analysis/fanuc_allowable_limit_tables.csv"),
|
||||
help="CSV 输出路径。",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""脚本入口。"""
|
||||
|
||||
args = parse_args()
|
||||
limit_types = tuple(int(item.strip()) for item in args.types.split(",") if item.strip())
|
||||
unknown_types = [item for item in limit_types if item not in LIMIT_TYPES]
|
||||
if unknown_types:
|
||||
print(f"未知限制类型:{unknown_types}", file=sys.stderr)
|
||||
return 2
|
||||
if not 1 <= args.axis_start <= args.axis_end <= 9:
|
||||
print("轴范围必须满足 1 <= axis-start <= axis-end <= 9。", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
tables = iter_requested_tables(
|
||||
args.robot_ip,
|
||||
args.port,
|
||||
args.timeout,
|
||||
range(args.axis_start, args.axis_end + 1),
|
||||
limit_types,
|
||||
)
|
||||
args.csv.parent.mkdir(parents=True, exist_ok=True)
|
||||
write_csv(args.csv, tables)
|
||||
print_summary(tables)
|
||||
print(f"\nCSV 已写入:{args.csv}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
7
analysis/segment_02_acc_jerk_summary.csv
Normal file
7
analysis/segment_02_acc_jerk_summary.csv
Normal file
@@ -0,0 +1,7 @@
|
||||
Axis,AccPeakRadPerS2,AccLimitRadPerS2,AccRatio,AccWindowStartS,AccWindowEndS,AccLine,JerkPeakRadPerS3,JerkLimitRadPerS3,JerkRatio,JerkWindowStartS,JerkWindowEndS,JerkLine
|
||||
Joint1,16.638678,26.900000,0.618538,0.128012,0.135939,18,902.687973,224.220000,4.025903,6.312082,6.320127,791
|
||||
Joint2,14.521836,22.540000,0.644270,3.088013,3.096086,388,888.335197,187.860000,4.728709,2.904052,2.912069,365
|
||||
Joint3,14.267221,29.810000,0.478605,0.128012,0.135939,18,728.505873,248.460000,2.932085,0.135939,0.143989,19
|
||||
Joint4,-34.694506,39.990000,0.867580,6.832125,6.840105,856,-2222.596524,333.300000,6.668456,6.312082,6.320127,791
|
||||
Joint5,-16.329775,39.630000,0.412056,6.840105,6.848111,857,842.738923,330.270000,2.551667,6.936077,6.944096,869
|
||||
Joint6,34.766065,72.720000,0.478081,1.392021,1.399995,176,2678.050822,606.010000,4.419153,6.312082,6.320127,791
|
||||
|
Reference in New Issue
Block a user