Skip to content

Commit 85d0d57

Browse files
committed
add image viewer in machine vision exercise
1 parent 2e025ac commit 85d0d57

6 files changed

Lines changed: 197 additions & 5 deletions

File tree

database/exercises/db.sql

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,11 @@ COPY public.exercises_tools (id, exercise_id, tool_id) FROM stdin;
289289
72 25 console
290290
73 25 simulator
291291
74 25 rviz
292-
75 26 console
293-
76 26 simulator
294-
77 26 web_gui
295-
78 15 video
292+
75 25 web_gui
293+
76 26 console
294+
77 26 simulator
295+
78 26 web_gui
296+
79 15 video
296297
\.
297298

298299
--
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React, { useState } from "react";
2+
import { useExercise } from "Contexts/ExerciseContext";
3+
import WebGUIContainer, {
4+
connectApplication,
5+
} from "Components/exercise/WebGUIContainer";
6+
import WebGUIImage from "Components/exercise/WebGUIImage";
7+
8+
const WebGUI = () => {
9+
const exerciseContext = useExercise();
10+
const manager = exerciseContext.manager;
11+
12+
const [image, setImage] = useState<string | undefined>(undefined);
13+
14+
const updateCallback = (updateData: unknown) => {
15+
const data = updateData as any;
16+
17+
if (data.update?.image_right) {
18+
const img = JSON.parse(data.update.image_right);
19+
20+
if (img.image_right !== "") {
21+
setImage(`data:image/jpeg;base64,${img.image_right}`);
22+
}
23+
}
24+
};
25+
26+
const stateCallback = () => {};
27+
28+
connectApplication(manager, updateCallback, stateCallback);
29+
30+
return (
31+
<WebGUIContainer>
32+
<WebGUIImage style={{ width: "100%" }} src={image} />
33+
</WebGUIContainer>
34+
);
35+
};
36+
37+
export default WebGUI;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
.overlay {
2+
position: absolute;
3+
width: 100%;
4+
height: 100%;
5+
max-height: 100%;
6+
margin: 0 0 0 0;
7+
background: transparent !important;
8+
z-index: 1;
9+
}
10+
11+
#lap-time {
12+
padding-top: 10px;
13+
padding-right: 10px;
14+
text-align: end;
15+
color: red;
16+
}
17+
18+
#circuit-aerial {
19+
width: 300px;
20+
height: 150px;
21+
top: 16px;
22+
left: 16px;
23+
}
24+
25+
#circuit-img {
26+
width: 100%;
27+
height: 100%;
28+
}
29+
30+
#circuit-car-pos {
31+
position: absolute;
32+
width: 5px;
33+
height: 5px;
34+
border-radius: 5px;
35+
background-color: red;
36+
transition: all 0.1s ease-out;
37+
}
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+
}

exercises/machine_vision/python_template/HAL.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from geometry_msgs.msg import Point, Pose, PoseStamped, Quaternion
4343
from pcl_filter_msgs.msg import ColorFilter, ShapeFilter
4444
from tf_transformations import euler_from_quaternion, quaternion_from_euler
45+
from hal_interfaces.general.camera import CameraNode
4546

4647
# Build PATH and import Python classes from IFRA package:
4748
PATH = os.path.join(get_package_share_directory("ros2srrc_execution"), "python")
@@ -58,11 +59,30 @@
5859
from ros2srrc_data.msg import Action, Joint, Joints, Xyz, Ypr, Robpose
5960

6061
# Global variables
61-
rclpy.init(args=None)
62+
if not rclpy.ok():
63+
rclpy.init(args=None)
6264
UR5 = RBT()
6365
_home_joints = [0.0, -90.0, 0.0, 0.0, -90.0, 0.0]
6466
_perception_manager = None
6567

68+
# Camera node
69+
camera_node = CameraNode("/hand_camera/image_raw")
70+
71+
# Spin nodes so that subscription callbacks load topic data
72+
executor = MultiThreadedExecutor()
73+
executor.add_node(camera_node)
74+
75+
def __auto_spin():
76+
while rclpy.ok():
77+
try:
78+
executor.spin_once(timeout_sec=0)
79+
except Exception:
80+
pass
81+
time.sleep(1/90.0)
82+
83+
executor_thread = threading.Thread(target=__auto_spin, daemon=True)
84+
executor_thread.start()
85+
6686
#################################### UTILITY CLASSES ###################################################
6787

6888

@@ -1039,6 +1059,14 @@ def get_target_position(target_name):
10391059
return perception_mgr.get_target_position(target_name)
10401060

10411061

1062+
def getImage():
1063+
image = camera_node.getImage()
1064+
1065+
while image is None:
1066+
image = camera_node.getImage()
1067+
1068+
return image.data
1069+
10421070
#################################### MAIN FUNCTION ###################################################
10431071

10441072

0 commit comments

Comments
 (0)