diff --git a/.$api.drawio.bkp b/.$api.drawio.bkp
new file mode 100644
index 0000000..4143acf
--- /dev/null
+++ b/.$api.drawio.bkp
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.$architecture.drawio.bkp b/.$architecture.drawio.bkp
new file mode 100644
index 0000000..4ee36d9
--- /dev/null
+++ b/.$architecture.drawio.bkp
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.$chaine-traitement.drawio.bkp b/.$chaine-traitement.drawio.bkp
new file mode 100644
index 0000000..52de10c
--- /dev/null
+++ b/.$chaine-traitement.drawio.bkp
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.$compilation.drawio.bkp b/.$compilation.drawio.bkp
new file mode 100644
index 0000000..cf904db
--- /dev/null
+++ b/.$compilation.drawio.bkp
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index f9b1d30..5c561f8 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,54 +1,102 @@
-FROM mcr.microsoft.com/devcontainers/base:ubuntu
+# syntax=docker/dockerfile:1
+FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04
-# Install dependencies for TeX Live installation and usage
-RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
+LABEL org.opencontainers.image.title="LaTeX Dev Container" \
+ org.opencontainers.image.description="Full TeX Live + LaTeX Workshop dev container" \
+ org.opencontainers.image.source="https://github.com/khannurien/latex-devcontainer" \
+ org.opencontainers.image.licenses="MIT"
+
+ARG USERNAME=vscode
+ARG TEXLIVE_YEAR=2026
+# Point this at a frozen snapshot (e.g. the TUG historic tlnet-final tree for a
+# given year) once TEXLIVE_YEAR is no longer the current release.
+ARG TEXLIVE_REPO="https://mirror.ctan.org/systems/texlive/tlnet"
+# Docs are what `texdoc` reads; sources are almost never needed.
+ARG INSTALL_DOCFILES=1
+ARG INSTALL_SRCFILES=0
+
+# Runtime dependencies. Note: biber, latexmk, latexindent and chktex all ship
+# with scheme-full, so they are deliberately NOT installed from apt -- the apt
+# versions routinely disagree with the TeX Live ones (biblatex/biber in
+# particular). What apt provides here is what TeX Live cannot: perl itself, the
+# perl modules latexindent needs, ghostscript for pdfcrop/ps2pdf/epstopdf, and
+# fontconfig so xelatex/lualatex can see system fonts.
+RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
perl \
- biber \
wget \
+ ca-certificates \
fontconfig \
+ ghostscript \
make \
git \
curl \
python3 \
python3-pygments \
+ libyaml-tiny-perl \
+ libfile-homedir-perl \
+ libunicode-linebreak-perl \
+ liblog-log4perl-perl \
+ liblog-dispatch-perl \
&& rm -rf /var/lib/apt/lists/*
-# Set non-root user provided by Microsoft base image
-ARG USERNAME=vscode
-
-# Switch to user home directory
-USER $USERNAME
-WORKDIR /home/$USERNAME
-
-# Set TeX Live paths and update environment
ENV TEXLIVE_INSTALL_PREFIX="/usr/local/texlive"
-ENV TEXLIVE_INSTALL_TEXDIR="${TEXLIVE_INSTALL_PREFIX}/2025"
-ENV PATH="${TEXLIVE_INSTALL_TEXDIR}/bin/x86_64-linux:${PATH}"
+ENV TEXLIVE_INSTALL_TEXDIR="${TEXLIVE_INSTALL_PREFIX}/${TEXLIVE_YEAR}"
-# Download and install TeX Live 2025 as root
-RUN wget -q https://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz && \
- tar -xzf install-tl-unx.tar.gz && \
- rm install-tl-unx.tar.gz && \
- cd install-tl-* && \
- echo "selected_scheme scheme-full" > texlive.profile && \
- echo "TEXDIR ${TEXLIVE_INSTALL_TEXDIR}" >> texlive.profile && \
- echo "TEXMFCONFIG /home/vscode/.texlive2025/texmf-config" >> texlive.profile && \
- echo "TEXMFVAR /home/vscode/.texlive2025/texmf-var" >> texlive.profile && \
- echo "instopt_adjustpath 1" >> texlive.profile && \
- echo "tlpdbopt_autobackup 0" >> texlive.profile && \
- sudo perl ./install-tl --profile=texlive.profile --no-interaction && \
- cd .. && rm -rf install-tl-*
+# Install TeX Live as root. instopt_adjustpath symlinks the binaries into
+# /usr/local/bin, which keeps the image architecture-agnostic.
+RUN set -eux; \
+ cd /tmp; \
+ # mirror.ctan.org redirects to a *different* random mirror on every request, so
+ # resolve it once and take the tarball, its checksum and the packages all from
+ # that same host. Retry because individual mirrors have broken TLS or lag sync.
+ for attempt in 1 2 3 4 5; do \
+ rm -f install-tl-unx.tar.gz install-tl-unx.tar.gz.sha512; \
+ resolved="$(curl -fsSL --retry 3 -o install-tl-unx.tar.gz.sha512 \
+ -w '%{url_effective}' "${TEXLIVE_REPO}/install-tl-unx.tar.gz.sha512")" || continue; \
+ base="${resolved%/*}"; \
+ curl -fsSL --retry 3 -o install-tl-unx.tar.gz "${base}/install-tl-unx.tar.gz" || continue; \
+ if sha512sum -c install-tl-unx.tar.gz.sha512; then TL_REPO="${base}"; break; fi; \
+ echo "attempt ${attempt}: checksum mismatch from ${base}, retrying"; \
+ done; \
+ test -n "${TL_REPO:-}"; \
+ echo "using mirror: ${TL_REPO}"; \
+ tar -xzf install-tl-unx.tar.gz; \
+ cd install-tl-*/; \
+ { \
+ echo "selected_scheme scheme-full"; \
+ echo "TEXDIR ${TEXLIVE_INSTALL_TEXDIR}"; \
+ echo "TEXMFCONFIG /home/${USERNAME}/.texlive${TEXLIVE_YEAR}/texmf-config"; \
+ echo "TEXMFVAR /home/${USERNAME}/.texlive${TEXLIVE_YEAR}/texmf-var"; \
+ echo "TEXMFHOME /home/${USERNAME}/texmf"; \
+ echo "instopt_adjustpath 1"; \
+ echo "tlpdbopt_autobackup 0"; \
+ echo "tlpdbopt_install_docfiles ${INSTALL_DOCFILES}"; \
+ echo "tlpdbopt_install_srcfiles ${INSTALL_SRCFILES}"; \
+ } > texlive.profile; \
+ perl ./install-tl --profile=texlive.profile --no-interaction --repository "${TL_REPO}"; \
+ cd /tmp; rm -rf install-tl-*; \
+ ln -s "$(ls -1 "${TEXLIVE_INSTALL_TEXDIR}/bin" | head -n1)" "${TEXLIVE_INSTALL_TEXDIR}/bin/default"; \
+ mkdir -p "/home/${USERNAME}/.texlive${TEXLIVE_YEAR}/texmf-config" \
+ "/home/${USERNAME}/.texlive${TEXLIVE_YEAR}/texmf-var" \
+ "/home/${USERNAME}/texmf"; \
+ chown -R "${USERNAME}:${USERNAME}" "/home/${USERNAME}/.texlive${TEXLIVE_YEAR}" "/home/${USERNAME}/texmf"
-# User-writeable directories for TeX Live and LuaLaTeX
-ENV TEXMFCONFIG="/home/vscode/.texlive2025/texmf-config"
-ENV TEXMFVAR="/home/vscode/.texlive2025/texmf-var"
-ENV LUAOTFLOAD_HOME="/home/vscode/.texlive2025/texmf-var/luatex-cache"
-ENV LUAOTFLOAD_DB="/home/vscode/.texlive2025/texmf-var/luatex-cache/database.sqlite"
+# Stable, architecture-independent path to the engines.
+ENV PATH="${TEXLIVE_INSTALL_TEXDIR}/bin/default:${PATH}"
+ENV TEXMFCONFIG="/home/${USERNAME}/.texlive${TEXLIVE_YEAR}/texmf-config"
+ENV TEXMFVAR="/home/${USERNAME}/.texlive${TEXLIVE_YEAR}/texmf-var"
+ENV TEXMFHOME="/home/${USERNAME}/texmf"
-# Install latexmk
-RUN sudo tlmgr option autobackup 0 && \
- sudo tlmgr install latexmk && \
- sudo tlmgr path add
+USER $USERNAME
+
+# Warm the luaotfload font database so the first lualatex run isn't a minutes-long
+# font scan, and fail the build early if the toolchain is broken.
+RUN set -eux; \
+ tlmgr --version; \
+ lualatex --version >/dev/null; \
+ biber --version >/dev/null; \
+ latexmk --version >/dev/null; \
+ latexindent --version >/dev/null; \
+ luaotfload-tool --update --quiet
-# Set the working directory back to a neutral location
WORKDIR /workspace
diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json
new file mode 100644
index 0000000..32ffe06
--- /dev/null
+++ b/.devcontainer/devcontainer-lock.json
@@ -0,0 +1,19 @@
+{
+ "features": {
+ "ghcr.io/khannurien/devcontainer-features/claude-code:1": {
+ "version": "1.2.0",
+ "resolved": "ghcr.io/khannurien/devcontainer-features/claude-code@sha256:17eb69bfd15ff5206c0bff2945935a4349f7423cabee461a0ba2681d52205db9",
+ "integrity": "sha256:17eb69bfd15ff5206c0bff2945935a4349f7423cabee461a0ba2681d52205db9"
+ },
+ "ghcr.io/khannurien/devcontainer-features/opencode:1": {
+ "version": "1.1.0",
+ "resolved": "ghcr.io/khannurien/devcontainer-features/opencode@sha256:8df3f8c98a104ce7e089ba3e8df39e0d3890753c8dc92e03deb54527d30d3574",
+ "integrity": "sha256:8df3f8c98a104ce7e089ba3e8df39e0d3890753c8dc92e03deb54527d30d3574"
+ },
+ "ghcr.io/khannurien/devcontainer-features/rtk:1": {
+ "version": "1.1.0",
+ "resolved": "ghcr.io/khannurien/devcontainer-features/rtk@sha256:4a60b3a1d1b64904de3d856c61cdf7e5943e3c74d114609787dc88d9db7c788f",
+ "integrity": "sha256:4a60b3a1d1b64904de3d856c61cdf7e5943e3c74d114609787dc88d9db7c788f"
+ }
+ }
+}
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 91d468c..de08d3b 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,9 +1,16 @@
{
"name": "LaTeX Dev Container",
- "build": {
- "dockerfile": "Dockerfile",
- "context": ".."
- },
+ "image": "ghcr.io/khannurien/latex-devcontainer:2026.08",
+ // "build": {
+ // "dockerfile": "Dockerfile",
+ // "context": ".."
+ // },
+ "remoteUser": "vscode",
+ "updateRemoteUserUID": true,
+ // Local extensions, packaged and installed by the script below:
+ // .devcontainer/overleaf-keymap -- Overleaf's keyboard shortcuts
+ // .devcontainer/paste-figure -- Ctrl+V an image -> figure environment
+ "postCreateCommand": "bash .devcontainer/install-extensions.sh",
"customizations": {
"vscode": {
"settings": {
@@ -26,7 +33,18 @@
"latex-workshop.intellisense.package.enabled": true,
"latex-workshop.intellisense.unimathsymbols.enabled": true,
"latex-workshop.intellisense.citation.backend": "biblatex",
+ // Linting and formatting (chktex and latexindent ship with TeX Live)
+ "latex-workshop.chktex.enabled": true,
+ "latex-workshop.chktex.run": "onSave",
+ "latex-workshop.formatting.latex": "latexindent",
+ "[latex]": {
+ "editor.formatOnSave": true
+ },
"latex-workshop.latex.outDir": "build",
+ "latex-workshop.latex.clean.subfolder.enabled": true,
+ "search.exclude": {
+ "**/build": true
+ },
"latex-workshop.latex.autoBuild.run": "onFileChange",
"latex-workshop.latex.autoBuild.interval": 3000,
"latex-workshop.synctex.afterBuild.enabled": true,
@@ -107,15 +125,17 @@
]
}
],
- "latex-workshop.latex.recipe.default": "lastUsed"
+ "latex-workshop.latex.recipe.default": "lastUsed",
+ // Overleaf-style image paste, from .devcontainer/paste-figure. Ctrl+V (or a
+ // drag & drop) of an image into a .tex file writes it here and inserts a
+ // figure environment around it; edit "pasteFigure.template" to change the
+ // block that gets inserted.
+ "pasteFigure.directory": "img"
},
"extensions": [
"james-yu.latex-workshop",
- "wayou.vscode-todo-highlight"
+ "jgclark.vscode-todo-highlight"
]
}
- },
- "remoteUser": "vscode",
- "updateRemoteUserUID": true,
- "features": {}
+ }
}
diff --git a/.devcontainer/install-extensions.sh b/.devcontainer/install-extensions.sh
new file mode 100644
index 0000000..6177e6e
--- /dev/null
+++ b/.devcontainer/install-extensions.sh
@@ -0,0 +1,103 @@
+#!/usr/bin/env bash
+# Install this project's local VS Code extensions into the container's VS Code server.
+#
+# devcontainer.json has no place for keyboard shortcuts or editor behaviour beyond
+# settings: customizations.vscode only understands "settings" and "extensions", and
+# keybindings.json is resolved by the VS Code client, not by the server -- so
+# dropping a file into ~/.vscode-server/data/User/ does nothing. What *does* travel
+# with the container is an extension. So each directory listed below is packaged as
+# a .vsix here and installed; edit those directories to change behaviour, this
+# script only ships them.
+set -euo pipefail
+
+here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+extensions=(
+ overleaf-keymap # Overleaf's keyboard shortcuts
+ paste-figure # Ctrl+V an image -> saved to img/ and wrapped in a figure
+)
+
+# The Dev Containers extension installs customizations.vscode.extensions around
+# the same time postCreateCommand fires, and both writes touch extensions.json.
+# Wait (bounded) for its marker so we don't interleave with it.
+marker="$HOME/.vscode-server/data/Machine/.installExtensionsMarker"
+for _ in $(seq 1 60); do
+ [ -e "$marker" ] && break
+ sleep 1
+done
+
+# The remote CLI isn't on PATH during postCreateCommand; find the server binary.
+code_server="$(ls -1t "$HOME"/.vscode-server/bin/*/bin/code-server 2>/dev/null | head -n1 || true)"
+if [ -z "$code_server" ]; then
+ echo "install-extensions: no VS Code server found, skipping local extensions." >&2
+ exit 0
+fi
+
+build="$(mktemp -d)"
+trap 'rm -rf "$build"' EXIT
+
+for name in "${extensions[@]}"; do
+ src="${here}/${name}"
+ vsix="${build}/${name}.vsix"
+
+ python3 - "$src" "$vsix" <<'PY'
+import json, mimetypes, os, sys, zipfile
+from xml.sax.saxutils import escape
+
+src, out = sys.argv[1], sys.argv[2]
+pkg = json.load(open(f"{src}/package.json"))
+ident, version, publisher = pkg["name"], pkg["version"], pkg["publisher"]
+kinds = ",".join(pkg.get("extensionKind", ["workspace"]))
+
+manifest = f"""
+
+
+
+ {escape(pkg["displayName"])}
+ {escape(pkg["description"])}
+ {escape(",".join(pkg.get("categories", [])))}
+ {escape(",".join(pkg.get("categories", [])))}
+ Public
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+# Every file of the extension goes in, so extensions can ship code, not just a manifest.
+files = []
+for root, _, names in os.walk(src):
+ for name in sorted(names):
+ path = os.path.join(root, name)
+ files.append((path, os.path.relpath(path, src).replace(os.sep, "/")))
+
+defaults = {"json": "application/json", "js": "application/javascript",
+ "vsixmanifest": "text/xml", "xml": "text/xml"}
+for path, _ in files:
+ ext = os.path.splitext(path)[1].lstrip(".").lower()
+ if ext and ext not in defaults:
+ defaults[ext] = mimetypes.guess_type(path)[0] or "application/octet-stream"
+
+content_types = "\n".join(
+ ['',
+ '',
+ *[f' ' for ext, ctype in sorted(defaults.items())],
+ '', ''])
+
+with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:
+ z.writestr("extension.vsixmanifest", manifest)
+ z.writestr("[Content_Types].xml", content_types)
+ for path, rel in files:
+ z.write(path, f"extension/{rel}")
+PY
+
+ "$code_server" --install-extension "$vsix" --force
+done
diff --git a/.devcontainer/overleaf-keymap/package.json b/.devcontainer/overleaf-keymap/package.json
new file mode 100644
index 0000000..a356c1b
--- /dev/null
+++ b/.devcontainer/overleaf-keymap/package.json
@@ -0,0 +1,144 @@
+{
+ "name": "overleaf-keymap",
+ "displayName": "Overleaf Keymap",
+ "description": "Makes VS Code's keyboard shortcuts behave like Overleaf's.",
+ "publisher": "local",
+ "version": "1.0.0",
+ "license": "MIT",
+ "engines": {
+ "vscode": "^1.75.0"
+ },
+ "categories": [
+ "Keymaps"
+ ],
+ "extensionKind": [
+ "workspace"
+ ],
+ "contributes": {
+ "keybindings": [
+ {
+ "//": "Overleaf: Ctrl+B -> \\textbf{...}. Only inside LaTeX files, so Ctrl+B still toggles the sidebar everywhere else.",
+ "key": "ctrl+b",
+ "mac": "cmd+b",
+ "command": "latex-workshop.shortcut.textbf",
+ "when": "editorTextFocus && !editorReadonly && editorLangId =~ /^latex$|^latex-expl3$|^doctex$|^bibtex$|^rsweave$/"
+ },
+ {
+ "//": "Overleaf: Ctrl+I -> \\textit{...}",
+ "key": "ctrl+i",
+ "mac": "cmd+i",
+ "command": "latex-workshop.shortcut.textit",
+ "when": "editorTextFocus && !editorReadonly && editorLangId =~ /^latex$|^latex-expl3$|^doctex$|^bibtex$|^rsweave$/"
+ },
+ {
+ "//": "Overleaf: Ctrl+Enter -> compile. (Ctrl+S also compiles here, via latex-workshop.latex.autoBuild.run = onFileChange.)",
+ "key": "ctrl+enter",
+ "mac": "cmd+enter",
+ "command": "latex-workshop.build",
+ "when": "editorTextFocus && !virtualWorkspace && editorLangId =~ /^latex$|^latex-expl3$|^doctex$|^bibtex$|^rsweave$/"
+ },
+ {
+ "//": "Overleaf: Ctrl+D -> delete current line (VS Code default: add selection to next find match).",
+ "key": "ctrl+d",
+ "mac": "cmd+d",
+ "command": "editor.action.deleteLines",
+ "when": "textInputFocus && !editorReadonly"
+ },
+ {
+ "//": "Overleaf: Ctrl+Shift+D -> duplicate selection (VS Code default: show Run and Debug).",
+ "key": "ctrl+shift+d",
+ "mac": "cmd+shift+d",
+ "command": "editor.action.duplicateSelection",
+ "when": "textInputFocus && !editorReadonly"
+ },
+ {
+ "//": "Overleaf: Ctrl+U -> uppercase the selection (VS Code default: soft undo of cursor motion).",
+ "key": "ctrl+u",
+ "mac": "ctrl+u",
+ "command": "editor.action.transformToUppercase",
+ "when": "editorTextFocus && !editorReadonly"
+ },
+ {
+ "//": "Overleaf: Ctrl+Shift+U -> lowercase the selection (VS Code default: focus the Output panel).",
+ "key": "ctrl+shift+u",
+ "mac": "ctrl+shift+u",
+ "command": "editor.action.transformToLowercase",
+ "when": "editorTextFocus && !editorReadonly"
+ },
+ {
+ "//": "Overleaf: Ctrl+Shift+L -> go to line number (VS Code default: select all occurrences of selection).",
+ "key": "ctrl+shift+l",
+ "mac": "cmd+shift+l",
+ "command": "workbench.action.gotoLine",
+ "when": "editorTextFocus"
+ },
+ {
+ "//": "Overleaf: Ctrl+G -> find next (VS Code default: go to line).",
+ "key": "ctrl+g",
+ "mac": "cmd+g",
+ "command": "editor.action.nextMatchFindAction",
+ "when": "editorFocus"
+ },
+ {
+ "//": "Overleaf: Ctrl+Shift+G -> find previous (VS Code default: show Source Control).",
+ "key": "ctrl+shift+g",
+ "mac": "cmd+shift+g",
+ "command": "editor.action.previousMatchFindAction",
+ "when": "editorFocus"
+ },
+ {
+ "//": "Overleaf: F2 -> toggle fold (VS Code default: rename symbol, which LaTeX has no use for).",
+ "key": "f2",
+ "mac": "f2",
+ "command": "editor.toggleFold",
+ "when": "editorTextFocus && editorLangId =~ /^latex$|^latex-expl3$|^doctex$|^bibtex$|^rsweave$/"
+ },
+ {
+ "//": "Overleaf: Alt+Shift+0 -> unfold all",
+ "key": "alt+shift+0",
+ "mac": "cmd+alt+0",
+ "command": "editor.unfoldAll",
+ "when": "editorTextFocus"
+ },
+ {
+ "//": "Overleaf: Alt+Shift+1 -> fold all",
+ "key": "alt+shift+1",
+ "mac": "cmd+alt+1",
+ "command": "editor.foldAll",
+ "when": "editorTextFocus"
+ },
+ {
+ "//": "Overleaf: Alt+Left/Right -> start/end of line (VS Code default: navigate back/forward, still on Ctrl+Alt+- and Ctrl+Shift+-).",
+ "key": "alt+left",
+ "mac": "ctrl+a",
+ "command": "cursorHome",
+ "when": "editorTextFocus"
+ },
+ {
+ "key": "alt+right",
+ "mac": "ctrl+e",
+ "command": "cursorEnd",
+ "when": "editorTextFocus"
+ },
+ {
+ "key": "alt+shift+left",
+ "mac": "ctrl+shift+a",
+ "command": "cursorHomeSelect",
+ "when": "editorTextFocus"
+ },
+ {
+ "key": "alt+shift+right",
+ "mac": "ctrl+shift+e",
+ "command": "cursorEndSelect",
+ "when": "editorTextFocus"
+ },
+ {
+ "//": "Overleaf: Alt+Space -> open autocomplete (Ctrl+Space is already the VS Code default).",
+ "key": "alt+space",
+ "mac": "alt+space",
+ "command": "editor.action.triggerSuggest",
+ "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible"
+ }
+ ]
+ }
+}
diff --git a/.devcontainer/paste-figure/extension.js b/.devcontainer/paste-figure/extension.js
new file mode 100644
index 0000000..46d8983
--- /dev/null
+++ b/.devcontainer/paste-figure/extension.js
@@ -0,0 +1,196 @@
+// Overleaf-style image paste for LaTeX: Ctrl+V an image into a .tex file and it
+// is written into the project and replaced by a \begin{figure} block.
+//
+// This has to be a paste *provider* rather than one of the usual "paste image"
+// extensions (mushan.vscode-paste-image and friends): those shell out to
+// xclip/pbpaste/powershell on whichever machine runs the extension host, which
+// for a dev container is the container -- where there is no clipboard. VS Code
+// serialises the clipboard payload from the client to the provider instead, so
+// DataTransferItem.asFile() gives us the bytes the user copied on their desktop.
+
+const vscode = require('vscode')
+const path = require('path')
+
+const LANGUAGES = ['latex', 'latex-expl3', 'rsweave', 'doctex']
+
+// What \includegraphics can actually consume, for payloads that carry a filename.
+const IMAGE_EXTENSIONS = new Set([
+ '.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.tif', '.tiff', '.svg', '.pdf', '.eps'
+])
+
+// Clipboard bitmaps arrive with a mime type and a placeholder name, so the
+// extension has to come from the mime instead.
+const IMAGE_MIMES = new Map([
+ ['image/png', '.png'],
+ ['image/jpeg', '.jpg'],
+ ['image/jpg', '.jpg'],
+ ['image/gif', '.gif'],
+ ['image/webp', '.webp'],
+ ['image/bmp', '.bmp'],
+ ['image/tiff', '.tif'],
+ ['image/svg+xml', '.svg'],
+ ['application/pdf', '.pdf']
+])
+
+// 'files' matches any DataTransferItem backed by a file (drag & drop, or a copy
+// from the OS file manager); the explicit mimes cover clipboard bitmaps.
+const MIME_HINTS = ['files', ...IMAGE_MIMES.keys()]
+
+// Names the OS invents for a clipboard bitmap; not worth keeping.
+const GENERIC_NAMES = new Set(['', 'image', 'images', 'clipboard', 'unnamed', 'untitled', 'download'])
+
+function settings(document) {
+ return vscode.workspace.getConfiguration('pasteFigure', document)
+}
+
+/** First image-ish file in the payload, or undefined -- in which case we let VS Code paste normally. */
+function findImage(dataTransfer) {
+ const candidates = []
+ dataTransfer.forEach((item, mime) => {
+ const file = item.asFile()
+ if (!file) {
+ return
+ }
+ const named = path.extname(file.name || '').toLowerCase()
+ if (IMAGE_EXTENSIONS.has(named)) {
+ candidates.push({ file, ext: named })
+ return
+ }
+ const byMime = IMAGE_MIMES.get(mime.toLowerCase().split(';')[0].trim())
+ if (byMime) {
+ candidates.push({ file, ext: byMime })
+ }
+ })
+ return candidates[0]
+}
+
+function timestamp() {
+ const d = new Date()
+ const pad = n => String(n).padStart(2, '0')
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`
+}
+
+/** A filename LaTeX will not choke on: no spaces, no accents, no braces. */
+function slugify(raw) {
+ return raw
+ .normalize('NFKD')
+ .replace(/[\u0300-\u036f]/g, '') // drop the accents NFKD just split off
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, '-')
+ .replace(/^-+|-+$/g, '')
+}
+
+function defaultName(file) {
+ const slug = slugify(path.basename(file.name || '', path.extname(file.name || '')))
+ return GENERIC_NAMES.has(slug) ? `pasted-${timestamp()}` : slug
+}
+
+/**
+ * Ask for the image name, pre-filled with the generated one so Enter keeps it.
+ * Whatever is typed goes through the same slugify; an extension typed out of habit
+ * is dropped since the real one comes from the payload. undefined means Escape.
+ */
+async function askName(file, ext, directory, token) {
+ const fallback = defaultName(file)
+ const resolve = input => {
+ const typed = input.trim()
+ const stem = path.extname(typed).toLowerCase() === ext ? typed.slice(0, -ext.length) : typed
+ return slugify(stem) || fallback
+ }
+ const shown = vscode.workspace.asRelativePath(directory, false)
+ const input = await vscode.window.showInputBox({
+ title: 'Pasted image name',
+ prompt: 'Enter keeps the suggested name, Escape cancels the paste.',
+ value: fallback,
+ validateInput: input => ({
+ message: `Saved as ${shown}/${resolve(input)}${ext}`,
+ severity: vscode.InputBoxValidationSeverity.Info
+ })
+ }, token)
+ return input === undefined ? undefined : resolve(input)
+}
+
+/**
+ * Where images live and what \includegraphics paths are relative to. Both are the
+ * workspace folder when there is one -- latexmk runs from there, and the existing
+ * slides use workspace-relative paths like img/logos.png.
+ */
+function locate(document) {
+ const folder = vscode.workspace.getWorkspaceFolder(document.uri)
+ const root = folder ? folder.uri : vscode.Uri.joinPath(document.uri, '..')
+ const relative = settings(document).get('directory') || 'img'
+ return { root, directory: vscode.Uri.joinPath(root, ...relative.split('/').filter(Boolean)) }
+}
+
+async function freeUri(directory, base, ext) {
+ for (let i = 0; i < 1000; i++) {
+ const uri = vscode.Uri.joinPath(directory, i === 0 ? `${base}${ext}` : `${base}-${i}${ext}`)
+ try {
+ await vscode.workspace.fs.stat(uri)
+ } catch {
+ return uri
+ }
+ }
+ throw new Error(`too many files named ${base}${ext} in ${directory.fsPath}`)
+}
+
+/** Save the image next to the document and return the figure snippet to insert. */
+async function insertFigure(document, dataTransfer, token) {
+ const image = findImage(dataTransfer)
+ if (!image) {
+ return undefined
+ }
+
+ const data = await image.file.data()
+ if (token.isCancellationRequested || data.length === 0) {
+ return undefined
+ }
+
+ const { root, directory } = locate(document)
+ const name = await askName(image.file, image.ext, directory, token)
+ if (name === undefined || token.isCancellationRequested) {
+ return undefined
+ }
+
+ await vscode.workspace.fs.createDirectory(directory)
+ const target = await freeUri(directory, name, image.ext)
+ await vscode.workspace.fs.writeFile(target, data)
+
+ const relative = path.relative(root.fsPath, target.fsPath).split(path.sep).join('/')
+ const template = settings(document).get('template')
+ const body = Array.isArray(template) ? template.join('\n') : String(template)
+ // $ and \ are snippet syntax; the path is slugified but the directory may not be.
+ return new vscode.SnippetString(body.replace(/\$IMAGE/g, relative.replace(/[$\\}]/g, '\\$&')))
+}
+
+/** Failures must not swallow the paste: report, then fall back to a normal paste. */
+async function trySnippet(document, dataTransfer, token) {
+ try {
+ return await insertFigure(document, dataTransfer, token)
+ } catch (err) {
+ vscode.window.showErrorMessage(`Could not insert the pasted image: ${err instanceof Error ? err.message : String(err)}`)
+ return undefined
+ }
+}
+
+function activate(context) {
+ const selector = LANGUAGES.map(language => ({ language, scheme: 'file' }))
+ const kind = vscode.DocumentPasteEditKind?.Empty?.append('latex', 'figure')
+
+ context.subscriptions.push(vscode.languages.registerDocumentPasteEditProvider(selector, {
+ async provideDocumentPasteEdits(document, _ranges, dataTransfer, _context, token) {
+ const snippet = await trySnippet(document, dataTransfer, token)
+ return snippet ? [new vscode.DocumentPasteEdit(snippet, 'Insert figure', kind)] : undefined
+ }
+ }, { providedPasteEditKinds: kind ? [kind] : [], pasteMimeTypes: MIME_HINTS }))
+
+ // Same handling for images dragged into the editor.
+ context.subscriptions.push(vscode.languages.registerDocumentDropEditProvider(selector, {
+ async provideDocumentDropEdits(document, _position, dataTransfer, token) {
+ const snippet = await trySnippet(document, dataTransfer, token)
+ return snippet ? new vscode.DocumentDropEdit(snippet, 'Insert figure', kind) : undefined
+ }
+ }, { providedDropEditKinds: kind ? [kind] : [], dropMimeTypes: MIME_HINTS }))
+}
+
+module.exports = { activate, deactivate() {} }
diff --git a/.devcontainer/paste-figure/package.json b/.devcontainer/paste-figure/package.json
new file mode 100644
index 0000000..cca7b89
--- /dev/null
+++ b/.devcontainer/paste-figure/package.json
@@ -0,0 +1,59 @@
+{
+ "name": "latex-paste-figure",
+ "displayName": "LaTeX Paste Figure",
+ "description": "Overleaf-style Ctrl+V: pasting an image into a .tex file saves it and inserts a figure environment.",
+ "publisher": "local",
+ "version": "1.1.0",
+ "license": "MIT",
+ "engines": {
+ "vscode": "^1.97.0"
+ },
+ "categories": [
+ "Other"
+ ],
+ "//": "Must run in the container: the provider writes the image into the workspace.",
+ "extensionKind": [
+ "workspace"
+ ],
+ "capabilities": {
+ "untrustedWorkspaces": {
+ "supported": true
+ }
+ },
+ "main": "./extension.js",
+ "activationEvents": [
+ "onLanguage:latex",
+ "onLanguage:latex-expl3",
+ "onLanguage:rsweave",
+ "onLanguage:doctex"
+ ],
+ "contributes": {
+ "configuration": {
+ "title": "LaTeX Paste Figure",
+ "properties": {
+ "pasteFigure.directory": {
+ "type": "string",
+ "default": "img",
+ "scope": "resource",
+ "markdownDescription": "Folder the pasted image is written to, relative to the workspace root. Created on demand."
+ },
+ "pasteFigure.template": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "scope": "resource",
+ "default": [
+ "\\begin{figure}",
+ "\t\\centering",
+ "\t\\includegraphics[width=${1:0.8\\linewidth}]{$IMAGE}",
+ "\t\\caption{${2:Enter Caption}}",
+ "\t\\label{fig:${3:enter-label}}",
+ "\\end{figure}"
+ ],
+ "markdownDescription": "Lines inserted at the cursor, in [snippet syntax](https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax). `$IMAGE` is replaced by the path of the saved image; `${1:...}` are the tab stops you cycle through with `Tab`."
+ }
+ }
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
index e964244..5e6189a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -299,3 +299,6 @@ TSWLatexianTemp*
# option is specified. Footnotes are the stored in a file with suffix Notes.bib.
# Uncomment the next line to have this generated file ignored.
#*Notes.bib
+
+# artifacts directory
+build/
diff --git a/.vscode/ltex.dictionary.fr.txt b/.vscode/ltex.dictionary.fr.txt
new file mode 100644
index 0000000..c66d5bf
--- /dev/null
+++ b/.vscode/ltex.dictionary.fr.txt
@@ -0,0 +1 @@
+cyberphysiques
diff --git a/api.drawio b/api.drawio
new file mode 100644
index 0000000..64c0991
--- /dev/null
+++ b/api.drawio
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/architecture.drawio b/architecture.drawio
new file mode 100644
index 0000000..666ceaf
--- /dev/null
+++ b/architecture.drawio
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/slides.bbl-SAVE-ERROR b/build/slides.bbl-SAVE-ERROR
index d67819b..ae5727e 100644
--- a/build/slides.bbl-SAVE-ERROR
+++ b/build/slides.bbl-SAVE-ERROR
@@ -19,6 +19,102 @@
\refsection{0}
\datalist[entry]{nty/global//global/global/global}
+ \entry{barrProgrammingEmbeddedSystems2006}{book}{}{}
+ \name{author}{3}{}{%
+ {{hash=1f590f436d602e75c03b0fa9947edc3c}{%
+ family={Barr},
+ familyi={B\bibinitperiod},
+ given={Michael},
+ giveni={M\bibinitperiod}}}%
+ {{hash=bd484e4aca6f877f72c70a316e5125ff}{%
+ family={Massa},
+ familyi={M\bibinitperiod},
+ given={Anthony\bibnamedelima J.},
+ giveni={A\bibinitperiod\bibinitdelim J\bibinitperiod}}}%
+ {{hash=1f590f436d602e75c03b0fa9947edc3c}{%
+ family={Barr},
+ familyi={B\bibinitperiod},
+ given={Michael},
+ giveni={M\bibinitperiod}}}%
+ }
+ \list{location}{1}{%
+ {Beijing ; Sebastopol}%
+ }
+ \list{publisher}{1}{%
+ {O'Reilly}%
+ }
+ \strng{namehash}{0a26d209ca1cae7c5ec3bb9d00259019}
+ \strng{fullhash}{0a26d209ca1cae7c5ec3bb9d00259019}
+ \strng{fullhashraw}{0a26d209ca1cae7c5ec3bb9d00259019}
+ \strng{bibnamehash}{0a26d209ca1cae7c5ec3bb9d00259019}
+ \strng{authorbibnamehash}{0a26d209ca1cae7c5ec3bb9d00259019}
+ \strng{authornamehash}{0a26d209ca1cae7c5ec3bb9d00259019}
+ \strng{authorfullhash}{0a26d209ca1cae7c5ec3bb9d00259019}
+ \strng{authorfullhashraw}{0a26d209ca1cae7c5ec3bb9d00259019}
+ \field{sortinit}{B}
+ \field{sortinithash}{d7095fff47cda75ca2589920aae98399}
+ \field{labelnamesource}{author}
+ \field{labeltitlesource}{shorttitle}
+ \field{edition}{2nd ed}
+ \field{isbn}{978-0-596-00983-0}
+ \field{shorttitle}{Programming Embedded Systems}
+ \field{title}{Programming Embedded Systems: With {{C}} and {{GNU}} Development Tools}
+ \field{year}{2006}
+ \true{nocite}
+ \keyw{C (Computer program language),C++ (Computer program language),Embedded computer systems,Programming}
+ \endentry
+ \entry{hansonArchitecturalApproachAutonomic2004}{inproceedings}{}{}
+ \name{author}{4}{}{%
+ {{hash=4915505c877ffd461c4868a94cb7679e}{%
+ family={Hanson},
+ familyi={H\bibinitperiod},
+ given={James\bibnamedelima E.},
+ giveni={J\bibinitperiod\bibinitdelim E\bibinitperiod}}}%
+ {{hash=4147c55dc56de3d11fe3c4fe16cc86b0}{%
+ family={Whalley},
+ familyi={W\bibinitperiod},
+ given={Ian},
+ giveni={I\bibinitperiod}}}%
+ {{hash=6abf78bc82939db4b1774c03c8c54df3}{%
+ family={Chess},
+ familyi={C\bibinitperiod},
+ given={David\bibnamedelima M.},
+ giveni={D\bibinitperiod\bibinitdelim M\bibinitperiod}}}%
+ {{hash=4561b6ab0ad044c0176daf9d8ad2d79e}{%
+ family={Kephart},
+ familyi={K\bibinitperiod},
+ given={Jeffrey\bibnamedelima O.},
+ giveni={J\bibinitperiod\bibinitdelim O\bibinitperiod}}}%
+ }
+ \list{location}{1}{%
+ {USA}%
+ }
+ \list{publisher}{1}{%
+ {IEEE Computer Society}%
+ }
+ \strng{namehash}{568767d2b19ed559a526df3fd764b238}
+ \strng{fullhash}{c14defffae113d8c7dc0a82e266e9afb}
+ \strng{fullhashraw}{c14defffae113d8c7dc0a82e266e9afb}
+ \strng{bibnamehash}{568767d2b19ed559a526df3fd764b238}
+ \strng{authorbibnamehash}{568767d2b19ed559a526df3fd764b238}
+ \strng{authornamehash}{568767d2b19ed559a526df3fd764b238}
+ \strng{authorfullhash}{c14defffae113d8c7dc0a82e266e9afb}
+ \strng{authorfullhashraw}{c14defffae113d8c7dc0a82e266e9afb}
+ \field{sortinit}{H}
+ \field{sortinithash}{23a3aa7c24e56cfa16945d55545109b5}
+ \field{labelnamesource}{author}
+ \field{labeltitlesource}{title}
+ \field{abstract}{We describe an architectural approach to achieving the goals of autonomic computing. The architecture that we outline describes interfaces and behavioral requirements for individual system components, describes how interactions among components are established, and recommends design patterns that engender the desired system-level properties of self-configuration, self-optimization, self-healing and self-protection. We have validated many of these ideas in two prototype autonomic computing systems.}
+ \field{booktitle}{Proceedings of the {{First International Conference}} on {{Autonomic Computing}}}
+ \field{isbn}{978-0-7695-2114-5}
+ \field{month}{5}
+ \field{series}{{{ICAC}} '04}
+ \field{title}{An {{Architectural Approach}} to {{Autonomic Computing}}}
+ \field{year}{2004}
+ \true{nocite}
+ \field{pages}{2\bibrangedash 9}
+ \range{pages}{8}
+ \endentry
\entry{inderwildiImpactIntelligentCyberphysical2020}{article}{}{}
\name{author}{4}{}{%
{{hash=9f6e3f6f71503f1109985d3b9d96d734}{%
@@ -60,12 +156,8 @@
\field{langid}{english}
\field{number}{3}
\field{title}{The Impact of Intelligent Cyber-Physical Systems on the Decarbonization of Energy}
- \field{urlday}{19}
- \field{urlmonth}{10}
- \field{urlyear}{2025}
\field{volume}{13}
\field{year}{2020}
- \field{urldateera}{ce}
\field{pages}{744\bibrangedash 771}
\range{pages}{28}
\verb{doi}
@@ -103,12 +195,8 @@
\field{month}{1}
\field{number}{1}
\field{title}{The Vision of Autonomic Computing}
- \field{urlday}{5}
- \field{urlmonth}{12}
- \field{urlyear}{2024}
\field{volume}{36}
\field{year}{2003}
- \field{urldateera}{ce}
\field{pages}{41\bibrangedash 50}
\range{pages}{10}
\verb{doi}
@@ -148,11 +236,7 @@
\field{month}{5}
\field{shorttitle}{Cyber {{Physical Systems}}}
\field{title}{Cyber {{Physical Systems}}: {{Design Challenges}}}
- \field{urlday}{17}
- \field{urlmonth}{9}
- \field{urlyear}{2025}
\field{year}{2008}
- \field{urldateera}{ce}
\field{pages}{363\bibrangedash 369}
\range{pages}{7}
\verb{doi}
diff --git a/build/slides.pdf b/build/slides.pdf
index ac4e005..eb58f4d 100644
Binary files a/build/slides.pdf and b/build/slides.pdf differ
diff --git a/chaine-traitement.drawio b/chaine-traitement.drawio
new file mode 100644
index 0000000..7ca2407
--- /dev/null
+++ b/chaine-traitement.drawio
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/compilation.drawio b/compilation.drawio
new file mode 100644
index 0000000..d585655
--- /dev/null
+++ b/compilation.drawio
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/img/architecture.png b/img/architecture.png
new file mode 100644
index 0000000..b8a1fea
Binary files /dev/null and b/img/architecture.png differ
diff --git a/img/arducam.png b/img/arducam.png
new file mode 100644
index 0000000..a1f34c9
Binary files /dev/null and b/img/arducam.png differ
diff --git a/img/boston-dynamics-spot.png b/img/boston-dynamics-spot.png
new file mode 100644
index 0000000..8ccd76d
Binary files /dev/null and b/img/boston-dynamics-spot.png differ
diff --git a/img/boston-dynamics-weapon.png b/img/boston-dynamics-weapon.png
new file mode 100644
index 0000000..8df6173
Binary files /dev/null and b/img/boston-dynamics-weapon.png differ
diff --git a/img/breadboard-power.png b/img/breadboard-power.png
new file mode 100644
index 0000000..a2ab8f7
Binary files /dev/null and b/img/breadboard-power.png differ
diff --git a/img/chaine-traitement.png b/img/chaine-traitement.png
new file mode 100644
index 0000000..2b88333
Binary files /dev/null and b/img/chaine-traitement.png differ
diff --git a/img/china-humanoid-robots.png b/img/china-humanoid-robots.png
new file mode 100644
index 0000000..c7f73b0
Binary files /dev/null and b/img/china-humanoid-robots.png differ
diff --git a/img/compilation.png b/img/compilation.png
new file mode 100644
index 0000000..ee6314c
Binary files /dev/null and b/img/compilation.png differ
diff --git a/img/defenseur-du-temps-1.png b/img/defenseur-du-temps-1.png
new file mode 100644
index 0000000..532fd6a
Binary files /dev/null and b/img/defenseur-du-temps-1.png differ
diff --git a/img/defenseur-du-temps.png b/img/defenseur-du-temps.png
new file mode 100644
index 0000000..1c84080
Binary files /dev/null and b/img/defenseur-du-temps.png differ
diff --git a/img/drones-ukraine.png b/img/drones-ukraine.png
new file mode 100644
index 0000000..5bb1585
Binary files /dev/null and b/img/drones-ukraine.png differ
diff --git a/img/farrah-satellites.png b/img/farrah-satellites.png
new file mode 100644
index 0000000..bbafe48
Binary files /dev/null and b/img/farrah-satellites.png differ
diff --git a/img/fritzing-led.png b/img/fritzing-led.png
new file mode 100644
index 0000000..5ce6543
Binary files /dev/null and b/img/fritzing-led.png differ
diff --git a/img/goliath.png b/img/goliath.png
new file mode 100644
index 0000000..77d68dc
Binary files /dev/null and b/img/goliath.png differ
diff --git a/img/grove-lcd.png b/img/grove-lcd.png
new file mode 100644
index 0000000..cc5d250
Binary files /dev/null and b/img/grove-lcd.png differ
diff --git a/img/grove-shield.png b/img/grove-shield.png
new file mode 100644
index 0000000..c80a80f
Binary files /dev/null and b/img/grove-shield.png differ
diff --git a/img/grove-ultrasonic.png b/img/grove-ultrasonic.png
new file mode 100644
index 0000000..38f8d5a
Binary files /dev/null and b/img/grove-ultrasonic.png differ
diff --git a/img/hammond-electric-dog.png b/img/hammond-electric-dog.png
new file mode 100644
index 0000000..3fb9137
Binary files /dev/null and b/img/hammond-electric-dog.png differ
diff --git a/img/ihm-terminal.png b/img/ihm-terminal.png
new file mode 100644
index 0000000..ae95825
Binary files /dev/null and b/img/ihm-terminal.png differ
diff --git a/img/ihm-web.png b/img/ihm-web.png
new file mode 100644
index 0000000..741a41d
Binary files /dev/null and b/img/ihm-web.png differ
diff --git a/img/jetson-orin-nano.png b/img/jetson-orin-nano.png
new file mode 100644
index 0000000..c7d7547
Binary files /dev/null and b/img/jetson-orin-nano.png differ
diff --git a/img/joy-it-sbc-motodriver2.png b/img/joy-it-sbc-motodriver2.png
new file mode 100644
index 0000000..a6acf36
Binary files /dev/null and b/img/joy-it-sbc-motodriver2.png differ
diff --git a/img/metro-paris.png b/img/metro-paris.png
new file mode 100644
index 0000000..2ed775e
Binary files /dev/null and b/img/metro-paris.png differ
diff --git a/img/microcontrollers.png b/img/microcontrollers.png
new file mode 100644
index 0000000..b786af7
Binary files /dev/null and b/img/microcontrollers.png differ
diff --git a/img/missile.png b/img/missile.png
new file mode 100644
index 0000000..1648285
Binary files /dev/null and b/img/missile.png differ
diff --git a/img/os-answer.png b/img/os-answer.png
new file mode 100644
index 0000000..cbbbaad
Binary files /dev/null and b/img/os-answer.png differ
diff --git a/img/os-question.png b/img/os-question.png
new file mode 100644
index 0000000..092e315
Binary files /dev/null and b/img/os-question.png differ
diff --git a/img/pico-bootsel.png b/img/pico-bootsel.png
new file mode 100644
index 0000000..16985f9
Binary files /dev/null and b/img/pico-bootsel.png differ
diff --git a/img/raspberry-pi-pico-wh.png b/img/raspberry-pi-pico-wh.png
new file mode 100644
index 0000000..ed60e0a
Binary files /dev/null and b/img/raspberry-pi-pico-wh.png differ
diff --git a/img/robot-industriel.png b/img/robot-industriel.png
new file mode 100644
index 0000000..eec93fe
Binary files /dev/null and b/img/robot-industriel.png differ
diff --git a/img/robot-joker.png b/img/robot-joker.png
new file mode 100644
index 0000000..e91d32a
Binary files /dev/null and b/img/robot-joker.png differ
diff --git a/img/rpi-pico-pinout.png b/img/rpi-pico-pinout.png
new file mode 100644
index 0000000..283b3d5
Binary files /dev/null and b/img/rpi-pico-pinout.png differ
diff --git a/img/rubo-1.png b/img/rubo-1.png
new file mode 100644
index 0000000..db8b323
Binary files /dev/null and b/img/rubo-1.png differ
diff --git a/img/rubo.png b/img/rubo.png
new file mode 100644
index 0000000..5c36f99
Binary files /dev/null and b/img/rubo.png differ
diff --git a/img/serinette.png b/img/serinette.png
new file mode 100644
index 0000000..deee80b
Binary files /dev/null and b/img/serinette.png differ
diff --git a/img/spi.png b/img/spi.png
new file mode 100644
index 0000000..127e11c
Binary files /dev/null and b/img/spi.png differ
diff --git a/img/stele-liquidateurs-kharkiv.png b/img/stele-liquidateurs-kharkiv.png
new file mode 100644
index 0000000..5ddf583
Binary files /dev/null and b/img/stele-liquidateurs-kharkiv.png differ
diff --git a/img/ts.png b/img/ts.png
deleted file mode 100644
index a295a97..0000000
Binary files a/img/ts.png and /dev/null differ
diff --git a/img/usv-ifremer.png b/img/usv-ifremer.png
new file mode 100644
index 0000000..3651599
Binary files /dev/null and b/img/usv-ifremer.png differ
diff --git a/img/vaucanson-duck.png b/img/vaucanson-duck.png
new file mode 100644
index 0000000..e13abcb
Binary files /dev/null and b/img/vaucanson-duck.png differ
diff --git a/img/vl53l1x-tof.png b/img/vl53l1x-tof.png
new file mode 100644
index 0000000..ea3012c
Binary files /dev/null and b/img/vl53l1x-tof.png differ
diff --git a/raspberry-pi-pico.drawio b/raspberry-pi-pico.drawio
new file mode 100644
index 0000000..7e1c35d
--- /dev/null
+++ b/raspberry-pi-pico.drawio
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/slides.bib b/slides.bib
index a1ebfdc..134546e 100644
--- a/slides.bib
+++ b/slides.bib
@@ -9,7 +9,6 @@
publisher = {IEEE},
address = {Orlando, FL, USA},
doi = {10.1109/ISORC.2008.25},
- urldate = {2025-09-17},
abstract = {Cyber-Physical Systems (CPS) are integrations of computation and physical processes. Embedded computers and networks monitor and control the physical processes, usually with feedback loops where physical processes affect computations and vice versa. The economic and societal potential of such systems is vastly greater than what has been realized, and major investments are being made worldwide to develop the technology. There are considerable challenges, particularly because the physical components of such systems introduce safety and reliability requirements qualitatively different from those in generalpurpose computing. Moreover, physical components are qualitatively different from object-oriented software components. Standard abstractions based on method calls and threads do not work. This paper examines the challenges in designing such systems, and in particular raises the question of whether today's computing and networking technologies provide an adequate foundation for CPS. It concludes that it will not be sufficient to improve design processes, raise the level of abstraction, or verify (formally or otherwise) designs that are built on today's abstractions. To realize the full potential of CPS, we will have to rebuild computing and networking abstractions. These abstractions will have to embrace physical dynamics and computation in a unified way.},
isbn = {978-0-7695-3132-8},
langid = {english},
@@ -25,18 +24,22 @@
pages = {744--771},
issn = {1754-5692, 1754-5706},
doi = {10.1039/C9EE01919G},
- urldate = {2025-10-19},
abstract = {Assessing the impact of digital technologies and artificial intelligence, so-called intelligent cyber-physical systems, on emission reduction in the critical sector of energy provision. , The decarbonisation of energy provision is key to managing global greenhouse gas emissions and hence mitigating climate change. Digital technologies such as big data, machine learning, and the Internet of Things are receiving more and more attention as they can aid the decarbonisation process while requiring limited investments. The orchestration of these novel technologies, so-called cyber-physical systems (CPS), provides further, synergetic effects that increase efficiency of energy provision and industrial production, thereby optimising economic feasibility and environmental impact. This comprehensive review article assesses the current as well as the potential impact of digital technologies within CPS on the decarbonisation of energy systems. Ad hoc calculation for selected applications of CPS and its subsystems estimates not only the economic impact but also the emission reduction potential. This assessment clearly shows that digitalisation of energy systems using CPS completely alters the marginal abatement cost curve (MACC) and creates novel pathways for the transition to a low-carbon energy system. Moreover, the assessment concludes that when CPS are combined with artificial intelligence (AI), decarbonisation could potentially progress at an unforeseeable pace while introducing unpredictable and potentially existential risks. Therefore, the impact of intelligent CPS on systemic resilience and energy security is discussed and policy recommendations are deducted. The assessment shows that the potential benefits clearly outweigh the latent risks as long as these are managed by policy makers.},
langid = {english},
}
-@techreport{ibmArchitecturalBlueprintAutonomic2006,
- title = {{An Architectural Blueprint for Autonomic Computing}},
- author = {{IBM}},
- year = {2006},
- month = jun,
- urldate = {2025-01-21},
- langid = {english},
+@inproceedings{hansonArchitecturalApproachAutonomic2004,
+ title = {An {{Architectural Approach}} to {{Autonomic Computing}}},
+ booktitle = {Proceedings of the {{First International Conference}} on {{Autonomic Computing}}},
+ author = {Hanson, James E. and Whalley, Ian and Chess, David M. and Kephart, Jeffrey O.},
+ year = 2004,
+ month = may,
+ series = {{{ICAC}} '04},
+ pages = {2--9},
+ publisher = {IEEE Computer Society},
+ address = {USA},
+ abstract = {We describe an architectural approach to achieving the goals of autonomic computing. The architecture that we outline describes interfaces and behavioral requirements for individual system components, describes how interactions among components are established, and recommends design patterns that engender the desired system-level properties of self-configuration, self-optimization, self-healing and self-protection. We have validated many of these ideas in two prototype autonomic computing systems.},
+ isbn = {978-0-7695-2114-5}
}
@article{kephartVisionAutonomicComputing2003,
@@ -50,7 +53,19 @@
pages = {41--50},
issn = {0018-9162},
doi = {10.1109/MC.2003.1160055},
- urldate = {2024-12-05},
copyright = {https://ieeexplore.ieee.org/Xplorehelp/downloads/license-information/IEEE.html},
langid = {english},
}
+
+@book{barrProgrammingEmbeddedSystems2006,
+ title = {Programming Embedded Systems: With {{C}} and {{GNU}} Development Tools},
+ shorttitle = {Programming Embedded Systems},
+ author = {Barr, Michael and Massa, Anthony J. and Barr, Michael},
+ year = 2006,
+ edition = {2nd ed},
+ publisher = {O'Reilly},
+ address = {Beijing ; Sebastopol},
+ isbn = {978-0-596-00983-0},
+ lccn = {TK7895.E42 B37 2006},
+ keywords = {C (Computer program language),C++ (Computer program language),Embedded computer systems,Programming},
+}
diff --git a/slides.cls b/slides.cls
index 0aacea9..362b5bb 100644
--- a/slides.cls
+++ b/slides.cls
@@ -1,6 +1,6 @@
\ProvidesClass{slides}[27/01/2025 v1]
-\PassOptionsToPackage{dvipsnames}{xcolor}
+\PassOptionsToPackage{dvipsnames, table}{xcolor}
\PassOptionsToPackage{unicode}{hyperref}
\PassOptionsToPackage{naturalnames}{hyperref}
\LoadClass[aspectratio=169,10pt]{beamer}
@@ -11,11 +11,17 @@
% listings (should be loaded before csquotes)
\RequirePackage{minted}
+\setmintedinline{
+ breaklines,
+ breakanywhere,
+ breakanywheresymbolpre={}
+}
% language and encoding
\RequirePackage[french]{babel}
\RequirePackage[T1]{fontenc}
-\RequirePackage{csquotes}
+\RequirePackage[autostyle]{csquotes}
+\MakeOuterQuote{"}
% biblatex for references on slides
\RequirePackage[backend=biber]{biblatex}
@@ -54,6 +60,8 @@
% captions
\RequirePackage{caption}
\captionsetup{font=scriptsize,labelfont=scriptsize}
+% do not prepend "Figure X:" in captions
+\captionsetup[figure]{labelformat=empty}
% beamer theme
\usetheme{moloch}
@@ -72,7 +80,8 @@
% https://steeven9.github.io/USI-LaTeX/html/packages_hyperref_babel_xcolor3.html
% Section 3.2 of:
% https://mirror.ibcp.fr/pub/CTAN/macros/latex/contrib/beamer-contrib/themes/moloch/moloch.pdf
-\definecolor{ubo}{HTML}{8c3759}
+\definecolor{ubo}{HTML}{5e548e}
+\definecolor{rowgray}{gray}{0.94}
\setbeamercolor{progress bar}{fg=ubo}
\setbeamercolor{title separator}{fg=ubo}
\setbeamercolor{frametitle}{bg=ubo}
@@ -90,6 +99,10 @@
\molochset{subsectionpage=progressbar}
% table of contents
\setbeamertemplate{section in toc}[sections numbered]
+\makeatletter
+\patchcmd{\beamer@sectionintoc}{\vskip1.5em}{\vskip0.3em}{}{}
+\patchcmd{\beamer@subsectionintoc}{\vskip0.75em}{\vskip0.15em}{}{}
+\makeatother
% smaller first-level bullet points
\setbeamertemplate{itemize item}{\textbullet}
% smaller bibliography entries
@@ -107,24 +120,48 @@
\end{tikzpicture}
}
+% tikz
+\usetikzlibrary{arrows.meta}
% fonts and symbols
-\RequirePackage{pifont}
-\newcommand{\cmark}{\color{YellowGreen}\ding{51}}
-\newcommand{\xmark}{\color{BrickRed}\ding{55}}
\RequirePackage{textcomp}
\RequirePackage{emoji}
-% markers
+% circles
\RequirePackage{circledsteps}
\pgfkeys{/csteps/inner color=white}
\pgfkeys{/csteps/fill color=black}
-\newcommand{\DONE}{%
- \CircledParamOpts{inner color=black, outer color=LimeGreen, fill color=LimeGreen}{1}{\textbf{DONE}} %
+% markers
+\RequirePackage{pifont}
+\newcommand{\cmark}{{\color{YellowGreen}\ding{51}}}
+\newcommand{\xmark}{{\color{BrickRed}\ding{55}}}
+\newcommand{\imark}{{\color{Orange}\ding{109}}}
+% pills
+\RequirePackage{xparse}
+\NewDocumentCommand{\DONE}{g}{%
+ \IfNoValueTF{#1}{%
+ \CircledParamOpts{inner color=black, outer color=LimeGreen, fill color=LimeGreen}
+ {1}{\textbf{DONE}}%
+ }{%
+ \CircledParamOpts{inner color=black, outer color=LimeGreen, fill color=LimeGreen}
+ {1}{\textbf{DONE --} #1}%
+ }%
}
-\newcommand{\TODO}{%
- \CircledParamOpts{inner color=black, outer color=Goldenrod, fill color=Goldenrod}{1}{\textbf{TODO}} %
+\NewDocumentCommand{\TODO}{g}{%
+ \IfNoValueTF{#1}{%
+ \CircledParamOpts{inner color=black, outer color=Goldenrod, fill color=Goldenrod}
+ {1}{\textbf{TODO}}%
+ }{%
+ \CircledParamOpts{inner color=black, outer color=Goldenrod, fill color=Goldenrod}
+ {1}{\textbf{TODO --} #1}%
+ }%
}
-\newcommand{\FIXME}{%
- \CircledParamOpts{inner color=white, outer color=Red, fill color=Red}{1}{\textbf{FIXME}} %
+\NewDocumentCommand{\FIXME}{g}{%
+ \IfNoValueTF{#1}{%
+ \CircledParamOpts{inner color=white, outer color=Red, fill color=Red}
+ {1}{\textbf{FIXME}}%
+ }{%
+ \CircledParamOpts{inner color=white, outer color=Red, fill color=Red}
+ {1}{\textbf{FIXME --} #1}%
+ }%
}
% custom font size for a slide (cf. https://tex.stackexchange.com/a/401409)
\RequirePackage{environ}
@@ -145,17 +182,21 @@
\RequirePackage{graphicx}
\RequirePackage{subcaption}
% tables
+\RequirePackage{array}
\RequirePackage{tabularx}
\RequirePackage{booktabs}
% various tabular columns with text wrapping
-\newcolumntype{L}{>{\arraybackslash}m{\linewidth}}
-\newcolumntype{M}{>{\centering\arraybackslash}m{0.33\linewidth}}
-\newcolumntype{Y}{>{\centering\arraybackslash}m{0.15\linewidth}}
-\newcolumntype{y}{>{\arraybackslash}m{0.15\linewidth}}
-% generic horizontally centered column with line breaks
-\newcolumntype{Z}{>{\centering\arraybackslash}X}
-% vertical centering of text in all tabularx columns
-\renewcommand\tabularxcolumn[1]{m{#1}}
+\newcolumntype{F}{>{\arraybackslash}m{\linewidth}}
+\newcolumntype{L}[1]{>{\raggedright\arraybackslash}m{#1}}
+\newcolumntype{C}[1]{>{\centering\arraybackslash}m{#1}}
+\newcolumntype{R}[1]{>{\raggedleft\arraybackslash}m{#1}}
+% avoid spacing between rules and row background
+\setlength{\abovetopsep}{0pt}
+\setlength{\belowrulesep}{0pt}
+\setlength{\aboverulesep}{0pt}
+\setlength{\belowbottomsep}{0pt}
+% vertical row padding
+\renewcommand{\arraystretch}{1.5} % scales vertical row spacing
% links
\RequirePackage{hyperref}
@@ -177,15 +218,6 @@
\column{0.4\linewidth}
\ifx\beamer@shortauthor\@empty\else\usebeamertemplate*{author}\fi
\column{0.4\linewidth}
- % \begin{table}[]
- % \begin{flushleft}
- % \small
- % \begin{tabular}{ll}
- % Rapporteur & Blabla \\
- % Rapporteur & Blabla
- % \end{tabular}
- % \end{flushleft}
- % \end{table}
\end{columns}
\begin{columns}[b]
\column{0.4\linewidth}
diff --git a/slides.pdf b/slides.pdf
index ac4e005..eb58f4d 100644
Binary files a/slides.pdf and b/slides.pdf differ
diff --git a/slides.tex b/slides.tex
index ea0783c..8d043de 100644
--- a/slides.tex
+++ b/slides.tex
@@ -3,7 +3,7 @@
\title{Objets connectés et robotique}
\subtitle{L3 IFA, S5}
-\date{Novembre 2025}
+\date{Octobre 2026}
\titlegraphic{\flushright\includegraphics[height=1.3cm]{img/logos.png}\vspace{.5cm}}
\author{
@@ -12,8 +12,7 @@
\begin{flushleft}
\begin{tabular}{ll}
& Vincent Lannurien~\inst{1} \\
- & \url{vincent.lannurien@univ-brest.fr} \\ \\
- & Inspiré du cours de Yvon Autret et Jean Vareille
+ & \href{mailto:vincent.lannurien@univ-brest.fr}{\texttt{vincent.lannurien@univ-brest.fr}}
\end{tabular}
\end{flushleft}
\end{table}
@@ -28,7 +27,7 @@
\begin{table}[]
\centering
\begin{flushleft}
- \begin{tabular}{L}
+ \begin{tabular}{F}
\inst{1}~Lab-STICC, CNRS UMR 6285, Université de Bretagne Occidentale, Brest
\end{tabular}
\end{flushleft}
@@ -43,7 +42,9 @@
\maketitle
-\begin{frame}{Introduction}
+\section{Introduction}
+
+\begin{frame}{Quelques systèmes embarqués...}
\begin{figure}
\begin{subfigure}{.3\textwidth}
\centering
@@ -58,7 +59,7 @@
\includegraphics[width=\linewidth]{img/exemples/Patriot20missile202520Aug202016.jpg}
\end{subfigure}
\end{figure}
- \begin{figure}
+ \begin{figure}
\begin{subfigure}{.18\textwidth}
\centering
\includegraphics[width=\linewidth]{img/exemples/therac-25.jpg}
@@ -74,32 +75,32 @@
\end{figure}
\end{frame}
-\begin{frame}
+\begin{frame}{... devenus tristement célèbres}
\begin{itemize}
\item \url{https://en.wikipedia.org/wiki/Mars_Climate_Orbiter}
- \begin{itemize}
- \item problème d'unités (système métrique vs système impérial)
- \end{itemize}
+ \begin{itemize}
+ \item problème d'unités (système métrique vs système impérial)
+ \end{itemize}
\item \url{https://en.wikipedia.org/wiki/Ariane_flight_V88}
- \begin{itemize}
- \item erreur de conversion (\textit{overflow})
- \end{itemize}
+ \begin{itemize}
+ \item erreur de conversion (\textit{overflow})
+ \end{itemize}
\item \url{https://en.wikipedia.org/wiki/14th_Quartermaster_Detachment}
- \begin{itemize}
- \item bug d'arrondi (\textit{overflow})
- \end{itemize}
+ \begin{itemize}
+ \item bug d'arrondi (\textit{overflow})
+ \end{itemize}
\item \url{https://en.wikipedia.org/wiki/Therac-25}
- \begin{itemize}
- \item erreur de programmation concurrente (\textit{race condition})
- \end{itemize}
+ \begin{itemize}
+ \item erreur de programmation concurrente (\textit{race condition})
+ \end{itemize}
\item \url{https://en.wikipedia.org/wiki/Death_of_Elaine_Herzberg}
- \begin{itemize}
- \item détection d'obstacle inopérante
- \end{itemize}
+ \begin{itemize}
+ \item détection d'obstacle inopérante
+ \end{itemize}
\item \url{https://en.wikipedia.org/wiki/Titan_submersible_implosion}
- \begin{itemize}
- \item certifications de sécurité insuffisantes
- \end{itemize}
+ \begin{itemize}
+ \item certifications de sécurité insuffisantes
+ \end{itemize}
\end{itemize}
\end{frame}
@@ -121,17 +122,17 @@
\end{frame}
\begin{framefont}{\small}
-\begin{frame}[t]{Plan de la présentation}
- \begin{columns}
- \column{0.5\textwidth}
- \tableofcontents%[hideallsubsections]
+ \begin{frame}[t]{Plan du cours}
+ \begin{columns}
+ \column{0.5\textwidth}
+ \tableofcontents%[hideallsubsections]
- \column{0.5\textwidth}
- \begin{center}
- \includegraphics[width=0.9\columnwidth]{img/logos.png}
- \end{center}
- \end{columns}
-\end{frame}
+ \column{0.5\textwidth}
+ \begin{center}
+ \includegraphics[width=.7\columnwidth]{img/rubo-1.png}
+ \end{center}
+ \end{columns}
+ \end{frame}
\end{framefont}
\section{Organisation de l'UE}
@@ -139,41 +140,41 @@
\begin{frame}{Organisation de l'UE -- Contenu}
\begin{itemize}
\item Objectifs :
- \begin{itemize}
- \item Savoir concevoir des systèmes cyberphysiques simples, autonomes
- \item Comprendre les compromis entre performance (latence, débit) et consommation (énergie, mémoire)
- \end{itemize}
+ \begin{itemize}
+ \item Savoir concevoir des systèmes cyberphysiques simples, autonomes
+ \item Comprendre les compromis entre performance (latence, débit) et consommation (énergie, mémoire)
+ \end{itemize}
\item Méthode :
- \begin{itemize}
- \item Le plus de \textbf{manipulations} possible !
- \item Dimension \textbf{matérielle} (un peu d'électronique, un peu de mécanique)
- \item Dimension \textbf{logicielle} (de l'informatique !)
- \end{itemize}
+ \begin{itemize}
+ \item Le plus de \textbf{manipulations} possible !
+ \item Dimension \textbf{matérielle} (un peu d'électronique, un peu de mécanique)
+ \item Dimension \textbf{logicielle} (de l'informatique !)
+ \end{itemize}
\end{itemize}
\end{frame}
\begin{frame}{Organisation de l'UE -- Logistique}
\begin{itemize}
\item Volume horaire :
- \begin{itemize}
- \item CM : 8h
- \item TP : 14h
- \end{itemize}
+ \begin{itemize}
+ \item CM : 8h
+ \item TP : 14h
+ \end{itemize}
\item Travail en groupes :
- \begin{itemize}
- \item Binômes dans les groupes de TP
- \item Deux binômes par robot
- \end{itemize}
+ \begin{itemize}
+ \item Un robot par personne
+ \item Binômes possibles dans les groupes de TP
+ \end{itemize}
\item Examen :
- \begin{itemize}
- \item Démo lors des TP
- \item Rendu de projet
- \end{itemize}
+ \begin{itemize}
+ \item Démo \textbf{individuelle} lors des TP
+ \item Rendu de projet sur \href{https://moodlesciences.univ-brest.fr/moodle/course/view.php?id=867}{Moodle}
+ \end{itemize}
\item Intervenants :
- \begin{itemize}
- \item Vincent Lannurien -- \url{vincent.lannurien@univ-brest.fr}
- \item Alan Le Boudec -- \url{alan.leboudec@univ-brest.fr}
- \end{itemize}
+ \begin{itemize}
+ \item Vincent Lannurien -- \href{mailto:vincent.lannurien@univ-brest.fr}{\texttt{vincent.lannurien@univ-brest.fr}}
+ \item Kilian Le Gall -- \href{mailto:kilian.legall@univ-brest.fr}{\texttt{kilian.legall@univ-brest.fr}}
+ \end{itemize}
\end{itemize}
\end{frame}
@@ -185,7 +186,7 @@
\begin{itemize}
\item Prise en main de l'environnement de développement
\item Implantation d'algorithmes simples sur le robot
- \item Calcul déporté
+ \item Étude des compromis performances/ressources
\end{itemize}
\vspace{24px}
@@ -203,36 +204,148 @@
\begin{frame}{Étapes du projet}
\begin{itemize}
\item Commande : Fonctionnement de base d'un robot
- \begin{itemize}
- \item Envoi de messages : avancer, reculer, tourner, etc.
- \item Communications : filaire (série), sans-fil (radio)
- \item Interface web de commande : Node.js, JavaScript
- \end{itemize}
+ \begin{itemize}
+ \item Envoi de messages : avancer, reculer, tourner, etc.
+ \item Communications : filaire (série), sans-fil (radio)
+ \item Interface web de commande : Node.js, JavaScript
+ \end{itemize}
\item Environnement : capteurs et actionneurs
- \begin{itemize}
- \item Utilisation d'un capteur de distance
- \item Fonctionnement des moteurs
- \end{itemize}
+ \begin{itemize}
+ \item Utilisation d'un capteur de distance
+ \item Fonctionnement des moteurs
+ \end{itemize}
\item Contrôle : Programmation de missions simples
- \begin{itemize}
- \item Comportement autonome :
- \begin{itemize}
- \item Avancer jusqu'à trouver un obstacle
- \item Avancer jusqu'à trouver une ouverture
- \item Faire un quart de tour
- \item Contourner un objet
- \end{itemize}
- \end{itemize}
+ \begin{itemize}
+ \item Comportement autonome :
+ \begin{itemize}
+ \item Avancer jusqu'à trouver un obstacle
+ \item Avancer jusqu'à trouver une ouverture
+ \item Faire un quart de tour
+ \item Contourner un objet
+ \end{itemize}
+ \end{itemize}
\item En option :
- \begin{itemize}
- \item Plus de capteurs
- \item Plus de cartes
- \end{itemize}
+ \begin{itemize}
+ \item Plus de capteurs (second capteur de distance, caméra monochrome...)
+ \item Plus de cartes (calcul déporté, communication entre robots...)
+ \end{itemize}
\end{itemize}
\end{frame}
\section{Contexte}
+\begin{frame}{Histoire de la robotique -- Automates [\href{https://educ.arte.tv/program/faire-l-histoire-le-canard-de-vaucanson-l-automate-disparu}{réf.}]}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.5\linewidth]{img/vaucanson-duck.png}
+ \caption{Canard digérant (Jacques de Vaucanson, 1739)}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Histoire de la robotique -- Automates}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.3\linewidth]{img/serinette.png}
+ \caption{Serinette de Jacotel (famille Husson-Jacotel, début du XIX\textsuperscript{e})}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Histoire de la robotique -- Automates [\href{https://ingeniumcanada.org/fr/le-reseau/articles/les-chiens-electriques-revent-ils-de-gateries-a-haute-tension-henry-joseph}{réf.}]}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.7\linewidth]{img/hammond-electric-dog.png}
+ \caption{Chien électrique (John Hammond et Benjamin Miessner, 1915)}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Histoire de la robotique -- Automates}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.8\linewidth]{img/defenseur-du-temps-1.png}
+ \caption{\textit{Le Défenseur du Temps} (Jacques Monestier, 1979)}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Histoire de la robotique -- Robots}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.6\linewidth]{img/goliath.png}
+ \caption{Goliath, mine filoguidée activable à distance (Normandie, 1944)}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Histoire de la robotique -- Robots}
+ \begin{columns}
+ \column{0.5\linewidth}
+ \begin{itemize}
+ \item Accident de Tchernobyl (1986) :
+ \begin{itemize}
+ \item \textbf{Liquidateurs} : personnel civil et militaire intervenu immédiatement sur les lieux de la catastrophe nucléaire
+ \item 600 000 personnes ont reçu des doses équivalentes à Hiroshima ou Nagazaki
+ \begin{itemize}
+ \item Reconnaissance, détection de la radioactivité
+ \item Démantèlement des débris du réacteur
+ \item Documentation (photographie)
+ \item Échantillonnage des matériaux
+ \item Enfouissement des déchets
+ \end{itemize}
+ \end{itemize}
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.8\linewidth]{img/stele-liquidateurs-kharkiv.png}
+ \caption{Stèle à la mémoire des liquidateurs (Kharkiv, Ukraine)}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{Histoire de la robotique -- Robots}
+ \begin{columns}
+ \column{0.4\linewidth}
+ \begin{itemize}
+ \item Développement de robots télécommandés pour l'inspection et le nettoyage \textbf{à distance}
+ \begin{itemize}
+ \item \textbf{Télédétection} : caméra, capteurs de radiation
+ \item \textbf{Télémétrie} : transmission des données vers un poste de contrôle
+ \item \textbf{Télémanipulation} : bras robotique pour manipuler les débris
+ \end{itemize}
+ \end{itemize}
+
+ \column{0.6\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\linewidth]{img/robot-joker.png}
+ \caption{Robot \textbf{Joker} (1986)}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{Histoire de la robotique -- Robots}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.7\linewidth]{img/metro-paris.png}
+ \caption{Ligne 14 du métro de Paris}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Automates ou robots ?}
+ \begin{itemize}
+ \item \textbf{Automate} :
+ \begin{itemize}
+ \item répète une séquence fixe de mouvements mécaniques
+ \item ne s'adapte pas à son environnement
+ \end{itemize}
+ \item \textbf{Robot} :
+ \begin{itemize}
+ \item possède des capacités de \textit{perception}
+ \item peut \textit{interagir} avec son environnement
+ \item peut prendre des décisions \textit{autonomes}
+ \end{itemize}
+ \end{itemize}
+\end{frame}
+
\begin{frame}{Définitions -- Systèmes cyberphysiques}
\begin{definition}
\textit{Cyber-Physical Systems (CPS) are integrations of computation with physical processes.}~\footfullcite{leeCyberPhysicalSystems2008}
@@ -260,23 +373,6 @@
\footnotetext{\fullcite{inderwildiImpactIntelligentCyberphysical2020}}
\end{frame}
-\begin{frame}{Définitions -- Systèmes autonomes}
- \begin{columns}
- \column{0.5\linewidth}
- \begin{itemize}
- \item Robotique, systèmes autonomes~\footnote[frame]{\fullcite{kephartVisionAutonomicComputing2003}} :
- \begin{itemize}
- \item \textbf{Capteurs} : produisent une vue de l'environnement du système
- \item \textbf{Actionneurs} : commandent le système selon son état et sa consigne
- \end{itemize}
- \item Objets connectés, \textit{Internet of Things} (\textit{IoT}) : contrôle, commande à distance
- \end{itemize}
-
- \column{0.5\linewidth}
- \includegraphics[width=\columnwidth]{img/mape-k.png}
- \end{columns}
-\end{frame}
-
\begin{frame}{Quelques tendances [\href{https://digital-strategy.ec.europa.eu/fr/policies/next-generation-internet-things}{réf.}]}
\begin{itemize}
\item 41 milliards d'appareils \textit{IoT} déployés en 2025
@@ -339,13 +435,13 @@
\begin{itemize}
\item \textbf{Circuit intégré} : calcul (processeur), mémoire (mémoire vive et non-volatile), interfaces d'Entrées/Sorties
- \item \textbf{Non-programmable} : son comportement est gravé dans le silicone
+ \item \textbf{Non-programmable} : son comportement est gravé dans le silicium
\item \textbf{Utilisation} : partout où un processeur généraliste n'est pas nécessaire
- \begin{itemize}
- \item + : Plus simple à comprendre, à vérifier
- \item + : Consommation d'énergie minimale
- \item - : Peu flexible
- \end{itemize}
+ \begin{itemize}
+ \item + : Plus simple à comprendre, à vérifier
+ \item + : Consommation d'énergie minimale
+ \item - : Peu flexible
+ \end{itemize}
\item \textbf{Coût} : en 2018, en moyenne, \$0.03 pour un µc 8-bit, \$1 pour un µc 32-bit $\rightarrow$ ratio $\frac{1}{100}$ par rapport à un PC grand public
\end{itemize}
@@ -354,127 +450,432 @@
\end{columns}
\end{frame}
-\subsection{Arduino}
+\begin{frame}{Microcontrôleurs : Cartes électroniques}
+ \begin{itemize}
+ \item \textbf{Énergie} (VCC, GND, tension d'alimentation)
+ \item \textbf{Architecture} (horloge, mémoire)
+ \item \textbf{Signal} et \textbf{interfaces} (GPIO, ADC, PWM)
+ \item \textbf{Capteurs} (communications série, UART, I²C, SPI)
+ \item \textbf{Actionneurs} (étage de puissance, moteurs, servomoteurs)
+ \item \textbf{Mécanique} (moteur + réducteur, roues, châssis)
+ \end{itemize}
+\end{frame}
-\begin{frame}{Arduino Uno : caractéristiques (ATmega328P)}
+\begin{frame}{Principes de câblage}
+ % \FIXME{} exemples... pas pédago là
+
+ \begin{itemize}
+ \item Tensions, masse et référence commune
+ \item Différence entre \textbf{tension d'alimentation} et \textbf{tension logique}
+ \item Transporter l'\textbf{énergie} : une différence de tension provoque la circulation d'un courant lorsque le circuit électrique est fermé (exemple : LED branchée entre $3.3V$ et $GND$)
+ \item Transporter l'\textbf{information} : la tension n'est pas forcément constante ; le microcontrôleur peut la faire varier pour transmettre un signal (tension proche de $0V$ = niveau logique à 0, tension proche de $3.3V$ = niveau logique à 1)
+ \end{itemize}
+\end{frame}
+
+\begin{frame}{Principes de câblage}
+ % \FIXME{} exemples... pas pédago là
+
+ \begin{itemize}
+ \item Commander l'allumage d'une LED, le démarrage d'un moteur, lire l'état d'un bouton, envoyer des données sur un bus de communication, etc.
+ \item \textbf{ADC} (\textit{Analog to Digital Converter}) : mesure la tension et la transforme en valeur numérique utilisable par le programme
+ \item Tension = différence de potentiel entre deux points
+ \begin{itemize}
+ \item $U_{AB} = V_{A} - V_{B}$
+ \end{itemize}
+ \item Il faut donc toujours une \textbf{référence}
+ \begin{itemize}
+ \item On choisit un point comme $0V$ : $GND$
+ \item $GND$ sert également de chemin de retour au courant du signal
+ \end{itemize}
+ \item GPIO à $3.3V$ signifie "GPIO à $3.3V$ au-dessus de $GND$"
+ \end{itemize}
+\end{frame}
+
+\begin{frame}{Principes de câblage}
+ % \FIXME{} exemples... pas pédago là
+
+ \begin{itemize}
+ \item Pull-up et pull-down : résistance de rappel pour forcer un état logique par défaut
+ \item Exemple : bouton poussoir connecté entre $GND$ et un GPIO configuré en entrée avec pull-up
+ \begin{itemize}
+ \item Bouton relâché : GPIO = $3.3V$ (niveau logique à 1)
+ \item Bouton appuyé : GPIO = $0V$ (niveau logique à 0)
+ \end{itemize}
+ \item Nécessaire lorsque le circuit n'est pas fermé (bouton relâché) pour éviter que le GPIO "flotte" et prenne des valeurs aléatoires
+ \end{itemize}
+\end{frame}
+
+\begin{frame}{Architecture}
\begin{columns}
- \column{0.65\linewidth}
- $\rightarrow$ Plateforme de \textbf{prototypage}
+ \column{.5\textwidth}
+ \begin{figure}
+ \includegraphics[width=\textwidth]{img/architecture.png}
+ \end{figure}
- $\rightarrow$ Permet de reprogrammer le système
+ \column{.5\textwidth}
+ \begin{alertblock}{Architecture des ordinateurs}
+ Le \textit{programme} est stocké en \textbf{flash} ; le \textbf{processeur} en exécute les \textit{instructions} ; l'\textbf{horloge} \textit{cadence} son fonctionnement ; la \textbf{RAM} conserve les \textit{données} nécessaires à l'exécution.
+ \end{alertblock}
- \begin{itemize}
- \item Alimentation : 7 à 12V ou via port USB (40 mA min.)
- \item Horloge : 16 MHz
- \item Mémoire SRAM : 2 kB
- \item Mémoire EEPROM : 1 kB
- \item Mémoire flash : 32 kB
- \item Broches d'E/S : D0 à D13 (40 mA par broche)
- \item Entrées analogiques : A0 à A5
- \item Bus série, I2C et SPI
- \item Gestion des interruptions
- \item Dimensions : 74 x 53 x 15 mm
- \end{itemize}
-
- \column{0.35\linewidth}
- \includegraphics[width=\columnwidth]{img/arduino-uno.jpg}
- \includegraphics[width=\columnwidth]{img/atmega328p.jpg}
+ Quelle différence entre un ordinateur et un microcontrôleur ?
\end{columns}
\end{frame}
-\begin{frame}{Arduino Uno : détails [\href{https://learn.sparkfun.com/tutorials/what-is-an-arduino\#whats-on-the-board}{réf.}]}
- \begin{columns}
- \column{0.5\linewidth}
- \includegraphics[width=\columnwidth]{img/arduino_uno_breakout.png}
+\subsection{Raspberry Pi Pico et écosystème}
- \column{0.5\linewidth}
- \begin{enumerate}
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \item ...
- \end{enumerate}
- \end{columns}
-\end{frame}
-
-\begin{frame}{Arduino Uno : détails [\href{https://learn.sparkfun.com/tutorials/what-is-an-arduino\#whats-on-the-board}{réf.}]}
- \begin{columns}
- \column{0.5\linewidth}
- \includegraphics[width=\columnwidth]{img/arduino_uno_breakout.png}
-
- \column{0.5\linewidth}
- \begin{enumerate}
- \item Connecteur USB
- \item Connecteur \textit{barrel jack}
- \item Pins \textbf{GND} (masse) : référentiel 0V
- \item Pin \textbf{5V}
- \item Pin \textbf{3.5V}
- \item Pins \textbf{analogiques} (Entrées) -- A0 à A5
- \item Pins \textbf{numériques} (E/S) -- 0 à 13
- \item Pins \textbf{numériques} (PWM) -- 3, 5, 6, 9, 10, 11
- \item Pin \textbf{AREF} : référentiel plafond en tension
- \item Bouton \textit{reset}
- \item Témoin d'alimentation
- \item Témoins \textbf{Tx / Rx}
- \item Circuit intégré \textbf{ATmega328P}
- \item Régulateur de tension (sécurité)
- \end{enumerate}
- \end{columns}
-\end{frame}
-
-\begin{frame}{Arduino Uno : \textit{shields}}
+\begin{frame}
\begin{center}
- \includegraphics[width=0.7\linewidth]{img/arduino-uno-shields.jpg}
- \url{https://www.shieldlist.org/}
+ \includegraphics[width=0.5\textwidth]{img/rubo-1.png}
\end{center}
\end{frame}
-\subsection{Programmation}
+\begin{frame}{Raspberry Pi Pico WH : caractéristiques (RP2040)}
+ \begin{columns}
+ \column{0.4\linewidth}
+ \begin{alertblock}{Plateforme de \emph{prototypage}}
+ Permet de reprogrammer le système !
+ \end{alertblock}
-\begin{frame}[fragile]{Programmation Arduino -- Pas de système d'exploitation !}
- \begin{minted}{c}
- void setup() {
- // put your setup code here, to run once:
+ \begin{itemize}
+ \item Microcontrôleur RP2040
+ \item CPU bicœur Arm Cortex-M0+ @ 133 MHz
+ \item 264 kB SRAM
+ \item 2 MB flash
+ \item Wi-Fi 2.4GHz 802.11n
+ \item Bluetooth 5.2
+ \item 26 broches GPIO
+ \item 2x UART, 2x SPI, 2x I²C, 16x PWM
+ \item Tension supportée : $1.8$ à $5.5V$
+ \end{itemize}
- }
-
- void loop() {
- // put your main code here, to run repeatedly:
-
- }
- \end{minted}
+ \column{0.6\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\textwidth]{img/raspberry-pi-pico-wh.png}
+ \end{figure}
+ \end{columns}
\end{frame}
-\begin{frame}{Programmation Arduino -- Lectures, écritures}
+\begin{frame}{\textit{Shield} générique}
+ \begin{columns}
+ \column{0.4\linewidth}
+ \begin{itemize}
+ \item Simplifie le câblage des capteurs et actionneurs
+ \begin{itemize}
+ \item Redistribue les broches (GPIO, VCC, GND) du Raspberry Pi Pico sur des connecteurs standards à 4 fils : $GND$, $VCC$, $signal_2$, $signal_1$
+ \end{itemize}
+ \item Fournit une masse commune sur les connecteurs (I²C, SPI...)
+ \item Fournit une tension d'alimentation régulée
+ \end{itemize}
+
+ \column{0.6\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\textwidth]{img/grove-shield.png}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{\textit{Breadboard} et abaisseur de tension}
+ \begin{columns}
+ \column{0.5\linewidth}
+ \begin{itemize}
+ \item \textit{Breadboard} : permet de réaliser des montages électroniques sans soudure
+ \item Abaisseur de tension : permet d'alimenter le robot en $5V$ à partir d'une source à une tension plus haute
+ \begin{itemize}
+ \item \textit{e.g.}, une pile $9V$
+ \end{itemize}
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\textwidth]{img/breadboard-power.png}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{Afficheur LCD}
+ \begin{columns}
+ \column{0.5\linewidth}
+ \begin{itemize}
+ \item Afficheur LCD 16x2 caractères
+ \item Interface I²C
+ \item Permet d'afficher des informations sur le robot
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\textwidth]{img/grove-lcd.png}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{\textit{Shield} de commande de moteurs}
+ \begin{columns}
+ \column{0.5\linewidth}
+ \begin{itemize}
+ \item L298N : 2x "Pont en H"
+ \item Interface numérique sur quatre broches de signal
+ \begin{itemize}
+ \item IN1, IN2 : commande des deux moteurs gauches
+ \item IN3, IN4 : commande des deux moteurs droits
+ \end{itemize}
+ \item Applique une quantité réglable de puissance à une charge
+ \item Permet de commander deux moteurs à courant continu
+ \item Permet de faire varier la vitesse et le sens de rotation des moteurs
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\textwidth]{img/joy-it-sbc-motodriver2.png}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{Télémètre à ultrasons}
+ \begin{columns}
+ \column{0.5\linewidth}
+ \begin{itemize}
+ \item Capteur de distance à ultrasons
+ \item Interface numérique sur une broche
+ \begin{itemize}
+ \item 3 fils : \textbf{SIG} (signal numérique bidirectionnel) + VCC + GND
+ \end{itemize}
+ \item Permet de mesurer la distance entre le robot et un obstacle
+ \item Principe : émettre une impulsion sonore et mesurer le temps de retour de l'écho
+ \begin{itemize}
+ \item Distance = $\frac{v_{son} \times t_{echo}}{2}$, avec $v_{son} \approx 343 m/s$ à $20°C$
+ \end{itemize}
+ \item Précision : 1 mm, portée : 30 mm à 700 mm
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\textwidth]{img/grove-ultrasonic.png}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{Télémètre à infrarouges}
+ \begin{columns}
+ \column{0.5\linewidth}
+ \begin{itemize}
+ \item Capteur de distance à infrarouges
+ \item Interface I²C
+ \item Permet de mesurer la distance entre le robot et un obstacle
+ \item Principe : émettre une impulsion lumineuse et mesurer le temps de retour de l'écho
+ \begin{itemize}
+ \item Distance = $\frac{v_{lumiere} \times t_{echo}}{2}$, avec $v_{lumiere} \approx 3\times10^8 m/s$
+ \end{itemize}
+ \item Précision : 1 mm, portée : environ 2500 mm
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\textwidth]{img/vl53l1x-tof.png}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{Caméra monochrome}
+ \begin{columns}
+ \column{0.5\linewidth}
+ \begin{itemize}
+ \item Caméra monochrome, focus fixe, angle de vision à 60°
+ \item Interface I²C
+ \item Permet de capturer des images et de les transmettre au microcontrôleur
+ \item Résolution : QVGA (320x240 pixels), format : RAW6/RAW8, taille : 76 800 octets (75 KB)
+ \item Débit : 30 fps $\times$ 76 800 octets = 2250 KB/s
+ \item Consommation d'énergie : $\lesssim$ 2 mW
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\textwidth]{img/arducam.png}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\subsection{Programmation et gestion des Entrées/Sorties}
+
+\begin{frame}{Rôle d'un système d'exploitation}
+ \begin{alertblock}{Quizz}
+ Qu'est-ce que fournit un système d'exploitation ?
+ \end{alertblock}
+
+ \begin{center}
+ \includegraphics[width=0.1\linewidth]{img/os-question.png}
+ \end{center}
+\end{frame}
+
+\begin{frame}{Rôle d'un système d'exploitation}
+ \begin{center}
+ \includegraphics[width=0.5\linewidth]{img/os-answer.png}
+ \end{center}
+\end{frame}
+
+\begin{frame}[fragile]{Programmation d'un microcontrôleur}
+ \begin{itemize}
+ \item \textbf{Pas de système d'exploitation !}
+ \item Le programme est exécuté directement par le microcontrôleur
+ \item Pas de gestion de la mémoire, pas de processus
+ \item Accès direct aux ressources matérielles
+ \item Programmation en langage C/C++ ou MicroPython
+ \end{itemize}
+\end{frame}
+
+\begin{frame}{Programmation d'un microcontrôleur -- Lectures, écritures}
\begin{columns}
\column{0.5\linewidth}
\begin{itemize}
\item \textbf{GPIO} : \textit{General-Purpose Input Output}
- \item Lecture analogique : tension sur un \textit{pin}
- \begin{itemize}
- \item Conversion analogique vers numérique (\textit{ADC})
- \item $[0.0, 5.0[V \rightarrow int [0, 1024[$
- \item Précision : 10 bits, résolution : 4.9 mV/valeur
- \end{itemize}
- \item Lecture/Écriture numérique : états binaires
- \begin{itemize}
- \item $0$ (\texttt{LOW}, 0V)
- \item $1$ (\texttt{HIGH}, 3.3V ou 5V)
- \end{itemize}
+ \item Lecture \textbf{analogique} : mesure d'une \textit{tension} sur certaines broches
+ \begin{itemize}
+ \item Conversion analogique vers numérique (\textit{ADC})
+ \item $[0.0, 3.3]\,V \rightarrow \texttt{int}\ [0,4095]$
+ \item Précision : 12 bits, résolution théorique : $\approx 0.81$ mV/valeur
+ \end{itemize}
+ \item Lecture/Écriture \textbf{numérique} : états \textit{logiques} (binaires)
+ \begin{itemize}
+ \item $0$ (\texttt{LOW}, 0V)
+ \item $1$ (\texttt{HIGH}, 3.3V ou 5V)
+ \end{itemize}
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.8\columnwidth]{img/rpi-pico-pinout.png}
+ \caption{Raspberry Pi Pico WH : broches GPIO\textbf{26}, GPIO\textbf{27} et GPIO\textbf{28} en entrées ADC (peuvent lire la tension en sortie d'un périphérique).}
+ \end{figure}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{Raspberry Pi Pico -- \textit{Pinout} (brochage)}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.8\columnwidth]{img/rpi-pico-pinout.png}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Programmation Raspberry Pi Pico -- Comparatif des interfaces}
+ \begin{table}[ht]
+ \centering
+
+ \begin{tabular}{L{2cm} C{2.2cm} C{3.8cm} C{3.8cm}}
+ \toprule
+ \textbf{Interface} &
+ \textbf{Niveau d'abstraction} &
+ \textbf{\Circled{+}} &
+ \textbf{\Circled{-}} \\
+ \midrule
+
+ \rowcolor{rowgray}
+ \textbf{MicroPython} (Python)
+ & Haut
+ & Simple, interactif
+ & Performance, déterminisme \\
+
+ \textbf{Pico SDK} (C/C++)
+ & Bas à moyen
+ & Contrôle, performances
+ & Lisibilité, compilation \\
+
+ \rowcolor{rowgray}
+ \textbf{Arduino} (C++)
+ & Haut
+ & Portabilité, écosystème
+ & Contrôle, performances \\
+ \bottomrule
+ \end{tabular}
+ \end{table}
+\end{frame}
+
+\begin{frame}{Programmation Raspberry Pi Pico -- Comparatif des interfaces}
+ \begin{alertblock}{Une même tache dans différent paradigmes}
+ Configurer GP15 en sortie et faire clignoter une LED.
+ \end{alertblock}
+
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.7\linewidth]{img/fritzing-led.png}
+ \end{figure}
+\end{frame}
+
+\begin{frame}[fragile]{Programmation Raspberry Pi Pico -- MicroPython}
+ \begin{minted}[fontsize=\small]{python}
+from machine import Pin
+from time import sleep
+
+led = Pin(15, Pin.OUT)
+
+while True:
+ led.toggle()
+ sleep(1)
+ \end{minted}
+\end{frame}
+
+\begin{frame}[fragile]{Programmation Raspberry Pi Pico -- C++ Pico SDK}
+ \begin{minted}[fontsize=\small]{c}
+#include "pico/stdlib.h"
+
+int main() {
+ gpio_init(15);
+ gpio_set_dir(15, GPIO_OUT);
+
+ while (true) {
+ gpio_put(15, 1);
+ sleep_ms(1000);
+
+ gpio_put(15, 0);
+ sleep_ms(1000);
+ }
+}
+ \end{minted}
+\end{frame}
+
+\begin{frame}[fragile]{Programmation Raspberry Pi Pico -- Arduino API}
+ \begin{minted}[fontsize=\small]{cpp}
+void setup() {
+ pinMode(15, OUTPUT);
+}
+
+void loop() {
+ digitalWrite(15, HIGH);
+ delay(1000);
+ digitalWrite(15, LOW);
+ delay(1000);
+}
+ \end{minted}
+\end{frame}
+
+\begin{frame}{Quizz}
+ \begin{alertblock}{Quizz}
+ Comment faire varier l'intensité de la LED pour produire un effet de fondu, en boucle ?
+ \end{alertblock}
+\end{frame}
+
+\begin{frame}{PWM}
+ \begin{columns}
+ \column{0.5\linewidth}
+ \begin{itemize}
\item Écriture analogique ?
- \begin{itemize}
- \item Le microcontrôleur ne peut pas générer une tension arbitraire...
- \end{itemize}
+ \begin{itemize}
+ \item Le microcontrôleur ne peut pas générer une tension arbitraire...
+ \end{itemize}
+ \item Décrire un phénomène continu par des modulations
+ \item Technique : \textit{PWM} (\textit{Pulse Width Modulation})
+ \begin{itemize}
+ \item Générer un signal carré de fréquence fixe
+ \item Faire varier le rapport cyclique (durée du niveau haut / période)
+ \item La valeur moyenne du signal est proportionnelle au rapport cyclique
+ \end{itemize}
\end{itemize}
\column{0.5\linewidth}
@@ -483,28 +884,32 @@
\end{center}
\begin{itemize}
- \item Décrire un phénomène continu par des modulations
- \item Technique : \textit{PWM}
+ \item Assuré par un composant matériel dédié pour ne pas monopoliser le processeur
\end{itemize}
\end{columns}
\end{frame}
-\begin{frame}{Programmation Arduino -- Exercice}
- \begin{exampleblock}{Un premier programme Arduino}
- On utilise le simulateur :
+\begin{frame}[fragile]{Mesurer le temps}
+ Dans le but d'évaluer les performances, mesurer la durée d'une action, etc.
- \begin{itemize}
- \item \href{https://wokwi.com/projects/new/arduino-uno}{https://wokwi.com/projects/new/arduino-uno}
- \end{itemize}
- \end{exampleblock}
+ \begin{minted}{cpp}
+#include "pico/stdlib.h"
+#include
- \begin{enumerate}
- \item \TODO Écriture numérique : allumer puis éteindre une LED sur le pin 5 ;
- \item \TODO Écriture analogique : faire varier l'intensité de la LED pour produire un effet de fondu, en boucle.
- \end{enumerate}
+int main() {
+ stdio_init_all();
+
+ uint64_t start = time_us_64();
+ sleep_ms(250);
+ uint64_t stop = time_us_64();
+
+ uint64_t elapsed_us = stop - start;
+ printf("Elapsed: %.3f ms\n", elapsed_us / 1000.0);
+}
+ \end{minted}
\end{frame}
-\subsection{Entrées/Sorties}
+\subsection{Communications}
\begin{frame}{Communications série : UART [\href{http://bts2m.free.fr/TP_Arduino/01_UART.html}{réf.}]}
\begin{itemize}
@@ -513,7 +918,7 @@
\item Liaison avec 3 fils : \textbf{Tx} (\textit{transmit} de l'émetteur), \textbf{Rx} (\textit{receive} du récepteur), \textbf{GND} (masse)
\item \textit{Full duplex} : émission et réception simultanées ; point à point : 2 éléments
\item Vitesse : en général 9600 bauds (bits/seconde), au maximum 115200 bauds (environ 14 kB/seconde...)
- \item "Masqué" derrière le port USB de l'Arduino !
+ \item "Masqué" derrière le port USB du Raspberry !
\end{itemize}
\begin{center}
@@ -521,9 +926,48 @@
\end{center}
\end{frame}
-\begin{frame}{Communications série : I2C [\href{https://passionelectronique.fr/liaisons-series-uart-i2c-spi/}{réf.}]}
+\begin{frame}[fragile]{Communications série : UART -- Exemple : écriture}
+ \begin{minted}{cpp}
+#include
+#include "pico/stdlib.h"
+
+int main() {
+ // UART <-> USB
+ stdio_init_all();
+
+ while (true) {
+ // Affichage dans le terminal série
+ printf("Hello, World!\n");
+ sleep_ms(1000);
+ }
+}
+ \end{minted}
+\end{frame}
+
+\begin{frame}[fragile]{Communications série : UART -- Exemple : lecture}
+ \begin{minted}{cpp}
+#include
+#include "pico/stdlib.h"
+
+int main() {
+ // UART <-> USB
+ stdio_init_all();
+
+ while (true) {
+ // Lecture (non-bloquante !) depuis le terminal série
+ int c = getchar_timeout_us(0);
+ if (c != PICO_ERROR_TIMEOUT) {
+ printf("Reçu : %c\n", c);
+ }
+ // Reste du programme...
+ }
+}
+ \end{minted}
+\end{frame}
+
+\begin{frame}{Communications série : I²C [\href{https://passionelectronique.fr/liaisons-series-uart-i2c-spi/}{réf.}]}
\begin{itemize}
- \item \textbf{I2C} : \textit{Inter-Integrated Circuit}
+ \item \textbf{I²C} : \textit{Inter-Integrated Circuit}
\item Topologie "maître-esclave", adresses uniques, bits de lecture/écriture
\item Liaison avec trois fils : \textbf{SDA} (signal de données), \textbf{SCL} (signal d'horloge), \textbf{GND} (masse)
\item \textit{Semi duplex} : communications bidirectionnelles, mais pas simultanées ; multi-points
@@ -535,13 +979,38 @@
\end{center}
\end{frame}
-\begin{frame}{Communications sans-fil : Zigbee}
+% \begin{frame}[fragile]{Communications série : I²C -- Exemple}
+% \begin{minted}{cpp}
+% printf("TODO");
+% \end{minted}
+% \end{frame}
+
+\begin{frame}{Communications série : SPI [\href{https://passionelectronique.fr/liaisons-series-uart-i2c-spi/}{réf.}]}
\begin{itemize}
- \item \textbf{Zigbee} : protocole / norme ouverte IEEE 802.15.4
- \item Topologie en maillage (\textit{mesh})
- \item Communications sans-fil courte distance (10 mètres @ 1 mW)
- \item Bande de fréquences non réservée : pas de licence
- \item Vitesse : 2.5 à 30 kB/seconde ; très faible consommation d'énergie
+ \item \textbf{SPI} : \textit{Serial Peripheral Interface}
+ \item Topologie "maître-esclave", pas d'adresses, bits de lecture/écriture
+ \item Liaison avec quatre fils : \textbf{MOSI} (signal de données maître vers esclave), \textbf{MISO} (signal de données esclave vers maître), \textbf{SCK} (signal d'horloge), \textbf{CS} (sélection de l'esclave)
+ \item \textit{Full duplex} : communications bidirectionnelles simultanées ; multi-points
+ \item Vitesse : en général 1 Mbps, au maximum 10 Mbps (environ 1.25 MB/seconde)
+ \end{itemize}
+
+ \begin{center}
+ \includegraphics[width=0.6\linewidth]{img/spi.png}
+ \end{center}
+\end{frame}
+
+% \begin{frame}[fragile]{Communications série : SPI -- Exemple}
+% \begin{minted}{cpp}
+% printf("TODO");
+% \end{minted}
+% \end{frame}
+
+\begin{frame}{Communications sans-fil : Wi-Fi}
+ \begin{itemize}
+ \item \textbf{Wi-Fi} : norme IEEE 802.11, réseau local sans-fil
+ \item Topologie "point d'accès" (AP) ou "ad-hoc"
+ \item Vitesse : de quelques MB/s à environ 1 GB/s selon la norme (802.11a/b/g/n/ac/ax)
+ \item Portée : de quelques mètres à quelques dizaines de mètres selon l'environnement
\end{itemize}
\begin{center}
@@ -549,12 +1018,46 @@
\end{center}
\end{frame}
-\begin{frame}
+\begin{frame}{Communications sans-fil : comparaison}
\begin{center}
\includegraphics[width=0.9\linewidth]{img/zigbee-table.png}
\end{center}
\end{frame}
+\subsection{Chaîne de compilation}
+
+\begin{frame}{Chaîne de compilation croisée}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\linewidth]{img/compilation.png}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Flasher le Pico : chargeur de démarrage}
+ \begin{columns}
+ \column{0.6\linewidth}
+ \begin{enumerate}
+ \item Brancher le Pico en USB en maintenant le bouton \texttt{BOOTSEL}
+ \item Le Pico apparaît comme un disque USB nommé \texttt{RPI-RP2}
+ \item Copier le firmware compilé (fichier \texttt{.uf2})
+ \item Le Pico redémarre et le nouveau programme s'exécute.
+ \end{enumerate}
+
+ \column{0.4\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=\columnwidth]{img/pico-bootsel.png}
+ \end{figure}
+ \end{columns}
+
+ \medskip
+ \begin{alertblock}{En pratique}
+ Le programme fonctionne : on peut demander l'entrée en mode BOOTSEL par USB.
+
+ S'il ne répond plus : maintenir \textbf{BOOTSEL} en rebranchant le câble, puis charger le même fichier UF2.
+ \end{alertblock}
+\end{frame}
+
\section{Robotique}
\subsection{Généralités}
@@ -577,6 +1080,88 @@
\end{itemize}
\end{frame}
+\begin{frame}{Qu'est-ce qu'un robot ? -- Mobilité}
+ \begin{figure}
+ \begin{subfigure}{.48\textwidth}
+ \centering
+ \includegraphics[width=.8\linewidth]{img/china-humanoid-robots.png}
+ \end{subfigure}
+ \begin{subfigure}{.48\textwidth}
+ \centering
+ \includegraphics[width=.6\linewidth]{img/boston-dynamics-spot.png}
+ \end{subfigure}
+ \end{figure}
+ \begin{figure}
+ \begin{subfigure}[t]{.48\textwidth}
+ \vspace{0pt}
+ \centering
+ \includegraphics[width=.8\linewidth]{img/drones-ukraine.png}
+ \end{subfigure}
+ \begin{subfigure}[t]{.48\textwidth}
+ \vspace{0pt}
+ \centering
+ \includegraphics[width=.8\linewidth]{img/usv-ifremer.png}
+ \end{subfigure}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Qu'est-ce qu'un robot ? -- Charge utile [\href{https://www.thespacereview.com/article/4951/1}{réf.}]}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.8\linewidth]{img/farrah-satellites.png}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Qu'est-ce qu'un robot ? -- Charge utile}
+ \begin{figure}
+ \begin{subfigure}{.48\textwidth}
+ \centering
+ \includegraphics[width=\linewidth]{img/boston-dynamics-weapon.png}
+ \end{subfigure}
+ \begin{subfigure}{.48\textwidth}
+ \centering
+ \includegraphics[width=\linewidth]{img/robot-industriel.png}
+ \end{subfigure}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Qu'est-ce qu'un robot ? -- Intelligence}
+ \begin{figure}
+ \begin{subfigure}{.48\textwidth}
+ \centering
+ \includegraphics[width=0.8\linewidth]{img/microcontrollers.png}
+ \end{subfigure}
+ \begin{subfigure}{.48\textwidth}
+ \centering
+ \includegraphics[width=0.8\linewidth]{img/jetson-orin-nano.png}
+ \end{subfigure}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{Définitions -- Systèmes autonomes}
+ \begin{columns}
+ \column{0.5\linewidth}
+ \begin{itemize}
+ \item Robotique, systèmes autonomes~\footnote[frame]{\fullcite{kephartVisionAutonomicComputing2003}} :
+ \begin{itemize}
+ \item \textbf{Capteurs} : produisent une vue de l'environnement du système
+ \item \textbf{Actionneurs} : commandent le système selon son état et sa consigne
+ \end{itemize}
+ \item Objets connectés, \textit{Internet of Things} (\textit{IoT}) : contrôle, commande à distance
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \includegraphics[width=\columnwidth]{img/mape-k.png}
+ \end{columns}
+\end{frame}
+
+\begin{frame}{Intelligence -- Chaîne de l'information}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.7\linewidth]{img/chaine-traitement.png}
+ \end{figure}
+\end{frame}
+
% \begin{frame}{Capteurs -- Classes}
% \begin{itemize}
% \item Proprioceptifs :
@@ -617,109 +1202,495 @@
\column{0.5\linewidth}
\includegraphics[width=\columnwidth]{img/sensor-error.png}
+
+ \begin{exampleblock}{Exemple : capteur de distance à ultrasons}
+ distance $\rightarrow$ temps de vol d'une onde ultrasonore
+ $\rightarrow$ durée d'une impulsion électrique $\rightarrow$
+ mesure du temps par le Pico $\rightarrow$ $d = v \times t / 2$
+ \end{exampleblock}
\end{columns}
\end{frame}
+% \begin{frame}{Intelligence -- Actionneurs}
+% \begin{columns}
+% \column{0.5\linewidth}
+% \begin{itemize}
+% \item \TODO{} plus de détails sur les actionneurs
+% \end{itemize}
+
+% \column{0.5\linewidth}
+% \TODO
+% \end{columns}
+
+% \end{frame}
+
\begin{frame}{Intelligence -- Contrôle, commandes, asservissement}
\begin{itemize}
\item Éléments contrôlés en boucle fermée : \textbf{asservissements}
\item Système automatique qui ajuste son comportement en fonction de \textbf{consignes} et de \textbf{mesures}
\item Exemple :
-
- \begin{exampleblock}{\emoji{repeat} Boucle d'asservissement}
- \begin{enumerate}
- \item \emoji{dart} \textbf{Consigne} : objectif à atteindre (ex : avancer à 10 cm du mur)
- \item \emoji{bar-chart} \textbf{Mesure} : capteur qui lit la position (GPS), la distance réelle (LiDAR), etc.
- \item \emoji{cross-mark} \textbf{Erreur} : différence entre consigne et mesure
- \item \emoji{brain} \textbf{Contrôleur} : calcule la \textbf{commande} à partir de l'erreur
- \item \emoji{robot} \textbf{Commande} : signal envoyé au système (ex : augmenter la vitesse des moteurs)
- \end{enumerate}
- \end{exampleblock}
- \end{itemize}
+
+ \begin{exampleblock}{\emoji{repeat} Boucle d'asservissement}
+ \begin{enumerate}
+ \item \emoji{dart} \textbf{Consigne} : objectif à atteindre (ex : avancer à 10 cm du mur)
+ \item \emoji{bar-chart} \textbf{Mesure} : capteur qui lit la position (GPS), la distance réelle (LiDAR), etc.
+ \item \emoji{cross-mark} \textbf{Erreur} : différence entre consigne et mesure
+ \item \emoji{brain} \textbf{Contrôleur} : calcule la \textbf{commande} à partir de l'erreur
+ \item \emoji{robot} \textbf{Commande} : signal envoyé au système (ex : augmenter la vitesse des moteurs)
+ \end{enumerate}
+ \end{exampleblock}
+ \end{itemize}
+\end{frame}
+
+\begin{frame}{Problème : le positionnement}
+ \begin{columns}
+ \column{0.6\linewidth}
+ \begin{itemize}
+ \item \textbf{Cap} = \textbf{consigne} + \textbf{erreur}
+ \begin{itemize}
+ \item Comment savoir où se trouve le robot ?
+ \item Comment savoir où se dirige le robot ?
+ \item Comment savoir où se trouve un obstacle ?
+ \end{itemize}
+ \end{itemize}
+
+ \column{0.4\linewidth}
+ \begin{figure}
+ \centering
+ \includegraphics[width=.8\linewidth]{img/missile.png}
+ \end{figure}
+ \end{columns}
+
+ \begin{exampleblock}{The Missile Knows Where It Is [\href{https://knowyourmeme.com/memes/the-missile-knows-where-it-is}{réf.}]}
+ \textit{The missile knows where it is at all times. It knows this because it knows where it isn't. By subtracting where it is from where it isn't, or where it isn't from where it is (whichever is greater), it obtains a difference, or deviation. The guidance subsystem uses deviations to generate corrective commands to drive the missile from a position where it is to a position where it isn't, and arriving at a position where it wasn't, it now is. Consequently, the position where it is, is now the position that it wasn't, and it follows that the position that it was, is now the position that it isn't.}
+ \end{exampleblock}
+\end{frame}
+
+\begin{frame}{Systèmes de positionnement -- Principes}
+ \begin{itemize}
+ \item \textbf{Positionnement global} : localisation dans un référentiel géographique
+ \begin{itemize}
+ \item \textbf{GNSS} (GPS, Galileo, etc.) : triangulation (trilatération) à partir de signaux satellites
+ \end{itemize}
+
+ \vspace{0.4cm}
+
+ \item \textbf{Positionnement local} : estimation de la position par rapport à l'environnement
+ \begin{itemize}
+ \item \textbf{Odométrie} : mesure des déplacements à partir de capteurs (ex : roues, inertie)
+ \item \textbf{LiDAR} : mesure des distances par laser
+ \item \textbf{Radar} : mesure des distances et vitesses par ondes radio
+ \item \textbf{Caméras} : analyse des images par vision par ordinateur
+ \end{itemize}
+
+ \vspace{0.4cm}
+
+ \item \textbf{Localisation} : exploitation des mesures pour estimer la position
+ \begin{itemize}
+ \item SLAM (\textit{Simultaneous Localization and Mapping}) : estimer les déplacements et la position dans un environnement local
+ \end{itemize}
+ \end{itemize}
+\end{frame}
+
+\begin{frame}{Systèmes de positionnement -- Comparaison}
+ \begin{table}
+ \centering
+ \small
+ \begin{tabular}{L{2cm} L{4.2cm} L{5cm}}
+ \toprule
+ \textbf{Technologie} & \textbf{Avantages} & \textbf{Limites} \\
+ \midrule
+
+ \rowcolor{rowgray}
+ \textbf{GPS/GNSS}
+ & Position absolue, couverture mondiale
+ & Peu fiable en intérieur, précision variable \\
+
+ \textbf{LiDAR}
+ & Mesures précises, cartographie 2D/3D
+ & Coût, sensibilité aux conditions météo \\
+
+ \rowcolor{rowgray}
+ \textbf{Radar}
+ & Robuste aux intempéries, mesure des vitesses
+ & Résolution spatiale souvent inférieure au LiDAR \\
+
+ \textbf{Caméras}
+ & Faible coût, information visuelle riche
+ & Sensibilité à l'éclairage, calcul intensif \\
+
+ \bottomrule
+ \end{tabular}
+ \end{table}
+
+ $\rightarrow$ \textbf{Fusion de capteurs} : combiner plusieurs technologies pour compenser leurs limites.
\end{frame}
\subsection{Commande à distance}
-\begin{frame}{Commande -- Un langage pour se parler}
+\begin{frame}[fragile]{Commande -- Communications}
\begin{itemize}
\item Architecture :
- \begin{itemize}
- \item PC : Envoi de commandes filaire $\rightarrow$ USB $\rightarrow$ Robot Arduino
- \item PC : Envoi de commandes sans fil $\rightarrow$ XBee $\rightarrow$ Robot Arduino
- \end{itemize}
- \item Peu importe le type de liaison, le même programme s'exécute sur l'Arduino
- \item $\rightarrow$ Il faut programmer l'Arduino pour \textbf{recevoir}, \textbf{décoder} et \textbf{exécuter} des lignes de commande.
+ \begin{itemize}
+ \item PC : Envoi de commandes filaire $\rightarrow$ USB $\rightarrow$ Robot
+ \item Téléphone : Envoi de commandes sans fil $\rightarrow$ Wi-Fi $\rightarrow$ Robot
+ \end{itemize}
+ \item Peu importe le type de liaison, le même programme s'exécute sur le microcontrôleur
+ \item $\rightarrow$ Il faut programmer le Raspberry pour \textbf{recevoir}, \textbf{décoder} et \textbf{exécuter} des lignes de commande.
\end{itemize}
\end{frame}
-\begin{frame}{Commande -- Capteurs et actionneurs}
- \begin{alertblock}{Sur nos robots...}
- \begin{itemize}
- \item De quels capteurs disposons-nous ?
- \item De quels actionneurs disposons-nous ?
- \end{itemize}
- \end{alertblock}
-\end{frame}
-
-\begin{frame}{Commande -- Un langage pour se parler}
- \begin{alertblock}{Communications}
+\begin{frame}[fragile]{Commande -- Communications}
+ \begin{alertblock}{Communications avec le robot}
\begin{itemize}
\item Objectifs : avancer, reculer, tourner...
\item Quels éléments va-t-on commander ?
\item Sur la base de quelles informations ?
- \item $\rightarrow$ De quelles \textbf{commandes} avons-nous besoin ?
+ \item $\rightarrow$ De quelles \textbf{protocole} avons-nous besoin ?
\end{itemize}
\end{alertblock}
\end{frame}
-\begin{frame}{Commande -- Un langage pour se parler -- Syntaxe}
- \begin{itemize}
- \item Une ligne de commande peut contenir :
- \begin{itemize}
- \item Une commande élémentaire : \texttt{[cmd]}
- \item Une séquence de commandes élémentaires : \texttt{[[cmd\_1][cmd\_2]]}
- \end{itemize}
- \item Une commande peut prendre un paramètre : \texttt{[cmd param]}
- \begin{itemize}
- \item Séparation par un espace
- \end{itemize}
- \end{itemize}
+\begin{frame}[fragile]{Commande -- Communications -- Syntaxe des commandes (JSON)}
+ \begin{center}
+ \resizebox{0.9\textwidth}{!}{%
+ \footnotesize
+ \begin{tabular}{p{0.42\textwidth}p{0.53\textwidth}}
+ \toprule
+ \textbf{Commande} & \textbf{Effet} \\
+ \midrule
- \begin{exampleblock}{Exemple de ligne de commande}
- \texttt{[[mda 255][mga 255]]}
- \end{exampleblock}
+ \rowcolor{rowgray}
+ \mintinline{json}|{"left":200,"right":200}|
+ & Avancer des deux côtés pendant une seconde par défaut \\
+
+ \mintinline{json}|{"left":200,"right":-200,"duration":3000}|
+ & Tourner sur place pendant trois secondes \\
+
+ \rowcolor{rowgray}
+ \mintinline{json}|{"stop":true}|
+ & Arrêter immédiatement les roues et annuler la mission du robot \\
+
+ \mintinline{json}|{"telemetry":500}| / \mintinline{json}|{"telemetry":0}|
+ & Envoyer la télémétrie toutes les 500 ms / désactiver la télémétrie périodique \\
+
+ \rowcolor{rowgray}
+ \mintinline{json}|{"mission":1}| / \mintinline{json}|{"mission":0}|
+ & Sélectionner la mission 1 / désactiver la mission du robot \\
+
+ \mintinline{json}|{"info":true}|
+ & Envoyer une ligne séparée contenant les informations du robot \\
+
+ \rowcolor{rowgray}
+ \mintinline{json}|{"seq":7,"left":100,"right":100}|
+ & Associer l'identifiant 7 aux télémétries suivantes \\
+
+ \bottomrule
+ \end{tabular}
+ }
+ \end{center}
+\end{frame}
+
+\begin{frame}[fragile]{L'étiquette \texttt{seq}}
+ \begin{itemize}
+ \item Choisie par l'émetteur
+ \item Chaque commande qui la fournit remplace la valeur mémorisée
+ \item Toutes les lignes de télémétrie suivantes portent la dernière valeur
+ \end{itemize}
+\end{frame}
+
+\begin{frame}[fragile]{Commande -- Communications -- Syntaxe des réponses (JSON)}
+ \begin{center}
+ \resizebox{0.9\textwidth}{!}{%
+ \footnotesize
+ \begin{tabular}{p{0.27\textwidth}p{0.68\textwidth}}
+ \toprule
+ \textbf{Champ} & \textbf{Signification} \\
+ \midrule
+
+ \rowcolor{rowgray}
+ \mintinline{text}|seq|
+ & Identifiant de la dernière commande, ou \mintinline{json}|-1| si aucun n'a été fourni \\
+
+ \mintinline{text}|ms|
+ & Temps écoulé depuis le démarrage du robot, en millisecondes \\
+
+ \rowcolor{rowgray}
+ \mintinline{text}|left|, \mintinline{text}|right|
+ & Vitesses actuellement demandées aux roues, et non vitesses mesurées \\
+
+ \mintinline{text}|moving|
+ & \mintinline{json}|true| si la vitesse demandée d'au moins une roue est non nulle \\
+
+ \rowcolor{rowgray}
+ \mintinline{text}|mission|
+ & Numéro de la mission sélectionnée ; \mintinline{json}|0| signifie qu'aucune mission n'est active \\
+
+ \mintinline{text}|front_mm|, \mintinline{text}|side_mm|
+ & Dernière distance mesurée en millimètres, ou \mintinline{json}|-1| si indisponible \\
+
+ \rowcolor{rowgray}
+ \mintinline{text}|front_ok|, \mintinline{text}|side_ok|
+ & Indique si la dernière mesure du capteur est valide ; à vérifier avant d'utiliser la distance \\
+
+ \mintinline{text}|front_q|, \mintinline{text}|side_q|
+ & Qualité du signal : \mintinline{text}|front_q| vaut toujours \mintinline{json}|-1| ;
+ \mintinline{text}|side_q| indique le taux de signal du VL53L1X, ou \mintinline{json}|-1| si indisponible \\
+
+ \rowcolor{rowgray}
+ \mintinline{text}|done|
+ & Présent avec la valeur \mintinline{json}|true| uniquement lorsque le contrôle de sécurité arrête les roues \\
+ \bottomrule
+ \end{tabular}
+ }
+ \end{center}
\end{frame}
\subsection{Interface utilisateur}
-\begin{frame}[fragile]{Programmation Node.js}
+\begin{frame}{IHM -- Terminal série}
\begin{itemize}
- \item Langage : \textbf{JavaScript}
- \item Programmation \textbf{asynchrone}, ou \textbf{événementielle}
+ \item \textbf{Terminal} : interface en ligne de commande
+ \item Permet de communiquer avec le microcontrôleur via l'UART
+ \item Le Pico expose un \textbf{port série virtuel} \textit{via} USB
+ \begin{itemize}
+ \item Ex. : \texttt{/dev/ttyACM0} sous Linux
+ \end{itemize}
+ \item On peut envoyer des commandes et lire les réponses du microcontrôleur
+ \item On utilise \texttt{screen} ou \texttt{minicom} pour ouvrir un terminal série
+ \begin{itemize}
+ \item \texttt{screen /dev/ttyUSB0 115200}
+ \begin{itemize}
+ \item \texttt{Ctrl-A} puis \texttt{Ctrl-D} pour quitter \texttt{screen}
+ \end{itemize}
+ \item \texttt{minicom -D /dev/ttyUSB0 -b 115200}
+ \begin{itemize}
+ \item \texttt{Ctrl-A} puis \texttt{X} pour quitter \texttt{minicom}
+ \end{itemize}
+ \end{itemize}
\end{itemize}
-
- \vspace{20px}
-
- \begin{minted}{javascript}
- // Charger une bibliothèque
- const { Something } = require('something');
-
- // Instancier un objet
- const thing = new Something({ "some": "value" });
-
- // Écouter un événement
- thing.on('someEvent', (data) => {
- // Faire quelque chose...
- console.log(data);
- });
- \end{minted}
\end{frame}
-% \begin{frame}{IHM}
-% ...
-% \end{frame}
+\begin{frame}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.7\linewidth]{img/ihm-terminal.png}
+ \end{figure}
+\end{frame}
-\subsection{Comportement autonome}
+\begin{frame}
+ \begin{figure}
+ \centering
+ \includegraphics[width=0.6\linewidth]{img/ihm-web.png}
+ \end{figure}
+\end{frame}
+
+\begin{frame}{IHM -- Interface web}
+ \begin{alertblock}{Quelle différence ?}
+ \textit{Missions firmware} et \textit{missions téléphone} ?
+ \end{alertblock}
+\end{frame}
+
+\subsection{Missions}
+
+\begin{frame}{Missions -- Interface de programmation du robot}
+ \begin{itemize}
+ \item \textbf{Protocole de commande} : envoyer des commandes JSON au robot et recevoir la télémétrie
+ \item \textbf{Interfaces de programmation} :
+ \begin{itemize}
+ \item fonctions C++ dans \texttt{Robot.h} pour piloter le robot "de l'intérieur"
+ \item fonctions JavaScript dans \texttt{robot.js} pour piloter le robot "depuis l'extérieur"
+ \end{itemize}
+ \item \textbf{Missions} : séquences de commandes à exécuter sur le robot
+ \end{itemize}
+\end{frame}
+
+\begin{frame}[fragile]{Missions -- Programmer des déplacements}
+ \begin{itemize}
+ \item \textbf{Objectif} : programmer une séquence de mouvements
+ \begin{itemize}
+ \item Avancer pendant 1 s, puis reculer pendant 1 s
+ \item Effectuer un quart de tour à gauche
+ \end{itemize}
+
+ \item \textbf{Commande} : vitesse des roues gauche/droite et durée
+ \end{itemize}
+
+ \begin{minted}[fontsize=\small]{json}
+{"left":200,"right":200,"duration":1000}
+ \end{minted}
+
+ \begin{itemize}
+ \item \textbf{Enchaînement} : une nouvelle commande remplace la commande en cours
+ \begin{itemize}
+ \item Attendre la ligne \texttt{done:true} avant d'envoyer le mouvement suivant
+ \item Le champ \texttt{seq} renvoie le numéro de la dernière commande reçue : attendre un \texttt{done} dont le \texttt{seq} est $\geq$ à celui envoyé
+ \end{itemize}
+ \end{itemize}
+\end{frame}
+
+\begin{frame}{Missions -- Deux architectures}
+ \begin{table}
+ \centering
+ \small
+ \begin{tabular}{L{3cm} L{4cm} L{4cm}}
+ \toprule
+ & \textbf{Téléphone (\texttt{robot.js})}
+ & \textbf{Robot (\texttt{Robot.h})} \\
+ \midrule
+
+ \rowcolor{rowgray}
+ \textbf{Exécution}
+ & Navigateur du téléphone
+ & Microcontrôleur Pico \\
+
+ \textbf{Commandes}
+ & Envoyées successivement par HTTP
+ & Exécutées localement \\
+
+ \rowcolor{rowgray}
+ \textbf{Synchronisation}
+ & Attente du message \texttt{done}
+ & Surveillance de l'horloge (\texttt{nowMs()}) \\
+
+ \textbf{Conséquence}
+ & Délais liés aux échanges réseau
+ & Enchaînement immédiat possible \\
+
+ \bottomrule
+ \end{tabular}
+ \end{table}
+
+ \vspace{0.3cm}
+
+ \begin{itemize}
+ \item \textbf{Téléphone} : l'API \texttt{Mission} automatise l'enchaînement des commandes.
+ \item \textbf{Robot} : la mission s'exécute dans \texttt{Robot::update()}, sans attente bloquante (\texttt{sleep\_ms()}).
+ \end{itemize}
+\end{frame}
+
+\begin{frame}{Missions -- API et protocole de communication}
+ \centering
+
+ \begin{tikzpicture}[
+ >=Latex,
+ block/.style={
+ draw,
+ rounded corners=3pt,
+ fill=blue!5,
+ text width=3.2cm,
+ minimum height=3.4cm,
+ align=left,
+ inner sep=6pt,
+ font=\small
+ }
+ ]
+
+ % Téléphone
+ \node[block] (js) at (-4,0) {
+ \textbf{Téléphone}\\
+ \textit{Navigateur web}\\[0.3cm]
+
+ \textbf{API JavaScript}\\
+ \texttt{robot.js}\\[0.2cm]
+
+ \footnotesize
+ \texttt{Robot} : commandes\\
+ \texttt{Mission} : séquences
+ };
+
+ % Robot
+ \node[block] (cpp) at (4,0) {
+ \textbf{Robot}\\
+ \textit{Raspberry Pi Pico}\\[0.3cm]
+
+ \textbf{API C++}\\
+ \texttt{Robot.h}\\[0.2cm]
+
+ \footnotesize
+ \texttt{drive()} : moteurs\\
+ \texttt{mission()} : séquences
+ };
+
+ % Protocole
+ \node[align=center] at (0,2.3) {
+ \textbf{Protocole JSON}\\
+ \scriptsize HTTP / Wi-Fi
+ };
+
+ % Commandes
+ \draw[->, thick, blue]
+ ([yshift=0.7cm]js.east) --
+ node[midway, above=4pt, align=center,
+ font=\scriptsize, text=blue] {
+ \textbf{Commandes}\\
+ \texttt{\{"seq":42,...\}}
+ }
+ ([yshift=0.7cm]cpp.west);
+
+ % Télémétrie
+ \draw[->, thick, ForestGreen]
+ ([yshift=-0.7cm]cpp.west) --
+ node[midway, below=4pt, align=center,
+ font=\scriptsize, text=ForestGreen] {
+ \textbf{Télémétrie}\\
+ \texttt{\{"seq":42,"done":true,...\}}
+ }
+ ([yshift=-0.7cm]js.east);
+
+ \end{tikzpicture}
+
+ \vspace{0.4cm}
+
+ \begin{itemize}
+ \item \textbf{Deux possibilités} : séquencer les mouvements
+ depuis le téléphone ou directement sur le robot.
+ \end{itemize}
+\end{frame}
+
+\begin{frame}[fragile]{Missions -- Tourner de 90°}
+ \begin{itemize}
+ \item \textbf{Principe} : faire tourner les roues en sens opposés
+ \end{itemize}
+
+ \begin{minted}[fontsize=\small]{json}
+{"left":-180,"right":180,"duration":600}
+ \end{minted}
+
+ \begin{itemize}
+ \item \textbf{Problème} : sans encodeur ni gyroscope, le robot ne mesure pas son angle de rotation.
+
+ \item \textbf{Solution} : calibrer la durée du mouvement
+ \begin{enumerate}
+ \item Effectuer un virage de durée connue
+ \item Mesurer l'angle obtenu
+ \item Ajuster la durée proportionnellement
+ \end{enumerate}
+ \end{itemize}
+
+ \[
+ t_{\text{nouveau}} =
+ t_{\text{ancien}} \times
+ \frac{90^\circ}{\theta_{\text{mesuré}}}
+ \]
+
+ \begin{itemize}
+ \item \textbf{Limite} : calibration dépendante de la vitesse,
+ du sol et du niveau de batterie.
+ \end{itemize}
+\end{frame}
+
+\subsection{Comportement autonome et contrôle}
+
+\begin{frame}{Un constat : le besoin de calibration}
+ \begin{itemize}
+ \item Les moteurs ne sont pas identiques
+ \item Les roues ne sont pas identiques
+ \item Le robot n'est pas parfaitement symétrique
+ \item Le sol n'est pas parfaitement plat
+ \item $\rightarrow$ Le robot ne se déplace pas \textit{exactement comme prévu}
+ \end{itemize}
+\end{frame}
\begin{frame}{Comportement autonome -- Origines historiques}
\begin{columns}
@@ -747,17 +1718,17 @@
\column{0.7\linewidth}
\begin{itemize}
\item Comment stabiliser la vitesse des machines à vapeur (moulins, locomotives, etc.) ?
- \item \textbf{Régulateur à boules} pour machines à vapeur (James Watt \emoji{flag-scotland}) (XX~\textsuperscript{ème})
+ \item \textbf{Régulateur à boules} pour machines à vapeur (James Watt \emoji{flag-scotland}) (XVIII~\textsuperscript{ème})
\item Plus la machine accélère, plus les boules s'écartent, réduisant l'arrivée de vapeur $\rightarrow$ diminution de la vitesse
\item Initialement proposé par Christian Huygens \emoji{flag-netherlands} (XVII~\textsuperscript{ème})
\item Principe purement mécanique
\item Limites :
- \begin{itemize}
- \item Réponse lente
- \item Pas de correction d'erreur persistante
- \item Pas d'anticipation des variations rapides
- \item Pas adapté aux systèmes numériques !
- \end{itemize}
+ \begin{itemize}
+ \item Réponse lente
+ \item Pas de correction d'erreur persistante
+ \item Pas d'anticipation des variations rapides
+ \item Pas adapté aux systèmes numériques !
+ \end{itemize}
\end{itemize}
\end{columns}
\end{frame}
@@ -790,7 +1761,7 @@
Équation du PID en version discrète (pour une implantation numérique) :
\begin{equation}
- u[n] = K_p \cdot e[n] + K_i \cdot \sum_{i=0}^{n} e[i] + K_d \cdot (e[n] - e[n-1])
+ u[n] = K_p \cdot e[n] + K_i \cdot \sum_{k=0}^{n} e[k] + K_d \cdot (e[n] - e[n-1])
\end{equation}
\end{frame}
@@ -798,38 +1769,51 @@
Équation du PID en version discrète (pour une implantation numérique) :
\begin{equation}
- u[n] = K_p \cdot e[n] + K_i \cdot \sum_{i=0}^{n} e[i] + K_d \cdot (e[n] - e[n-1])
+ u[n] = K_p \cdot e[n] + K_i \cdot \sum_{k=0}^{n} e[k] + K_d \cdot (e[n] - e[n-1])
\end{equation}
\begin{itemize}
\item Composante $p$ : correction proportionnelle à l'erreur
- \begin{itemize}
- \item \emoji{cross-mark} Problème : erreur persistante $\rightarrow$ délai de correction très long
- \end{itemize}
+ \begin{itemize}
+ \item \emoji{cross-mark} Problème : erreur persistante $\rightarrow$ délai de correction très long
+ \end{itemize}
\item Composante $i$ : correction proportionnelle à la somme des erreurs précédentes
- \begin{itemize}
- \item \emoji{cross-mark} Problème : instabilité de la trajectoire $\rightarrow$ oscillations
- \end{itemize}
+ \begin{itemize}
+ \item \emoji{cross-mark} Problème : instabilité de la trajectoire $\rightarrow$ oscillations
+ \end{itemize}
\item Composante $d$ : correction prédictive basée sur la variation de l'erreur
- \begin{itemize}
- \item \emoji{check-mark} L'erreur augmente $\rightarrow$ on "accélère" la correction
- \item \emoji{check-mark} L'erreur diminue $\rightarrow$ on "freine" la correction
- \end{itemize}
+ \begin{itemize}
+ \item \emoji{check-mark} L'erreur augmente $\rightarrow$ on "accélère" la correction
+ \item \emoji{check-mark} L'erreur diminue $\rightarrow$ on "freine" la correction
+ \end{itemize}
\end{itemize}
\end{frame}
\begin{frame}{Contrôle -- Objectifs du PID}
- \centering
- \begin{tabular}{
- >{\centering\arraybackslash}p{0.45\linewidth}
- >{\centering\arraybackslash}p{0.45\linewidth}
- }
- \textbf{Sans PID} & \textbf{Avec PID} \\[4pt]
- \midrule \\
- \textcolor{red!70!black}{Réactions brutales} & \textcolor{green!50!black}{Réactions fluides} \\[6pt]
- \textcolor{red!70!black}{Oscillations} & \textcolor{green!50!black}{Stabilité} \\[6pt]
- \textcolor{red!70!black}{Dépassement} & \textcolor{green!50!black}{Précision} \\
- \end{tabular}
+ \begin{center}
+ {
+ \renewcommand{\arraystretch}{2}
+
+ \begin{tabular}{C{0.45\textwidth} C{0.45\textwidth}}
+ \toprule
+ \textbf{Sans PID} & \textbf{Avec PID} \\
+ \midrule
+
+ \rowcolor{rowgray}
+ \textcolor{BrickRed}{Réactions brutales}
+ & \textcolor{ForestGreen}{Réactions fluides} \\
+
+ \textcolor{BrickRed}{Oscillations}
+ & \textcolor{ForestGreen}{Stabilité} \\
+
+ \rowcolor{rowgray}
+ \textcolor{BrickRed}{Dépassement}
+ & \textcolor{ForestGreen}{Précision} \\
+
+ \bottomrule
+ \end{tabular}
+ }
+ \end{center}
\end{frame}
\subsection{Calcul déporté}
@@ -853,36 +1837,351 @@
\begin{frame}{Calcul déporté -- Une affaire de compromis}
\begin{itemize}
\item Compromis techniques :
- \begin{itemize}
- \item \textbf{Temps de calcul} : PID simple adapté à l'Arduino Uno ?
- \item \textbf{Mémoire} : quel historique des erreurs peut-on stocker ?
- \item \textbf{Énergie} : gains avec le PID ?
- \item \textbf{Latence} : calcul local = réponse rapide
- \end{itemize}
+ \begin{itemize}
+ \item \textbf{Temps de calcul} : PID simple adapté au Raspberry Pi Pico ?
+ \item \textbf{Mémoire} : quel historique des erreurs peut-on stocker ?
+ \item \textbf{Énergie} : gains avec le PID ?
+ \item \textbf{Latence} : calcul local = réponse rapide
+ \end{itemize}
\end{itemize}
\end{frame}
-% \section{Présentation du robot}
+\section{Présentation des TP}
-% \begin{frame}{Inventaire (\textit{Bill of Material})}
-% \begin{center}
-% \includegraphics[width=0.5\linewidth]{img/point.png}
-% \end{center}
-% \end{frame}
+\begin{frame}{Organisation du dépôt}
+ \begin{itemize}
+ \item \texttt{robot/} : programme, drivers capteurs et actionneurs, sécurité
+ \begin{itemize}
+ \item \texttt{main.cpp} : boucle principale du robot
+ \item \texttt{Robot.h} : missions embarquées
+ \item \texttt{Config.h} : configuration matérielle
+ \end{itemize}
+ \item \texttt{robot/web/} : application web intégrée au firmware
+ \begin{itemize}
+ \item \texttt{index.html} : interface utilisateur
+ \item \texttt{robot.js} : envoi HTTP des commandes, lecture périodique de la télémétrie, exécution des séquences
+ \item \texttt{missions.js} : missions exécutées sur le téléphone
+ \end{itemize}
+ \item \texttt{schematics/} : schéma de câblage (Fritzing)
+ \end{itemize}
+\end{frame}
+
+\begin{frame}{Deux liaisons, deux usages}
+ \begin{itemize}
+ \item \textbf{Ordinateur $\longleftrightarrow$ Pico (USB) :}
+ \begin{itemize}
+ \item chargement du programme
+ \item messages sur le port série
+ \item \textit{en mode série}, commandes et télémétrie échangées sur ce même port
+ \end{itemize}
+ \item \textbf{Téléphone $\longleftrightarrow$ Pico (WiFi) :}
+ \begin{itemize}
+ \item le Pico sert la page HTML
+ \item le navigateur du téléphone exécute le JavaScript
+ \item \textit{en mode WiFi}, commandes et télémétrie échangées en HTTP
+ \end{itemize}
+ \end{itemize}
+
+ \begin{alertblock}{En pratique}
+ Le WiFi ne remplace pas complètement l'USB : toute modification du C++ \emph{ou de la page web} nécessite de recompiler et de recharger le Pico.
+ \end{alertblock}
+\end{frame}
+
+\begin{frame}{Mission sur le Pico ou sur le téléphone ?}
+ \begin{columns}[T]
+ \column{0.5\linewidth}
+ \textbf{Mission sur le Pico} (\texttt{Robot.h})
+
+ \begin{itemize}
+ \item \Circled{1} Relevé capteur
+ \item \Circled{2} Décision dans la boucle du Pico
+ \item \Circled{3} Commande des moteurs
+ \end{itemize}
+
+ \column{0.5\linewidth}
+ \textbf{Mission sur le téléphone} (\texttt{missions.js})
+
+ \begin{itemize}
+ \item \Circled{1} Relevé capteur
+ \item \Circled{2} Télémétrie par WiFi
+ \item \Circled{3} Décision dans le navigateur
+ \item \Circled{4} Commande par WiFi
+ \item \Circled{5} Commande des moteurs
+ \end{itemize}
+ \end{columns}
+
+ \begin{alertblock}{En pratique}
+ Le trajet par le téléphone ajoute des délais et des risques de perte de communication.
+ \end{alertblock}
+\end{frame}
+
+\begin{frame}{Progression des séances de TP}
+ \begin{center}
+ \begin{tabular}{@{}lp{0.30\textwidth}p{0.55\textwidth}@{}}
+ \toprule
+ \textbf{TP} & \textbf{Thème} & \textbf{Objectifs} \\
+ \midrule
+
+ \rowcolor{rowgray}
+ 1 & Découverte & Compiler, charger, piloter par USB puis WiFi \\
+ 2 & Missions sur le téléphone & Programmer des mouvements depuis la page \\
+ \rowcolor{rowgray}
+ 3 & Interface web & Commandes et mesures échangées en HTTP \\
+ 4 & Deux boucles de décision & Comparer les distances d'arrêt robot / téléphone \\
+ \rowcolor{rowgray}
+ 5 & Suivi de mur & Corriger la trajectoire sur le robot \\
+ 6 & Correcteur PID & Atténuer les oscillations du suivi de mur \\
+ \rowcolor{rowgray}
+ 7 & Mission autonome & Modéliser le robot comme un système à états \\
+
+ \bottomrule
+ \end{tabular}
+ \end{center}
+\end{frame}
\appendix
\section{Bibliographie}
\begin{frame}[allowframebreaks]{Références}
+ \nocite{*}
\printbibliography[heading=none]
\end{frame}
-% \appendix
+\section{API de programmation}
-% \section{Annexes}
+\begin{frame}{Mission firmware ou mission téléphone}
+ \small
+ \begin{tabular}{@{}p{0.16\textwidth}p{0.36\textwidth}p{0.4\textwidth}@{}}
+ & \textbf{Firmware} & \textbf{Téléphone} \\
+ \hline
+ Code & \texttt{Robot::mission()} & \texttt{Mission.run()}, configurée par \texttt{missions.js} \\
+ Capteurs & mis à jour avant chaque appel & dernière télémétrie reçue \\
+ Réaction & arrêt dans la même passe & télémétrie, puis requête, puis commande d'arrêt \\
+ Perte de liaison & \texttt{enforceSafety()} arrête et annule & plus de commandes ; échéance et délai de liaison du robot \\
+ \end{tabular}
-% \begin{frame}{Annexe 1 -- ...}
-% \end{frame}
+ \bigskip
+ Les deux passent par les mêmes commandes de déplacement et les mêmes contrôles de sécurité du firmware.
+\end{frame}
+
+\subsection{API Robot.h}
+
+\begin{frame}{Boucle principale}
+ \texttt{Robot::update()} s'exécute à chaque passe, dans cet ordre :
+ \begin{enumerate}
+ \item mise à jour des capteurs ;
+ \item \texttt{readCommands()} ;
+ \item \texttt{mission()} ;
+ \item \texttt{enforceSafety()} ;
+ \item télémétrie ;
+ \item affichage.
+ \end{enumerate}
+
+ \bigskip
+ \alert{Rien de ce qu'elle appelle ne doit attendre} : une passe bloquée retarde aussi les contrôles d'échéance et de liaison.
+\end{frame}
+
+\begin{frame}[fragile]{Se déplacer : \texttt{drive()}}
+ \begin{footnotesize}
+ \begin{verbatim}
+void drive(int left, int right, uint32_t durationMs = DEFAULT_DURATION_MS);
+\end{verbatim}
+ \end{footnotesize}
+
+ \begin{itemize}
+ \item Fixe les deux vitesses, de $-255$ à $+255$, et arme une échéance.
+ \item \texttt{durationMs} est plafonnée à \texttt{MAX\_DURATION\_MS} (5000).
+ \item Chaque appel remplace le précédent : pour continuer, rappeler
+ \texttt{drive()} avant l'échéance.
+ \end{itemize}
+
+ \bigskip
+ \texttt{enforceSafety()} arrête les roues à l'échéance ou à la perte de liaison.
+ \begin{itemize}
+ \item \texttt{motors.stop()} arrête tout de suite.
+ \item Ne pas appeler \texttt{motors.set()} : un mouvement sans échéance est traité comme une faute et arrêté à la passe suivante.
+ \end{itemize}
+\end{frame}
+
+\begin{frame}[fragile]{Une mission firmware}
+ \begin{scriptsize}
+ \begin{verbatim}
+static constexpr const char* MISSION_NAMES = "wiggle";
+
+void mission() {
+ switch (_mission) {
+ case 0: break;
+ case 1: wiggle(); break;
+ default: break;
+ }
+}
+
+bool _wiggleLeft = false;
+uint32_t _wiggleAt = 0;
+
+void wiggle() {
+ if (nowMs() - _wiggleAt < 400) return; // not yet
+ _wiggleAt = nowMs();
+ _wiggleLeft = !_wiggleLeft;
+ drive(_wiggleLeft ? -150 : 150, _wiggleLeft ? 150 : -150, 400);
+}
+\end{verbatim}
+ \end{scriptsize}
+
+ \small
+ Une mission rend la main à chaque appel, garde son état dans des membres, utilise \texttt{nowMs()} pour le temps et \texttt{drive()} pour bouger.
+\end{frame}
+
+\begin{frame}[fragile]{API \texttt{Robot.h} : une mission firmware}
+ \begin{scriptsize}
+ \begin{verbatim}
+void wiggle() {
+ if (nowMs() - _wiggleAt < 400) return; // pas encore
+ _wiggleAt = nowMs();
+ _wiggleLeft = !_wiggleLeft;
+ drive(_wiggleLeft ? -150 : 150, _wiggleLeft ? 150 : -150, 400);
+}
+\end{verbatim}
+ \end{scriptsize}
+
+ \begin{itemize}
+ \item \texttt{mission()} est appelée à chaque passe de la boucle et ne doit jamais attendre.
+ \item Une mission est donc une machine à états : \texttt{\_wiggleLeft} est l'état, \texttt{\_wiggleAt} la date de la dernière transition. Chaque appel change d'état ou rend la main.
+ \item \texttt{drive()} arme une échéance ; \texttt{enforceSafety()} arrête les roues quand elle passe.
+ \item Les capteurs sont lus avant chaque appel : la mission peut s'arrêter dans la même passe.
+ \end{itemize}
+\end{frame}
+
+\begin{frame}[fragile]{Capteurs et temps}
+ \texttt{front} (ultrasons) et \texttt{side} (VL53L1X, désactivé par défaut) sont mis à jour au début de chaque passe.
+ \begin{footnotesize}
+ \begin{verbatim}
+if (front.valid() && front.distanceMm() < 200) { ... }
+ \end{verbatim}
+ \end{footnotesize}
+ \begin{itemize}
+ \item \texttt{valid()} : la \textbf{dernière} mesure a réussi.
+ \item \texttt{distanceMm()} vaut $-1$ sauf si \texttt{valid()}.
+ \end{itemize}
+
+ \bigskip
+ \texttt{nowMs()} repasse à zéro après environ 49 jours :
+ \begin{footnotesize}
+ \begin{verbatim}
+if (nowMs() - startedAt > 1000) // juste : survit au passage à zéro
+if (nowMs() > startedAt + 1000) // faux : casse au passage à zéro
+ \end{verbatim}
+ \end{footnotesize}
+\end{frame}
+
+\begin{frame}{Ajouter une commande}
+ \texttt{execute()} reçoit chaque commande analysée comme un
+ \texttt{JsonReader} :
+
+ \medskip
+ \begin{tabular}{@{}ll@{}}
+ \texttt{has(key)} & champ présent ? \texttt{null} vaut absent \\
+ \texttt{integer(key, fallback = 0)} & le champ en \texttt{long} \\
+ \texttt{boolean(key, fallback = false)} & le champ en \texttt{bool} \\
+ \end{tabular}
+
+ \bigskip
+ \begin{itemize}
+ \item \texttt{has()} distingue un champ absent d'un zéro.
+ \item \texttt{execute()} ne doit pas attendre.
+ \item Pour répondre, suivre \texttt{sendInfo()} : remplir un \texttt{JsonWriter}, puis passer son \texttt{finish()} à \texttt{\_radio.send()}.
+ \end{itemize}
+\end{frame}
+
+\subsection{API robot.js}
+
+\begin{frame}[fragile]{Le client \texttt{Robot}}
+ \begin{footnotesize}
+ \begin{verbatim}
+import { Robot, Mission } from './robot.js';
+
+const bot = new Robot();
+bot.start(); // GET /api/lines toutes les 100 ms
+const seq = bot.forward(200, 1500); // POST /api/command, rend seq aussitôt
+await bot.waitUntilDone(seq);
+ \end{verbatim}
+ \end{footnotesize}
+
+ \small
+ \begin{tabular}{@{}lp{0.6\textwidth}@{}}
+ \texttt{drive}, \texttt{forward}, \texttt{turnLeft}\ldots & envoient une commande, rendent son \texttt{seq} sans attendre \\
+ \texttt{stop()}, \texttt{mission(n)}, \texttt{telemetry(ms)} & \texttt{\{stop\}}, \texttt{\{mission\}}, \texttt{\{telemetry\}} \\
+ \texttt{last}, \texttt{info} & dernière télémétrie, dernière ligne \texttt{info} \\
+ \texttt{connected} & le robot a répondu dans la dernière seconde \\
+ \texttt{on(listener)} & appelé pour chaque ligne reçue \\
+ \end{tabular}
+\end{frame}
+
+\begin{frame}[fragile]{Attendre le robot}
+ \begin{footnotesize}
+ \begin{verbatim}
+waitUntil(test, timeoutMs = 10000, aborted = null)
+waitUntilDone(seq, timeoutMs = 6000, aborted = null)
+\end{verbatim}
+ \end{footnotesize}
+
+ \begin{itemize}
+ \item \texttt{waitUntil()} rend le premier message reçu après l'appel pour
+ lequel \texttt{test(message)} est vrai, ou \texttt{null} à
+ l'expiration ou si \texttt{aborted()} est vrai.
+ \item \texttt{waitUntilDone(seq)} attend une ligne \texttt{done} dont le
+ \texttt{seq} est \textbf{au moins} \texttt{seq} : une commande
+ suivante remplace l'étiquette.
+ \item Préférer \texttt{waitUntilDone()} à une attente de la durée
+ demandée : \texttt{ms} dit quand le robot a arrêté les roues.
+ \end{itemize}
+\end{frame}
+
+\begin{frame}[fragile]{Une mission téléphone : \texttt{Mission}}
+ \begin{footnotesize}
+ \begin{verbatim}
+new Mission('carre')
+ .forward(180, 1000)
+ .turnRight(180, 600)
+ .repeat(4)
+ .stopWhen((t) => t.front_ok && t.front_mm < 200, 'quelque chose devant')
+ .run(bot); // -> { completed, stoppedAt, reason }
+\end{verbatim}
+ \end{footnotesize}
+
+ \begin{itemize}
+ \item \texttt{run()} règle la télémétrie à 100 ms, envoie chaque étape et
+ attend sa ligne \texttt{done}, au plus sa durée plus 2 s.
+ \item Elle s'arrête plus tôt si \texttt{stopWhen} est vrai, si
+ \texttt{aborted()} est vrai ou si une étape n'a pas de \texttt{done}.
+ \item Elle laisse toujours le robot arrêté.
+ \item \texttt{stopWhen()} teste la dernière télémétrie reçue, pas une
+ mesure en direct.
+ \end{itemize}
+\end{frame}
+
+\begin{frame}[fragile]{API \texttt{robot.js} : une mission téléphone}
+ \begin{scriptsize}
+ \begin{verbatim}
+const bot = new Robot();
+bot.start();
+
+await new Mission('carre')
+ .forward(180, 1000)
+ .turnRight(180, 600)
+ .repeat(4)
+ .stopWhen((t) => t.front_ok && t.front_mm < 200, 'quelque chose devant')
+ .run(bot);
+\end{verbatim}
+ \end{scriptsize}
+
+ \begin{itemize}
+ \item Chaque étape envoie une commande et attend sa ligne \texttt{done}.
+ \item \texttt{stopWhen()} teste la dernière télémétrie reçue : réagir
+ demande une requête, puis une commande d'arrêt.
+ \item Même commandes, même sécurité firmware qu'une mission firmware.
+ \end{itemize}
+\end{frame}
\end{document}