Skip to content

Commit 7d5597a

Browse files
committed
DWA exercise files
1 parent c169cac commit 7d5597a

4 files changed

Lines changed: 283 additions & 239 deletions

File tree

exercises/dynamic_window_approach/frontend/WebGUI.tsx

Lines changed: 230 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,24 @@ import Arrow from "./resources/arrow.svg";
1010
import "./css/GUICanvas.css";
1111

1212
function WebGUI() {
13-
const meter = 73; // 1m = 73px
13+
const meter = 73;
14+
15+
const VMAX = 2.0;
16+
const WMAX = 2.0;
17+
const VIEW_MARGIN = 10;
18+
const HEAT_SIZE = 280;
19+
const HEAT_MARGIN = 10;
1420

1521
const [laser, setLaser] = useState<number[][]>([]);
1622
const [maxRange, setMaxRange] = useState<number>(1000);
17-
const [carForce, setCarForce] = useState<number[]>([2 * meter, 0]);
18-
const [avgForce, setAvgForce] = useState<number[]>([2 * meter, 0]);
19-
const [obsForce, setObsForce] = useState<number[]>([2 * meter, -Math.PI / 2]);
23+
24+
const [currentV, setCurrentV] = useState(0);
25+
const [currentW, setCurrentW] = useState(0);
26+
2027
const [targetPose, setTargetPose] = useState<number[] | undefined>(undefined);
28+
29+
const [dynamicWindow, setDynamicWindow] = useState<any[]>([]);
30+
2131
const exerciseContext = useExercise();
2232
const [manager, setManager] = useState(exerciseContext.manager);
2333

@@ -29,56 +39,42 @@ function WebGUI() {
2939
let data = updateData as any;
3040
data = data.update;
3141

32-
if (data.map) {
33-
const dataToDraw = JSON.parse(data.map);
42+
if (!data.map) return;
3443

35-
setLaser(dataToDraw.laser);
36-
setMaxRange(dataToDraw.max_range);
37-
const carForceDist = getDist(dataToDraw.car[0], dataToDraw.car[1]);
38-
setCarForce([
39-
carForceDist * meter,
40-
getAng(dataToDraw.car[0], dataToDraw.car[1]),
41-
]);
42-
const avgForceDist = getDist(
43-
dataToDraw.average[0],
44-
dataToDraw.average[1]
45-
);
46-
setAvgForce([
47-
avgForceDist * meter,
48-
getAng(dataToDraw.average[0], dataToDraw.average[1]),
49-
]);
50-
const obsForceDist = getDist(
51-
dataToDraw.obstacle[0],
52-
dataToDraw.obstacle[1]
44+
const dataToDraw = JSON.parse(data.map);
45+
46+
setLaser(dataToDraw.laser);
47+
setMaxRange(dataToDraw.max_range);
48+
49+
if (dataToDraw.bestVelocity) {
50+
setCurrentV(dataToDraw.bestVelocity[0]);
51+
setCurrentW(dataToDraw.bestVelocity[1]);
52+
}
53+
54+
if (dataToDraw.dynamicWindow) {
55+
setDynamicWindow(
56+
dataToDraw.dynamicWindow.map((i: any) => [i[0], i[1], i[2]])
5357
);
54-
setObsForce([
55-
obsForceDist * meter,
56-
getAng(dataToDraw.obstacle[0], dataToDraw.obstacle[1]),
57-
]);
58-
const targetDist = getDist(
59-
dataToDraw.pose[0] - dataToDraw.target[0],
60-
dataToDraw.pose[1] - dataToDraw.target[1]
58+
}
59+
60+
if (dataToDraw.pose && dataToDraw.target) {
61+
const targetDist = Math.sqrt(
62+
Math.pow(dataToDraw.pose[0] - dataToDraw.target[0], 2) +
63+
Math.pow(dataToDraw.pose[1] - dataToDraw.target[1], 2)
6164
);
62-
const targetAng = getAng(
63-
dataToDraw.pose[0] - dataToDraw.target[0],
64-
dataToDraw.pose[1] - dataToDraw.target[1]
65+
66+
const targetAng = Math.atan2(
67+
dataToDraw.pose[1] - dataToDraw.target[1],
68+
dataToDraw.pose[0] - dataToDraw.target[0]
6569
);
70+
6671
setTargetPose([
6772
targetDist * meter,
6873
targetAng - dataToDraw.pose[2] - Math.PI,
6974
]);
7075
}
7176
};
7277

73-
const getDist = (x: number, y: number) => {
74-
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
75-
};
76-
77-
const getAng = (x: number, y: number) => {
78-
const ang = Math.atan2(y, x);
79-
return ang;
80-
};
81-
8278
const stateCallback = (state: string) => {
8379
if (state === states.TOOLS_READY) {
8480
setTargetPose(undefined);
@@ -87,54 +83,213 @@ function WebGUI() {
8783

8884
connectApplication(manager, updateCallback);
8985

90-
return (
91-
<WebGUIContainer id="f1-road">
92-
<img src={F1Car} id="f1-car" />
93-
{laser.map((element) => {
86+
// =========================================================
87+
// ===================== WINDOW SCENE ======================
88+
// =========================================================
89+
const WindowScene = () => (
90+
<div style={{ position: "relative", width: "100%", height: "300px" }}>
91+
{/* ---------------- COCHE ---------------- */}
92+
<img
93+
src={F1Car}
94+
id="f1-car"
95+
style={{
96+
position: "absolute",
97+
left: "25%",
98+
top: "80%",
99+
transform: "translate(-50%, -50%)",
100+
}}
101+
/>
102+
103+
{/* ---------------- TARGET ---------------- */}
104+
{targetPose && (
105+
<div
106+
className="target-container"
107+
style={{
108+
position: "absolute",
109+
left: "25%",
110+
top: "80%",
111+
transform: `translate(-50%, -50%) rotate(${-targetPose[1]}rad)`,
112+
height: targetPose[0],
113+
}}
114+
>
115+
<div id="target" />
116+
</div>
117+
)}
118+
119+
{/* ---------------- LASER ---------------- */}
120+
{laser.map((element, i) => {
94121
const ang = -element[1];
95122
const length = (element[0] / maxRange) * 100;
123+
96124
return (
97125
<hr
126+
key={i}
98127
className="laser-beam"
99128
style={{
100-
rotate: "z " + ang + "rad",
101-
width: "calc(" + length + "%)",
129+
position: "absolute",
130+
left: "25%",
131+
top: "60%",
132+
transformOrigin: "0% 50%",
133+
transform: `rotate(${ang}rad)`,
134+
width: `${length}%`,
102135
}}
103136
/>
104137
);
105138
})}
106-
<img
107-
className="arrow green"
108-
src={Arrow}
109-
style={{ height: carForce[0], rotate: "z " + -carForce[1] + "rad" }}
110-
/>
111-
<img
112-
className="arrow red"
113-
src={Arrow}
114-
style={{ height: obsForce[0], rotate: "z " + -obsForce[1] + "rad" }}
115-
/>
116-
<img
117-
className="arrow"
118-
src={Arrow}
139+
140+
{/* ---------------- VISOR VELOCIDADES (INTOCADO) ---------------- */}
141+
<div
119142
style={{
120-
height: avgForce[0],
121-
rotate: "z " + -avgForce[1] + "rad",
122-
zIndex: "6",
143+
position: "absolute",
144+
left: "75%",
145+
top: "25%",
146+
transform: "translate(-50%, -50%)",
147+
width: "280px",
148+
height: "140px",
149+
zIndex: 10,
123150
}}
124-
/>
125-
{targetPose && (
126-
<div
127-
className="target-container"
151+
>
152+
<svg
153+
width={280}
154+
height={140}
128155
style={{
129-
height: targetPose[0],
130-
rotate: "z " + -targetPose[1] + "rad",
156+
border: "1.5px solid #333",
157+
borderRadius: "10px",
158+
background: "#fafafa",
159+
boxShadow: "0 4px 10px rgba(0,0,0,0.1)",
131160
}}
132161
>
133-
<div id="target" />
134-
</div>
135-
)}
162+
{(() => {
163+
const lines = [];
164+
165+
for (let w = -WMAX; w <= WMAX; w += 0.5) {
166+
const x = 130 - (w / WMAX) * (130 - VIEW_MARGIN);
167+
168+
lines.push(
169+
<line
170+
key={`w-${w}`}
171+
x1={x}
172+
y1={VIEW_MARGIN}
173+
x2={x}
174+
y2={140 - VIEW_MARGIN}
175+
stroke={w === 0 ? "#444" : "#ddd"}
176+
strokeWidth={w === 0 ? 1.5 : 0.5}
177+
/>
178+
);
179+
}
180+
181+
for (let v = 0; v <= VMAX; v += 0.5) {
182+
const y = 70 - (v / VMAX) * (70 - VIEW_MARGIN);
183+
184+
lines.push(
185+
<line
186+
key={`v-${v}`}
187+
x1={VIEW_MARGIN}
188+
y1={y}
189+
x2={280 - VIEW_MARGIN}
190+
y2={y}
191+
stroke={v === 0 ? "#444" : "#ddd"}
192+
strokeWidth={v === 0 ? 1.5 : 0.5}
193+
/>
194+
);
195+
}
196+
197+
return lines;
198+
})()}
199+
200+
<line x1={130} y1={VIEW_MARGIN} x2={130} y2={140 - VIEW_MARGIN} stroke="#444" />
201+
<line x1={VIEW_MARGIN} y1={70} x2={280 - VIEW_MARGIN} y2={70} stroke="#444" />
202+
203+
<circle
204+
cx={130 - (currentW / WMAX) * (130 - VIEW_MARGIN)}
205+
cy={70 - (currentV / VMAX) * (70 - VIEW_MARGIN)}
206+
r={5}
207+
fill="#ff4d4d"
208+
stroke="#222"
209+
strokeWidth={1.5}
210+
/>
211+
212+
<text x={10} y={110} fontSize="13" fill="#222">
213+
v: {currentV.toFixed(2)} m/s
214+
</text>
215+
<text x={10} y={130} fontSize="13" fill="#222">
216+
w: {currentW.toFixed(2)} rad/s
217+
</text>
218+
</svg>
219+
</div>
220+
221+
{/* ---------------- HEATMAP (INTOCADO) ---------------- */}
222+
<div
223+
style={{
224+
position: "absolute",
225+
left: "75%",
226+
top: "50%",
227+
transform: "translate(-50%, 0)",
228+
width: `${HEAT_SIZE}px`,
229+
height: `${HEAT_SIZE / 2}px`,
230+
zIndex: 10,
231+
}}
232+
>
233+
<svg
234+
width={HEAT_SIZE}
235+
height={HEAT_SIZE / 2}
236+
style={{
237+
border: "1.5px solid #333",
238+
borderRadius: "10px",
239+
background: "#fafafa",
240+
boxShadow: "0 4px 10px rgba(0,0,0,0.1)",
241+
}}
242+
>
243+
{dynamicWindow.length > 0 &&
244+
(() => {
245+
const wContainer = HEAT_SIZE;
246+
const hContainer = HEAT_SIZE / 2;
247+
248+
const vValues = dynamicWindow.map(d => d[0]);
249+
const wValues = dynamicWindow.map(d => d[1]);
250+
251+
const minV = Math.min(...vValues);
252+
const maxV = Math.max(...vValues);
253+
const minW = Math.min(...wValues);
254+
const maxW = Math.max(...wValues);
255+
256+
const safeW = (maxW - minW) || 1;
257+
const safeV = (maxV - minV) || 1;
258+
259+
return dynamicWindow.map(([v, w, score], i) => {
260+
const x =
261+
HEAT_MARGIN +
262+
((maxW - w) / safeW) * (wContainer - 2 * HEAT_MARGIN);
263+
264+
const y =
265+
hContainer -
266+
HEAT_MARGIN -
267+
((v - minV) / safeV) * (hContainer - 2 * HEAT_MARGIN);
268+
269+
const r = Math.floor(255 * (1 - score));
270+
const g = Math.floor(255 * score);
271+
272+
return (
273+
<circle
274+
key={i}
275+
cx={x}
276+
cy={y}
277+
r={4}
278+
fill={`rgb(${r},${g},0)`}
279+
/>
280+
);
281+
});
282+
})()}
283+
</svg>
284+
</div>
285+
</div>
286+
);
287+
288+
return (
289+
<WebGUIContainer id="f1-road">
290+
<WindowScene />
136291
</WebGUIContainer>
137292
);
138293
}
139294

140-
export default WebGUI;
295+
export default WebGUI;

0 commit comments

Comments
 (0)