yincheng.zhong
2025-11-23 212ccb49d3e7c7fa138c5f9d335d0b8c5a08d2a3
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
o
Œ#iLLã@sPdZddlmZddlZddlZddlZddlZddlZddlZddl Z   ddl
m
Z
m Z m Z ddl mZddlmZddlZddlmZdd   lmZmZdd
lmZmZmZmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$e
d dd e j%d Z&Gdd„dƒZ'ej(Gdd„dƒƒZ)Gdd„dƒZ*Gdd„dƒZ+d'dd„Z,d(dd„Z-d)d!d"„Z.d*d%d&„Z/dS)+us
硬件在环 (HITL) ä»¿çœŸå™¨ï¼šç”Ÿæˆ $GPRMI/$GPIMU ä¼ æ„Ÿå™¨æ•°æ®ï¼Œå¹¶ä¸Ž STM32H7 é€šè¿‡ PythonLink é—­çŽ¯ã€‚
é)Ú annotationsN)ÚdatetimeÚ  timedeltaÚtimezone)ÚPath)ÚCallableé)Úgeo)ÚDifferentialDriveModelÚDifferentialDriveState) Ú ControlStatusÚ
PoseStatusÚPythonLinkDecoderÚPythonLinkFrameÚ StackStatusÚ StateStatusÚbuild_gpimu_sentenceÚbuild_gprmi_sentenceÚdecode_control_statusÚdecode_pose_statusÚdecode_stack_statusÚdecode_state_statusÚparse_ascii_messagei¼é©Útzinfoc@sTeZdZdZd"d#dd„Zd   d
d œd$dd„Zdd„Zdd„Zd%d&dd„Zd'd d!„Z d   S)(Ú   RunLoggeru^异步日志器:按 GPS æ—¶é—´ä¼˜å…ˆæŽ’序写入;无 GPS æ—¶é—´åˆ™å›žé€€åˆ°ä¸»æœºæ—¶é—´ã€‚ç@@Úpathú
Path | strÚflush_delay_msÚfloatcCs~t|ƒ|_|jjjddd|jjddd|_t ¡|_g|_    t
  ¡|_ t  ¡|_|d|_t
j|jddd|_|j ¡dS)   NT)ÚparentsÚexist_okÚwúutf-8)Úencodingç@@z hitl-runlog©ÚtargetÚnameÚdaemon)rrÚparentÚmkdirÚopenÚ_fileÚqueueÚQueueÚ_queueÚ_bufferÚ threadingÚEventÚ_stopÚ    itertoolsÚcountÚ_counterÚ _flush_delayÚThreadÚ_loopÚ_workerÚstart)Úselfrr ©r@ú1E:\GIT\Lawnmower_STM32H7\python\hitl\simulator.pyÚ__init__,s
 
þ
 
 
zRunLogger.__init__Nr©Ú
gps_time_sÚ source_rankÚprefixÚstrÚcontentrDú float | NonerEÚintcCsRt ¡}|dur
dnd}t|dur|n|ƒ}|j |||ft|jƒ|||f¡dS)Nrr)Útimer!r2ÚputÚnextr9)r?rFrHrDrEÚarrivalÚpriorityZkey_timer@r@rAÚlog:sûÿz RunLogger.logcCs:|j ¡|j ¡|jdd|jjs|j ¡dSdS)NT)Úforce)r6Úsetr=ÚjoinÚ _flush_bufferr/ÚclosedÚclose©r?r@r@rArVOs
 
 ÿzRunLogger.closecCsz|j ¡r |j ¡r |jr;z|jjdd}t |j|¡Wn
tj   y'Ynw| 
¡|j ¡r |j ¡r |js dSdS)Ngš™™™™™©?©Útimeout) r6Úis_setr2Úemptyr3ÚgetÚheapqÚheappushr0ÚEmptyrT)r?Úitemr@r@rAr<Vsÿ"úzRunLogger._loopFrQÚboolcCsjt ¡}|jr3|jd\}}}}}|s!|||jkr!|j ¡s!dSt |j¡| |||¡|jsdSdS©Nr)rKr3r:r6rZr]ÚheappopÚ _write_line)r?rQÚnowÚkeyZ_seqrNrFrHr@r@rArT_s ûzRunLogger._flush_bufferrfútuple[int, float, int]c   Csh|\}}}|dkrd|d›d}nd|d›d}d|›d|›d   |›d
|›d  }|j |¡|j ¡dS) NrzGPS=z09.3fÚszHOST=z012.3fú[z r=z] z: Ú
)r/ÚwriteÚflush)  r?rfrFrHrOZ timestamp_srEZts_labelÚliner@r@rArdhs
 zRunLogger._write_line)r)rrr r!)rFrGrHrGrDrIrErJ)F)rQra)rfrgrFrGrHrG)
Ú__name__Ú
__module__Ú __qualname__Ú__doc__rBrPrVr<rTrdr@r@r@rAr)s ú      rc@sŠeZdZUdZded<ded<ded<dZded  <d
Zd ed <d Zded<dZded<dZ   d ed<dZ
d ed<dZ d ed<dZ d ed<dS)Ú
HitlConfigu&HITL ä»¿çœŸè¿è¡Œå¿…需的配置项。rGÚ
uart2_portú
str | NoneÚ
uart5_portÚ
origin_gga)çrwrwztuple[float, float, float]Ú initial_enurwr!Úinitial_heading_degiÂrJÚ gps_baudrateiÚ log_baudrategö(\Âõè?Ú track_widthgÍÌÌÌÌÌì?Úbaseline_distanceg@Úmax_linear_speedg€a@Úmax_angular_speed_degN) rnrorprqÚ__annotations__rxryrzr{r|r}r~rr@r@r@rArrvs
       rrc@sveZdZUdZded<ded<ded<ded <d
ed <dd d d„Zdd„Zdd„Zd!dd„Zd"d#dd„Z d$dd„Z
dS)%ÚSerialEndpointu线程安全的串口包装。rtÚportrJÚbaudrater!rYzserial.Serial | NoneÚ_serialúthreading.LockÚ_lockrwcCs&||_||_||_d|_t ¡|_dS©N)r‚rƒrYr„r4ÚLockr†)r?r‚rƒrYr@r@rArBs
zSerialEndpoint.__init__cCsN|jsdS|jr|jjrdStj|j|j|jdd|_|j ¡|j ¡dS)Nr)rYÚ write_timeout)   r‚r„Úis_openÚserialÚSerialrƒrYÚreset_input_bufferÚreset_output_bufferrWr@r@rAr.—sü
zSerialEndpoint.opencCs*|jrz |j ¡Wd|_dSd|_wdSr‡)r„rVrWr@r@rArV¥s
 üzSerialEndpoint.closeÚdataÚbytescCsF|r|jsdS|j|j |¡}WdƒdS1swYdSr‡)r„r†rk)r?rÚ_r@r@rArk¬s
 
"ÿzSerialEndpoint.writerÚsizeÚreturncCs0|jsdSz|j |¡WStjyYdSw©Nó)r„Úreadr‹ÚSerialException)r?r’r@r@rAr–²sÿzSerialEndpoint.readcCs.|jsdSz|j ¡WStjyYdSwr”)r„Úreadliner‹r—rWr@r@rAr˜ºs ÿzSerialEndpoint.readlineN)rw)r‚rtrƒrJrYr!)rr)r)r’rJr“r)r“r) rnrorprqr€rBr.rVrkr–r˜r@r@r@rAr‡s
 
 rc@sneZdZUdZded<ded<ded<ded <d
ed <d ed <d ed<ded<ded<ded<ded<ded<ded<ded<ded<ded<d ed!<d"ed#<d$ed%<ddded)d*„Zd+d,„Zd-d.„Zd/d0„Zd1d2„Z   d3d4„Z
d5d6„Z d7d8„Z dfd;d<„Z dfd=d>„Zdgd@dA„ZdhdDdE„ZedidHdI„ƒZdjdLdM„ZdkdRdS„Zd&dTdUœdld]d^„Zd&dTdUœdld_d`„Zd&dTdUœdmdbdc„Zd&S)nÚ HitlSimulatoru9电脑仿真环境与 STM32H7 æŽ§åˆ¶æ¿ä¹‹é—´çš„æ¡¥æ¢ã€‚rrÚconfigz
geo.OriginÚoriginr
Úmodelr…Ú _state_lockr Ú _latest_stater!Ú_target_linearÚ_target_angularrÚ    _sim_timerÚuart2Úlog_uartrÚ_decoderzthreading.EventÚ_runningzlist[threading.Thread]Ú_threadsz%Callable[[float, float], None] | NoneÚ
on_controlzCallable[[str], None] | NoneÚon_logz&Callable[[ControlStatus], None] | NoneÚon_control_statusz#Callable[[PoseStatus], None] | NoneÚon_pose_statusz$Callable[[StateStatus], None] | NoneÚon_state_statusz$Callable[[StackStatus], None] | NoneÚon_stack_statusNÚ
run_loggerúRunLogger | NonecCs||_t |j¡|_t|j|jt  |j
¡d|_ |j j |j d|j d|j d|jdt ¡|_|j j ¡|_d|_d|_t|jƒ|_t|j|jdd|_t|j|jdd|_t |j!ƒ|_"t #¡|_$g|_%||_&d|_'d|_(d|_)d|_*d|_+d|_,d|_-dS) N)r|r~Zmax_angular_speedrré©ÚeastÚnorthÚupÚ heading_degrwrX皙™™™™¹?).ršr  Ú parse_originrvr›r
r|r~ÚmathÚradiansrrœÚresetrxryr4rˆrÚstateÚcopyržrŸr Ú_initial_timestamp_from_ggar¡rrsrzr¢rur{r£rÚ_handle_control_framer¤r5r¥r¦r­r§r¨r©rªr«r¬Zon_ascii)r?ršr­r@r@rArBÚs>
ýü
 
 
zHitlSimulator.__init__c  Csš|j ¡rdS|j ¡|j ¡|j ¡tj|jdddtj|j dddtj|j
dddtj|j dddtj|j dddg|_ |j D]}| ¡qDdS)Nz  hitl-physTr(z
hitl-gprmiz
hitl-gpimuz hitl-ctrlzhitl-log)r¥rZr¢r.r£rRr4r;Ú _loop_physicsÚ _loop_gprmiÚ _loop_gpimuÚ _loop_controlÚ  _loop_logr¦r>©r?Útr@r@rAr>s
 
 
 
û
 
ÿzHitlSimulator.startcCsR|j ¡sdS|j ¡|jD]}|jddq|j ¡|j ¡|j ¡dS)Ngð?rX)r¥rZÚclearr¦rSr¢rVr£rÃr@r@rAÚstops
 
 
 
 
zHitlSimulator.stopcCs t ¡}|j ¡rNt ¡}t||dƒ}|}|j!|j |j|j  |¡ 
¡}||_ |j t |d7_ Wdƒn1s=wYt d¡|j ¡s dSdS)Ng-Cëâ6?)Úsecondsg{®Gázt?)rKÚ perf_counterr¥rZÚmaxrrœÚsteprŸr r»ržr¡rÚsleep)r?ÚlastreÚdtrºr@r@rAr¾s
ý
øzHitlSimulator._loop_physicscCóbd}|j ¡r/t ¡}| ¡\}}|r"|j |¡|jd||dd| ||¡|j ¡sdSdS)NrµzPY->STM32 UART2 GPFMIrrC)  r¥rZrKrÈÚ_build_gprmi_sentencer¢rkÚ
_log_asciiÚ_sleep_remaining©r?Úperiodr>ÚsentencerDr@r@rAr¿*ó
   úzHitlSimulator._loop_gprmicCrÎ)Nç{®Gáz„?zPY->STM32 UART2 GPIMUrrC)  r¥rZrKrÈÚ_build_gpimu_sentencer¢rkrÐrÑrÒr@r@rArÀ4rÕzHitlSimulator._loop_gpimucCsT|j ¡r(|j d¡}|r|jd|dd|j |¡nt d¡|j ¡sdSdS)Né€zSTM32->PY UART2 CTRL_RAWr©rEgü©ñÒMb`?)  r¥rZr¢r–Ú _log_binaryr¤ÚfeedrKrË)r?rr@r@rArÁ>s
 ÿ
øzHitlSimulator._loop_controlc   Csx|jjs|j ¡rt d¡|j ¡s   dS|j ¡rº|j ¡}|s't d¡q|jddd    ¡}|s3qd}t
|ƒ}|rŸt |ƒ}|rW|j rW|  |¡|j d|t|jƒdd   d
}t|ƒ}|rs|jrs| |¡|j d |t|jƒdd   d
}t|ƒ}|r|jr| |¡|j d |t|jƒdd   d
}t|ƒ}|rŸ|jrŸ| |¡d
}|s©|jr©| |¡|s³|j d |dd|j ¡sdSdS)Ngà?rÖr%Úreplace©ÚerrorsFzSTM32 UART5 CTRLrrCTzSTM32 UART5 POSEzSTM32 UART5 STATEz STM32 UART5rÙ)ršrur¥rZrKrËr£r˜ÚdecodeÚstriprrr©Ú _log_textÚ_ascii_timestamp_to_secondsÚ timestamp_msrrªrr«rr¬r¨)    r?rmÚtextZhandledÚmsgÚctrlÚposerºÚstackr@r@rArÂIsn
 
 
ÿ
 
 
 
 
ü
 
ü
 
ü
 
 
 
ÐzHitlSimulator._loop_logr“útuple[bytes | None, float]c Csn| ¡\}}t |j|j|j|j¡\}}}t |j¡}t  |ƒ}t
|||||j |j |j ||j|j|jjd |fS)N) Ú    timestampÚlat_degÚlon_degÚalt_mÚeast_velÚ   north_velÚup_velr´Ú pitch_degÚroll_degÚ
baseline_m)Ú   _snapshotr  Z
enu_to_llar±r²r³r›Zheading_math_to_navÚheadingÚ_seconds_of_dayrZ east_velocityZnorth_velocityZ up_velocityrñròršr})r?rºrêZlatZlonZaltZ heading_navrDr@r@rArσs&  õ òz#HitlSimulator._build_gprmi_sentencecCs.| ¡\}}t|ƒ}t||j|j|jd|fS)N)rêÚaccel_gÚ
gyro_deg_sÚ temperature_c)rôrörZ body_accel_grørù)r?rºrêrDr@r@rArיs üùz#HitlSimulator._build_gpimu_sentenceú'tuple[DifferentialDriveState, datetime]cCs:|j|j ¡|jfWdƒS1swYdSr‡)rržr»r¡rWr@r@rArô©s$ÿzHitlSimulator._snapshotÚframerc CsŒ|j|j|_|j|_Wdƒn1swY|jr&| |j|j¡|jrD|j dd|jd›d|jd›d|j›d|j    ›¡dSdS)NzSTM32->PY CTRL_FRAMEzforward=z.3fz turn=z pwm=ú/)
rÚforwardrŸÚturnr r§r­rPÚ steering_pwmÚ throttle_pwm)r?rûr@r@rAr½­s
þ&þÿz#HitlSimulator._handle_control_framer>rÓcCs.t ¡|}||}|dkrt |¡dSdSrb)rKrÈrË)r>rÓÚelapsedÚ  remainingr@r@rArѹs
ÿzHitlSimulator._sleep_remainingrvrGcCsH|sdSzt |¡}Wn
tyYdSw||j_||_t|ƒ|_dSr‡)r    r¶Ú
ValueErrorršrvr›r¼r¡)r?rvZ
new_originr@r@rAÚ update_originÃs ÿzHitlSimulator.update_originr±r²r³r´cCsZ|j |jj||||d|jj ¡|_d|_d|_WdƒdS1s&wYdS)Nr°rw)rrœr¹rºr»ržrŸr )r?r±r²r³r´r@r@rAÚ reset_stateÎs "üzHitlSimulator.reset_staterrCrFÚpayloadrrDrIrErJcCs8|jr|sdS|jddd ¡}|jj||||ddS)Nr%rÜrÝrC)r­rßràrP)r?rFrrDrErär@r@rArÐØs
zHitlSimulator._log_asciicCs*|jr|sdS|jj|| ¡||ddS©NrC)r­rPÚhex)r?rFrrDrEr@r@rArÚås
 
 
ÿzHitlSimulator._log_binaryräcCs"|jsdS|jj||||ddSr)r­rP)r?rFrärDrEr@r@rAráószHitlSimulator._log_textr‡)ršrrr­r®)r“ré)r“rú)rûr)r>r!rÓr!)rvrG)r±r!r²r!r³r!r´r!)rFrGrrrDrIrErJ)rFrGrärGrDrIrErJ)rnrorprqr€rBr>rÆr¾r¿rÀrÁrÂrÏr×rôr½Ú staticmethodrÑrrrÐrÚrár@r@r@rAr™Ãs\
 '  
 
 
:
 
 
 
    
úúúr™rÍrr“cCs$|jdur |jtjdS| tj¡S)Nr)rrÜrÚutcÚ
astimezone)rÍr@r@rAÚ_ensure_utc_datetimeÿs
 r r!cCs,t|ƒ}|jd|jd|j|jdS)Néé<g€„.A)r ÚhourÚminuteÚsecondÚ microsecond)rÍr
r@r@rArösÿþýÿröÚrawúfloat | int | NonerIc  Cs|durdSzt|ƒ}Wn ttfyYdSwtt|ƒƒd›}t|dd…ƒ}t|dd…ƒ}t|dd…ƒ}t|dd…ƒ}d|krKdkr|n|d
Sd|krZdkr|n|d
Sd|kridkr|n|d
S|d |d||d
S|d
S) NZ09drr¯éré   érr r')r!Ú TypeErrorrrJÚabs)rÚvalueräÚhhÚmmÚssZmsr@r@rArâs( ÿþþÿrâÚggarGc     Csä|pd d¡}t|ƒdkrl|drl|d}zKt|dd…ƒ}t|dd…ƒ}t|dd…ƒ}d|vr=td | d¡dƒnd
}t tj¡ ¡}t|j |j
|j tjd }|j |||t|d ƒd WSt tfykYnwt tj¡S)NÚú,rrr¯rrÚ.z0.rwri@B)rrrr)ÚsplitÚlenrJr!rrerr
ÚdateÚyearÚmonthÚdayrÜrÚ
IndexError) rÚpartsZtime_strrrrÚfracÚtodayÚbaser@r@rAr¼ s"ÿ r¼)rÍrr“r)rÍrr“r!)rrr“rI)rrGr“r)0rqÚ
__future__rÚ dataclassesr]r7r·r0r4rKrrrÚpathlibrÚtypingrr‹rr Zdynamicsr
r Ú   protocolsr r rrrrrrrrrrrr
Ú    GPS_EPOCHrÚ dataclassrrrr™r rörâr¼r@r@r@rAÚ<module>s8    <M<
>