|
18 | 18 | WB = 2.9 # [m] wheel base of vehicle
|
19 | 19 |
|
20 | 20 | show_animation = True
|
21 |
| - |
| 21 | +pause_simulation = False # 新增暂停标志 |
22 | 22 |
|
23 | 23 | class State:
|
24 | 24 |
|
@@ -141,7 +141,94 @@ def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"):
|
141 | 141 | plt.arrow(x, y, length * math.cos(yaw), length * math.sin(yaw),
|
142 | 142 | fc=fc, ec=ec, head_width=width, head_length=width)
|
143 | 143 | plt.plot(x, y)
|
144 |
| - |
| 144 | +def plot_vehicle(x, y, yaw, steer=0.0, color='blue'): |
| 145 | + """ |
| 146 | + 绘制带四个车轮的车辆模型 |
| 147 | + Args: |
| 148 | + x, y: 车辆中心位置 |
| 149 | + yaw: 车辆航向角 |
| 150 | + steer: 转向角 |
| 151 | + color: 车辆颜色 |
| 152 | + """ |
| 153 | + # 车辆参数 |
| 154 | + LENGTH = WB + 1.0 # 车长 |
| 155 | + WIDTH = 2.0 # 车宽 |
| 156 | + WHEEL_LEN = 0.6 # 车轮长度 |
| 157 | + WHEEL_WIDTH = 0.2 # 车轮宽度 |
| 158 | + |
| 159 | + def plot_wheel(x, y, yaw, steer=0.0): |
| 160 | + """绘制单个车轮""" |
| 161 | + wheel = np.array([ |
| 162 | + [-WHEEL_LEN/2, WHEEL_WIDTH/2], |
| 163 | + [WHEEL_LEN/2, WHEEL_WIDTH/2], |
| 164 | + [WHEEL_LEN/2, -WHEEL_WIDTH/2], |
| 165 | + [-WHEEL_LEN/2, -WHEEL_WIDTH/2], |
| 166 | + [-WHEEL_LEN/2, WHEEL_WIDTH/2] |
| 167 | + ]) |
| 168 | + |
| 169 | + # 如果有转向角,先旋转车轮 |
| 170 | + if steer != 0: |
| 171 | + c, s = np.cos(steer), np.sin(steer) |
| 172 | + rot_steer = np.array([[c, -s], [s, c]]) |
| 173 | + wheel = wheel @ rot_steer.T |
| 174 | + |
| 175 | + # 考虑车辆朝向 |
| 176 | + c, s = np.cos(yaw), np.sin(yaw) |
| 177 | + rot_yaw = np.array([[c, -s], [s, c]]) |
| 178 | + wheel = wheel @ rot_yaw.T |
| 179 | + |
| 180 | + # 平移到指定位置 |
| 181 | + wheel[:, 0] += x |
| 182 | + wheel[:, 1] += y |
| 183 | + |
| 184 | + plt.plot(wheel[:, 0], wheel[:, 1], color=color) |
| 185 | + |
| 186 | + # 计算车身四个角点 |
| 187 | + corners = np.array([ |
| 188 | + [-LENGTH/2, WIDTH/2], |
| 189 | + [LENGTH/2, WIDTH/2], |
| 190 | + [LENGTH/2, -WIDTH/2], |
| 191 | + [-LENGTH/2, -WIDTH/2], |
| 192 | + [-LENGTH/2, WIDTH/2] |
| 193 | + ]) |
| 194 | + |
| 195 | + # 旋转矩阵 |
| 196 | + c, s = np.cos(yaw), np.sin(yaw) |
| 197 | + Rot = np.array([[c, -s], [s, c]]) |
| 198 | + |
| 199 | + # 旋转并平移车身 |
| 200 | + rotated = corners @ Rot.T |
| 201 | + rotated[:, 0] += x |
| 202 | + rotated[:, 1] += y |
| 203 | + |
| 204 | + # 绘制车身 |
| 205 | + plt.plot(rotated[:, 0], rotated[:, 1], color=color) |
| 206 | + |
| 207 | + # 绘制四个车轮 |
| 208 | + # 前轮(左) |
| 209 | + plot_wheel(x + LENGTH/4 * c - WIDTH/2 * s, |
| 210 | + y + LENGTH/4 * s + WIDTH/2 * c, |
| 211 | + yaw, steer) |
| 212 | + # 前轮(右) |
| 213 | + plot_wheel(x + LENGTH/4 * c + WIDTH/2 * s, |
| 214 | + y + LENGTH/4 * s - WIDTH/2 * c, |
| 215 | + yaw, steer) |
| 216 | + # 后轮(左) |
| 217 | + plot_wheel(x - LENGTH/4 * c - WIDTH/2 * s, |
| 218 | + y - LENGTH/4 * s + WIDTH/2 * c, |
| 219 | + yaw) |
| 220 | + # 后轮(右) |
| 221 | + plot_wheel(x - LENGTH/4 * c + WIDTH/2 * s, |
| 222 | + y - LENGTH/4 * s - WIDTH/2 * c, |
| 223 | + yaw) |
| 224 | + |
| 225 | +# 添加键盘事件处理函数 |
| 226 | +def on_key(event): |
| 227 | + global pause_simulation |
| 228 | + if event.key == ' ': # 空格键 |
| 229 | + pause_simulation = not pause_simulation |
| 230 | + elif event.key == 'escape': |
| 231 | + exit(0) |
145 | 232 |
|
146 | 233 | def main():
|
147 | 234 | # target course
|
@@ -177,18 +264,29 @@ def main():
|
177 | 264 | if show_animation: # pragma: no cover
|
178 | 265 | plt.cla()
|
179 | 266 | # for stopping simulation with the esc key.
|
180 |
| - plt.gcf().canvas.mpl_connect( |
181 |
| - 'key_release_event', |
182 |
| - lambda event: [exit(0) if event.key == 'escape' else None]) |
183 |
| - plot_arrow(state.x, state.y, state.yaw) |
| 267 | + plt.gcf().canvas.mpl_connect('key_release_event', on_key) |
| 268 | + plot_vehicle(state.x, state.y, state.yaw, di) # di 是转向角 |
184 | 269 | plt.plot(cx, cy, "-r", label="course")
|
185 | 270 | plt.plot(states.x, states.y, "-b", label="trajectory")
|
186 | 271 | plt.plot(cx[target_ind], cy[target_ind], "xg", label="target")
|
187 | 272 | plt.axis("equal")
|
188 | 273 | plt.grid(True)
|
189 | 274 | plt.title("Speed[km/h]:" + str(state.v * 3.6)[:4])
|
| 275 | + plt.legend() # 添加这一行显示图例 |
| 276 | + |
| 277 | + # 添加暂停状态显示 |
| 278 | + if pause_simulation: |
| 279 | + plt.text(0.02, 0.95, 'PAUSED', transform=plt.gca().transAxes, |
| 280 | + bbox=dict(facecolor='red', alpha=0.5)) |
| 281 | + |
190 | 282 | plt.pause(0.001)
|
191 | 283 |
|
| 284 | + # 暂停处理 |
| 285 | + while pause_simulation: |
| 286 | + plt.pause(0.1) # 降低 CPU 占用 |
| 287 | + if not plt.get_fignums(): # 检查窗口是否被关闭 |
| 288 | + exit(0) |
| 289 | + |
192 | 290 | # Test
|
193 | 291 | assert lastIndex >= target_ind, "Cannot goal"
|
194 | 292 |
|
|
0 commit comments