yincheng.zhong
2 天以前 567085ead3f6adaabd884f16ab4b17c62e8f0403
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
ó
6ÖiùÂãó•SrSSKJrJrJrJrJrJr SSKrSr   "SS5r
\ S:XaÜSSK r SS/SS/S   S//r \
"\ 5r\RS
S 5 \"S 5H¦r\R%5r\"\\5 \S S- \R*-r\R.\SS- \R0-S--r\R4S\\"\5-S--r\R4S\\"\5-S--r\R\\4\5 M¨  gg)u
MowerController
 
控制器假设与契约:
- åæ ‡ç³»ï¼šXY å¹³é¢å•位为米(RTK æä¾›ï¼‰ï¼ŒZ å¿½ç•¥ç”¨äºŽå¹³é¢è·Ÿè¸ªã€‚
- èˆªå‘(heading):弧度,范围任意,控制器会处理环绕。
- GPS é¢‘率(位置/速度/heading/attitude)约 10Hz;IMU(加速度、角速度)100Hz。
- æŽ§åˆ¶è¾“出频率:74Hz,返回两个信号:forward(-100..100,对应 -1..+1 m/s)和 turn(-100..100,对应 -pi/2..+pi/2 rad/s,正为左转)。
- è·¯å¾„文件:JSON åˆ—表 [[x,y],[x,y],...]
 
设计说明:
- ä½¿ç”¨çº¯è¿½è¸ªï¼ˆpure pursuit)方法选取前视点,并基于角度误差使用 P æŽ§åˆ¶è®¡ç®—期望偏航速率(rad/s)。
- å‰è¿›é€Ÿåº¦ä¸Žåèˆªè¯¯å·®è€¦åˆï¼šåèˆªè¯¯å·®å¤§æ—¶é™ä½Žå‰è¿›é€Ÿåº¦ä»¥ä¿è¯å¯è½¬å¼¯èƒ½åŠ›ã€‚
 
接口:
- controller = MowerController(path_points, params)
- controller.update_gps(pos_xy, heading, vel_xy, timestamp)
- controller.update_imu(accel3, gyro3, timestamp)
- controller.compute_control(timestamp) -> {'forward': int, 'turn': int, 'info': {...}}
 
é)Úatan2ÚhypotÚsinÚcosÚpiÚacosNcó’•U[:¼aUS[--nU[:¼aMU[*:aUS[-- nU[*:aMU$)Né)r)Úas Ú3e:\GIT\Lawnmower_STM32H7\python\mower_controller.pyÚ
wrap_angler sEۈ
Œr‹'Ø ˆQ”‰V‰ ˆð Œr'à
Œrˆc‹'Ø   ˆQ”‰V‰ ˆð Œrˆc'à €Hócól•\rSrSrSSjrSSjrSSjrSrSrSr  S   r
S
r S r   S r
S r SSjr Srg)ÚMowerControlleré#NcóÞ •UVs/sHn[U5PM  nn[U5S:Xa [S5eX@lU=(d 0nUR    SS5n/n[ [U5S-
5HŽnXHupšXHS-up¼UR Xš45 X¹-
n XÊ-
n[XÞ5nXö:”dM>US:”dMF[Xö-5n[ SUS-5H(nUUS-- nUR XU--X®U--45 M*  M  UR US5 Xpl [UR5Ul
/Ul S/Ul [ URS-
5H‚nURUunnURUS-unn[UU-
UU-
5nURR U5 URR URSU-5 M„  UR(aURSOSUl SUlU=(d 0nUR   S S5UlUR   S
S 5UlUR   S ["S - 5UlUR SS 5UlSUlSUlUR   SS5UlUR   SS5UlUR   SS5UlUR   SS 5UlUR   SS5UlUR   SS5UlUR   SS5UlUR   SS5UlUR   SS5UlUR   S S5UlUR   S!S"5Ul UR   S#S$5Ul!UR   S%S$5Ul"UR   S&S5Ul#UR   S'S5Ul$SUl%UR S(S)5Ul&SUl'SUl(SUl)SUl*SUl+SUl,UR   S*S+5Ul-UR   S,S+5Ul.SUl/UR S-S5Ul0S.Ul1SUl2SUl3S/Ul4SUl5SUl6S0Ul7S1Ul8URnUl9UR S2S35Ul:UR   S4S+5Ul;UR   S5S5Ul<UR   S6S5Ul=UR   S7S5Ul>UR   S8["S9- 5Ul?UR S:S;5Ul@S<UlAUR S=S5UlBUR   S>S"5UlCUR   S?S@5UlDSUlESUlFS<UlGSAUlHS<UlIS<UlJSUlKUR SBS35UlLUR   SCS"5UlMUR   SDSE5UlN[Ÿ5UlP[£SFSG5UlRUR¤R§SH5 gs snf)INrzpath_points cannot be emptyÚmax_segment_lengthç333333ã?éçíµ ÷ư>éÿÿÿÿçÚmax_forward_mpsÚmax_reverse_mps皙™™™™É?Ú max_yawrateéÚaccelÚ k_headinggÍÌÌÌÌÌü?Ú k_heading_dgÐ?Úk_xteç333333ó?Úk_curvÚ
base_speedçà?Úlookahead_timeç@Ú lookahead_mingffffffÖ?Ú lookahead_maxçø?Úgoal_toleranceç333333Ó?Ú  slow_zoneÚfinal_approach_dist皙™™™™é?Úmax_xteçð?Úxte_gain_scaleÚcurv_gain_scaleÚheading_err_gain_scaleÚ yawrate_alphagÍÌÌÌÌÌì?Ú k_heading_ig{®Gáz„?Úk_xte_iÚlarge_err_thresholdé
)rrÚ
goto_startÚ follow_pathÚstart_toleranceg¸…ëQ¸¾?Ústart_heading_toleranceÚstart_approach_distÚstart_slow_speedÚstart_min_speedÚstart_turn_first_headingé2Úmin_follow_speedg{®Gáz´?FÚ recovery_distÚrecovery_exit_distÚrecovery_speedgš™™™™™á?ÚrotateÚwaypoint_turn_toleranceÚwaypoint_approach_distÚwaypoint_turn_heading_tol皙™™™™¹?zcontroller_log.csvÚwzgt,x,y,heading,target_x,target_y,target_dist,target_idx,xte,heading_err,forward,turn,speed,stage,status
)TÚtupleÚlenÚ
ValueErrorÚ original_pathÚgetÚrangeÚappendrÚintÚpathÚnÚsegment_lengthsÚ cum_lengthsÚ total_lengthÚ last_progressrrrrrÚcurrent_speed_targetÚ current_speedrr r!r#r$r&r(r)r+r-r.r0r2r3r4Úyawrate_filterr5Úlast_heading_errÚ    last_timeÚ  last_curvÚheading_err_sumÚxte_sumÚmax_sumr6r7Úconsecutive_large_errr8Úmax_large_err_countÚposÚheadingÚvelÚ imu_accelÚimu_gyroÚSTAGE_GOTO_STARTÚSTAGE_FOLLOW_PATHÚstager<r=r>r?r@rArCÚ in_recoveryrDrErFÚcurrent_target_idxÚnearest_path_idxÚfinishedÚ goto_substageÚwaypoint_approach_modeÚwaypoint_turn_modeÚwaypoint_turn_target_idxrHrIrJÚsetÚwaypoint_turnedÚopenÚlog_fileÚwrite)ÚselfÚ path_pointsÚparamsÚprPÚp_paramsÚ max_seg_lenÚ    densifiedÚiÚx1Úy1Úx2Úy2ÚdxÚdyÚseg_lenÚstepsÚkÚratioÚaxÚayÚbxÚbyÚLs                        r Ú__init__ÚMowerController.__init__$sì€ñ,7Ó7©; aœ˜qž©;ˆ Ð7Ü ˆ}Ó  Ó "ÜÐ:Ó;Ð ;ð+Ôð—<˜RˆØ—l‘lÐ#7¸Ó=ˆ ؈    Ü”s˜=Ó)¨AÑ-Ö.ˆAØ"Ñ%‰FˆBØ" q¡5Ñ)‰FˆBØ × Ñ ˜b˜XÔ &Ø‘ˆBØ‘ˆBܘB“mˆGØÕ$¨°4­ä˜GÑ2Ó3ä˜q %¨!¡)Ö,AØ ¨¡™OEØ×$Ñ$ b°©:¡o°rÀ¹J±Ð%GÖHó-ñ/ð   ×ј rÑ*Ô+ØŒ   ÜT—Y‘Y“ˆŒà!ˆÔؘ5ˆÔܐt—v‘v ‘zÖ"ˆAØ—Y‘Y˜q‘\‰FˆBØ—Y‘Y˜q ™s‘^‰FˆBÜb˜‘e˜R ™UÓ#ˆAØ ×  Ñ  × 'Ñ '¨Ô *Ø × Ñ × #Ñ # D×$4Ñ$4°RÑ$8¸1Ñ$<Ö =ñ #ð 59×4D×4D˜D×,Ñ,¨RÒ0È#ˆÔà ˆÔð LbˆØ Ÿu™uÐ%6¸Ó<ˆÔØ Ÿu™uÐ%6¸Ó<ˆÔØŸ5™5 ´°1±Ó5ˆÔð—U‘U˜7 CÓ(ˆŒ
Ø$'ˆÔ!Ø ˆÔ🙘{¨CÓ0ˆŒØŸ5™5 °Ó5ˆÔØ—U‘U˜7 CÓ(ˆŒ
Ø—e‘e˜H cÓ*ˆŒ ØŸ%™%  ¨cÓ2ˆŒð Ÿe™eÐ$4°cÓ:ˆÔàŸU™U ?°DÓ9ˆÔØŸU™U ?°CÓ8ˆÔØŸe™eÐ$4°cÓ:ˆÔØŸ™˜{¨CÓ0ˆŒØ#$§5¡5Ð)>ÀÓ#DˆÔ ð—u‘u˜Y¨Ó,ˆŒ ØŸe™eÐ$4°cÓ:ˆÔð !Ÿu™uÐ%6¸Ó<ˆÔØ&'§e¡eÐ,DÀcÓ&JˆÔ#ð"ˆÔØŸU™U ?°DÓ9ˆÔØ #ˆÔ؈ŒØˆŒð #ˆÔ؈Œ ؈Œ ØŸ5™5 °Ó5ˆÔØ—u‘u˜Y¨Ó-ˆŒ ð&'ˆÔ"Ø#$§5¡5Ð)>ÀÓ#DˆÔ Ø#%ˆÔ ð
ˆŒØˆŒ ؈ŒØˆŒØˆŒ ð!-ˆÔØ!.ˆÔØ×*Ñ*ˆŒ
ð !Ÿu™uÐ%6¸Ó=ˆÔØ'(§u¡uÐ-FÈÓ'MˆÔ$Ø#$§5¡5Ð)>ÀÓ#DˆÔ Ø !§¡Ð&8¸$Ó ?ˆÔØ Ÿu™uÐ%6¸Ó=ˆÔà()¯©Ð.HÌ"ÈRÉ%Ó(PˆÔ%à !§¡Ð&8¸$Ó ?ˆÔà ˆÔØŸU™U ?°CÓ8ˆÔØ"#§%¡%Ð(<¸cÓ"BˆÔØŸe™eÐ$4°dÓ;ˆÔð#$ˆÔØ !ˆÔ؈Œ à%ˆÔð',ˆÔ#Ø"'ˆÔØ(,ˆÔ%Ø'(§u¡uÐ-FÈÓ'MˆÔ$Ø&'§e¡eÐ,DÀcÓ&JˆÔ#Ø)*¯©Ð/JÈCÓ)PˆÔ&Ü"›uˆÔäÐ1°3Ó7ˆŒ à  ‰ ×ÑðGõ Hùòa8s…Y*cól•[U5Ul[U5UlUb[U5Ulgg)z„Update GPS-like measurements at ~10Hz.
 
pos_xy: (x,y) in meters
heading: radians
vel_xy: (vx, vy) in m/s in global frame (optional)
N)rMrfÚfloatrgrh)r{Úpos_xyrgÚvel_xyÚ timestamps     r Ú
update_gpsÚMowerController.update_gps¹s0€ô˜“=ˆŒÜ˜W“~ˆŒ Ø Ñ Ü˜V“}ˆDHð rcóD•[U5Ul[U5Ulg)z:Update IMU (100Hz). accel3 and gyro3 are 3-element tuples.N)rMrirj)r{Úaccel3Úgyro3r˜s    r Ú
update_imuÚMowerController.update_imuÅs€ä˜v›ˆŒÜ˜e› ˆ rcóX•UupEUupgUup‰X†-
n
X—-
n Xª-X»--n U S:Xa[XF-
XW-
5$XF-
U
-XW-
U --U - n U S:a[XF-
XW-
5$U S:”a[XH-
XY-
5$XmU
--nX}U --n[XN-
X_-
5nXN-
U -X_-
U
--
S:”aSOSnUU-$)zDCalculate cross track error (perpendicular distance to line segment)rrr)r)r{Úp1Úp2rfÚxÚyrƒr„r…r†r‡rˆÚl2ÚtÚpxÚpyÚxteÚsigns                  r Ú _calc_xteÚMowerController._calc_xteÊsè€à‰ˆØ‰ˆØ‰ˆð‰WˆØ ‰WˆØ ‰UR‘U‰]ˆà ‹7ܘ™ ¡Ó(Ð (ð‰fb‰[˜A™F B™;Ñ &¨"Ñ ,ˆà ˆq‹5ܘ™ ¡Ó(Ð (Ø ‹Uܘ™ ¡Ó(Ð (à˜‘d‘ˆBؘ‘d‘ˆBܘ™ ¡Ó'ˆCà™ ™  q¡v¨r¡kÑ1°AÓ5‘1¸2ˆDؘ#‘:Ð rcóv•US::dU[UR5S-
:¼agURUS-
nURUnURUS-nUSUS-
nUSUS-
nUSUS-
nUSUS-
n[Xe5n   [X‡5n
[[ X©-
55n U [
S- :„$)uu
检测路径点idx是否是拐点(需要原地转向)
 
拐点定义:前后两段路径方向变化超过45度
rrFr)rNrUrÚabsr r) r{ÚidxÚp_prevÚp_currÚp_nextÚdx1Údy1Údx2Údy2Úheading1Úheading2Ú
angle_diffs             r Ú_is_waypoint_turn_neededÚ(MowerController._is_waypoint_turn_neededçsրð !‹8sœc $§)¡)›n¨qÑ0Ó0Øð—‘˜3 ™7Ñ#ˆØ—‘˜3‘ˆØ—‘˜3 ™7Ñ#ˆàQ‰i˜& ™)Ñ#ˆØQ‰i˜& ™)Ñ#ˆØQ‰i˜& ™)Ñ#ˆØQ‰i˜& ™)Ñ#ˆô˜“?ˆÜ˜“?ˆôœ HÑ$7Ó8Ó9ˆ
ðœB ™FÑ"Ð"rcó(•URcgURupSn[S5n[S[UR5S-
5HÁnURUS-
nURUnURUS-nUSUS-
n USUS-
n
USUS-
n USUS-
n [ X©5n [ XË5n[ [Xí-
55nU[S- :”dM[USU-
USU-
5nUU:dM½UnUnMÃ  X44$)ul
在原始路径中找到最接近当前位置的拐点
 
Returns: (waypoint_idx, distance) æˆ– (None, None)
N©NNÚinfrrr)
rfr•rRrNrPrr®r rr)r{r£r¤Únearest_waypoint_idxÚ nearest_distr‚r°r±r²r³r´rµr¶r·r¸r¹Údists                 r Ú_find_nearest_original_waypointÚ/MowerController._find_nearest_original_waypoints:€ð 8‰8Ñ Øàx‰x‰ˆØ#ÐܘU“|ˆ ôqœ#˜d×0Ñ0Ó1°AÑ5Ö6ˆAà×'Ñ'¨¨A©Ñ.ˆFØ×'Ñ'¨Ñ*ˆFØ×'Ñ'¨¨A©Ñ.ˆFà˜‘)˜f Q™iÑ'ˆCؘ‘)˜f Q™iÑ'ˆCؘ‘)˜f Q™iÑ'ˆCؘ‘)˜f Q™iÑ'ˆCä˜S“ˆHܘS“ˆHÜœZ¨Ñ(;Ó<Ó=ˆJðœB ™FÕ"ܘV A™Y¨™]¨F°1©I¸©MÓ:Ø˜,Õ&Ø#'LØ+,Ò(ñ)7ð,$Ð1Ð1rcó´•U[UR5S-
:¼abUS:”a[URUSURUS-
S-
nURUSURUS-
S-
n[X25$gURUS-SURUS-
nURUS-SURUS-
n[X25$)uB获取原始路径点idx处的目标航向(下一段的方向)rrr©rNrPr)r{r¯r‡rˆs    r Ú_get_target_heading_at_waypointÚ/MowerController._get_target_heading_at_waypoint*sò€à ”#d×(Ñ(Ó)¨AÑ-Ó -àQ‹wØ×'Ñ'¨Ñ,¨QÑ/°$×2DÑ2DÀSÈÁUÑ2KÈAÑ2NÑNØ×'Ñ'¨Ñ,¨QÑ/°$×2DÑ2DÀSÈÁUÑ2KÈAÑ2NÑNÜ˜R“}Ð$Øð× Ñ   a¡Ñ (¨Ñ +¨d×.@Ñ.@ÀÑ.EÀaÑ.HÑ HˆØ × Ñ   a¡Ñ (¨Ñ +¨d×.@Ñ.@ÀÑ.EÀaÑ.HÑ HˆÜR‹}Ðrc  óP•URcURSSSS4$URup#URURupE[XB-
XS-
5nUS:”aSn[   UR5nOE[ SURS-
5n[ [ UR5URS-5nURn [S5n
[Xx5H/n URU upE[XB-
XS-
5n XÊ:dM+U n
U n M1  XlU   n [U   [   UR55H-n URU upE[XB-
XS-
5n XÁ:¼dM+U n  O  [ UR5S-
n SnSU s=::a[   UR5S-
:aïO OìURU S-
nURU nURU   S-nUSUS-
USUS-
4nUSUS-
USUS-
4n[USUS5n[USUS5nUS:”acUS:”a]USUS-USUS--n[ S [ SUUU-- 55n[[[U5-
5nU[ US5- nURU XíU   4$)
简化的Pure Pursuit前视点查找 - åªä½¿ç”¨ç‚¹ç´¢å¼•
 
从nearest_path_idx开始向前搜索,找到第一个距离>=lookahead_dist的点
 
Returns: (lookahead_xy, curvature, lookahead_idx, nearest_idx)
rrr1éér¾rrçð¿) rfrUrprrNÚmaxÚminr•rRr®rr)r{Úlookahead_distr£r¤r§r¨Úcurrent_nearest_distÚ search_startÚ
search_endÚ nearest_idxrÀr‚ÚdÚ lookahead_idxÚ    curvaturer¡r¢Úp3Úv1Úv2Úl1r¥ÚdotÚ  cos_angleÚ angle_changes                         r Ú_find_lookahead_pointÚ%MowerController._find_lookahead_point9sª€ð 8‰8Ñ Ø—9‘9˜Q‘<  a¨Ð*Ð *àx‰x‰ˆð—‘˜4×0Ñ0Ñ1‰ˆÜ$ R¡V¨R©VÓ4Ðð   #Ó %؈LܘTŸY™Y›‰Jô˜q $×"7Ñ"7¸!Ñ";Ó<ˆLÜœS §¡›^¨T×-BÑ-BÀRÑ-GÓHˆJà×+Ñ+ˆ ܘU“|ˆ ä|Ö0ˆAØ—Y‘Y˜q‘\‰FˆBܐb‘f˜b™fÓ%ˆAØÕØ  Ø’ ñ 1ð!,Ôð$ˆ ܐ{¤C¨¯   ©   £NÖ3ˆAØ—Y‘Y˜q‘\‰FˆBܐb‘f˜b™fÓ%ˆAØÕ"Ø ! Ùñ 4ô  §   ¡   ›N¨QÑ.ˆMðˆ Ø  Õ 0œc $§)¡)›n¨qÑ0Ö 0Ø—‘˜;¨™?Ñ+ˆBØ—‘˜;Ñ'ˆBØ—‘˜;¨™?Ñ+ˆBàQ‘%˜"˜Q™%‘-  A¡¨¨A©¡Ð/ˆBؐQ‘%˜"˜Q™%‘-  A¡¨¨A©¡Ð/ˆBär˜!‘u˜b ™eÓ$ˆBܐr˜!‘u˜b ™eÓ$ˆBàD‹y˜R $›Yؘ‘e˜B˜q™E‘k B q¡E¨"¨Q©%¡KÑ/Ü ¤c¨#¨s°b¸2±g©Ó&?Ó@   Ü"¤2¬¨Y«Ñ#7Ó8 Ø(¬3¨r°4«=Ñ8  ày‰y˜Ñ'¨À;ÐNÐNrcó„•[US5(a[UR5S:a S[S54$URup[ S[UR5S-
5HÉnX0R ;aMURUS-
nURUnURUS-nUSUS-
nUSUS-
nUSUS-
n USUS-
n
[X‡5n [X©5n [[XË-
55n U [S- :”dM®[USU-
USU-
5nX>4s $  S[S54$)u 
在原始路径中查找下一个未到达的拐点
按照路径顺序查找,不跳过
 
Returns: (waypoint_idx, distance) æˆ– (None, inf) å¦‚果没有找到
rPéNr¾rrr) ÚhasattrrNrPr•rfrRrwrr®r rr)r{r£r¤r‚Úprev_ptÚcurr_ptÚnext_ptr³r´rµr¶Úangle1Úangle2rÜrÁs               r rÂrÁsZ€ôt˜_×-Ñ-´°T×5GÑ5GÓ1HÈ1Ó1LØœ˜u›Ð%Ð %àx‰x‰ˆôqœ#˜d×0Ñ0Ó1°AÑ5Ö6ˆAà×(Ñ(Ó(Ùð×(Ñ(¨¨1©Ñ-ˆGØ×(Ñ(¨Ñ+ˆGØ×(Ñ(¨¨1©Ñ-ˆGð˜!‘*˜w q™zÑ)ˆCؘ!‘*˜w q™zÑ)ˆCؘ!‘*˜w q™zÑ)ˆCؘ!‘*˜w q™zÑ)ˆCô˜3“_ˆFܘ3“_ˆFÜœz¨&©/Ó:Ó;ˆLðœb 1™fÕ$ä˜W Q™Z¨!™^¨W°Q©Z¸!©^Ó<Øw’ñ37ð6”U˜5“\Ð!Ð!rcóR•UcgU[UR5S-
:¼aFUS:”a?URUS-
nURUn[USUS-
USUS-
5$gURUnURUS-nUSUS-
nUSUS-
n[Xe5$)uœ
获取拐点处的目标航向(下一段路径的方向)
 
Args:
    waypoint_idx: æ‹ç‚¹åœ¨åŽŸå§‹è·¯å¾„ä¸­çš„ç´¢å¼•
 
Returns: ç›®æ ‡èˆªå‘(弧度)
rrrrÅ)r{Ú waypoint_idxrârãrär‡rˆs       r rÆrÇ«sрð Ñ Øð œ3˜t×1Ñ1Ó2°QÑ6Ó 6ؘaÓØ×,Ñ,¨\¸AÑ-=Ñ>Ø×,Ñ,¨\Ñ:Ü˜W Q™Z¨'°!©*Ñ4°g¸a±jÀ7È1Á:Ñ6MÓNÐNàà×$Ñ$ \Ñ2ˆØ×$Ñ$ \°AÑ%5Ñ6ˆà Q‰Z˜' !™*Ñ $ˆØ Q‰Z˜' !™*Ñ $ˆäR‹}ÐrcóÀ•Uup#URnURUupVU[UR5S-
:aŽURUS-upxXu-
n X†-
n
X™-Xª--n U S:”acX%-
U -X6-
U
--U - n [S[   SU 55n X\U   --n XlU
--nX--
nX>-
n[ UU5nXú-UU   --
nUS:”aUOU*nUXÞ4$[ X%-
X6-
5nUXV4$)uO
简化的横向误差计算 - åŸºäºŽæœ€è¿‘点
 
Returns: (xte, proj_x, proj_y)
rg•Ö&è .>rr1r)rprUrNrÌrÍr)r{rfr£r¤rÒr§r¨ÚnxÚnyr‡rˆÚseg_len2r¦Úproj_xÚproj_yÚ    xte_vec_xÚ  xte_vec_yÚxte_magÚcrossr©s                    r Ú_compute_xte_simpleÚ#MowerController._compute_xte_simpleÈs€ð ‰ˆØ×+Ñ+ˆ 𗑘;Ñ'‰ˆð œ˜TŸY™Y›¨!Ñ+Ó +Ø—Y‘Y˜{¨Q™Ñ/‰FˆBð‘ˆBØ‘ˆBØ‘u˜r™u‘}ˆHà˜$‹à‘f˜b‘[ A¡F¨B¡;Ñ.°(Ñ:Ü˜œS  a›[Ó)à "™f™Ø "™f™ð™J   Ø™J  Ü     ¨9Ó5ð"™¨°R©Ñ7Ø!&¨£‘g°°à˜FÐ*Ð*ôA‘F˜A™FÓ#ˆØBˆ{Ðrc ó65•UR(aSSSS0S.$URb URcSSSS0S.$Uc[R"5nURcSnOU(aXR-
OSnU(aUO!UR(aURU-OSUlURup4UR
UR :XGa¤URSnUSU-
nUS U-
n[Xg5n[Xv5n [XR-
5n
[US
5(a URcS Ul URS :XGa [U
5UR::aS Ul OìURU
-S -n [![#X°R$- S-55n ['S[)SU 55n Sn Uupï[Xã-
Xô-
5nSnUR*R-USSUSSUSSURSSUSSUSSUSSUSU
SSU SU SUR
S35 UR*R/5 XÜSUR
U
US.S.$URS :XGaU n[UUR-
5nX€R0:a SUl GOäX€R2:a@X€R2- nSSU--nUR4UR6UR4-
U--nOSn[)UR6S-S5n[U5[8- nSSSU-
--n['UR:S-UU-U-5n['UUR:S-5nURU-n [![#UUR<- S-55n ['S[)SU 55n [![#X°R$- S-55n ['S[)SU 55n Uupï[Xã-
Xô-
5nSnUR*R-USSUSSUSSURSSUSSUSSUSSUSUSSU SU SUSSUR
S35 UR*R/5 XÜSUR
UUS .S.$URS:XGaÝ[?UR5S :”aWURS SURSS-
nURS S   URSS   -
n[UU5nOSn[UUR-
5nX€R0:Ga9[U5UR@:GaURBUlSUl SUl"UR:Ul#[![#UR:UR<- S-55nURSupï[Xã-
Xô-
5nSnUR*R-USSUSSUSSURSSUSSUSSUSSUSUSSUS!UR:SSUR
S"35 UR*R/5 USS#UR
URDS$.S.$US:¼aS OS%nU[)UR$S-['S&[U5S-55-n [![#X°R$- S-55n ['S[)SU 55n Uupï[Xã-
Xô-
5nSnUR*R-USSUSSUSSURSSUSSUSSUSSUSUSS!U SSSSUR
S'35 UR*R/5 SU S(US).S.$URcSnOU(aXR-
OSnU(aUO!UR(aURU-OSUlURup4URIUR5un n!n"[URS%SU-
URS%S U-
5n#URJ(dÚURL(dÉURO5un$n%U$b³U%S*:a­U$URP;aS+Ul%U$Ul)UR*R-USSUSSUSSURSSURTU$SSSURTU$S SSU%SSU$SU SS,UR
S-35 UR*R/5 URJ(GanURTURRun&n'U&U-
nU'U-
n[Xg5n%U%URV:a‰S.Ul%S+Ul&UR*R-USSUSSUSSURSSU&SSU'SSU%SSURRSU SS,UR
S/35 UR*R/5 GO¤[Xv5n([U(UR-
5n)U%S:aU%S- n*UR:SSU*---nOUR6S0-n[U)5[8- nSSSU-
--nUU-n['UR:S-U5nURU)-S -n [![#UUR<- S-55n ['S[)SU 55n [![#X°R$- S-55n ['S[)SU 55n UR*R-USSUSSUSSURSSU&SSU'SSU%SSURRSU SSU)SSU SU SUSSUR
S135 UR*R/5 XÜS2U%U)US3.S.$URL(Ga,URYURR5n+[U+UR-
5n[U5URZ:aÛURPR]URR5 S.Ul&UR*R-USSUSSUSSURSSURTURRSSSURTURRS   SSURRSU SSUSS4UR
S535 UR*R/5 GOURU-S-n [![#X°R$- S-55n ['S[)SU 55n UR*R-USSUSSUSSURSSURTURRSSSURTURRS SSURRSU SSUSS!U SUR
S635 UR*R/5 SU S7UU+S8.S.$[URF5n,[U 5n-UR^UR`U,--S9[)U-S5--
n.['UR^[)URbU.55n/[U 5n0URd(dU0URf:”a€S+Ul2UR*R-USSUSSUSSURSSU!SSU"SSU0SSURhSU SS,UR
S:35 UR*R/5 URd(aU0URj:a€S.Ul2UR*R-USSUSSUSSURSSU!SSU"SSU0SSURhSU SS,UR
S;35 UR*R/5 URmU/5uun1n2n3n4n5U4Ul"[U2U-
U1U-
5n6['S<[)SU URn- 55n7URp*U7-URr-n8[U6U8-5n[UUR-
5nUS:”aUURt-
U- OSn9UUl:U=RvUU-- sl;U=RxU U-- sl<['URz*[)URzURv55Ul;['URz*[)URzURx55Ul<URU-UR|U9--UR~URv--UR€URx--n SS[U35UR‚--- n:[U5n;SSU;UR„--- n[U 5n<SSU<URr--- n=U:U-U=-S=-n>[)U:UU=5n?SU>-S>U?--n@UR6U@-nUR†U -S UR†-
URˆ--UlDUR$nA['UA*[)UAURˆ55n URd(a“[U"U-
U!U-
5n6['S<[)SU URn- 55n7URp*U7-URr-n8[U6U8-5n[UUR-
5nURU-n URŠnU#URŒ:nBU#URŽ:nC[U 5URS-:”d[U5[8S?- :”aEU=R’S - slIUR’UR”:”aSUlISUl;SUl<OSUlIURd(dˆWB(aK['S9[)SU#URŒ- 55nDWC(aWD['SU#URŽ- 5-nDUWD-nWC(a/US-n[U 5S@:”d[U5[8SA- :”aUS0-nUR
URB:XaÞURd(dÍWB(a UR–OUR–S-nE[U5SB:aœUWE:a–U1U2pþ[Xã-
Xô-
5nUR*R-USSUSSUSSURSSUSSUSSUSSURDSU SSUSSCWESSUR
SD35 UR*R/5 UEnUR˜WB(aSOS-nFUURF-
nGUFU-nHU=RF['UH*[)UHUG55- sl#UR
UR :Xa%[U5SE:”aURš*S-Ul#O[[U5SE:”a&URd(dURš*S-Ul#URFS:a[U5S?:aSUl#U#URœ:aS+UlSSSS0S.$U#URœS-:aJURDURžSF-
:¼a-[U5S9:a[U 5S>:aS+UlSSSS0S.$UbUO[R"5nIURFnJU1U2pþ[Xã-
Xô-
5nUR<S:wa$[![#WJUR<- S-55OSnK['S[)SUK55nKUR$S:wa#[![#X°R$- S-55OSnL['S[)SUL55nLUR*R-WISSUSSUSSURSSUSSUSSUSSURDSU SSUSSWKSULSWJSSUR
SG35 UR*R/5 UR<S:wa.[![#URFUR<- S-55OSn ['S[)SU 55n U S:XGa@UR(Gd.[U5SB:GaUR<S:wa.[![#UR–UR<- S-55OSnMUMS:waØU1U2pþ[Xã-
Xô-
5nUR*R-WISSUSSUSSURSSUSSUSSUSSURDSU SSUSSWMS[![#X°R$- S-55SUR–SSUR
SH35 UR*R/5 UMn UR–Ul#UR$S:wa#[![#X°R$- S-55OSn ['S[)SU 55n UR(aSOSInNUNX44URUUURFU U1U24U3U#SJ.
nOWInPU1U2pþ[Xã-
Xô-
5nUR*R-UPSSUSSUSSURSSUSSUSSUSSURDSU SSUSSU SU SURFSSUR
SUNSK35 UR*R/5 XÜUOS.$)LztCompute forward and turn outputs mapped to [-100,100].
 
Returns dict with keys: forward(int), turn(int), info(dict)
rÚstatusrq)ÚforwardÚturnÚinfoNÚno_stategyé&1¬Œ?rrrrrGÚmovegÍÌÌÌÌÌô?édiœÿÿÿz.3fÚ,z,0.000,z,rotate_to_start
Úrotate_to_start)rörmÚ heading_errÚ dist_to_startÚaligngš™™™™™Ù?rr1r/r%r*r"z,moving_to_start
Úmoving_to_start)rörmrrÿz,0,z,reached_start
Ú reached_start)rörmÚ advanced_idxrgš™™™™™©?z,align_at_start
Úalign_at_start)rörÿr'Tz,0.000,0,0,0.000,z,enter_waypoint_approach
Fz,reached_waypoint
gffffffæ?z,approaching_waypoint
Úapproaching_waypoint)röÚ waypoint_distrÿÚ target_speedz ,0,0,0.000,z,exit_waypoint_turn
z,waypoint_turning
Úwaypoint_turning)rörÿÚtarget_headingr,z,enter_recovery
z,exit_recovery
rËgUUUUUUÕ?rg@g333333Ã?r9g@z,0,0,z,enforce_min_speed
g333333@r
z    ,pre_map
z,force_min_forward
Úrunning)
rörfrgÚdesired_headingrÿr\Ú yawrate_cmdÚ    target_xyÚpath_curvatureÚ
final_distÚ
)PrqrfrgÚtimer_rmrkrUrrr rárrr®rArrTÚroundrrÌrÍryrzÚflushr<r>r?r$rr@rrNr=rlror\rórsrtrÂrwrurPrHrÆrJÚaddr(r&r)rnrDrprErÝr0r!r2r^rarbrcr r6r7r3r4r5r]rFr-r.r8rdrerCrrr+rV)Qr{r˜Údtr£r¤Ú start_pointr‡rˆrÚdesired_heading_to_startÚheading_err_to_startr Ú turn_signalÚforward_signalÚtarget_xÚtarget_yÚ target_distÚ
target_idxr rÿÚ
dist_ratioÚ speed_scaleÚbase_target_speedÚ heading_ratioÚ  err_scalerÚpath_dxÚpath_dyÚdesired_start_headingÚheading_err_finalÚf_sigrªr©rírîrrèrÚ
waypoint_xÚ
waypoint_yÚdesired_heading_to_waypointÚheading_err_to_waypointÚ speed_ratior
Ú actual_speedrñÚbase_lookaheadÚ lookaheadÚ dist_to_pathÚlxÚlyrrÔrÒÚ path_headingÚxte_normalizedÚxte_correctionÚ heading_err_dÚ
curv_scaleÚheading_err_absÚxte_absÚ   xte_scaleÚ
geom_scaleÚ min_scaleÚ blend_scalerÚ in_slow_zoneÚin_final_approachÚ
dist_scaleÚ dynamic_minrÚ speed_changeÚ
max_changeÚlog_tÚ pre_map_speedÚpre_map_forwardÚ pre_map_turnÚ forced_signalrörùr¦sQ                                                                                 r Úcompute_controlÚMowerController.compute_controlôs[€ð
==Ø ¨!°hÀ
Ð5KÑLÐ Là 8‰8Ñ ˜tŸ|™|Ñ3Ø ¨!°hÀ
Ð5KÑLÐ Lð Ñ ÜŸ   š   › ˆIð >‰>Ñ !؉Bæ/8Ÿ^™^Ò+¸eˆBÞ&/™ÈTÏ^Ï^°d·n±nÀrÒ6IÐadˆŒàx‰x‰ˆð :‰:˜×.Ñ.Ô .ØŸ)™) A™,ˆKؘQ‘ !Ñ#ˆBؘQ‘ !Ñ#ˆBÜ! "›MˆMô(-¨R£}Ð $Ü#-Ð.FÏÉÑ.UÓ#VÐ  ô˜4 ×1Ñ1°T×5GÑ5GÑ5OØ%-Ô"ð×!Ñ! XÔ-äÐ+Ó,°×0MÑ0MÓMØ)/DÕ&à"&§.¡.Ð3GÑ"GÈ#Ñ"MKÜ"%¤e¨[×;KÑ;KÑ-KÈsÑ,RÓ&SÓ"TKÜ"% d¬C°°[Ó,AÓ"BKØ%&Nà)4Ñ&HÜ"'¨© °h±lÓ"CKØ!"JØ—M‘M×'Ñ'¨9°S¨/¸¸1¸S¸'ÀÀ1ÀSÀ'ÈÈ4Ï<É<ÐX[ÐJ\Ð\]Ø+3°C¨.¸¸(À3¸ÀqÈÐUXÐHYÐYZÐ[eÐZfðg1Ø1EÀcÐ0JÈ!ÈNÐK[Ð[\Ð]hÐ\iÐipÐqu×q{Ñq{Ðp|ð}Oð)PôQð—M‘M×'Ñ'Ô)Ø'5Ø"3¸d¿j¹jÐYmØ)6ñUñðð
×!Ñ! VÔ+à":Ü(¨¸4¿<¹<Ñ)GÓH ð!×#7Ñ#7Ó7Ø)0DÖ&ð%×'?Ñ'?Ó?Ø%2×5MÑ5MÑ%M˜
Ø&)¨C°*Ñ,<Ñ&<˜ Ø,0×,AÑ,AÀTÇ_Á_ÐW[×WlÑWlÑElÐpzÑDzÑ,zÑ)à&)˜ Ü,/°·±À#Ñ0EÀsÓ,KÐ)ô%(¨ Ó$4´rÑ$9MØ # c¨S°=Ñ-@Ñ&AÑ AIÜ#& t×';Ñ';¸cÑ'AÐCTÐWbÑCbÐenÑCnÓ#oLÜ#& |°T×5IÑ5IÈCÑ5OÓ#PLà"&§.¡.°;Ñ">KÜ%(¬° ¸t×?SÑ?SÑ0SÐWZÑ/ZÓ)[Ó%\NÜ%(¨¬s°3¸Ó/GÓ%HNÜ"%¤e¨[×;KÑ;KÑ-KÈsÑ,RÓ&SÓ"TKÜ"% d¬C°°[Ó,AÓ"BKà)4Ñ&HÜ"'¨© °h±lÓ"CKØ!"JØ—M‘M×'Ñ'¨9°S¨/¸¸1¸S¸'ÀÀ1ÀSÀ'ÈÈ4Ï<É<ÐX[ÐJ\Ð\]Ø)1°#¨°a¸À°~ÀQÀ{ÐSVÐFWÐWXÐYcÐXdðe/Ø/:¸3Ð.?¸qÀÐ@PÐPQÐR]ÐQ^Ð^_Ð`lÐmpÐ_qÐqrÐsw×s}Ñs}Ðr~ðQð)RôSð—M‘M×'Ñ'Ô)à'5Ø"3¸d¿j¹jÐ[hØ'2ñUñðð ×!Ñ! WÔ,ܐt—y‘y“> AÓ%Ø"Ÿi™i¨™l¨1™o°· ±   ¸!± ¸Q±Ñ?GØ"Ÿi™i¨™l¨1™o°· ±   ¸!± ¸Q±Ñ?GÜ,1°'¸7Ó,CÑ)à,/Ð)Ü$.Ð/DÀtÇ|Á|Ñ/SÓ$TÐ!ð"×$8Ñ$8Ô8¼sÐCTÓ?UÐX\×XtÑXtÔ?tØ!%×!7Ñ!7D”JØ)-DÔ&à./DÔ+à)-×)=Ñ)=DÔ&䤠t×';Ñ';¸d×>RÑ>RÑ'RÐVYÑ&YÓ ZÓ[EØ)-¯©°1©Ñ&HÜ"'¨© °h±lÓ"CKØ!"JØ—M‘M×'Ñ'¨9°S¨/¸¸1¸S¸'ÀÀ1ÀSÀ'ÈÈ4Ï<É<ÐX[ÐJ\Ð\]Ø+3°C¨.¸¸(À3¸ÀqÈÐUXÐHYÐYZÐ[eÐZfðg1Ø1BÀ3Ð0GÀqÈÈÈsÐSW×SgÑSgÐhkÐRlÐlmÐnr×nxÑnxÐmyðzJð)KôLð—M‘M×'Ñ'Ô)Ø',°aÈOÐfj×fpÑfpðCG÷CZñCZñB[ñ\ð\ð!2°QÓ 6™1¸BDØ"&¬¨T×-=Ñ-=ÀÑ-CÄSÈÌsÐSdÓOeÐhkÑOkÓElÓ)mÑ"mKÜ"%¤e¨[×;KÑ;KÑ-KÈsÑ,RÓ&SÓ"TKÜ"% d¬C°°[Ó,AÓ"BKØ)4Ñ&HÜ"'¨© °h±lÓ"CKØ!"JØ—M‘M×'Ñ'¨9°S¨/¸¸1¸S¸'ÀÀ1ÀSÀ'ÈÈ4Ï<É<ÐX[ÐJ\Ð\]Ø+3°C¨.¸¸(À3¸ÀqÈÐUXÐHYÐYZÐ[eÐZfðg1Ø1BÀ3Ð0GÀsÈ;È-ÐWXÐY^Ð_bÐXcÐcdÐei×eoÑeoÐdpðqBð)CôDð—M‘M×'Ñ'Ô)Ø'(°+ÐRbðtEñHFñGðGð >‰>Ñ !؉Bæ/8Ÿ^™^Ò+¸eˆBÞ&/™ÈTÏ^Ï^°d·n±nÀrÒ6IÐadˆŒðx‰x‰ˆØ"×6Ñ6°t·x±xÓ@шˆVVô˜4Ÿ9™9 R™=¨Ñ+¨aÑ/°·±¸2±¸qÑ1AÀAÑ1EÓFˆ
ð×*×*°4×3J×3Jà*.×*NÑ*NÓ*PÑ 'ˆL˜-ðÑ(Ø Ó#Ø D×$8Ñ$8Ó8Ø.2Ô+Ø0<Ô-Ø— ‘ ×#Ñ# y° o°Q°q¸°g¸Q¸qÀ¸gÀQÀtÇ|Á|ÐTWÐFXÐXYØ'+×'9Ñ'9¸,Ñ'GÈÑ'JÈ3Ð&OÈqØ'+×'9Ñ'9¸,Ñ'GÈÑ'JÈ3Ð&OÈqØ'4°SÐ&9¸¸<¸.ÈØ'*¨3 iÐ/@ÀÇÁÀ ÐLfð  %hôið
— ‘ ×#Ñ#Ô%ð × &× &Ð &Ø%)×%7Ñ%7¸×8UÑ8UÑ%VÑ "ˆJ˜
ؘa‘ˆBؘa‘ˆBÜ! "›MˆMð˜t×;Ñ;Ó;à.3Ô+Ø*.Ô'Ø— ‘ ×#Ñ# y° o°Q°q¸°g¸Q¸qÀ¸gÀQÀtÇ|Á|ÐTWÐFXÐXYØ'1°#Ð&6°a¸
À3Ð7GÀqØ'4°SÐ&9¸¸4×;XÑ;XÐ:YÐYZØ'*¨3 iÐ/@ÀÇÁÀ ÐL_ð%aôbð— ‘ ×#Ñ#Ö%ô/4°B«mÐ+Ü*4Ð5PÐSW×S_ÑS_Ñ5_Ó*`Ð'ð! 3Ó&Ø"/°#Ñ"5KØ#'×#7Ñ#7¸3ÀÀ{ÑARÑ;RÑ#S‘Là#'§?¡?°SÑ#8Lô!$Ð$;Ó <¼rÑ A Ø #¨¨}Ñ)<Ñ"=Ñ= Ø+¨iÑ7 Ü" 4×#7Ñ#7¸#Ñ#=¸|ÓL ð#Ÿn™nÐ/FÑFÈÑL ä!$¤U¨L¸4×;OÑ;OÑ,OÐSVÑ+VÓ%WÓ!XÜ!$ T¬3¨s°NÓ+CÓ!DÜ!¤%¨×7GÑ7GÑ)GÈ3Ñ(NÓ"OÓP Ü! $¬¨C°Ó(=Ó> à— ‘ ×#Ñ# y° o°Q°q¸°g¸Q¸qÀ¸gÀQÀtÇ|Á|ÐTWÐFXÐXYØ'1°#Ð&6°a¸
À3Ð7GÀqØ'4°SÐ&9¸¸4×;XÑ;XÐ:YÐYZØ'*¨3 i¨qÐ1HÈÐ0MÈQÈ~ÐN^Ð^_Ð`kÐ_lÐlmÐnzÐ{~Ðmð@AðBF÷BLñBLðAMðMdð%eôfð— ‘ ×#Ñ#Ô%à#1Ø4Ø%2Ø#:Ø$0ñ    Qñðð × "× "Ð "à!×AÑAÀ$×B_ÑB_Ó`ˆNÜ$ ^°d·l±lÑ%BÓCˆKô;Ó $×"@Ñ"@Ó@à×$Ñ$×(Ñ(¨×)FÑ)FÔGØ*/Ô'Ø— ‘ ×#Ñ# y° o°Q°q¸°g¸Q¸qÀ¸gÀQÀtÇ|Á|ÐTWÐFXÐXYØ'+×'9Ñ'9¸$×:WÑ:WÑ'XÐYZÑ'[Ð\_Ð&`Ð`aØ'+×'9Ñ'9¸$×:WÑ:WÑ'XÐYZÑ'[Ð\_Ð&`ða-Ø-1×-JÑ-JÐ,KÈ1Ø'*¨3 i¨q°¸SÐ0AÀÈTÏZÉZÈLÐXmð   %oôpð
— ‘ ×#Ñ#Ö%ð#Ÿn™n¨{Ñ:¸SÑ@ Ü!¤%¨×7GÑ7GÑ)GÈ3Ñ(NÓ"OÓP Ü! $¬¨C°Ó(=Ó> à— ‘ ×#Ñ# y° o°Q°q¸°g¸Q¸qÀ¸gÀQÀtÇ|Á|ÐTWÐFXÐXYØ'+×'9Ñ'9¸$×:WÑ:WÑ'XÐYZÑ'[Ð\_Ð&`Ð`aØ'+×'9Ñ'9¸$×:WÑ:WÑ'XÐYZÑ'[Ð\_Ð&`ða-Ø-1×-JÑ-JÐ,KÈ1Ø'*¨3 i¨q°¸SÐ0AÀÀ[ÀMÐQXÐY]×YcÑYcÐXdÐdwð   %yôzð
— ‘ ×#Ñ#Ô%à#$¨kØ0Ø#.Ø&4ñDñðô˜4×-Ñ-Ó.ˆ ܐc“(ˆð×+Ñ+¨d×.AÑ.AÀLÑ.PÑPÐSVÔY\Ð]dÐfiÓYjÑSjÑjˆÜ˜×*Ñ*¬C°×0BÑ0BÀNÓ,SÓTˆ   ô˜3“xˆ Ø×× L°4×3EÑ3EÓ$EØ#ˆDÔ Ø M‰M× Ñ  9¨S /°°1°S°'¸¸1¸S¸'ÀÀ4Ç<Á<ÐPSÐBTÐTUØ#)¨# ,¨a°°s¨|¸1¸\È#Ð<NÈaÐPT×PeÑPeÐOfÐfgØ#& s )Ð+<¸T¿Z¹Z¸LÐHYð![ô \ð M‰M× Ñ Ô !à × ×   ¨t×/FÑ/FÓ FØ$ˆDÔ Ø M‰M× Ñ  9¨S /°°1°S°'¸¸1¸S¸'ÀÀ4Ç<Á<ÐPSÐBTÐTUØ#)¨# ,¨a°°s¨|¸1¸\È#Ð<NÈaÐPT×PeÑPeÐOfÐfgØ#& s )Ð+<¸T¿Z¹Z¸LÐHXð!Zô [ð M‰M× Ñ Ô !ð@D×?YÑ?YÐZcÓ?dÑ<‰ˆˆR. -°Ø"/ˆÔô˜R !™V R¨!¡VÓ,ˆ ô˜T¤3 s¨C°$·,±,Ñ,>Ó#?Ó@ˆØŸ*™*˜ ~Ñ5¸×8KÑ8KÑKˆô% \°NÑ%BÓCˆÜ  °4·<±<Ñ!?Ó@ˆ ðGIÈ1Ãf˜ t×'<Ñ'<Ñ<ÀÒBÐRSˆ Ø +ˆÔð    ×Ò  ¨bÑ 0Ñ0ÕØ  Š ˜˜b™Ñ  Ü" D§L¡L =´#°d·l±lÀD×DXÑDXÓ2YÓZˆÔܘDŸL™L˜=¬#¨d¯l©l¸D¿L¹LÓ*IÓJˆŒ ð—~‘~¨ Ñ3Ø×&Ñ&¨Ñ6ñ7à×&Ñ&¨×)=Ñ)=Ñ=ñ>ð—l‘l T§\¡\Ñ1ñ2ˆ ð˜C¤# nÓ"5¸×8LÑ8LÑ"LÑLÑMˆ
ô˜kÓ*ˆØ˜3 °4×3NÑ3NÑ!NÑNÑOˆ   ôc“(ˆØ˜3 ¨4×+>Ñ+>Ñ!>Ñ>Ñ?ˆ    ð! 9Ñ,¨yÑ8¸gÑFˆ
ܘ
 I¨yÓ9ˆ    Ø˜JÑ&¨¨y©Ñ8ˆ à—‘¨Ñ4ˆ ð $×1Ñ1°KÑ?Ø  ×!3Ñ!3Ñ3°t×7JÑ7JÑJñ KˆÔà×&Ñ&ˆ ܘ;˜,¬¨K¸×9LÑ9LÓ(MÓNˆ ð × × ä  ¨!¡¨V°a©ZÓ8ˆLÜ  ¤s¨3°°d·l±lÑ0BÓ'CÓDˆNØ"Ÿj™j˜[¨>Ñ9¸D×<OÑ<OÑOˆNÜ(¨¸Ñ)FÓGˆOÜ$ _°t·|±|Ñ%CÓDˆKØŸ.™.¨;Ñ6ˆKØ×.Ñ.ˆLà! D§N¡NÑ2ˆ Ø&¨×)AÑ)AÑAÐô ˆs‹8d×.Ñ.°Ñ4Ó 4¼¸KÓ8HÌ2ÈcÉ6Ó8QØ × &Ò &¨!Ñ +Õ &Ø×)Ñ)¨D×,DÑ,DÓDà-.Ô*Ø'(Ô$Ø ” øà)*ˆDÔ &ð××ÞÜ  ¤c¨#¨z¸D¿N¹NÑ/JÓ&KÓL
Þ$ؤ# c¨:¸×8PÑ8PÑ+PÓ"QÑQJØ 
Ñ* ö!Ø Ñ# ܐs“8˜d“?¤c¨+Ó&6¼¸B¹Ó&>Ø  CÑ'Lð :‰:˜×/Ñ/Ó /¸×8H×8HÞ3?˜$×/Ò/ÀT×EZÑEZÐ]`ÑE`ˆKܐ;Ó #Ó%¨,¸Ó*DØ%'¨˜(Ü# H¡L°(±,Ó? Ø— ‘ ×#Ñ# y° o°Q°q¸°g¸Q¸qÀ¸gÀQÀtÇ|Á|ÐTWÐFXÐXYØ'/° n°A°h¸s°^À1À[ÐQTÐDUÐUVÐW[×WnÑWnÐVoÐopØ'*¨3 i¨q°¸SÐ0AÀØ'2°3Ð&7°q¸¿¹¸ ÐDXð%Zô[ð— ‘ ×#Ñ#Ô%Ø* ð—
‘
¦\™c°sÑ;ˆØ# d×&8Ñ&8Ñ8ˆ ؘR‘Zˆ
Ø ×Òœc : +¬s°:¸|Ó/LÓMÑMÕð :‰:˜×.Ñ.Ó .ܐ;Ó #Ó%Ø&*×&:Ñ&:Ð%:¸SÑ%@Ô"øä;Ó #Ó%¨d×.>×.>Ø&*×&:Ñ&:Ð%:¸SÑ%@Ô"Ø×!Ñ! AÓ%¬#¨kÓ*:¸SÓ*@Ø%(Ô"ð ˜×+Ñ+Ó +Ø ˆDŒMØ ¨!°hÀ
Ð5KÑLÐ Lð ˜×,Ñ,¨sÑ2Ó 2Ø × #Ñ # t§v¡v°¡zÓ 1Ü  Ó ˜sÓ "¤s¨3£x°#£~Ø ˆDŒMØ ¨!°hÀ
Ð5KÑLÐ Lð'Ñ2‘  ¼¿ º   » ˆØ×*Ñ*ˆ ࠐ(ܘH™L¨(©,Ó7ˆ ØVZ×VjÑVjÐnoÓVoœ#œe ]°T×5IÑ5IÑ%IÈSÑ$PÓQÔRÐuvˆÜ˜d¤C¨¨_Ó$=Ó>ˆØMQ×M]ÑM]ÐabÓMb”sœ5 +×0@Ñ0@Ñ"@ÀCÑ!GÓHÔIÐhiˆ ܘ4¤ S¨,Ó!7Ó8ˆ Ø  ‰ ×јu S˜k¨¨1¨S¨'°°1°S°'¸¸4¿<¹<ÈÐ:LÈAØ'¨˜n¨A¨h°s¨^¸1¸[ÈÐ<MÈQÈt×OfÑOfÐNgÐghØ" 3˜i q¨°SÐ(9¸¸?Ð:KÈ1È\ÈNÐZ[Ð\iÐjmÐ[nÐnoØ#Ÿz™z˜l¨*ð6ô 7ð      ‰ ×ÑÔàZ^×ZnÑZnÐrsÓZsœœU D×$6Ñ$6¸×9MÑ9MÑ$MÐQTÑ#TÓUÔVÐyzˆÜ˜T¤3 s¨NÓ#;Ó<ˆð ˜QÔ  t§}§} }¼¸[Ó9IÈCÔ9OØ`d×`tÑ`tÐxyÓ`yœC¤ t×'<Ñ'<¸t×?SÑ?SÑ'SÐWZÑ&ZÓ [Ô\ð@AˆMØ Ó!Ø%'¨˜(Ü# H¡L°(±,Ó? Ø— ‘ ×#Ñ# u¨S k°°1°S°'¸¸1¸S¸'ÀÀ4Ç<Á<ÐPSÐBTÐTUØ'/° n°A°h¸s°^À1À[ÐQTÐDUÐUVÐW[×WnÑWnÐVoÐopØ'*¨3 i¨q°¸SÐ0AÀÀ=À/ÐQRÔSVÔW\Ð^i×l|Ñl|Ñ^|ðADñ^DóXEóTFðSGðGHØ'+×'<Ñ'<¸SÐ&AÀÀ4Ç:Á:À,ÐNbð%dôeð— ‘ ×#Ñ#Ô%Ø!.Ø%)×%:Ñ%:Ô"àLP×L\ÑL\Ð`aÓLa”cœ% ×/?Ñ/?Ñ!?À3Ñ FÓGÔHÐghˆ ܘ$¤ C¨Ó 5Ó6ˆ à#Ÿ}Ÿ}‘°)ˆàؐ6Ø—|‘|Ø.Ø&Ø!×/Ñ/Ø&ؘb˜Ø,Ø$ñ 
ˆð ˆØ (ܘH™L¨(©,Ó7ˆ Ø  ‰ ×јq ˜g Q q¨ g¨Q¨q°¨g°Q°t·|±|ÀCÐ6HÈØ'¨˜n¨A¨h°s¨^¸1¸[ÈÐ<MÈQÈt×OfÑOfÐNgÐghØ" 3˜i q¨°SÐ(9¸¸>Ð:JÈ!ÈKÈ=ÐXYÐZ^×ZlÑZlÐmpÐYqÐqrØ#Ÿz™z˜l¨!¨F¨8°2ð7ô   8ð      ‰ ×ÑÔà)ÈÑMÐMr)Hrlrkrr$rdrXr\r[ror3r.rqr+rrrgr4rarirjrnr#rr r6r!r7r8r`r^rZr_ryr)r(r&rrerrcr0rrCrVrprPrUrfrDrErFrWr-rmr>r=r@r?r<rArYrhrIrsrJrtrurHrwr2rbr5r])Nr½)Ú__name__Ú
__module__Ú __qualname__Ú__firstlineno__r’r™ržr«rºrÂrÆrÝrórKÚ__static_attributes__©rr rr#sI†ôSHôj
%ô%ò
ò:#ò:$2òL òFOòP("òTò:*÷XpNrrÚ__main__rÉr9)rrËrér÷gY@rørKr)Ú__doc__Úmathrrrrrrrr rrMÚjsonrUÚcr™rRr‚rKÚoutÚprintrÚfrgrÚhdrfr§r¨rRrr Ú<module>r]sðñ÷*2×1Û ò ÷ANñANðH ˆzÓãØ ˆqˆE1Q%˜˜A˜Ð €DÙ˜Ó€A؇LL˜CÔ Ù 2ŽYˆØ×ÑÓ!ˆÙ ˆaŒ à  ‰N˜UÑ " Q×%6Ñ%6Ñ 6ˆØ Y‰Y˜#˜f™+¨Ñ-°·±Ñ>ÀÑDÑ DˆØ U‰U1‰X˜™C ›G™  cÑ)Ñ )ˆØ U‰U1‰X˜™C ›G™  cÑ)Ñ )ˆØ    ‰ b˜W˜bÖ!òð r