I am an M.Sc. Mechatronics student at FAU Erlangen–Nürnberg,
writing my thesis on learned point-cloud compression:
reproducing and extending a state-of-the-art neural codec for 3D point
cloud attributes (sparse convolutions, multiscale entropy models,
variable-rate conditioning). The daily work is equal parts research and
engineering — rate-distortion evaluation you can defend, and training
campaigns that survive a shared SLURM cluster. I work primarily in
PyTorch (MinkowskiEngine for sparse 3D).
Before that I explored learned prediction inside control loops
(sEMG-driven tele-impedance — see the notes below); the through-line
of both projects is the same question: when can you trust a learned
model's numbers, and how do you build the instrumentation that tells
you?
Recent activity
— Released pcc-eval-toolkit: gate-checked Bjontegaard metrics, a defensible single-point metric for fixed-rate codecs, and RD overlay plots — the evaluation discipline my thesis reproduction forced me to build. repo →
— Released slurm-resilient-training: relay chains, durable checkpoints and single-writer guards that kept a multi-week point-cloud-codec training fleet alive on a 24h-walltime cluster with zero checkpoint loss. repo →
— Thesis reproduction milestone: our from-scratch training of a learned point-cloud attribute codec now matches the authors' released models on the standard 8iVFB benchmark under a cross-calibrated evaluation protocol (both evaluation lines verified against each other point-by-point). Write-up to follow with the thesis. notes →
— Started a self-paced deep-learning foundations refresher in parallel with thesis topic selection (target area: learned point-cloud / 3D Gaussian Splatting compression). Day 1 notes on what I caught myself getting wrong. notes →
— Ran a robustness ablation against the SINDy follow-up under non-polynomial sEMG (tanh saturation + fatigue drift + heteroscedastic noise). Prediction accuracy holds, but discovered equations nearly double in size — weakening the “sparse polynomial a human can read” framing from the day before. notes →
— Reframed SINDy as dynamics discovery + forward integration in the sEMG impedance project; sparse polynomial predictions beat LSTM at short horizons. repo →
— Posted a self-directed reflection on multimodal predictive systems for action-time prediction (below).
— Published an open-source design-space study on sEMG-driven impedance prediction (5 models × 3 horizons, ablations + MC-Dropout safety). repo →
Thesis: Learned Point-Cloud Attribute Compression
Point Cloud Attribute Compression with Improved Spatial Context Modeling
· FAU LMS · June–December 2026
The problem. Point clouds — millions of 3D points with
colours — are the raw material of AR/VR telepresence, autonomous driving
and cultural-heritage capture, and a single human-scale frame is tens of
megabytes uncompressed. MPEG standardises classical codecs for them (G-PCC);
the research frontier is learned compression, where a
sparse-convolutional network learns the entropy model end to end. My thesis
rebuilds a state-of-the-art learned attribute codec from
scratch and works on its entropy model.
What the codec actually is (what I wrote)
A whole pipeline, not a wrapper around someone's library:
Voxelisation and multiscale representation — points are
quantised onto an octree grid, giving a multiscale sparse tensor. The coarse
scale is a free prior for the fine one: the decoder has just
reconstructed it, so conditioning on it costs no bits.
Backbone — analysis and synthesis transforms built from
multiscale sparse convolution (MinkowskiEngine, evaluated only where
points exist, because 99.9% of the voxel grid is empty), coding the attribute
residual scale by scale.
Conditional entropy model — the network does not emit bits. It
predicts the distribution parameters of each latent (the location and
scale of a Laplacian), conditioned on the coarser scale and on already-decoded
colour channels.
Arithmetic coding — turns those predicted probabilities into an
actual bitstream. Rate is the cross entropy between the predicted distribution
and the true symbols, so the training objective and the final file size are
the same quantity, not a proxy for it.
Variable rate — one model covers the whole rate–distortion
curve instead of one model per rate point.
The authors did not release training code for the attribute branch, so
this pipeline was rebuilt from the paper and the MPEG contribution documents.
What is already verified
Bit-exact round-trip — encode → decode → re-encode
reproduces the bitstream byte for byte. This is the hardest available
test of whether an implementation is actually correct, and far stricter than
"the numbers look about right": the reconstruction may be lossy, the bitstream
may not drift.
Reproduced the authors' published rate–distortion table using their
released weights, point by point — which establishes that the machine
and the ruler are both right, and that my evaluation matches theirs. It is the
strongest calibration a reproduction can offer.
From-scratch training reaches a bitrate within +0.9% bpp of the
authors' published results (8iVFB v2).
Complexity is reported explicitly — encoding +9.7%, decoding
+14.2%, memory overhead ≈ 0. The improvement is built as a frozen
backbone plus an additive plug-in, because on an embedded accelerator memory is
the binding constraint.
The most interesting finding
From-scratch training started out clearly behind the published results. The gap
turned out not to be an implementation defect, the rate–distortion
weighting, the training schedule, or the capacity of the transform. It was
the density statistics of the training corpus.
The evidence is a rank reversal: with the same weights and the same
evaluation protocol, our model wins on our own corpus and the authors' model wins
on the standard test sequences. A ranking that flips with the corpus is a
statement about the data distribution, not about the code.
That produced a cheap diagnostic — a corpus shift probe: take
converged weights, fine-tune for a single epoch on a candidate corpus, and
measure how far the operating point is dragged. Before a week of GPU time goes
into it, you can tell whether a corpus is the distribution you will be evaluated
on. It touches only the dataset and contains no codec, which is why it can be
released on its own.
The same thread also mapped a failure boundary: the multiscale construction
degrades markedly on sparse point clouds — precisely the obstacle to
moving this family of methods from densely captured content to lidar data.
In progress, not yet a conclusion. Carrying the checkerboard spatial
context from learned image compression over to sparse point cloud attribute
entropy coding: split by coordinate parity, decode anchors first and non-anchors
second, aiming to keep the context gain while reducing decoding from
point-by-point serial to a constant number of passes. So far this is a
single sequence at a single rate point with a single seed —
that is not a BD-rate result and I do not report it as one. The full
rate–distortion evaluation is under way.
What this work actually runs on
PyTorchsparse convolution / MinkowskiEnginemultiscale & octree representationsconditional entropy modelsarithmetic coding (torchac)rate–distortion optimisationbit-exact conformance checkingMPEG G-PCC / AI-PCC common test conditionsTMC13pc_errorBD-rate / BD-PSNRHPC / SLURM parallel trainingreading and modifying C++ reference software
Notes
Not quite a blog. Just a place to leave the occasional write-up of
where my research thinking is, so I can point to it next time
someone asks.
What reproducing a learned codec taught me about honest evaluation
The headline task of my thesis sounds simple: take a state-of-the-art
learned point-cloud attribute codec, reproduce the paper's training
from scratch, then try to improve on it. Three months in, the thing I
would actually put on a slide is not a number — it is a list of ways
evaluation can quietly lie to you, and the instrumentation it took to
stop that.
Your benchmark is an instrument, and instruments need
calibration. Early on, our numbers and the paper's numbers
disagreed in ways that changed sign depending on which of our two
evaluation paths we used. The fix was metrological, not ML: run the
authors' released models through our pipeline and require
their published numbers to reproduce digit-for-digit before trusting
any measurement of our own models. That cross-calibration caught a
blocked-versus-whole-frame protocol difference, a color-space
convention mismatch at a model boundary, and a units convention in a
conditioning input — each worth more than a typical paper's claimed
improvement.
A refused metric is a result. BD-rate needs two
proper curves; a fixed-rate model gives you one point per sequence. My
first instinct — project the point onto the reference curve with a
local slope — was quietly assuming my own model's curve shape, and
my supervisor called it. The defensible version interpolates only the
reference curve, refuses to extrapolate, and says so in the plot
legend. I turned those refusal semantics into a small library, because
every compression project I have seen re-implements Bjontegaard badly
under deadline pressure.
Training reliability is an evaluation problem too.
Numbers are only comparable when the runs behind them are intact. On a
shared cluster with 24-hour walltime caps, keeping a fleet of 200+
epoch runs alive without a corrupted checkpoint took an explicit
design: chained jobs that reserve their successors, durable
checkpoints with epoch-indexed schedules, and a daemon enforcing that
one run never has two writers. All of it is in the second repo linked
above, with the incidents that motivated each piece.
The thesis results themselves — where the reproduction landed, and
what our checkpoint-averaging and finishing-schedule experiments
found — will be written up with the thesis. The tooling did not
need to wait.
Earlier research notes — control, teleoperation & sEMG (2026-05 archive)
Foundations refresher, day 1 — re-walking deep-learning basics before thesis kickoff
2026-05-26 — self-paced study, in parallel with thesis topic selection in learned point-cloud / 3D Gaussian Splatting compression
Before reading deeper into the learned-compression literature for thesis
work, I am running a structured re-walk through the foundations of deep
learning — slowly enough to actually look at each piece. The
PyTorch use in the sEMG project below sat on top of architectural priors
I had taken on trust; the goal of this refresher is to put those priors
back on first-principles ground before they become invisible scaffolding
under thesis-level work.
Day 1 covered the basics in one pass: what deep learning does mechanically
(rule-finding from examples rather than rule-writing), how a neural
network is structured (neuron → layer → depth as a feature
ladder), how training works (loss as a scalar, gradient descent as
blindfolded descent, learning rate as step size), and the overfitting /
underfitting distinction with the four standard remedies (more data, L2
regularisation, dropout, early stopping). The textbook material I won’t
rehash. What is worth recording from a day 1 is what I caught myself
getting wrong:
Negative weights. I had been carrying the implicit
picture that a “more important” feature gets a larger
weight. Working through a toy “should I go to the beach”
example forced the point that suppressing a decision is just
as legitimate as supporting it, and the way a network represents
“this feature pushes against the answer” is a weight with
negative sign and large magnitude. Trivial in hindsight; not how I had
been visualising it.
Depth as a strict ladder, not a soft metaphor. I knew
“deep” meant many layers, and that early layers learn
simple features. What I had not internalised is that each layer’s
input vocabulary is literally the previous layer’s output.
Edges → shapes → eye → face isn’t a slogan but the
actual data flow, and “eye” is just a stable activation in
some middle layer that the next layer uses as a primitive. That makes
depth a different kind of design choice than I had treated it as —
it controls the maximum composition height of the features the network
can express, not just the parameter count.
Underfitting ≠ a worse case of overfitting. Asked
to classify a hypothetical model with 70% train / 68% test accuracy,
I called it overfitting because both numbers looked bad. The correct
label is underfitting: both numbers being low and close
to each other points to insufficient capacity, not memorisation.
The two diseases need opposite treatments — underfitting wants
more capacity or longer training; overfitting wants regularisation,
dropout, or more data. Catching this confusion now is much cheaper
than catching it later, embedded in a real experiment where the data
and the architecture are both moving.
Day 2 will move into hand-writing a small model in PyTorch to make
the learning-rate and overfitting points concrete; from there into
CNNs, then into the learned-compression specifics (autoencoders,
quantisation, entropy coding, hyperprior) that the thesis area runs on.
Substantive updates to this site will probably come when those last
pieces start to load.
Robustness check: does SINDy still win when the generator isn’t polynomial?
2026-05-19 — ablation on the SINDy follow-up below
After writing the SINDy follow-up below I went back to a concern that
had been sitting at the edge of my own thinking. The synthetic
generator I used mixes a constant linear matrix from
stiffness to sEMG and adds additive heteroscedastic-in-sEMG noise.
That data-generating process sits structurally inside SINDy’s
degree-2 polynomial hypothesis class. A reader who has worked with
sparse-regression methods would reasonably ask: did SINDy win because
it discovered the generator, or because the generator happened to be
in its model class? Until I checked, I couldn’t tell.
So I ran an ablation. Trajectories unchanged; the sEMG observation
model gains three pieces of structure SINDy cannot fit cleanly:
(1) tanh saturation on the linear mixing
(emg = tanh(2·W·K)), modelling motor-unit
recruitment plateaus; (2) slow fatigue drift on W
(W_eff(t) = W₀ + 0.25·ΔW·sin(0.2π·t)),
making the observation map time-varying; (3) a
state-dependent noise floor
(σ ∝ σ₀ + 0.08·‖K‖) on top
of the existing amplitude-proportional component. Same 6/2 split,
same horizons, same SINDy hyperparameters. Ridge, SINDy, ESN, and an
MLP baseline (MLPRegressor with hidden_layer_sizes=(48, 24),
substituting for the PyTorch LSTM in the easy run so this ablation
runs in a torchless environment; both play the same “high-capacity
neural baseline” role) all run on both generators.
Stiffness error at the 100 ms horizon. SINDy is the only method whose error does not change materially between the two generators (+6%); Ridge, ESN, and MLP all degrade by 40–80%. Full 4×3×2 table (position + stiffness, three horizons, four methods, both generators) on GitHub.
The good news. SINDy as a predictor holds up. Position
error is essentially unchanged across the two generators (3.9 / 3.9
mm at 50 ms, 7.0 / 7.0, 12.4 / 12.4). Stiffness degrades only
marginally (8.5→8.9, 16.2→17.1, 29.2→31.3). The other
three methods take a clean hit on stiffness: Ridge +49%, ESN +42%,
MLP +82% at 100 ms. The mechanism, I think, is that SINDy’s
discovered position dynamics rely heavily on state-based extrapolation
(dx/dt ends up mostly a function of state, not sEMG),
so corrupting the sEMG observation barely touches the prediction.
The qualification I owe the SINDy follow-up below.
The discovered equations grow. On the easy generator SINDy kept
[1, 5, 3, 12, 12, 11] nonzero terms across the six state components
(44 total). On the hard generator it keeps [5, 11, 7, 22, 22, 16]
(83 total) — nearly twice as dense. The “sparse
polynomial a human can read” framing I leaned on in the post
below becomes much weaker once the sEMG isn’t linear: a
22-term degree-2 polynomial is still much smaller than the full
feature library, but it is no longer the kind of equation you would
print on a slide and reason about by eye. So that artefact-as-
interpretation claim was specific to the linear sEMG–K map;
under realistic sEMG nonlinearity, SINDy still works as a predictor,
but it works as a denser curve-fit, not as a transparent equation.
What this updates in my own thinking. Two things.
First, on prediction robustness I owe SINDy more credit than I would
have given it yesterday — the accuracy genuinely doesn’t
care about the kind of observation-model violations I expected to
break it. Second, the broader point I had been pulling toward
— that the value of a “discoverable” dynamics
method is the readable artefact, not the headline number — is
not free. The artefact is only readable when the data-generating
process itself is close to sparse-polynomial. Real human sEMG
presumably isn’t, and that is the test the result above does
not pass. Code:
sindy_robustness.py.
Discovering equations vs fitting them: a SINDy follow-up
2026-05-18 — sEMG impedance prediction, update (see the robustness check above for an important qualification on the “sparse, readable equation” claim)
I went back to the same simulated peg-in-hole data and put
SINDy in the comparison, this time the way it
is actually meant to be used: learn the differential equations
d(state)/dt = f(state, sEMG) directly from data,
keep only the sparse terms via Lasso, and integrate the current
state forward to the prediction horizon. Not static regression.
The result inverted my expectation. On position error,
SINDy gives 3.9 mm at 50 ms and 7.0 mm
at 100 ms — roughly twice as good as the
next method (Ridge) at both windows. ESN takes over at
200 ms with 10.6 mm. On stiffness, SINDy is best at
every horizon. LSTM, with ~32 k parameters against
~3 k training samples, finishes last across the board
(22–24 mm) — the textbook overfitting regime,
and simulation does not produce the kind of long-tail
nonlinearity that would force a network to earn its capacity.
What I keep coming back to is not the numbers. It is the
artefact: SINDy keeps 1 nonzero term in
dx/dt, 5 in dy/dt, 3 in
dz/dt, and 11–12 in each stiffness component.
These are sparse polynomials a human can read.
I can print the equation, change one coefficient, see the
effect. That is a different research object than the hidden
state of an LSTM.
Where this leaves me: when data is consistent with a compact
set of equations, finding those equations is more
honest than fitting an input-output map. Whether real
human-in-the-loop sEMG behaves that way is the next thing I want
to find out. Code and full numbers:
semg-impedance-prediction
on GitHub.
Predicting What the Operator Means: A Design Sketch for Physics-Constrained Tele-Impedance Delay Compensation
2026-05-15 — self-directed study, written while preparing thesis applications
This is a research proposal I have been sketching on my own
while applying for thesis topics in this area. It is not an
ongoing project — there is no trained model yet. I worked it
through end-to-end as a way of stress-testing my own understanding
before submitting applications that ask exactly this kind of
question.
In teleoperation, communication delays of 50–200 ms are
unavoidable, and they make the remote robot react late to the
operator's intent. The question I keep coming back to:
can deep learning predict the operator's future trajectory
and joint stiffness from their surface EMG, far enough in advance
to mask that delay — without breaking safety guarantees?
The answer, as I have read into the literature, looks like a
layered system rather than a single black box. Surface EMG already
leads force output by 30–80 ms (well documented in the sEMG-force literature), and
energy-observer safety nets from the teleoperation literature
catch the worst case. What seems to be missing is the middle
layer: a learned model that explicitly predicts intent 100–500 ms
into the future, slotted in between the natural sEMG lead and the
safety controller.
Proposed end-to-end architecture (no model trained yet): a learned component would occupy a precise gap between the natural sEMG lead and the classical safety controller.
Architecture I sketched. A pre-trained encoder
(NinaPro, 40 subjects) consumes 8-channel sEMG plus position and
velocity (14-dim input). Channel attention reweights the muscle
channels; a 2-layer LSTM (hidden = 48) tracks dynamics;
temporal attention summarises the recent window; softplus heads
produce the next trajectory and a positive-definite stiffness
vector. A physics-informed loss penalises the rate-of-change of
stiffness, so the model can't cheat by predicting wild swings.
Ablation plan. Before fixing the architecture, the
right move is a systematic comparison of five sequence models —
Linear, 1D-CNN, GRU, LSTM, TCN — and then ablations over hidden
size, depth, attention placement, and input modality (raw vs.
filtered sEMG, with vs. without position and velocity).
Pre-training would be followed by leave-one-subject-out
fine-tuning so that any cross-user numbers stay honest.
Trust the model, but verify. Uncertainty would be
estimated with MC-Dropout. When confidence drops, the system
falls back to a classical energy-based safety controller — the learned
prediction is only used when it has earned it.
What I like about this problem is the cleanliness of the
separation: physics provides a hard prior (positive stiffness,
bounded rate-of-change), a classical controller provides a safety
floor, and deep learning fills a well-defined gap (a multi-step-ahead
horizon that adaptive filters can't reach). The point isn't
"deep learning everywhere" — it is deciding precisely where
in the loop a learned component earns its place. That kind of
decision-making, more than any single architecture, is what I
want to keep working on.
Toward Multimodal Predictive Systems for Action-Time Prediction
2026-05-17 (updated 2026-05-18) — self-directed reflection, building on the SINDy follow-up
The SINDy follow-up on the sEMG impedance design-space study (see
Discovering equations vs fitting them above) left me with a
sharper version of a question I had only been gesturing at before. The
follow-up replaced LSTM-with-architectural-priors as the protagonist
with SINDy used as a dynamics learner — learn
d(state)/dt = f(state, sEMG) from data, keep only sparse
terms via Lasso, integrate forward to the target horizon. On the same
synthetic peg-in-hole data, SINDy gave 3.9 mm at 50 ms and
7.0 mm at 100 ms — roughly twice as good as the next
method — while LSTM, with ~32 k parameters against ~3 k
samples, finished last across the board.
Position error on a fixed 6/2 train/test split (users 0–5 train, users 6–7 test), shared across all four methods. Note this differs from the original 5-method study, which used leave-one-subject-out; the LSTM number here (24.3 mm at 200 ms) and the LSTM number in the earlier study (28.5 mm at 200 ms) are not directly comparable.
The numbers matter, but what stuck with me is the artefact.
SINDy keeps 1 nonzero term in dx/dt, 5 in
dy/dt, 3 in dz/dt. The full prediction model
fits on one page; each equation is a sparse polynomial a human can read,
falsify, and retrain in seconds. When the prediction is wrong at
200 ms, you can look at the equation and tell where the assumption
broke. That is a qualitatively different research object from the hidden
state of a recurrent network.
This is what reframes the original “safety fallback” problem
for me. The earlier instinct was that even a low-error learned model has
to defer to a classical, energy-based safety controller as a backup,
because no one knows how to be accountable for what a black box would
do in a situation no one has thought through. Causal ML is the
obvious candidate, and I do not want to dismiss it — I just
have not yet, in my reading so far, found a clean way to fit a
causal-graph formulation into the kind of inner control loop this
problem lives in; that read is provisional. But the SINDy result
points to a different path I had not considered before: don’t bolt an
interpretability layer onto a black-box prediction, don’t try to
constrain it from the outside — make the prediction object
itself something you can inspect.
The direction that pulls me — and I want to be upfront, it is an
area I am only beginning to read into — is multimodal
predictive systems in which the model’s belief about the
future is itself a physically grounded, observable, checkable artefact.
Sparse-polynomial discovery from sensor data is one minimal example: a
200 ms prediction is a few lines of algebra you can step through.
Learned physical simulators are another: a predicted half-second unfolds
in 3D and you watch it. They share the property that “is this
prediction safe to act on?” can be answered by inspecting the
prediction itself, not by adding an external filter beside it.
That reframing changes the problem from “make the AI prediction
more interpretable” to “make the AI’s future
the thing we inspect”. I do not pretend to know which architectural
family — diffusion priors, video transformers, learned simulators,
neural physics — does this best, or whether the framing survives
contact with real human sEMG, which has cross-talk, fatigue drift, and
motion artifacts the simulator does not produce. The next phase is to
find out. That is exactly why it is the direction I want to spend it on.