Skip to content

Commit b5ddf35

Browse files
Added dummy TableDetection and web app toggling of table detection (#130)
Co-authored-by: Sriram Kutty <sriramku@outlook.com>
1 parent 3d03af5 commit b5ddf35

File tree

5 files changed

+147
-5
lines changed

5 files changed

+147
-5
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env python3
2+
from builtin_interfaces.msg import Time
3+
from geometry_msgs.msg import Pose, PoseStamped, Point, Quaternion
4+
from std_srvs.srv import SetBool
5+
import rclpy
6+
from rclpy.node import Node
7+
from sensor_msgs.msg import CompressedImage
8+
from std_msgs.msg import Header
9+
from threading import Lock
10+
11+
12+
class TableDetectionNode(Node):
13+
DEFAULT_POSE_STAMPED = PoseStamped(
14+
header=Header(
15+
stamp=Time(sec=0, nanosec=0),
16+
frame_id="root",
17+
),
18+
# NOTE: This pose should be the same as the default pose in
19+
# `ada_planning_scene.yaml`
20+
pose=Pose(
21+
position=Point(x=0.08, y=-0.5, z=-0.48),
22+
orientation=Quaternion(x=0.0, y=0.0, z=0.0, w=1.0),
23+
),
24+
)
25+
26+
def __init__(self):
27+
"""
28+
Initializes the TableDetectionNode.
29+
"""
30+
super().__init__("table_detection")
31+
32+
# Keeps track of whether table detection is on or not
33+
self.is_on = False
34+
self.is_on_lock = Lock()
35+
36+
# Create the service
37+
self.srv = self.create_service(
38+
SetBool,
39+
"toggle_table_detection",
40+
self.toggle_table_detection_callback,
41+
)
42+
43+
# Subscribe to the camera feed
44+
self.subscription = self.create_subscription(
45+
CompressedImage,
46+
"camera/color/image_raw/compressed",
47+
self.camera_callback,
48+
1,
49+
)
50+
51+
# Create the publishers
52+
self.publisher_results = self.create_publisher(
53+
PoseStamped, "table_detection", 1
54+
)
55+
56+
def toggle_table_detection_callback(self, request, response):
57+
"""
58+
Callback function for the SetBool service. Safely toggles
59+
the table detection on or off depending on the request.
60+
"""
61+
self.get_logger().info("Incoming service request. turn_on: %s" % (request.data))
62+
if request.data:
63+
# Turn on table detection
64+
self.is_on_lock.acquire()
65+
self.is_on = True
66+
self.is_on_lock.release()
67+
response.success = True
68+
response.message = "Succesfully turned table detection on"
69+
else:
70+
self.is_on_lock.acquire()
71+
self.is_on = False
72+
self.is_on_lock.release()
73+
response.success = True
74+
response.message = "Succesfully turned table detection off"
75+
return response
76+
77+
def camera_callback(self, msg):
78+
"""
79+
Callback function for the camera feed. If table detection is on, this
80+
function will detect the table in the image and publish information about
81+
them to the /table_detection topic.
82+
"""
83+
self.get_logger().debug("Received image")
84+
self.is_on_lock.acquire()
85+
is_on = self.is_on
86+
self.is_on_lock.release()
87+
if is_on:
88+
TableDetectionNode.DEFAULT_POSE_STAMPED.header.stamp = msg.header.stamp
89+
self.publisher_results.publish(TableDetectionNode.DEFAULT_POSE_STAMPED)
90+
91+
92+
def main(args=None):
93+
rclpy.init(args=args)
94+
95+
table_detection = TableDetectionNode()
96+
97+
rclpy.spin(table_detection)
98+
99+
rclpy.shutdown()
100+
101+
102+
if __name__ == "__main__":
103+
main()

feeding_web_app_ros2_test/launch/feeding_web_app_dummy_nodes_launch.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<arg name="run_food_detection" default="true" description="Whether to run the dummy food detectoion node" />
66
<arg name="run_face_detection" default="true" description="Whether to run the dummy face detection node" />
77
<arg name="run_food_on_fork_detection" default="true" description="Whether to run the dummy food-on-fork detection node" />
8+
<arg name="run_table_detection" default="true" description="Whether to run the dummy table detection node" />
89
<arg name="run_real_sense" default="true" description="Whether to run the dummy RealSense node" />
910
<arg name="run_motion" default="true" description="Whether to run the dummy motion nodes" />
1011
<arg name="rgb_path" default="above_plate_2_rgb.jpg" description="The path to the RGB image/video to publish from the dummy node, relative to this node's share/data folder." />
@@ -45,6 +46,11 @@
4546
<!-- Perception: The FoodOnForkDetection node -->
4647
<node pkg="feeding_web_app_ros2_test" exec="FoodOnForkDetection" name="FoodOnForkDetection"/>
4748
</group>
49+
50+
<group if="$(var run_table_detection)">
51+
<!-- Perception: The TableDetection node -->
52+
<node pkg="feeding_web_app_ros2_test" exec="TableDetection" name="TableDetection"/>
53+
</group>
4854

4955
<group if="$(var run_motion)">
5056
<!-- Motion: The MoveAbovePlate action -->

feeding_web_app_ros2_test/setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"MoveFromMouthToRestingPosition = feeding_web_app_ros2_test.MoveFromMouthToRestingPosition:main",
4848
"MoveToStowLocation = feeding_web_app_ros2_test.MoveToStowLocation:main",
4949
"SegmentFromPoint = feeding_web_app_ros2_test.SegmentFromPoint:main",
50+
"TableDetection = feeding_web_app_ros2_test.TableDetection:main",
5051
# Scripts for the "TestROS" component
5152
"listener = feeding_web_app_ros2_test.subscriber:main",
5253
"reverse_string = feeding_web_app_ros2_test.reverse_string_service:main",

feedingwebapp/src/Pages/Constants.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ export const START_JOINT_CONTROLLER_ACTION_TYPE = 'ada_feeding_msgs/action/Trigg
9797
* the service name and the message type
9898
*/
9999
let ROS_SERVICE_NAMES = {}
100+
ROS_SERVICE_NAMES[MEAL_STATE.U_BiteSelection] = {
101+
serviceName: 'toggle_table_detection',
102+
messageType: 'std_srvs/srv/SetBool'
103+
}
100104
ROS_SERVICE_NAMES[MEAL_STATE.R_DetectingFace] = {
101105
serviceName: 'toggle_face_detection',
102106
messageType: 'std_srvs/srv/SetBool'

feedingwebapp/src/Pages/Home/MealStates/BiteSelection.jsx

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ import PropTypes from 'prop-types'
99

1010
// Local Imports
1111
import '../Home.css'
12-
import { useROS, createROSActionClient, callROSAction, destroyActionClient } from '../../../ros/ros_helpers'
12+
import {
13+
useROS,
14+
createROSActionClient,
15+
callROSAction,
16+
createROSService,
17+
createROSServiceRequest,
18+
destroyActionClient
19+
} from '../../../ros/ros_helpers'
1320
import { useWindowSize, convertRemToPixels } from '../../../helpers'
1421
import MaskButton from '../../../buttons/MaskButton'
1522
import {
@@ -19,6 +26,7 @@ import {
1926
ROS_ACTION_STATUS_SUCCEED,
2027
ROS_ACTION_STATUS_ABORT,
2128
ROS_ACTION_STATUS_CANCELED,
29+
ROS_SERVICE_NAMES,
2230
SEGMENTATION_STATUS_SUCCESS,
2331
MOVING_STATE_ICON_DICT
2432
} from '../../Constants'
@@ -82,8 +90,15 @@ const BiteSelection = (props) => {
8290
* Create the ROS Action Client. This is created in useRef to avoid
8391
* re-creating it upon re-renders.
8492
*/
85-
let { actionName, messageType } = ROS_ACTIONS_NAMES[MEAL_STATE.U_BiteSelection]
86-
let segmentFromPointAction = useRef(createROSActionClient(ros.current, actionName, messageType))
93+
let actionDetails = ROS_ACTIONS_NAMES[MEAL_STATE.U_BiteSelection]
94+
let segmentFromPointAction = useRef(createROSActionClient(ros.current, actionDetails.actionName, actionDetails.messageType))
95+
96+
/**
97+
* Create the ROS Service to toggle on/off table detection. This is created in
98+
* local state to avoid re-creating it upon every re-render.
99+
*/
100+
let serviceDetails = ROS_SERVICE_NAMES[MEAL_STATE.U_BiteSelection]
101+
let toggleTableDetectionService = useRef(createROSService(ros.current, serviceDetails.serviceName, serviceDetails.messageType))
87102

88103
/**
89104
* Callback function for when the user indicates that they are done with their
@@ -212,14 +227,27 @@ const BiteSelection = (props) => {
212227
)
213228

214229
/**
215-
* Cancel any running actions when the component unmounts
230+
* Cancel any running actions when the component unmounts. Also, toggle on table
231+
* detection when the component first mounts and toggle it off when the component
232+
* unmounts.
216233
*/
217234
useEffect(() => {
235+
// Create a service request
236+
let request = createROSServiceRequest({ data: true })
237+
// Call the service
238+
let service = toggleTableDetectionService.current
239+
service.callService(request, (response) => console.log('Got toggle table detection service response', response))
240+
218241
let action = segmentFromPointAction.current
219242
return () => {
243+
// Create a service request
244+
let request = createROSServiceRequest({ data: false })
245+
// Call the service
246+
service.callService(request, (response) => console.log('Got toggle face detection service response', response))
247+
// Destroy the action client
220248
destroyActionClient(action)
221249
}
222-
}, [segmentFromPointAction])
250+
}, [segmentFromPointAction, toggleTableDetectionService])
223251

224252
/** Get the continue button when debug mode is enabled
225253
*

0 commit comments

Comments
 (0)