Skip to content

Commit 09214dc

Browse files
authored
Merge pull request #3949 from JdeRobot/drone-hangar-fix
Drone hangar fix
2 parents 4bbb910 + 2b0c447 commit 09214dc

9 files changed

Lines changed: 565 additions & 0 deletions

File tree

database/exercises/db.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ COPY public.exercises (id, exercise_id, name, description, tags, status, url) FR
140140
27 conveyor_exercise Conveyor Belt Exercise Control a conveyor belt with ROS2 ["ROS2","INDUSTRIAL"] PROTOTYPE https://jderobot.github.io/RoboticsAcademy/exercises/IndustrialRobots/conveyor_exercise
141141
28 drone_cat_mouse Drone Cat Mouse Two-drone chase exercise: program the cat drone to catch the mouse drone ["ROS2","Drones","MULTI-AGENT","MULTI-ENTRYPOINT"] ACTIVE https://jderobot.github.io/RoboticsAcademy/exercises/Drones/drone_cat_mouse
142142
29 package_delivery Package Delivery Drone Package Delivery exercise ["ROS2","Drones"] ACTIVE https://jderobot.github.io/RoboticsAcademy/exercises/Drones/package_delivery
143+
30 drone_hangar Drone Hangar Escape a hangar filled with obstacles flying a drone ["ROS2","Drones"] ACTIVE https://jderobot.github.io/RoboticsAcademy/exercises/Drones/drone_hangar
143144
\.
144145

145146
--
@@ -197,6 +198,11 @@ COPY public.exercises_worlds (id, exercise_id, world_id, is_default) FROM stdin;
197198
71 27 71 True
198199
72 28 72 True
199200
73 29 57 True
201+
74 30 73 True
202+
75 30 74 False
203+
76 30 75 False
204+
77 30 76 False
205+
78 30 77 False
200206
\.
201207

202208
--
@@ -296,6 +302,9 @@ COPY public.exercises_tools (id, exercise_id, tool_id) FROM stdin;
296302
89 29 console
297303
90 29 simulator
298304
91 29 web_gui
305+
92 30 console
306+
93 30 simulator
307+
94 30 web_gui
299308
\.
300309

301310
--
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { useState, useEffect } from "react";
2+
import { states } from "jderobot-commsmanager";
3+
import { useExercise } from "Contexts/ExerciseContext";
4+
import WebGUIImage from "Components/exercise/WebGUIImage";
5+
import WebGUIContainer, {
6+
connectApplication,
7+
} from "Components/exercise/WebGUIContainer";
8+
9+
const WebGUI = () => {
10+
const exerciseContext = useExercise();
11+
const [rightImage, setRightImage] = useState<string | undefined>(undefined);
12+
const [leftImage, setLeftImage] = useState<string | undefined>(undefined);
13+
const [manager, setManager] = useState(exerciseContext.manager);
14+
15+
useEffect(() => {
16+
setManager(exerciseContext.manager);
17+
}, [exerciseContext]);
18+
19+
const updateCallback = (updateData: unknown) => {
20+
const data = updateData as any;
21+
const update = data.update;
22+
let image;
23+
if (update.image_right) {
24+
image = JSON.parse(update.image_right);
25+
if (image.image_right != "" && image.shape_right instanceof Array) {
26+
setRightImage(`data:image/png;base64,${image.image_right}`);
27+
}
28+
}
29+
if (update.image_left) {
30+
image = JSON.parse(update.image_left);
31+
if (image.image_left != "" && image.shape_left instanceof Array) {
32+
setLeftImage(`data:image/png;base64,${image.image_left}`);
33+
}
34+
}
35+
};
36+
37+
const stateCallback = (state: string) => {
38+
if (state === states.TOOLS_READY) {
39+
setLeftImage(undefined);
40+
setRightImage(undefined);
41+
}
42+
};
43+
44+
connectApplication(manager, updateCallback, stateCallback);
45+
46+
return (
47+
<WebGUIContainer>
48+
<WebGUIImage id="left_img" style={{ left: "0" }} src={leftImage} />
49+
<WebGUIImage id="right_img" style={{ left: "50%" }} src={rightImage} />
50+
</WebGUIContainer>
51+
);
52+
};
53+
54+
export default WebGUI;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import js from "@eslint/js";
2+
import globals from "globals";
3+
import tseslint from "typescript-eslint";
4+
import pluginReact from "eslint-plugin-react";
5+
import { defineConfig } from "eslint/config";
6+
7+
export default defineConfig([
8+
{
9+
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
10+
plugins: { js },
11+
extends: ["js/recommended"],
12+
languageOptions: {
13+
globals: globals.browser,
14+
parserOptions: {
15+
project: './tsconfig.json',
16+
tsconfigRootDir: import.meta.dirname
17+
},
18+
},
19+
},
20+
tseslint.configs.recommended,
21+
pluginReact.configs.flat.recommended,
22+
{
23+
settings: {
24+
react: {
25+
version: "detect",
26+
},
27+
},
28+
},
29+
]);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"lib": [
5+
"dom",
6+
"dom.iterable",
7+
"esnext"
8+
],
9+
"types": [
10+
"../../../react_frontend/svg.d.ts",
11+
"../../../react_frontend/png.d.ts",
12+
"../../../react_frontend/jpg.d.ts",
13+
"../../../react_frontend/zip.d.ts"
14+
],
15+
"paths": {
16+
"Assets/*": [ "../../../react_frontend/src/assets/*"],
17+
"Components/*": [ "../../../react_frontend/src/components/*"],
18+
"Utils/*": [ "../../../react_frontend/src/utils/*"],
19+
"Contexts/*": [ "../../../react_frontend/src/contexts/*"],
20+
"Helpers/*": [ "../../../react_frontend/src/helpers/*"],
21+
"Icons/*": [ "../../../react_frontend/src/icons/*"],
22+
"Styles/*": [ "../../../react_frontend/src/styles/*"],
23+
"Types/*": [ "../../../react_frontend/src/types/*"],
24+
"Constants/*": [ "../../../react_frontend/src/constants/*"],
25+
"Api": [ "../../../react_frontend/src/api/index.ts"],
26+
"Routes": [ "../../../react_frontend/src/routes/index.ts"],
27+
"*": [ "../../../react_frontend/node_modules/*"],
28+
},
29+
"baseUrl": "./",
30+
"rootDirs": [
31+
"./",
32+
"../../../react_frontend/src"
33+
],
34+
"typeRoots": ["../../../react_frontend/node_modules/@types"],
35+
"declaration": true,
36+
"allowJs": true,
37+
"skipLibCheck": true,
38+
"esModuleInterop": true,
39+
"allowSyntheticDefaultImports": true,
40+
"strict": true,
41+
"noImplicitAny": true,
42+
"forceConsistentCasingInFileNames": true,
43+
"noFallthroughCasesInSwitch": true,
44+
"module": "esnext",
45+
"moduleResolution": "node",
46+
"resolveJsonModule": true,
47+
"isolatedModules": true,
48+
"noEmit": true,
49+
"jsx": "react-jsx"
50+
},
51+
"include": [
52+
"./",
53+
"eslint.config.mts",
54+
"../../../react_frontend/src",
55+
"../../../react_frontend/png.d.ts",
56+
"../../../react_frontend/jpg.d.ts",
57+
"../../../react_frontend/svg.d.ts",
58+
"../../../react_frontend/zip.d.ts"
59+
]
60+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import time
2+
from datetime import datetime
3+
4+
start_time_internal_freq_control = None
5+
6+
7+
def tick(ideal_cycle: int = 50):
8+
global start_time_internal_freq_control
9+
finish_time = datetime.now()
10+
ideal_ms = 1000 / ideal_cycle
11+
12+
if start_time_internal_freq_control is None:
13+
start_time_internal_freq_control = finish_time
14+
return
15+
16+
dt = finish_time - start_time_internal_freq_control
17+
ms = (dt.days * 24 * 60 * 60 + dt.seconds) * 1000 + dt.microseconds / 1000.0
18+
19+
if ms < ideal_ms:
20+
time.sleep((ideal_ms - ms) / 1000.0)
21+
22+
start_time_internal_freq_control = finish_time
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import numpy as np
2+
import rclpy
3+
import threading
4+
import time
5+
import sys
6+
7+
from hal_interfaces.general.camera import CameraNode
8+
from jderobot_drones.drone_wrapper import DroneWrapper
9+
10+
IMG_WIDTH = 320
11+
IMG_HEIGHT = 240
12+
freq = 30.0
13+
14+
15+
# Mutes exceptions
16+
def custom_thread_excepthook(args):
17+
if "spin" in args.thread.name:
18+
return
19+
sys.__excepthook__(args.exc_type, args.exc_value, args.exc_traceback)
20+
21+
22+
threading.excepthook = custom_thread_excepthook
23+
24+
### HAL INIT ###
25+
26+
print("HAL initializing", flush=True)
27+
if not rclpy.ok():
28+
rclpy.init()
29+
30+
31+
CAM_FRONTAL_TOPIC = "/" + "drone" + "/frontal_cam/image_raw"
32+
CAM_VENTRAL_TOPIC = "/" + "drone" + "/ventral_cam/image_raw"
33+
34+
drone = DroneWrapper("drone")
35+
frontal_camera_node = CameraNode(CAM_FRONTAL_TOPIC)
36+
ventral_camera_node = CameraNode(CAM_VENTRAL_TOPIC)
37+
38+
# Spin nodes so that subscription callbacks load topic data
39+
executor = rclpy.executors.MultiThreadedExecutor()
40+
executor.add_node(frontal_camera_node)
41+
executor.add_node(ventral_camera_node)
42+
43+
44+
def __auto_spin() -> None:
45+
while rclpy.ok():
46+
try:
47+
executor.spin_once(timeout_sec=0)
48+
except Exception:
49+
pass
50+
time.sleep(1 / freq)
51+
52+
53+
executor_thread = threading.Thread(target=__auto_spin, daemon=True)
54+
executor_thread.start()
55+
56+
### GETTERS ###
57+
58+
59+
def get_frontal_image():
60+
image = frontal_camera_node.getImage()
61+
while image is None:
62+
image = frontal_camera_node.getImage()
63+
return image.data
64+
65+
66+
def get_ventral_image():
67+
image = ventral_camera_node.getImage()
68+
while image is None:
69+
image = ventral_camera_node.getImage()
70+
return image.data
71+
72+
73+
def get_position():
74+
pos = drone.get_position()
75+
return pos
76+
77+
78+
def get_velocity():
79+
vel = drone.get_velocity()
80+
return vel
81+
82+
83+
def get_yaw_rate():
84+
yaw_rate = drone.get_yaw_rate()
85+
return yaw_rate
86+
87+
88+
def get_orientation():
89+
orientation = drone.get_orientation()
90+
return orientation
91+
92+
93+
def get_roll():
94+
roll = drone.get_roll()
95+
return roll
96+
97+
98+
def get_pitch():
99+
pitch = drone.get_pitch()
100+
return pitch
101+
102+
103+
def get_yaw():
104+
yaw = drone.get_yaw()
105+
return yaw
106+
107+
108+
def get_landed_state():
109+
state = drone.get_landed_state()
110+
return state
111+
112+
113+
### SETTERS ###
114+
115+
116+
def set_cmd_pos(x, y, z, az):
117+
drone.set_cmd_pos(x, y, z, az)
118+
119+
120+
def set_cmd_vel(vx, vy, vz, az):
121+
drone.set_cmd_vel(vx, vy, vz, az)
122+
123+
124+
def set_cmd_mix(vx, vy, z, az):
125+
drone.set_cmd_mix(vx, vy, z, az)
126+
127+
128+
def takeoff(h=3):
129+
drone.takeoff(h)
130+
131+
132+
def land():
133+
drone.land()

0 commit comments

Comments
 (0)