precision camera stuff

This commit is contained in:
legop3
2026-06-29 21:06:21 -04:00
parent 79be3e1583
commit 0cc40b7608
12 changed files with 129 additions and 28 deletions
+12
View File
@@ -49,6 +49,7 @@ const CONTROL_ACTION_NAMES = [
'setServoAngle',
'nudgeServo',
'goServoHome',
'setCameraPrecisionMode',
'runMacro',
'stopAllMotion',
'sendOiCommand',
@@ -411,6 +412,15 @@ export function ControlSystemProvider({ children }) {
setServoAngle(target);
}, [pipeline.servoConfig, setServoAngle]);
const setCameraPrecisionMode = useCallback((active) => {
/*
This action only changes how the browser chooses servo increments. The Pi
already accepts decimal angle targets, so no server or rover command shape
changes are needed for precision camera mode.
*/
dispatch({ type: 'control/set-camera-precision-mode', payload: Boolean(active) });
}, []);
const runMacro = useCallback(
async (macroId) => {
const macro = state.macros.find((item) => item.id === macroId) || null;
@@ -675,6 +685,7 @@ export function ControlSystemProvider({ children }) {
setServoAngle,
nudgeServo,
goServoHome,
setCameraPrecisionMode,
runMacro,
stopAllMotion,
sendOiCommand,
@@ -701,6 +712,7 @@ export function ControlSystemProvider({ children }) {
setServoAngle,
nudgeServo,
goServoHome,
setCameraPrecisionMode,
runMacro,
stopAllMotion,
sendOiCommand,
+23
View File
@@ -20,6 +20,12 @@ function createCameraState() {
enabled: false,
angle: null,
config: null,
/*
Servo precision is UI/control state, not rover hardware state. Keeping it
beside the camera angle lets every camera-tilt surface use the same
precision setting while still sending the normal decimal angle commands.
*/
precisionMode: false,
};
}
@@ -110,6 +116,7 @@ export function controlReducer(state, action) {
...state.camera,
enabled: Boolean(action.payload?.config),
config: action.payload?.config ?? null,
precisionMode: action.payload?.config ? state.camera.precisionMode : false,
angle:
typeof action.payload?.angle === 'number'
? action.payload.angle
@@ -123,6 +130,22 @@ export function controlReducer(state, action) {
...state,
camera: { ...state.camera, angle: action.payload },
};
case 'control/set-camera-precision-mode':
if (state.camera.precisionMode === Boolean(action.payload)) {
return state;
}
return {
...state,
camera: {
...state.camera,
/*
Movement inputs own this flag because precision camera mode is meant
to follow movement precision mode automatically instead of becoming a
separate toggle the driver has to remember to reset.
*/
precisionMode: Boolean(action.payload),
},
};
case 'control/register-input-state': {
const sourceKey = action.payload?.source || 'unknown';
return {
@@ -21,6 +21,7 @@ import {
computeKeyboardAuxMotors,
computeKeyboardDriveVector,
getKeyboardDriveSpeedOptions,
isPrecisionDriveActive,
resolveKeyboardSpeeds,
} from './driveIntent.js';
import { trackAnalyticsEvent } from '../../analytics/index.js';
@@ -34,6 +35,7 @@ const TILT_INTERVAL_MIN = 5;
const TILT_INTERVAL_MAX = 500;
const TILT_SPEED_MIN = 1;
const TILT_SPEED_MAX = 100;
const PRECISION_SERVO_NUDGE_DEGREES = 0.25;
const VIDEO_FILTER_SEQUENCE = ['none', 'grayscale', 'greenscale'];
function normalizeVideoFilter(value) {
@@ -91,6 +93,7 @@ export default function KeyboardInputManager() {
runMacro,
stopAllMotion,
registerInputState,
setCameraPrecisionMode,
toggleHeadlight,
toggleLaser,
startHorn,
@@ -157,9 +160,16 @@ export default function KeyboardInputManager() {
const latest = latestRef.current;
if (!latest) return;
const tokensSnapshot = new Set(activeTokensRef.current);
const precisionActive = isPrecisionDriveActive(tokensSnapshot, latest.keymap);
const speedOptions = getKeyboardDriveSpeedOptions(tokensSnapshot, latest.keymap, latest.keyboardSpeeds);
const vector = computeKeyboardDriveVector(tokensSnapshot, latest.keymap);
const aux = computeKeyboardAuxMotors(tokensSnapshot, latest.keymap);
/*
Keyboard precision is a held modifier, so publish the camera precision
state from the same token snapshot that drives movement. This keeps the
servo UI in lockstep with the actual precision-driving intent.
*/
latest.setCameraPrecisionMode(precisionActive);
if (
vector.x !== lastVectorRef.current.x ||
vector.y !== lastVectorRef.current.y ||
@@ -219,7 +229,10 @@ export default function KeyboardInputManager() {
stopServoLoop();
return;
}
latest.nudgeServo(nextDirection * latest.servoStep);
const tokensSnapshot = new Set(activeTokensRef.current);
const precisionActive = isPrecisionDriveActive(tokensSnapshot, latest.keymap);
const servoStep = precisionActive ? PRECISION_SERVO_NUDGE_DEGREES : latest.servoStep;
latest.nudgeServo(nextDirection * servoStep);
servoIntervalRef.current = setTimeout(tick, latest.servoRepeatMs);
};
servoIntervalRef.current = setTimeout(tick, 0);
@@ -288,6 +301,7 @@ export default function KeyboardInputManager() {
latest?.stopHorn();
}
latest?.setMicPttActive(false);
latest?.setCameraPrecisionMode(false);
latest?.stopAllMotion();
latest?.registerInputState(SOURCE, { keys: [], vector: ZERO_VECTOR, aux: ZERO_AUX });
}, [stopServoLoop, stopSongLoop]);
@@ -369,6 +383,7 @@ export default function KeyboardInputManager() {
servoRepeatMs,
servoStep,
setAuxMotors,
setCameraPrecisionMode,
setDriveVector,
setMicPttActive,
setMode,
+11 -1
View File
@@ -54,6 +54,16 @@ export function computeKeyboardDriveVector(keys, keymap) {
};
}
export function isPrecisionDriveActive(keys, keymap) {
/*
The slow modifier is the canonical precision-drive signal shared by physical
keyboard input and the mobile pad's virtual-key path. Centralizing the check
keeps camera precision coupled to the same driver intent instead of copying
modifier-specific knowledge into each caller.
*/
return bindingActive(keymap?.slowModifier, keys);
}
export function computeKeyboardAuxMotors(keys, keymap) {
const allForward = bindingActive(keymap.auxAllForward, keys);
if (allForward) {
@@ -78,7 +88,7 @@ export function computeKeyboardAuxMotors(keys, keymap) {
}
export function getKeyboardDriveSpeedOptions(keys, keymap, keyboardSpeeds) {
const slowActive = bindingActive(keymap.slowModifier, keys);
const slowActive = isPrecisionDriveActive(keys, keymap);
return slowActive
? { baseSpeed: keyboardSpeeds.precisionSpeed, boostSpeed: keyboardSpeeds.precisionSpeed }
: { baseSpeed: keyboardSpeeds.baseSpeed, boostSpeed: keyboardSpeeds.turboSpeed };