Mission: practical Python • robotics runtime • July 27, 2026
Get the servo velocity right.
A seven-day, evidence-backed sprint for Physical Intelligence’s one-hour coding interview. The recruiter gave an unusually precise clue: you will write a Python script to measure servo velocity. This plan turns that sentence into a rehearsed engineering performance.
7 daysMon Jul 20 → Sun Jul 26
26–32 hhigh-value focused work
4 mocksincluding two full 60-minute runs
Jul 27target interview day
1. Ground truth, uncertainty, and the bet
Do not waste this week preparing for the wrong interview.
Direct evidence
“It will be a very practical coding question — we don’t do any leetcode… you will write a Python script to measure servo velocity.”
This email is the highest-confidence source in the entire plan. Treat every other prediction as secondary.
Verified
One-hour technical coding interview.
Python script.
Measure servo velocity.
Practical engineering, explicitly not LeetCode.
Physical Intelligence’s open-source docs use joint angles in radians and, depending on platform, 15, 20, or 50 Hz control; DROID uses seven joint-velocity dimensions.
Not verified
Whether input is a live servo API, a simulator, iterable samples, or a CSV/log.
Whether third-party libraries are allowed.
Whether “servo” means a single actuator or a multi-joint robot arm.
Whether they expect instantaneous, average, filtered, angular, or linear velocity.
Exact API names, units, and output format.
Informed inference
The most likely core is: repeatedly read position and time, calculate Δposition / Δtime, handle real-world sampling problems, and expose or print a useful estimate. The evaluation is likely less about exotic math than assumptions, interface design, correctness, tests, noise, timing, and communication.
Public interview-question search result
I found only one public Glassdoor report, for a Lead Robot Test Pilot, describing timed physical tasks (shirt folding and cup stacking) and a general robotics-experience question. It is a different role and provides no coding prompt. No credible public source surfaced this servo-velocity interview question. Therefore, this plan does not pretend to reproduce a leaked prompt.
Return a testable value rather than burying logic inside I/O.
Verify with a hand-calculated constant-velocity example.
The strong solution
Correct contract
States assumptions; consistent units; signed direction; clear definition of instantaneous vs windowed velocity.
Separation
Hardware/sample acquisition, estimation, and reporting are separate. Synthetic data can test the estimator.
Robust time
Uses supplied device timestamps or a monotonic clock; never wall-clock time for intervals.
Edge cases
First sample, duplicate/out-of-order time, malformed values, angular wrap, and empty/short streams.
Noise awareness
Explains why differentiation amplifies noise; adds filtering only after a correct baseline.
Evidence
Small tests: stationary, constant velocity, irregular sampling, invalid timestamps, multi-axis.
Do not overengineer early. A candidate who spends 35 minutes designing a Kalman filter before producing Δx/Δt is in danger. Get a correct thin slice, test it, then add robustness in descending value order.
Readiness gates by Sunday night
You can implement the baseline from a blank file in 8 minutes.
You can add streaming, validation, and four tests in 25 minutes total.
You can explain forward vs central difference, filtering tradeoffs, and monotonic time without notes.
You can complete the full mock with a working solution by minute 40 and spend the remainder testing/refining.
You score ≥ 16/20 twice using the rubric below, with no zero in correctness or communication.
3. Seven-day execution calendar
Times are adjustable; preserve order and deliverables. Work in 50/10 or 75/15 focus cycles.
Schedule principle: every study block ends with an artifact—code, tests, a spoken recording, or a written error log. Passive reading is capped at 25% of the week.
MON • JUL 20
Baseline + problem decomposition
Build the simplest correct estimator and find your Python weak spots.
4–5 h
Closed-book diagnostic. From a blank editor, write a function taking timestamped positions and returning velocities. Add any tests you naturally think of. Stop at 45 minutes—do not polish.
Postmortem. Mark every hesitation: Python syntax, API ambiguity, math, test design, edge cases, typing, debugging. This becomes your personal gap list.
Core implementation. Implement scalar batch and streaming estimators using only the standard library. Cover first sample and dt <= 0.
Python refresh. Practice dataclasses, type hints, iterators/generators, exceptions, argparse, csv, math.isfinite, and collections.deque.
Exit artifact: velocity_v1.py + tests + one-page gap log. Baseline works from a blank file.
TUE • JUL 21
Timing, units, angular motion
Make the baseline physically and numerically honest.
4–4.5 h
Timing lab. Compare device timestamps, time.time(), time.monotonic(), monotonic_ns(), and perf_counter(). Write down which you would use and why.
Angular wraparound. Implement shortest signed angular difference for a period of 2π or 360°. Test 179° → −179° and the reverse.
Multi-axis extension. Accept a sequence of joint positions; validate dimensional consistency; return per-joint signed velocities plus optional speed norm.
I/O practice. Read timestamp,joint_0,... CSV; stream output; report malformed rows with useful line numbers. Keep estimation pure.
Explain aloud. Units (rad/s vs deg/s), velocity vs speed, sampling rate vs measured dt, and why wall time can jump.
Know enough signal processing to make sound tradeoffs—without disappearing into theory.
4–5 h
Derivatives. Derive and code forward/backward difference and central difference. Understand endpoint behavior and irregular sample spacing.
Synthetic experiment. Generate constant and sinusoidal motion with timestamp jitter and position noise. Compare raw finite differences with ground truth; calculate MAE/RMSE.
Filtering. Implement a rolling-window slope via least-squares and a simple exponential moving average. Compare noise reduction, lag, startup, and memory.
Optional library literacy. Understand numpy.gradient and Savitzky–Golay conceptually, but be able to solve without NumPy/SciPy.
Decision memo. “Which estimator would I ship for feedback, telemetry, offline analysis, and safety limit detection?” One paragraph each.
Exit artifact: noisy-data benchmark, two filtered estimators, plot or metrics table, tradeoff memo.
THU • JUL 23
Production shape + mock #1
Turn math into a maintainable little system.
4–4.5 h
Design exercise. Define a Sample, a sensor/servo protocol, a velocity estimator, and a reporter. Add dependency injection so no hardware is required for tests.
Runtime hazards. Discuss polling rate, blocking reads, backpressure, thread safety (only if needed), clock domain mismatch, dropped samples, and shutdown via KeyboardInterrupt.
Mock #1. Use Prompt A below. Screen-record or voice-record. No notes or web. Ask clarifying questions aloud before coding.
Grade and redo. Score with the 20-point rubric. Fix only after writing down what caused each miss. Reimplement the weakest part from blank.
PI context. Read openpi overview and normalization/action-space docs; connect the task to joint actions, action frequencies, and robot runtime reliability.
Debugging practice. Seed five defects into yesterday’s code. Diagnose using failing tests and small prints/debugger. Explain hypotheses before changing code.
Story prep. Prepare concise examples of a runtime/reliability bug, ambiguous hardware interface, high-stakes tradeoff, and cross-functional debugging.
Full mock #3. Randomly select Prompt C or D. Use the likely 60-minute structure. No autocomplete if the real environment is unknown.
Review. Watch at 1.5×. Track silence, premature coding, untested assumptions, meandering, and whether a working baseline appeared by minute 25.
Targeted repair. Drill the two lowest rubric categories. Do not broadly review topics you already own.
Full mock #4. Fresh prompt, ideally with another person acting as interviewer. Ask them to interrupt and change one requirement.
Final compare. You need ≥16/20 and a complete baseline by minute 30. If not, schedule one 45-minute corrective block Sunday morning.
Exit artifact: two rubric sheets; one ≥16/20; final “default approach” committed to memory—not memorized code.
SUN • JUL 26
Taper, logistics, confidence
Consolidate; do not exhaust yourself.
2.5–3 h
Final corrective mock only if needed. Otherwise do a relaxed blank-file baseline + three tests and stop.
One-page review. Rewrite from memory: formula, assumptions, clock choice, edge cases, filters, test matrix, interview opening, company motivation.
Environment. Confirm time zone and meeting link; Python version; editor; terminal; screen sharing; charger; headphones; reliable internet; quiet room; backup hotspot. If interviewing from India, verify the calendar conversion twice.
Warm-up package. Prepare one 10-minute exercise for Monday: simple moving average + two tests. Nothing novel.
Hard stop. Finish by early evening, eat normally, take a walk, and protect 8 hours of sleep. The last 5% of content is not worth degraded working memory.
Wake/move/eat. No frantic studying. Confirm meeting time and connectivity.
10-minute warm-up; run two tests. Review only the one-page sheet. Close unrelated apps and notifications.
Open editor/terminal, water, meeting link, charger. Use restroom. Two minutes of slow breathing.
Join early. Your objective is not to know a hidden answer; it is to collaborate visibly toward a correct, tested program.
4. Servo-velocity technical primer
The compact body of knowledge most likely to matter.
Definitions first
Velocity
Rate of change of position, including direction. Angular velocity is commonly rad/s or deg/s; linear velocity is m/s.
Speed
Magnitude of velocity. For a scalar signed joint velocity, speed is abs(v). For multi-axis velocity, clarify whether they want per-axis values or a norm.
v ≈ Δq / Δt = (q₂ − q₁) / (t₂ − t₁)
For samples (tᵢ, qᵢ), the simplest online estimate at sample i is the backward difference. It is causal and low-memory, but differentiation magnifies position noise.
Estimator choices
Method
Strength
Weakness
Best interview use
Two-point backward difference
Simple, online, minimal latency
Noisy
Always build this baseline first
Central difference
Usually more accurate at interior points
Needs a future sample; endpoint handling
Offline arrays/logs
Window endpoints
Reduces some noise
Lag; ignores interior structure
Quick windowed improvement
Least-squares slope
Uses every point; handles irregular times
Window lag and more code
Strong practical extension
EMA of raw velocity
Constant memory, tunable smoothing
Phase lag; parameter depends on timing
Streaming telemetry
Savitzky–Golay derivative
Smooth derivative, preserves shapes
Dependency, parameter constraints, typically offline/windowed
Discuss if libraries/offline analysis allowed
Why timestamps are not optional
Never assume a nominal 50 Hz loop means every interval is exactly 20 ms. Scheduling and I/O jitter exist.
Prefer timestamps generated by the device/data source if they share one stable clock domain.
If you timestamp locally, use a monotonic clock for intervals. Python documents time.monotonic() as unable to go backward and unaffected by system-clock updates.
Nanosecond integer variants avoid float precision loss, but remember to convert ns to seconds before reporting rad/s.
If position and timestamp come from separate reads, discuss temporal skew; an atomic sample API is better.
Angular wraparound
If an encoder reports angles in a wrapped interval, naïve subtraction may invent a huge jump. The shortest signed difference for period P is:
def shortest_delta(current: float, previous: float, period: float) -> float:
return (current - previous + period / 2) % period - period / 2
But do not apply wrap correction blindly: some joint encoders report continuous/unwrapped angle, and some joints physically cannot cross the wrap boundary. Ask.
Least-squares slope for noisy, irregular samples
Fit q = a + vt over a small recent window. The slope is:
v = Σ(tᵢ − t̄)(qᵢ − q̄) / Σ(tᵢ − t̄)²
Subtracting the mean also improves numerical conditioning when timestamps are large absolute numbers. Reject a zero denominator. A time-based window can behave more consistently than a fixed number of samples when rates vary.
Edge-case decision table
Condition
Safe default
Say aloud
First sample
Return None; cache it
There is not yet an interval.
dt == 0
Reject/skip explicitly
Division is undefined; duplicate timestamp may indicate bad data.
dt < 0
Reject, do not silently sort a live stream
Out-of-order data or mixed clock domains.
NaN/∞
Validate with math.isfinite
Invalid samples should not poison estimator state.
Tiny dt
Optionally enforce minimum interval
Quantization/noise creates huge estimates.
Large gap
Return average over gap or reset, per contract
It may no longer represent instantaneous behavior.
Changing joint count
Raise a descriptive error
Dimensional consistency is part of the API.
Position noise
Baseline first; optional window/filter
Smoothing trades variance for lag.
Velocity limit exceeded
Report; do not command hardware unless asked
Measurement and actuation/safety policy are separate concerns.
5. Likely prompt shape + reference architecture
Use for practice, not as a prediction of exact wording.
Practice prompt
“You have a servo object whose read_position() method returns its current angular position. Write a Python program that measures and reports the servo’s velocity as it moves. Assume you can call it repeatedly.”
Your first 90 seconds
Restate: “I’ll estimate signed angular velocity from position samples and elapsed time.”
Ask: Does the API provide timestamps? What are position units and wrap behavior?
Ask: Is this one servo or multiple joints? Do you want each interval, a windowed estimate, or an average over a run?
Ask: Standard library only? What should happen with invalid readings or non-increasing timestamps?
Propose: “I’ll start with a small testable estimator using two samples, verify it, then add the live polling loop and robustness.”
Reference skeleton—know the reasoning, not the exact text
from dataclasses import dataclass
import math
from typing import Optional
@dataclass(frozen=True)
class Sample:
timestamp_s: float
position_rad: float
class VelocityEstimator:
def __init__(self) -> None:
self._previous: Optional[Sample] = None
def update(self, sample: Sample) -> Optional[float]:
if not (math.isfinite(sample.timestamp_s)
and math.isfinite(sample.position_rad)):
raise ValueError("sample values must be finite")
previous = self._previous
if previous is None:
self._previous = sample
return None
dt = sample.timestamp_s - previous.timestamp_s
if dt <= 0:
# Deliberate policy: invalid input does not corrupt valid state.
raise ValueError("timestamps must be strictly increasing")
velocity = (sample.position_rad - previous.position_rad) / dt
self._previous = sample
return velocity
Then connect I/O without contaminating the estimator
import time
from collections.abc import Callable, Iterator
def sample_servo(
read_position_rad: Callable[[], float],
period_s: float,
) -> Iterator[Sample]:
while True:
# If the hardware gives an atomic position+timestamp sample, prefer it.
position = read_position_rad()
timestamp = time.monotonic()
yield Sample(timestamp, position)
time.sleep(period_s)
def run(read_position_rad: Callable[[], float], period_s: float = 0.02) -> None:
estimator = VelocityEstimator()
try:
for sample in sample_servo(read_position_rad, period_s):
velocity = estimator.update(sample)
if velocity is not None:
print(f"{velocity:.4f} rad/s")
except KeyboardInterrupt:
pass
Timing nuance worth saying: in this skeleton, the timestamp is taken after read_position_rad(). If reads have variable latency, the ideal API returns position and capture time together. If not, timestamping immediately before or after is an approximation; midpoint bracketing can estimate capture time if needed.
How would this design change if estimates feed a real-time controller rather than telemetry?
6. Servo-velocity question bank
One confirmed problem family, twelve increasingly realistic variants, complete Python solutions, and a repeatable drill system.
Confirmed familyExact wording unknown
The recruiter confirmed that you will write a Python script to measure servo velocity. No trustworthy public source reveals the exact prompt. Questions 1–12 below are deliberately constructed variants—not claimed leaks—ranked by how directly they exercise that confirmed task.
Daily practice selector
Attempt the selected problem from a blank file before opening its hints or solution. Repeat a problem until you can hit its target time while narrating clearly.
Choose “Today’s drill” for a deterministic daily question or “Random drill” for a fresh draw.
30:00
How to practice each question
Minutes 0–3: read only the prompt; write the contract, assumptions, and 3 tests.
Minutes 3–20: produce the simplest working implementation.
Minutes 20–27: run tests, fix failures, and add the highest-value edge case.
Minutes 27–30: state complexity, limitations, and the next improvement.
Review: open Hint 1 only if stuck after 8 minutes; Hint 2 after 15; solution only after a real attempt.
Prompt. Implement servo_velocity(p0, t0, p1, t1). Positions are radians and timestamps are seconds. Return signed angular velocity in rad/s. Decide what to do when time does not increase.
Hint 1
Velocity is displacement divided by elapsed time. Signed displacement preserves direction.
Hint 2
Validate finite numeric inputs and require t1 > t0. Do not return infinity for a duplicate timestamp.
Solution and reasoning
Define the units and signed-output contract.
Reject non-finite inputs so NaN cannot silently propagate.
Compute dt and reject zero/negative intervals.
Return Δposition / Δtime.
import math
def servo_velocity(p0: float, t0: float, p1: float, t1: float) -> float:
values = (p0, t0, p1, t1)
if not all(math.isfinite(x) for x in values):
raise ValueError("positions and timestamps must be finite")
dt = t1 - t0
if dt <= 0:
raise ValueError("timestamps must be strictly increasing")
return (p1 - p0) / dt
assert servo_velocity(1.0, 10.0, 1.5, 10.25) == 2.0
assert servo_velocity(1.0, 0.0, 0.5, 0.25) == -2.0
assert servo_velocity(0.7, 3.0, 0.7, 4.0) == 0.0
Complexity: O(1) time and memory. Key interview statement: this is average velocity over the interval; with sufficiently close samples it approximates instantaneous velocity.
Prompt. Given any iterable of (timestamp_s, position_rad) pairs, yield one velocity for each interval without loading the stream into memory. The first sample produces no output.
Hint 1
Keep only the previous valid sample. A generator makes the function work with lists, files, and live streams.
Hint 2
Decide whether a bad sample should replace the previous valid state. A safe default is to raise before updating it.
Solution and reasoning
import math
from collections.abc import Iterable, Iterator
def velocities(
samples: Iterable[tuple[float, float]],
) -> Iterator[tuple[float, float]]:
previous: tuple[float, float] | None = None
for timestamp, position in samples:
if previous is None:
previous = (timestamp, position)
continue
prev_t, prev_p = previous
dt = timestamp - prev_t
if dt <= 0:
raise ValueError(f"non-increasing timestamp: {timestamp}")
yield timestamp, (position - prev_p) / dt
previous = (timestamp, position)
data = [(0.0, 1.0), (0.1, 1.2), (0.4, 1.8)]
result = list(velocities(data))
assert [t for t, _ in result] == [0.1, 0.4]
assert all(math.isclose(v, 2.0) for _, v in result)
Complexity: O(n) total time, O(1) auxiliary memory. Subtlety: the assignment to previous occurs after validation and calculation, so a rejected sample cannot corrupt valid state.
Prompt. A servo exposes read_position_rad(). Print its measured velocity around 50 times per second until interrupted. Calls occasionally take a few milliseconds.
Hint 1
Use a monotonic clock for intervals. Separate one loop iteration from the infinite loop so it remains testable.
Hint 2
Repeatedly sleeping 20 ms after work makes the period “work time + 20 ms.” Schedule against an advancing deadline instead.
Solution and reasoning
import time
from collections.abc import Callable
def monitor_servo(
read_position_rad: Callable[[], float],
rate_hz: float = 50.0,
) -> None:
if rate_hz <= 0:
raise ValueError("rate_hz must be positive")
period = 1.0 / rate_hz
previous: tuple[float, float] | None = None
deadline = time.monotonic()
try:
while True:
# Best case: hardware returns position and capture time atomically.
position = read_position_rad()
now = time.monotonic()
if previous is not None:
prev_t, prev_p = previous
dt = now - prev_t
if dt > 0:
print(f"{(position - prev_p) / dt:.4f} rad/s")
previous = (now, position)
deadline += period
delay = deadline - time.monotonic()
if delay > 0:
time.sleep(delay)
else:
# We missed one or more periods; don't burst to catch up.
deadline = time.monotonic()
except KeyboardInterrupt:
return
Tradeoff to discuss: ordinary Python cannot promise hard real-time behavior. This is adequate for a practical measurement/telemetry loop, but a control/safety loop may need hardware timestamps, a real-time process, bounds, and explicit missed-deadline metrics.
Prompt. Each sample contains a timestamp and positions for seven joints. Return the seven signed velocities. Reject a sample if its joint count changes.
Hint 1
Convert the positions to an immutable tuple at the boundary so a caller cannot mutate stored state.
Solution and reasoning
class JointVelocityEstimator:
def __init__(self, joint_count: int) -> None:
if joint_count <= 0:
raise ValueError("joint_count must be positive")
self.joint_count = joint_count
self.previous: tuple[float, tuple[float, ...]] | None = None
def update(self, timestamp: float, positions: list[float]) -> tuple[float, ...] | None:
current = tuple(positions)
if len(current) != self.joint_count:
raise ValueError(
f"expected {self.joint_count} joints, got {len(current)}"
)
if self.previous is None:
self.previous = (timestamp, current)
return None
prev_t, prev_q = self.previous
dt = timestamp - prev_t
if dt <= 0:
raise ValueError("timestamps must increase")
result = tuple((q - old_q) / dt for q, old_q in zip(current, prev_q))
self.previous = (timestamp, current)
return result
est = JointVelocityEstimator(3)
assert est.update(0.0, [1.0, 2.0, 3.0]) is None
assert est.update(0.5, [2.0, 2.0, 2.0]) == (2.0, 0.0, -2.0)
Clarify whether the interviewer wants per-joint velocities, speed magnitudes, or end-effector velocity. Joint velocities do not directly equal Cartesian end-effector velocity; that conversion involves the manipulator Jacobian and is a different problem.
Prompt. The encoder reports angles in [−π, π). A move from 179° to −179° is a small positive motion, not −358°. Modify the velocity calculation.
Hint 1
Normalize the raw difference into [−period/2, period/2).
Solution and reasoning
import math
def wrapped_delta(current: float, previous: float, period: float) -> float:
if period <= 0:
raise ValueError("period must be positive")
return (current - previous + period / 2) % period - period / 2
def wrapped_velocity(p0: float, t0: float, p1: float, t1: float) -> float:
dt = t1 - t0
if dt <= 0:
raise ValueError("timestamps must increase")
return wrapped_delta(p1, p0, 2 * math.pi) / dt
eps = 1e-12
v = wrapped_velocity(math.radians(179), 0, math.radians(-179), 1)
assert abs(v - math.radians(2)) < eps
v = wrapped_velocity(math.radians(-179), 0, math.radians(179), 1)
assert abs(v - math.radians(-2)) < eps
Ambiguous boundary: exactly half a period has two equivalent directions; this formula chooses the negative direction. Document it if that case matters. Also ask whether the hardware already provides unwrapped angles.
Prompt. Read timestamp_ns,position_deg from a large CSV and write timestamp_ns,velocity_rad_s. Include useful errors for malformed rows.
Hint 1
Stream rows with csv.DictReader. Convert nanoseconds to seconds and degrees to radians before differentiation.
Solution and reasoning
import csv
import math
from typing import TextIO
def convert_log(source: TextIO, destination: TextIO) -> None:
reader = csv.DictReader(source)
required = {"timestamp_ns", "position_deg"}
if not required.issubset(reader.fieldnames or []):
raise ValueError(f"required columns: {sorted(required)}")
writer = csv.DictWriter(
destination, fieldnames=["timestamp_ns", "velocity_rad_s"]
)
writer.writeheader()
previous: tuple[int, float] | None = None
for line_number, row in enumerate(reader, start=2):
try:
timestamp_ns = int(row["timestamp_ns"])
position_rad = math.radians(float(row["position_deg"]))
except (TypeError, ValueError) as exc:
raise ValueError(f"invalid row at line {line_number}") from exc
if previous is not None:
prev_ns, prev_rad = previous
dt_s = (timestamp_ns - prev_ns) / 1_000_000_000
if dt_s <= 0:
raise ValueError(f"time did not increase at line {line_number}")
writer.writerow({
"timestamp_ns": timestamp_ns,
"velocity_rad_s": (position_rad - prev_rad) / dt_s,
})
previous = (timestamp_ns, position_rad)
Policy question: fail fast versus skip-and-count malformed rows depends on the use case. For an interview, state the choice; do not silently discard data. Streaming gives O(1) auxiliary memory.
Q7. Estimate velocity from noisy, irregular samples
HardStrong follow-up
Target: 35 minutes • Skills: rolling window, linear regression, numerical stability
Prompt. Two-point differences are too noisy. Fit a line over the most recent N (time, position) samples and return its slope as velocity. Timestamps are irregular.
Hint 1
For a line q = a + vt, compute v = Σ(t−t̄)(q−q̄) / Σ(t−t̄)².
Hint 2
Center timestamps before multiplication. Use a bounded deque. Need at least two distinct timestamps.
Solution and reasoning
from collections import deque
class WindowSlope:
def __init__(self, window_size: int) -> None:
if window_size < 2:
raise ValueError("window_size must be at least 2")
self.samples: deque[tuple[float, float]] = deque(maxlen=window_size)
def update(self, timestamp: float, position: float) -> float | None:
if self.samples and timestamp <= self.samples[-1][0]:
raise ValueError("timestamps must increase")
self.samples.append((timestamp, position))
if len(self.samples) < 2:
return None
mean_t = sum(t for t, _ in self.samples) / len(self.samples)
mean_q = sum(q for _, q in self.samples) / len(self.samples)
numerator = sum(
(t - mean_t) * (q - mean_q) for t, q in self.samples
)
denominator = sum((t - mean_t) ** 2 for t, _ in self.samples)
if denominator == 0:
return None
return numerator / denominator
est = WindowSlope(4)
assert est.update(10.0, 21.0) is None
assert abs(est.update(10.1, 21.2) - 2.0) < 1e-12
assert abs(est.update(10.4, 21.8) - 2.0) < 1e-12
Tradeoff: a larger window reduces variance but increases lag when velocity changes. This simple implementation is O(N) per sample; for a bounded small window that may be the right clarity/performance tradeoff. Running sums can make it O(1), but are easier to get wrong.
Q8. Smooth velocity without assuming fixed sample rate
Hard
Target: 30 minutes • Skills: EMA, time constant, jitter-aware filtering
Prompt. Apply exponential smoothing to raw velocity estimates, but samples arrive at irregular intervals. Configure the filter with a time constant τ rather than a fixed alpha.
Hint 1
Use alpha = 1 - exp(-dt / tau). This adapts the weight to actual elapsed time.
Solution and reasoning
import math
class SmoothedVelocity:
def __init__(self, time_constant_s: float) -> None:
if time_constant_s <= 0:
raise ValueError("time constant must be positive")
self.tau = time_constant_s
self.previous_sample: tuple[float, float] | None = None
self.filtered: float | None = None
def update(self, timestamp: float, position: float) -> float | None:
if self.previous_sample is None:
self.previous_sample = (timestamp, position)
return None
prev_t, prev_q = self.previous_sample
dt = timestamp - prev_t
if dt <= 0:
raise ValueError("timestamps must increase")
raw = (position - prev_q) / dt
alpha = 1.0 - math.exp(-dt / self.tau)
self.filtered = (
raw if self.filtered is None
else self.filtered + alpha * (raw - self.filtered)
)
self.previous_sample = (timestamp, position)
return self.filtered
Interpretation: after one time constant, the filter moves about 63% toward a step input. Smaller τ responds faster but passes more noise. Filtering velocity after differentiation is simple; filtering position before differentiating has different phase/noise behavior.
Q9. Detect sustained overspeed without false alarms
Medium
Target: 25 minutes • Skills: state machine, hysteresis, safety boundaries
Prompt. Raise an event when absolute velocity exceeds 2 rad/s for at least 100 ms. Clear it only after speed stays below 1.8 rad/s. Samples are timestamped and irregular.
Hint 1
Track when the threshold was first crossed. Use different entry and exit thresholds to avoid chatter.
Solution and reasoning
class OverspeedDetector:
def __init__(self, enter: float, exit: float, dwell_s: float) -> None:
if not (0 < exit < enter and dwell_s >= 0):
raise ValueError("require 0 < exit < enter and nonnegative dwell")
self.enter = enter
self.exit = exit
self.dwell_s = dwell_s
self.above_since: float | None = None
self.active = False
def update(self, timestamp: float, velocity: float) -> bool:
speed = abs(velocity)
if self.active:
if speed < self.exit:
self.active = False
self.above_since = None
return self.active
if speed > self.enter:
if self.above_since is None:
self.above_since = timestamp
if timestamp - self.above_since >= self.dwell_s:
self.active = True
else:
self.above_since = None
return self.active
Important: this reports a condition; it does not command or stop hardware. Measurement, alerting, and safety actuation should remain explicit boundaries unless the prompt asks you to integrate them.
Q10. Test a measurement loop without a servo or real sleep
In production, prefer an atomic read_sample() returning both capture time and position. The separate callbacks here illustrate dependency injection, but also expose possible timestamp/position skew.
Q11. Compute offline velocities with central differences
Prompt. Given arrays of strictly increasing timestamps and positions, return one velocity per sample. Use one-sided differences at the endpoints and a three-point quadratic derivative for irregularly spaced interior samples.
Hint 1
For a simpler interview version, ask whether (q[i+1]-q[i-1])/(t[i+1]-t[i-1]) is acceptable. It is a secant across neighbors, but is not the exact three-point nonuniform quadratic formula.
Hint 2
Let h0=t[i]-t[i-1], h1=t[i+1]-t[i]. The derivative weights are -h1/(h0*(h0+h1)), (h1-h0)/(h0*h1), and h0/(h1*(h0+h1)).
Solution and reasoning
def offline_velocity(times: list[float], positions: list[float]) -> list[float]:
if len(times) != len(positions) or len(times) < 2:
raise ValueError("need equal arrays with at least two samples")
if any(b <= a for a, b in zip(times, times[1:])):
raise ValueError("times must be strictly increasing")
n = len(times)
result = [0.0] * n
result[0] = (positions[1] - positions[0]) / (times[1] - times[0])
result[-1] = (positions[-1] - positions[-2]) / (times[-1] - times[-2])
for i in range(1, n - 1):
h0 = times[i] - times[i - 1]
h1 = times[i + 1] - times[i]
w_prev = -h1 / (h0 * (h0 + h1))
w_here = (h1 - h0) / (h0 * h1)
w_next = h0 / (h1 * (h0 + h1))
result[i] = (
w_prev * positions[i - 1]
+ w_here * positions[i]
+ w_next * positions[i + 1]
)
return result
# q=t^2; exact derivative is 2t at the interior even with uneven samples.
t = [0.0, 1.0, 3.0]
q = [x * x for x in t]
v = offline_velocity(t, q)
assert abs(v[1] - 2.0) < 1e-12
Interview judgment: do not volunteer this formula unless the prompt needs it. For a live servo, it adds future-sample latency. The two-point estimator is usually the correct starting point.
Q12. Detect timing degradation in the measurement runtime
Prompt. The target rate is 50 Hz. Given sample timestamps, report actual mean rate, maximum interval, RMS jitter relative to 20 ms, and an estimated missed-period count.
Hint 1
Compute consecutive intervals. Jitter is dt - target_period. Estimate missed periods from how many target periods fit in a gap.
Solution and reasoning
import math
from dataclasses import dataclass
@dataclass(frozen=True)
class TimingHealth:
mean_rate_hz: float
max_interval_s: float
rms_jitter_s: float
estimated_missed: int
def timing_health(times: list[float], target_hz: float) -> TimingHealth:
if len(times) < 2 or target_hz <= 0:
raise ValueError("need two timestamps and a positive target rate")
intervals = [b - a for a, b in zip(times, times[1:])]
if any(dt <= 0 for dt in intervals):
raise ValueError("timestamps must increase")
target = 1.0 / target_hz
mean_dt = sum(intervals) / len(intervals)
rms_jitter = math.sqrt(
sum((dt - target) ** 2 for dt in intervals) / len(intervals)
)
# A 2.1-period gap implies one likely missing sample.
missed = sum(max(0, round(dt / target) - 1) for dt in intervals)
return TimingHealth(1.0 / mean_dt, max(intervals), rms_jitter, missed)
h = timing_health([0.00, 0.02, 0.04, 0.08, 0.10], 50.0)
assert h.estimated_missed == 1
assert abs(h.mean_rate_hz - 40.0) < 1e-12
Nuance: “missed” is an estimate because timestamps alone do not prove whether the producer dropped a sample or intentionally paused. Production observability should distinguish source sequence numbers, acquisition failures, and consumer backpressure when possible.
Recommended repetition schedule
Day
Timed drill
Review drill
Mastery criterion
Cycle 1
Q1 + Q2
Read Q3 solution
Both correct in 20 min total
Cycle 2
Q3
Q4 tests
Working baseline by min 18
Cycle 3
Q5 + Q6
Explain units aloud
Boundary and conversion tests pass
Cycle 4
Q7
Q8 tradeoffs
Derive slope without notes
Cycle 5
Q9 + Q10
Refactor Q3
Deterministic tests, no real sleep
Cycle 6
Q11 or Q12
Redo weakest question
Can explain why complexity is justified
Final cycle
Random question
One 60-minute mock
≥16/20 rubric and calm narration
7. Mock interview bank
Do these from blank files. Reveal hints only during review.
Prompt A — streaming single servo (foundation)
Implement measure_velocity(samples), where samples is an iterable of (timestamp_s, position_rad). Yield one signed velocity per valid interval. Define behavior for the first sample and invalid time.
Interviewer follow-ups
Accept a generator too; do not materialize it.
Add angular wraparound with configurable period.
Explain the complexity.
Target: O(1) memory, O(n) time, tests for stationary/negative/irregular/invalid time.
Prompt B — live servo API (most plausible)
You receive a servo.read_position() method. Report velocity at about 50 Hz until interrupted. The method is slow sometimes.
Follow-ups at fixed times
Minute 25: support seven joints.
Minute 40: reduce noise.
Minute 50: explain how you would ensure missed deadlines do not accumulate drift.
Look for: monotonic time, separation of I/O/estimation, schedule based on deadlines rather than repeated blind sleeps, dimensions, filter lag.
Prompt C — offline log + dirty data
Read a CSV of timestamp_ns,position_deg and output timestamp_ns,velocity_rad_s. Rows may be empty, malformed, duplicated, or out of order. Write code suitable for a large file.
Decisions
Stream vs sort? Skip vs fail? Header handling? Conversion degrees→radians and ns→seconds? Line-numbered errors? Make policies explicit.
Prompt D — robust windowed estimate
Position samples have Gaussian-ish noise and irregular timestamps. Implement velocity as a least-squares slope over the most recent N samples. Return None until at least two distinct timestamps exist.
Follow-ups
Use a time window rather than N.
Extend to multiple joints.
Discuss numerical stability for epoch-scale timestamps.
Prompt E — test-design discussion
You cannot access real hardware in CI. Design a test strategy for a velocity measurement component.
Deterministic fake servo driven by a known function q(t).
Injected clock rather than real sleep.
Unit tests for pure estimator.
Property tests: constant velocity; translation in time/position does not change result; scaling position scales velocity.
Recorded-log regression tests.
Hardware-in-loop smoke test for units, sign, rate, and wrap.
Pass target: 16/20 twice. Any score below 2 in correctness or communication requires focused repair even if total is high.
8. The 60-minute performance script
A time budget that keeps you from vanishing down a rabbit hole.
Minute
Action
Useful language
0–3
Listen, restate, clarify high-impact ambiguity.
“I’m hearing that we want signed angular velocity from sequential position readings. Before I choose the interface: do readings include timestamps and what are the units/wrap semantics?”
3–6
Give a thin plan and edge-case priority.
“I’ll make estimation pure and testable, verify two-point differentiation, then connect the servo loop. After that I’ll handle non-increasing time and whichever robustness case matters most.”
6–20
Implement working baseline.
Narrate structure and invariants, not every keystroke.
20–28
Run hand example/tests; fix real failures.
“For 0.5 rad in 0.25 s, I expect 2 rad/s. I’m testing the sign and units explicitly.”
28–43
Add requested I/O/extension.
“The baseline is working. Of filtering, multi-axis support, and malformed timestamps, which would you like me to prioritize?”
43–53
Edge cases, tests, cleanups.
“I’m choosing not to update state after a bad timestamp so one invalid reading doesn’t corrupt the next interval.”
53–58
Review behavior, complexity, limitations.
“This is O(1) memory and one pass. The main limitation is noise amplification; for telemetry I’d fit a short-window slope, while a controller needs a latency-aware choice.”
58–60
Invite questions.
“Would you like me to go deeper on timing accuracy, filtering, or how I’d validate against the real servo?”
Communication rules
Think in headlines. “I’m separating acquisition from estimation so tests need no hardware.”
Make assumptions visible. Do not hide uncertainty in code.
Test before polishing. A passing tiny example creates shared confidence.
When stuck: restate the invariant, create the smallest failing example, inspect values/units, then change one thing.
When interrupted: stop typing, listen, restate the change, name what remains valid, reprioritize.
Accept hints productively. “That makes sense; I’ll use a monotonic clock because we care about intervals, not civil time.”
Avoid: silently coding for 15 minutes; claiming real-time guarantees from ordinary Python; adding concurrency without need; using a filter you cannot explain; sorting a live out-of-order stream without discussion; confusing degrees and radians; reporting speed when signed velocity was requested.
9. Physical Intelligence context
Enough to connect your work to theirs—without cramming the entire research program.
What the public technical material says
Physical Intelligence develops general-purpose vision-language-action robot policies and publishes the experimental openpi codebase.
The openpi documentation describes multiple robot embodiments and action spaces. Joint angles use radians; common control frequencies listed are 20 Hz for UR5e/Franka and 50 Hz for ARX/Trossen, while DROID uses seven joint-velocity action dimensions at 15 Hz.
The π0.5 company post describes predicting a 50-step, one-second continuous low-level action chunk after a high-level subtask—an illustration of why runtime timing, action semantics, and reliable robot interfaces matter.
PI’s GitHub response says internal π0 data used joint positions, while DROID uses joint velocity; action/state representation is embodiment-dependent rather than universally unified.
A credible “why PI / why this task” answer
“I’m excited by the boundary between learned policies and physical systems. The model can be sophisticated, but its value depends on mundane runtime details being correct: timestamps, units, action representation, sampling jitter, validation, and failure behavior. A servo-velocity task is a small version of that boundary, and I like engineering where correctness has a physical meaning you can measure.”
Smart end-of-interview questions
“What are the hardest reliability problems at the boundary between learned action chunks and the robot runtime today?”
“How does the team decide what belongs in a shared runtime abstraction versus robot-specific integration code?”
“What does excellent performance in this role look like in the first three months?”
“How do you validate timing and action semantics across embodiments with different control rates and interfaces?”
“What surprised the team most when moving policies from offline evaluation onto real robots?”
10. Research trail and reading order
Primary sources first; public interview evidence clearly bounded.
Your recruiter email (primary, private): exact format clue—one hour, practical Python, measure servo velocity, no LeetCode.