Python port of the Julia 0.4 notebooks from Grundlagen der Robotik,
Universität Leipzig, SS 2016 (github.com/kzahedi/Robotik).
The originals are Julia 0.4.5 and run on no current Julia. They are kept
verbatim under julia-2016/ as the porting reference and the
source of the regression oracles.
conda create -y -n robotik python=3.11 numpy scipy matplotlib pytest jupyterlab jupytext ipykernel
conda activate robotik
pip install -e ".[dev,notebooks]"Ohne conda: pip install -e ".[dev,notebooks]" installiert zusätzlich den
Jupyter-Stack (jupyterlab, jupytext, ipykernel).
Starten mit jupyter lab und dann notebooks/ öffnen.
Die *_demo.ipynb-Spielplätze brauchen für Interaktivität das [notebooks]-Extra;
ohne es fallen sie auf statische Bilder zurück. Die Vorlesungsnotebooks enthalten
„Selbst implementieren“-Rümpfe, deren Prüfzellen sofortiges Feedback geben. Falls
Regler nur als Text erscheinen (VBox(...)): JupyterLab neu starten — der
Widget-Stack wird beim Serverstart geladen, ein Kernel-Neustart genügt nicht.
notebooks/stil_galerie_demo.ipynb zeigt alle sechs gestylten Demo-Diagramme
(robotik.demo_plots) einmal nebeneinander an echten Daten.
Numbering matches the slide decks, so 6_Zustandsschaetzung.pdf sits next to
06_zustandsschaetzung.ipynb.
| Notebook | Foliensatz | Inhalt |
|---|---|---|
02_kinematik |
2 | Transformationsmatrizen, Vorwärtskinematik, CCD, Jacobi (Spielplatz: 02_kinematik_demo.ipynb) |
03_dynamik |
3 | Pendel, Phasenraum, chaotisches Pendel, Einzugsgebiete (Spielplatz: 03_dynamik_demo.ipynb) |
06_zustandsschaetzung |
6 | Kalman-Filter: Kanonenkugel und GPS-Glättung, plus roboterzentrierte Filter-Animation mit schrumpfender Kovarianzellipse (Spielplatz: 06_zustandsschaetzung_demo.ipynb) |
07_radgetriebene_systeme |
7 | Differentialantrieb, Omni-Antrieb, Holonomie (Spielplatz: 07_radgetriebene_systeme_demo.ipynb) |
10_embodied_ai |
10 | Rekurrentes Neuron, Bifurkation, Hysterese (Spielplatz: 10_embodied_ai_demo.ipynb) |
Each notebook has the same shape: Lernziele → Setup → Themen → Aufgaben.
Every section runs on its own once Setup has run, so a misbehaving demo can be
skipped without restarting the kernel. Solutions are in solutions/ and can be
released separately.
Decks 1, 4, 5, 8, 9 and 11 have no notebook yet — they had no code in 2016.
.ipynb is the source of truth; jupytext keeps a py:percent mirror for
readable diffs. Edit either side, then:
jupytext --sync notebooks/*.ipynb solutions/*.ipynbconda activate robotik
python -m pytest -qThe suite needs no Jupyter kernel — notebook cells are executed directly under the Agg backend. 142 tests, roughly 4-5 minutes.
Three values printed by the 2016 notebooks survive as genuine numeric oracles and are pinned:
A(π, 1, -π, 2)— the 4×4 Denavit-Hartenberg matrixForwardKinematics([0,1,3])— the 4×3 joint-position matrixInverseKinematicsCCD([1,2], [π/2, 0.01, 0.01])→[1.5608, 0, 0]
Nothing else was recoverable: 181 of the stored outputs are merely
PyPlot.Figure(...) repr strings (167 of those from the omni-wheel notebook's
per-frame plotting loop alone). Everything else is covered by invariants —
rotation orthogonality, link-length preservation, planarity, reachability,
analytic Jacobian vs. central differences, energy conservation, small-angle
period, chaotic divergence, Kalman RMSE improvement and covariance positive
definiteness, wheel-speed round-tripping, neuron boundedness. These target the
failure mode a Julia→Python port is prone to: 1-based → 0-based index slips,
which corrupt results silently rather than raising.
Notebook-level tests additionally check top-to-bottom execution, per-section
independence, absence of absolute paths, mirror freshness, and that no notebook
redefines an algorithm that belongs in robotik/.
- Link lengths differ between notebooks. Forward Kinematics uses
(2, 0.75, 0.5), both inverse-kinematics notebooks(2, 1, 0.75). Preserved, not unified — each notebook's stored output only reproduces with its own set. Constants:LENGTHS_FK,LENGTHS_IK. - CCD's asymmetric step. The original subtracts the clamped angle
min(δ, acos(cθ))but adds the rawδ. The oracle depends on it. - The wheeled y-sign.
body_stepnegates the y component, as in 2016. Removing it mirrors every trajectory. - The GPS filter is mis-specified: measurement noise is σ = 1 while
Ris 1e-4. It still smooths, because only the ratio Q/R matters. Kept as a teaching point — Aufgabe 3 of notebook 06. - The cannonball filter's
dtmismatch. Its model matrixAscales velocity bydtbut the 2016 simulator advances position by the raw velocity each step, with nodtfactor — a factor-of-10 inconsistency invisible at the notebook's Q/R and kept verbatim so the numbers match 2016.
Neuron.ipynbwrote its backward bias sweep to column100+sinstead oflength(n)+s, overwriting most of the forward sweep and corrupting the hysteresis figure. It also wrote both sweeps to the same CSV filename. The port returns the two sweeps separately (sweep_bias_hysteresisreturns(forward, backward)).
ODE.jl'sode45→scipy.integrate.solve_ivp(RK45); ODE.jl has been unmaintained since roughly 2018.eye(n)→np.eye(n);readcsv/writecsv→ not needed;squeezeon row slices removed (a Julia 0.4 slice was a 1×N matrix, a numpy slice is a vector).- Per-frame
savefig("/Users/zahedi/Desktop/...")calls inside plotting loops (up to 1250 in a single loop, in the omni-wheel notebook) and the shellzipstep that followed them, replaced by in-notebook plots andplotting.animate_poses(). - The chaotic-pendulum basin scan defaulted to 1001×1001 = 1,002,001
integrations.
basin_of_attractiontakes astepparameter, defaulting to a grid that runs in seconds. - Globals (
gravity,dt,L,g) became function parameters.