Skip to content

Commit

Permalink
Allow users to set multiple parameter presets (#131)
Browse files Browse the repository at this point in the history
* Allow users to change the parameter preset from the Main Settings menu

* Change default setting of dummy face detection for easier testing

* Finalized the feature

* Updates from testing
  • Loading branch information
amalnanavati authored Apr 9, 2024
1 parent 12f2904 commit fcbbb18
Show file tree
Hide file tree
Showing 5 changed files with 358 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
class FaceDetectionNode(Node):
def __init__(
self,
face_detection_interval=90,
num_images_with_face=60,
open_mouth_interval=90,
num_images_with_open_mouth=30,
face_detection_interval=45,
num_images_with_face=30,
open_mouth_interval=45,
num_images_with_open_mouth=15,
):
"""
Initializes the FaceDetection node, which exposes a SetBool
Expand Down
3 changes: 3 additions & 0 deletions feedingwebapp/src/Pages/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ export const GET_PARAMETERS_SERVICE_TYPE = 'rcl_interfaces/srv/GetParameters'
export const SET_PARAMETERS_SERVICE_NAME = 'ada_feeding_action_servers/set_parameters'
export const SET_PARAMETERS_SERVICE_TYPE = 'rcl_interfaces/srv/SetParameters'

// The names of parameters users can change in the settings menu
export const DISTANCE_TO_MOUTH_PARAM = 'MoveToMouth.tree_kwargs.plan_distance_from_mouth'

/**
* The meaning of the status that motion actions return in their results.
* These should match the action definition(s).
Expand Down
8 changes: 8 additions & 0 deletions feedingwebapp/src/Pages/GlobalState.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export const SETTINGS_STATE = {
BITE_TRANSFER: 'BITE_TRANSFER'
}

// The name of the default parameter namespace
export const DEFAULT_NAMESPACE = 'default'

/**
* useGlobalState is a hook to store and manipulate web app state that we want
* to persist across re-renders and refreshes. It won't persist if cookies are
Expand All @@ -93,6 +96,7 @@ export const useGlobalState = create(
mealStateTransitionTime: Date.now(),
// The currently displayed settings page
settingsState: SETTINGS_STATE.MAIN,
settingsPresets: { current: DEFAULT_NAMESPACE, customNames: [] },
// The goal for the bite acquisition action, including the most recent
// food item that the user selected in "bite selection"
biteAcquisitionActionGoal: null,
Expand Down Expand Up @@ -156,6 +160,10 @@ export const useGlobalState = create(
set(() => ({
settingsState: settingsState
})),
setSettingsPresets: (settingsPresets) =>
set(() => ({
settingsPresets: settingsPresets
})),
setBiteAcquisitionActionGoal: (biteAcquisitionActionGoal) =>
set(() => ({
biteAcquisitionActionGoal: biteAcquisitionActionGoal
Expand Down
117 changes: 85 additions & 32 deletions feedingwebapp/src/Pages/Settings/BiteTransfer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import PropTypes from 'prop-types'
import { useId, Label, SpinButton } from '@fluentui/react-components'
import Button from 'react-bootstrap/Button'
import Dropdown from 'react-bootstrap/Dropdown'
import SplitButton from 'react-bootstrap/SplitButton'
// The Modal is a screen that appears on top of the main app, and can be toggled
// on and off.
import Modal from 'react-bootstrap/Modal'
Expand All @@ -14,9 +16,10 @@ import {
GET_PARAMETERS_SERVICE_NAME,
GET_PARAMETERS_SERVICE_TYPE,
SET_PARAMETERS_SERVICE_NAME,
SET_PARAMETERS_SERVICE_TYPE
SET_PARAMETERS_SERVICE_TYPE,
DISTANCE_TO_MOUTH_PARAM
} from '../Constants'
import { useGlobalState, MEAL_STATE, SETTINGS_STATE } from '../GlobalState'
import { useGlobalState, MEAL_STATE, SETTINGS_STATE, DEFAULT_NAMESPACE } from '../GlobalState'
import RobotMotion from '../Home/MealStates/RobotMotion'
import DetectingFaceSubcomponent from '../Home/MealStates/DetectingFaceSubcomponent'

Expand All @@ -26,6 +29,7 @@ import DetectingFaceSubcomponent from '../Home/MealStates/DetectingFaceSubcompon
*/
const BiteTransfer = (props) => {
// Get relevant global state variables
const settingsPresets = useGlobalState((state) => state.settingsPresets)
const setSettingsState = useGlobalState((state) => state.setSettingsState)
const globalMealState = useGlobalState((state) => state.mealState)
const setPaused = useGlobalState((state) => state.setPaused)
Expand Down Expand Up @@ -130,27 +134,27 @@ const BiteTransfer = (props) => {
let service = getParametersService.current
// First, attempt to get the current distance to mouth
let currentRequest = createROSServiceRequest({
names: ['current.MoveToMouth.tree_kwargs.plan_distance_from_mouth']
names: [settingsPresets.current.concat('.', DISTANCE_TO_MOUTH_PARAM)]
})
service.callService(currentRequest, (response) => {
console.log('Got current plan_distance_from_mouth response', response)
if (response.values[0].type === 0) {
if (response.values.length === 0 || response.values[0].type === 0) {
// Parameter not set
// Second, attempt to get the default distance to mouth
let defaultRequest = createROSServiceRequest({
names: ['default.MoveToMouth.tree_kwargs.plan_distance_from_mouth']
names: [DEFAULT_NAMESPACE.concat('.', DISTANCE_TO_MOUTH_PARAM)]
})
service.callService(defaultRequest, (response) => {
console.log('Got default plan_distance_from_mouth response', response)
if (response.values.length > 0) {
if (response.values.length > 0 && response.values[0].type === 8) {
setCurrentDistanceToMouth(getParameterValue(response.values[0]))
}
})
} else {
setCurrentDistanceToMouth(getParameterValue(response.values[0]))
}
})
}, [getParametersService, setCurrentDistanceToMouth, setDoneButtonIsClicked, setPaused])
}, [getParametersService, setCurrentDistanceToMouth, setDoneButtonIsClicked, setPaused, settingsPresets])

// Callback to set the distance to mouth parameter
const setDistanceToMouth = useCallback(
Expand All @@ -159,7 +163,7 @@ const BiteTransfer = (props) => {
let request = createROSServiceRequest({
parameters: [
{
name: 'current.MoveToMouth.tree_kwargs.plan_distance_from_mouth',
name: settingsPresets.current.concat('.', DISTANCE_TO_MOUTH_PARAM),
value: {
type: 8, // double array
double_array_value: fullDistanceToMouth
Expand All @@ -174,23 +178,29 @@ const BiteTransfer = (props) => {
}
})
},
[setParametersService, setCurrentDistanceToMouth]
[setParametersService, setCurrentDistanceToMouth, settingsPresets]
)

// Callback to restore the distance to mouth to the default
const restoreToDefaultButtonClicked = useCallback(() => {
let service = getParametersService.current
// Attempt to get the default distance to mouth
let defaultRequest = createROSServiceRequest({
names: ['default.MoveToMouth.tree_kwargs.plan_distance_from_mouth']
})
service.callService(defaultRequest, (response) => {
console.log('Got default plan_distance_from_mouth response', response)
if (response.values.length > 0) {
setDistanceToMouth(getParameterValue(response.values[0]))
}
})
}, [getParametersService, setDistanceToMouth])
const restoreToPreset = useCallback(
(preset) => {
console.log('restoreToPreset called with', preset)
let service = getParametersService.current
// Attempt to get the default distance to mouth
let defaultRequest = createROSServiceRequest({
names: [preset.concat('.', DISTANCE_TO_MOUTH_PARAM)]
})
service.callService(defaultRequest, (response) => {
console.log('Got plan_distance_from_mouth response', response)
if (response.values.length > 0 && response.values[0].type === 8) {
setDistanceToMouth(getParameterValue(response.values[0]))
} else {
restoreToPreset(DEFAULT_NAMESPACE)
}
})
},
[getParametersService, setDistanceToMouth]
)

// Callback to move the robot to the mouth
const moveToMouthButtonClicked = useCallback(() => {
Expand Down Expand Up @@ -275,7 +285,8 @@ const BiteTransfer = (props) => {
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
width: '100%'
width: '100%',
zIndex: 1
}}
>
<Label
Expand Down Expand Up @@ -306,7 +317,7 @@ const BiteTransfer = (props) => {
size: 'large'
}}
/>
<Button
<SplitButton
variant='warning'
className='mx-2 mb-2 btn-huge'
size='lg'
Expand All @@ -315,10 +326,17 @@ const BiteTransfer = (props) => {
width: '60%',
color: 'black'
}}
onClick={restoreToDefaultButtonClicked}
title={'Set to '.concat(DEFAULT_NAMESPACE)}
onClick={() => restoreToPreset(DEFAULT_NAMESPACE)}
>
Set to Default
</Button>
{settingsPresets.customNames
.filter((x) => x !== settingsPresets.current)
.map((preset) => (
<Dropdown.Item key={preset} onClick={() => restoreToPreset(preset)}>
Set to {preset}
</Dropdown.Item>
))}
</SplitButton>
</View>
<View
style={{
Expand Down Expand Up @@ -396,7 +414,8 @@ const BiteTransfer = (props) => {
distanceToMouthId,
moveToMouthButtonClicked,
moveAwayFromMouthButtonClicked,
restoreToDefaultButtonClicked
restoreToPreset,
settingsPresets
])

// When a face is detected, switch to MoveToMouth
Expand Down Expand Up @@ -435,18 +454,52 @@ const BiteTransfer = (props) => {
<>
<View
style={{
flex: 2,
flex: 4,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
width: '100%'
}}
>
<h5 style={{ textAlign: 'center', fontSize: textFontSize }}>Customize Bite Transfer</h5>
<p style={{ textAlign: 'center', fontSize: textFontSize, margin: 0 }} className='txt-huge'>
Bite Transfer Settings
</p>
</View>
<View
style={{
flex: 3,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
width: '100%'
}}
>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'end',
width: '100%'
}}
>
<p style={{ fontSize: textFontSize, textAlign: 'right', margin: '0rem' }}>Preset:</p>
</View>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'start',
width: '100%'
}}
>
<Button variant='secondary' disabled size='lg' style={{ marginLeft: '1rem' }}>
{settingsPresets.current}
</Button>
</View>
</View>
<View
style={{
flex: 16,
flex: 32,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
Expand All @@ -457,7 +510,7 @@ const BiteTransfer = (props) => {
</View>
<View
style={{
flex: 2,
flex: 4,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
Expand Down
Loading

0 comments on commit fcbbb18

Please sign in to comment.