@@ -14,16 +14,20 @@ def __init__(self, port='/dev/pts/6', baud=115200):
1414 self .x = 1225.0
1515 self .y = 300.0
1616 self .theta = math .pi / 2
17+
1718 self .vx = 0.0
1819 self .vy = 0.0
1920 self .vtheta = 0.0
21+
2022 self .last_update = time .time ()
2123 self .last_send = time .time ()
2224
2325 self .pid = [0 , 0 , 0 ]
2426 self .tof = {0 : 1000 , 1 : 1000 , 2 : 1000 }
25- self .waypoints = {} # waypoints by id
26- self .waypoint_order = [] # ordered list of ids
27+
28+ # --- waypoint system ---
29+ self .waypoints = {} # id -> waypoint
30+ self .waypoint_queue = [] # FIFO execution
2731 self .current_wp_id = None
2832
2933 self .thread = threading .Thread (target = self .run )
@@ -37,52 +41,53 @@ def update_position(self):
3741 dt = now - self .last_update
3842 self .last_update = now
3943
40- if self .start :
41- if self .waypoint_order :
42- wp = self .waypoints [self .waypoint_order [0 ]]
43- dx = wp ['x' ] - self .x
44- dy = wp ['y' ] - self .y
45- distance = math .hypot (dx , dy )
46- angle = math .atan2 (dy , dx )
47- angle_diff = self .normalize_angle (wp ['theta' ] - self .theta )
48-
49- if wp ['type' ] == 1 :
50- if distance < 5.0 and abs (angle_diff ) < 0.15 :
51- self .vx = 0.0
52- self .vy = 0.0
53- self .vtheta = 0.0
54- self .ser .write (f'SET;WAYPOINT;{ wp ["id" ]} \n ' .encode ('utf-8' ))
55- self .current_wp_id = wp ['id' ]
56- self .waypoint_order .pop (0 )
57- del self .waypoints [wp ['id' ]]
58- else :
59- speed = min (100.0 , distance / 2 )
60- self .vx = speed * math .cos (angle )
61- self .vy = speed * math .sin (angle )
62- self .vtheta = max (- 9.0 , min (9.0 , angle_diff * 2 ))
63- else :
64- speed = min (150.0 , distance / 2 )
65- self .vx = speed * math .cos (angle )
66- self .vy = speed * math .sin (angle )
67- self .vtheta = 0.0
68- if distance < 100 :
69- self .ser .write (f'SET;WAYPOINT;{ wp ["id" ]} \n ' .encode ('utf-8' ))
70- self .current_wp_id = wp ['id' ]
71- self .waypoint_order .pop (0 )
72- del self .waypoints [wp ['id' ]]
73- else :
74- self .vx = 0.0
75- self .vy = 0.0
44+ if self .start and self .waypoint_queue :
45+ wp_id = self .waypoint_queue [0 ]
46+ wp = self .waypoints [wp_id ]
47+
48+ dx = wp ['x' ] - self .x
49+ dy = wp ['y' ] - self .y
50+ distance = math .hypot (dx , dy )
51+
52+ target_angle = math .atan2 (dy , dx )
53+ theta_error = self .normalize_angle (wp ['theta' ] - self .theta )
54+
55+ # --- motion ---
56+ if wp ['type' ] == 1 : # precise waypoint
57+ speed = min (120.0 , distance * 1 )
58+ self .vtheta = max (- 6.0 , min (6.0 , theta_error * 2 ))
59+ else : # transit waypoint
60+ speed = min (180.0 , distance * 2 )
7661 self .vtheta = 0.0
7762
78- self .x += self .vx * dt
79- self .y += self .vy * dt
80- self .theta += self .vtheta * dt
81- self .theta = self .normalize_angle (self .theta )
63+ self .vx = speed * math .cos (target_angle )
64+ self .vy = speed * math .sin (target_angle )
65+
66+ reached_pos = distance < 5.0
67+ reached_theta = abs (theta_error ) < 0.15 or wp ['type' ] == 0
68+
69+ if reached_pos and reached_theta :
70+ self .vx = self .vy = self .vtheta = 0.0
71+ self .current_wp_id = wp_id
72+
73+ self .ser .write (f"SET;WAYPOINT;REACH;{ wp_id } \n " .encode ())
8274
83- if now - self .last_send > 0.1 and self .start :
84- print (f'[TX] SET;POS;{ int (self .x )} ;{ int (self .y )} ;{ self .theta :.5f} ' )
85- self .ser .write (f'SET;POS;{ int (self .x )} ;{ int (self .y )} ;{ self .theta :.5f} \n ' .encode ())
75+ self .waypoint_queue .pop (0 )
76+ del self .waypoints [wp_id ]
77+
78+ else :
79+ self .vx = self .vy = self .vtheta = 0.0
80+
81+ # --- integrate ---
82+ self .x += self .vx * dt
83+ self .y += self .vy * dt
84+ self .theta = self .normalize_angle (self .theta + self .vtheta * dt )
85+
86+ # --- periodic position ---
87+ if self .start and now - self .last_send > 0.1 :
88+ self .ser .write (
89+ f"SET;POS;{ int (self .x )} ;{ int (self .y )} ;{ self .theta :.5f} \n " .encode ()
90+ )
8691 self .last_send = now
8792
8893 def run (self ):
@@ -98,73 +103,62 @@ def run(self):
98103
99104 def handle_message (self , msg ):
100105 if msg == "GET;POS" :
101- print (f'[TX] SET;POS;{ int (self .x )} ;{ int (self .y )} ;{ self .theta :.3f} ' )
102- self .ser .write (f'SET;POS;{ int (self .x )} ;{ int (self .y )} ;{ self .theta :.3f} \n ' .encode ())
106+ self .ser .write (
107+ f"SET;POS;{ int (self .x )} ;{ int (self .y )} ;{ self .theta :.3f} \n " .encode ()
108+ )
103109
104110 elif msg == "GET;SPEED" :
105- print (f'[TX] SET;SPEED;{ int (self .vx )} ;{ int (self .vy )} ;{ int (self .vtheta )} ' )
106- self .ser .write (f'SET;SPEED;{ int (self .vx )} ;{ int (self .vy )} ;{ int (self .vtheta )} \n ' .encode ())
111+ self .ser .write (
112+ f"SET;SPEED;{ int (self .vx )} ;{ int (self .vy )} ;{ int (self .vtheta )} \n " .encode ()
113+ )
107114
108115 elif msg .startswith ("GET;DIST;" ):
109116 n = int (msg .split (';' )[2 ])
110117 dist = self .tof .get (n , 0 )
111- print (f'[TX] SET;DIST;{ n } ;{ dist } ' )
112- self .ser .write (f'SET;DIST;{ n } ;{ dist } \n ' .encode ())
118+ self .ser .write (f"SET;DIST;{ n } ;{ dist } \n " .encode ())
113119
114120 elif msg == "GET;PID" :
115- print (f'[TX] SET;PID;{ self .pid [0 ]} ;{ self .pid [1 ]} ;{ self .pid [2 ]} ' )
116- self .ser .write (f'SET;PID;{ self .pid [0 ]} ;{ self .pid [1 ]} ;{ self .pid [2 ]} \n ' .encode ())
121+ self .ser .write (f"SET;PID;{ self .pid [0 ]} ;{ self .pid [1 ]} ;{ self .pid [2 ]} \n " .encode ())
117122
118123 elif msg .startswith ("SET;PID" ):
119- print (f'[TX] OK;PID;1' )
120- self .ser .write (f'OK;PID;1\n ' .encode ())
124+ self .ser .write (b"OK;PID;1\n " )
121125
122126 elif msg .startswith ("SET;START;" ):
123127 self .start = msg .split (';' )[2 ] == '1'
124- print (f'[TX] OK;START;1' )
125- self .ser .write (f'OK;START;1\n ' .encode ())
128+ self .ser .write (b"OK;START;1\n " )
126129
130+ # --- MULTI WAYPOINT (clears previous) ---
127131 elif msg .startswith ("SET;WAYPOINT;" ):
128- parts = msg .split (';' )
129- if len (parts ) >= 7 :
132+ parts = msg .split (';' )[2 :]
133+
134+ if len (parts ) % 5 != 0 :
135+ self .ser .write (b"ERR;WAYPOINT;0\n " )
136+ return
137+
138+ # clear existing waypoints
139+ self .waypoints .clear ()
140+ self .waypoint_queue .clear ()
141+
142+ for i in range (0 , len (parts ), 5 ):
130143 wp = {
131- 'id' : int (parts [2 ]),
132- 'type' : int (parts [3 ]),
133- 'x' : float (parts [4 ]),
134- 'y' : float (parts [5 ]),
135- 'theta' : float (parts [6 ])
144+ 'id' : int (parts [i ]),
145+ 'type' : int (parts [i + 1 ]),
146+ 'x' : float (parts [i + 2 ]),
147+ 'y' : float (parts [i + 3 ]),
148+ 'theta' : float (parts [i + 4 ]),
136149 }
150+
137151 self .waypoints [wp ['id' ]] = wp
138- if wp ['id' ] not in self .waypoint_order :
139- self .waypoint_order .append (wp ['id' ])
140- self .ser .write (f'OK;WAYPOINT;{ wp ["id" ]} \n ' .encode ())
152+ self .waypoint_queue .append (wp ['id' ])
141153
142- elif msg .startswith ("SET;WAYPOINTS;" ):
143- parts = msg .split (';' )
144- if len (parts ) / 7 >= 1 :
145- self .waypoints .clear ()
146- self .waypoint_order .clear ()
147- for i in range (2 , len (parts ), 5 ):
148- wp = {
149- 'id' : int (parts [i ]),
150- 'type' : int (parts [i + 1 ]),
151- 'x' : float (parts [i + 2 ]),
152- 'y' : float (parts [i + 3 ]),
153- 'theta' : float (parts [i + 4 ])
154- }
155- self .waypoints [wp ['id' ]] = wp
156- if wp ['id' ] not in self .waypoint_order :
157- self .waypoint_order .append (wp ['id' ])
158- self .ser .write (f'OK;WAYPOINT;{ wp ["id" ]} \n ' .encode ())
159- self .ser .write (b'OK;WAYPOINTS;1\n ' )
154+ self .ser .write (f"OK;WAYPOINT;{ wp ['id' ]} \n " .encode ())
160155
161156 elif msg .startswith ("SET;POS" ):
162157 parts = msg .split (';' )
163158 self .x = float (parts [2 ])
164159 self .y = float (parts [3 ])
165160 self .theta = float (parts [4 ])
166- print (f'[TX] OK;POS;1' )
167- self .ser .write (b'OK;POS;1\n ' )
161+ self .ser .write (b"OK;POS;1\n " )
168162
169163 def stop (self ):
170164 self .running = False
@@ -174,13 +168,15 @@ def stop(self):
174168
175169if __name__ == '__main__' :
176170 parser = argparse .ArgumentParser (description = "Simulated PCB" )
177- parser .add_argument ('--port' , type = str , default = '/dev/pts/6' , help = 'Serial port to use' )
171+ parser .add_argument ('--port' , type = str , default = '/dev/pts/6' )
178172 args = parser .parse_args ()
179173
180174 sim = None
181175 try :
182176 sim = SimulatedPCB (port = args .port )
177+ while True :
178+ time .sleep (1 )
183179 except KeyboardInterrupt :
184180 if sim :
185181 sim .stop ()
186- print ("Simulation stopped" )
182+ print ("Simulation stopped" )
0 commit comments