From cd9e82938b7c2cf9f0fd18441a7d0c95c772d488 Mon Sep 17 00:00:00 2001 From: legop3 Date: Sun, 21 Dec 2025 03:55:55 -0500 Subject: [PATCH] faster sensors --- dist/roverd | Bin 6815928 -> 6815928 bytes dist/servoverifier | Bin 1769656 -> 1769656 bytes pi/roverd/sensor_stream.go | 2 +- webui/src/components/TelemetryPanel.jsx | 393 +++++++++++++++++++++++- 4 files changed, 380 insertions(+), 15 deletions(-) diff --git a/dist/roverd b/dist/roverd index 850b7117422a35623110be9351ae336c1e684da0..e888869c918a6ff465d8c848ed981316947a3c8e 100755 GIT binary patch delta 619 zcmbWxOLI#B0LAg>ZP6k*s4D8!c(f#%+MdLFs*Sy)f>*gNKyH7JytAXx7AkZ&{q_c%U zuhV$esDGRI8twVL`Q}IHw z9%3)iPwXQGi2cMMaez2T93qB@!^9EdC~=H9P7D)a;skM$I7OT$Mu<`33~`o-d_i<+ z$oToEt5u3b<(!O3OyXjSCAK8Q5m(MjQc`k3F3KgjELY^JT$6E`kn8PIm~iIoM~$^D#G-uv3XvFgz(~xhWaB zCAZ~{Oi5O9GA%PQD|3>Ug4~sPxhF+gko&SIOY%ULrPL0Fy~WOdr>-kC(&`vxsYq37 M;Q*Els*1WLE-i`XBqt{qmvZDnMH`y5F0JNr5~IzG2{JlnOozo6 zdy6h?%-9<&q(8tS@?c~#j|@C@7nV>#+tl;=^M=#^0`oRabjjUYfd?t zN0CZ8Kh~aoYB@P4v$WK-?bdQ3S+wrI`rsQ(+NDr&p=p(-7au#ZcDt2(Qq6n%JRZ-0 z7?PVTdA@vo)^mTq{xlJ)LG%$Dh>gT1Vl%OY=qI)k+lcLi zm+%qlcMv;?UBm#fn;0ba5JSXXVjr=e7$y!72Z=+(Vd4nkCj!J#;uvw9I6;gMqr^$# z6cPM{(EPCR@w>NE4u<5kj7eBbiAYo|iHR+7Nk~#saz@U|IXN%mazQSNBbOxIEeA91 zpOJVql62#iSq+D6H`=hwTEdOhV)1arPFQv$606sfwM4DH`qQ5s>Nn%Xu$?lasc6R= z$VyHw%N3cBtCE*%GAUD1kn3_orllw)nUPt!DYxXd+>tq%m%Fkc<*qkSnd|-c>Z(?Q Tot{yZnz&MzMz?3&`}X%Aad@^L diff --git a/dist/servoverifier b/dist/servoverifier index ef6e8c8fb1ce1d9d2ecc7fb7376b7003494daedd..73f97a24531a802ed9fc1a7f49beaddeb3976259 100755 GIT binary patch delta 367 zcmdndn6slXXF~_SW?4l}kY_=8dbn3)c8am3dr??;g}!%XesQpuS7KO}nMH7>Q&4bT zXzJvR{9=}oMrB0?!6hC(K^1-hX1e7Sy3Q30EDQ_`B0$Ux#O{8f44!9n1B37W3*4Mg zdBr)vHS>s~t6Gyln*d{*08^U)bDIE5n*eK@09%^?dz%19n*e8<09Ts;cbfoDn*eW{ z0AHH`f17~7HUU9-4=;nXB(u~+6U!uH(=?;Bv{d8d#AFk512eNkGB;&Ni zL?f=G(#)I`&a&iUy^_q_R9hnhBU4>NBV8lI5Cd~7QxhxG_RI)DAQl2*VIUR(Vo@L# d17dL?me`&dAsMogiWW`>TByI>j7h5L9{@mPZa4q{ delta 367 zcmdndn6slXXF~_SripiGgtuXZu|-s6p}(cSrH6T`m%eVIuSun|Pg0Iga;A@?dvH)t zM)2f~{9=}A9+{QF&Xq}d9=VpqnU!8yrD3@YEDQ_`B0$Ux#O{8f489!;c^LenwcqDD z#Ga^T4195A`u!$>HUY*q0j4$q<~9MAHUZW)0k$>)_BH{IHUZ8y0j@Ry?lu9QHUZu? z0lqc?{x$)DZ32Sw9$w~2MwTh&CWc7{24*S7X(oor7AdC5rsf8TW)>!9X-1~0sg}tW z$*EjPrI|S?oMp+ydL@~;skTN2My9%kM!JUPAqHkvhQ?OL?U@mRKr95r!aytn#G*hf d2E^h(EU`T^LNa6}6)l_&v`~M$8Ix4gKL7%VYc>D? diff --git a/pi/roverd/sensor_stream.go b/pi/roverd/sensor_stream.go index 9345a95c..037ab4db 100644 --- a/pi/roverd/sensor_stream.go +++ b/pi/roverd/sensor_stream.go @@ -14,7 +14,7 @@ import ( const ( sensorHeader = 19 sensorReadTimeout = 150 * time.Millisecond - sensorThrottleMinimum = 250 * time.Millisecond + sensorThrottleMinimum = 50 * time.Millisecond ) type SensorStreamer struct { diff --git a/webui/src/components/TelemetryPanel.jsx b/webui/src/components/TelemetryPanel.jsx index 5f0c57ff..17d6a865 100644 --- a/webui/src/components/TelemetryPanel.jsx +++ b/webui/src/components/TelemetryPanel.jsx @@ -15,6 +15,7 @@ export default function TelemetryPanel() { const sensors = frame?.sensors || {}; const voltage = sensors.voltageMv != null ? `${(sensors.voltageMv / 1000).toFixed(2)} V` : null; const current = sensors.currentMa != null ? `${sensors.currentMa} mA` : null; + const batteryTemp = sensors.batteryTemperatureC != null ? `${sensors.batteryTemperatureC} °C` : null; const charge = sensors.batteryChargeMah; const capacity = sensors.batteryCapacityMah; const updated = frame?.receivedAt ? new Date(frame.receivedAt).toLocaleTimeString() : null; @@ -65,24 +66,22 @@ export default function TelemetryPanel() { + -
-
Cliff Sensors
-
- - - - -
-
+ -
- - - - +
+
+ + +
+
+ + + +
)} @@ -165,3 +164,369 @@ function MotorCurrentBar({ label, value, overcurrent }) {
); } + +function DirtMeter({ label, value }) { + const pct = value == null ? 0 : Math.max(0, Math.min(100, (value / 255) * 100)); + const width = `${pct}%`; + return ( +
+
+
+
+
+ {label} + {value != null ? value : '--'} +
+
+ ); +} + +function IrPill({ label, value }) { + return ( +
+ {label}: {value != null && value !== 0 ? value : '--'} +
+ ); +} + +function SituationalMap({ sensors, variant = 'full' }) { + const size = variant === 'mini' ? 190 : 240; + const center = size / 2; + const innerCircle = variant === 'mini' ? 78 : 96; + const lightRingInner = variant === 'mini' ? 68 : 82; + const lightRingOuter = variant === 'mini' ? 78 : 92; + const cliffRingInner = variant === 'mini' ? 58 : 72; + const cliffRingOuter = variant === 'mini' ? 68 : 82; + const bumpRingInner = variant === 'mini' ? 82 : 104; + const bumpRingOuter = variant === 'mini' ? 90 : 114; + const wheelLineOffset = variant === 'mini' ? 34 : 42; + + const lightSignals = [ + sensors?.lightBumpLeftSignal, + sensors?.lightBumpFrontLeftSignal, + sensors?.lightBumpCenterLeftSignal, + sensors?.lightBumpCenterRightSignal, + sensors?.lightBumpFrontRightSignal, + sensors?.lightBumpRightSignal, + ].filter((v) => v != null); + const observedLightMax = lightSignals.length ? Math.max(...lightSignals) : 0; + const effectiveLightMax = Math.max(observedLightMax, 1200); // adaptive gain; these rarely hit 4095 + const bumps = sensors?.bumpsAndWheelDrops || {}; + const wheelCurrentLeft = sensors?.wheelLeftCurrentMa ?? 0; + const wheelCurrentRight = sensors?.wheelRightCurrentMa ?? 0; + const sideBrushCurrent = sensors?.sideBrushCurrentMa ?? 0; + const mainBrushCurrent = sensors?.mainBrushCurrentMa ?? 0; + const wheelOver = sensors?.wheelOvercurrents || {}; + + const lightSegments = [ + { label: 'L', start: -100, end: -70, value: sensors?.lightBumpLeftSignal, active: sensors?.lightBumper?.left }, + { label: 'FL', start: -70, end: -35, value: sensors?.lightBumpFrontLeftSignal, active: sensors?.lightBumper?.frontLeft }, + { label: 'CL', start: -35, end: -5, value: sensors?.lightBumpCenterLeftSignal, active: sensors?.lightBumper?.centerLeft }, + { label: 'CR', start: 5, end: 35, value: sensors?.lightBumpCenterRightSignal, active: sensors?.lightBumper?.centerRight }, + { label: 'FR', start: 35, end: 70, value: sensors?.lightBumpFrontRightSignal, active: sensors?.lightBumper?.frontRight }, + { label: 'R', start: 70, end: 100, value: sensors?.lightBumpRightSignal, active: sensors?.lightBumper?.right }, + ]; + + const cliffSegments = [ + { label: 'Cliff L', start: -95, end: -70, value: sensors?.cliffLeftSignal, active: sensors?.cliffLeft }, + { label: 'Cliff FL', start: -45, end: -15, value: sensors?.cliffFrontLeftSignal, active: sensors?.cliffFrontLeft }, + { label: 'Cliff FR', start: 15, end: 45, value: sensors?.cliffFrontRightSignal, active: sensors?.cliffFrontRight }, + { label: 'Cliff R', start: 70, end: 95, value: sensors?.cliffRightSignal, active: sensors?.cliffRight }, + ]; + + return ( +
+
Top-down
+
0–{effectiveLightMax}
+ + + + + {lightSegments.map((seg) => { + const pct = seg.value == null ? 0 : Math.max(0, Math.min(1, seg.value / effectiveLightMax)); + const color = pct > 0.75 ? '#f59e0b' : '#22c55e'; + return ( + + ); + })} + {cliffSegments.map((seg) => { + const pct = seg.value == null ? 0 : Math.max(0, Math.min(1, seg.value / 4095)); + const color = pct > 0.6 ? '#f59e0b' : '#22c55e'; + return ( + + ); + })} + + + + + + N + + +
+ ); +} + +function ArcSegment({ cx, cy, rInner, rOuter, startDeg, endDeg, color, pulse = false, opacity = 1 }) { + const rMid = (rInner + rOuter) / 2; + const strokeWidth = rOuter - rInner; + const path = describeArc(cx, cy, rMid, startDeg, endDeg); + return ( + <> + + {pulse ? ( + + ) : null} + + ); +} + +function describeArc(cx, cy, r, startDeg, endDeg) { + let start = startDeg; + let end = endDeg; + if (end <= start) end += 360; + const startPt = polarToCartesian(cx, cy, r, start); + const endPt = polarToCartesian(cx, cy, r, end); + return ['M', startPt.x, startPt.y, 'A', r, r, 0, 0, 1, endPt.x, endPt.y].join(' '); +} + +function polarToCartesian(cx, cy, r, angleDeg) { + const rad = toRad(angleDeg); + return { + x: cx + r * Math.cos(rad), + y: cy + r * Math.sin(rad), + }; +} + +function toRad(angleDeg) { + return ((angleDeg - 90) * Math.PI) / 180; +} + +function clamp01(value) { + return Math.max(0, Math.min(1, value)); +} + +function currentColor(value, overcurrent) { + if (overcurrent) return '#ef4444'; + const mag = Math.abs(value); + if (mag > 900) return '#f59e0b'; + if (mag > 300) return '#22c55e'; + return '#94a3b8'; +} + +function WheelVisual({ cx, cy, current, drop, overcurrent, label }) { + const mag = Math.abs(current); + const pct = clamp01(mag / 1200); + const color = currentColor(current, overcurrent); + const barH = 30; + const currentW = 12; + const dropW = 8; + const gap = 4; + const currentFill = barH * pct; + const sign = label === 'L' ? -1 : 1; + const currentCenterX = sign * (-dropW / 2 - gap / 2); + const dropCenterX = sign * (currentW / 2 + gap / 2); + return ( + + + + + + {label} + + + ); +} + +function SideBrushVisual({ cx, cy, current, overcurrent }) { + const mag = Math.abs(current); + const color = currentColor(current, overcurrent); + const opacity = clamp01(mag / 1000) * 0.6 + 0.3; + const armLength = 26; + const spinDuration = mag > 10 ? Math.max(0.35, 2.2 - mag / 500) : null; + return ( + + + {[0, 120, 240].map((deg) => { + const rad = toRad(deg); + const x2 = cx + armLength * Math.cos(rad); + const y2 = cy + armLength * Math.sin(rad); + return ; + })} + {overcurrent ? : null} + + ); +} + +function MainBrushVisual({ cx, cy, current, overcurrent, variant }) { + const mag = Math.abs(current); + const color = currentColor(current, overcurrent); + const opacity = clamp01(mag / 1000) * 0.6 + 0.3; + const rollerWidth = 44; + const rollerHeight = 8; + const patternA = `main-brush-pattern-a-${variant}`; + const patternB = `main-brush-pattern-b-${variant}`; + const dur = mag > 10 ? Math.max(0.35, 2.4 - mag / 500) : null; + const dir = current >= 0 ? 1 : -1; + return ( + + + {[patternA, patternB].map((id, idx) => ( + + + + {dur ? ( + + ) : null} + + ))} + + + + {overcurrent ? ( + + ) : null} + + ); +}