Skip to content

Instantly share code, notes, and snippets.

@contagon
Created September 18, 2025 18:50
Show Gist options
  • Save contagon/e42f6f55d608ba13c2376ecb7c3c28d5 to your computer and use it in GitHub Desktop.
Save contagon/e42f6f55d608ba13c2376ecb7c3c28d5 to your computer and use it in GitHub Desktop.
\newcommand{\fullref}[1]{\nameref*{#1} (Sec.~\ref*{#1})}
\begin{figure*}[t]
\centering
\begin{tikzpicture}[
node distance = -3mm and 3mm,
font = \small,
BIGBOX/.style={rectangle, draw=color7!80, fill=color7!20, thick, rounded corners=3pt, minimum height=29mm, minimum width=5.6cm},
BOXBASE/.style={rectangle, thick, rounded corners=2pt, minimum height=10mm, minimum width=1.5cm},
BOX1/.style={BOXBASE, draw=color0!60, fill=color0!5},
BOX2/.style={BOXBASE, draw=color1!60, fill=color1!5},
BOX3/.style={BOXBASE, draw=color2!60, fill=color2!5},
]
%Nodes
\node (origin) {};
\node (scan) [align=center] {\ac{lidar} Scan};
\node[BOX1] (curvature) [above=11mm of scan, align=center] {Compute \\ Curvature};
\node[BOX1] (extract) [right=of curvature, align=center] {Extract \\ Features};
\node[BOX1] (normals) [right=of extract, align=center] {Compute \\ Normals};
\node[BOX2] (init) [right=0.75cm of normals, align=center] {Initialize};
\node[BOX2] (match) [above right=of init, align=center] {Match \\ Scan};
\node[BOX2] (linopt) [below right =of init, align=center] {Semi-Lin. \\ Optim.};
\node[BOX2] (fullopt) [below right=of match, align=center] {Full \\ Optim.};
% dummy node in the middle to place large figure
\node[BOX2, draw=none, fill=none] (middle) [right=of init] {};
\node[BOX3] (keyscan) [right=-3mm and 7mm of fullopt, align=center] {Keyscan \\ Selection};
\node[BOX3] (marg) [right=of keyscan, align=center] {Marg.};
\node[BOX3] (map) [right=of marg, align=center] {Repair \\ Map};
\node[text=white] (output_pose) [below=11mm of marg, align=center] {Pose};
\node[text=white] (output_map) [below=11mm of map, align=center] {Map};
\node (output) at ($(output_pose)!0.5!(output_map)$) {Smoothed Poses \& Map};
\node(corner) at (output-|linopt) {};
\path[->, thick]
(scan.north) edge (curvature.south)
(curvature.east) edge (extract.west)
(extract.east) edge (normals.west)
(normals.east) edge (init.west)
(init.north) edge[bend left=30] (match.west)
($ (match.south) +(0.4cm,0cm) $) edge[bend left=15]($ (linopt.north) +(0.4cm,0cm) $)
($ (linopt.north) -(0.4cm,0cm) $) edge[bend left=15]($ (match.south) -(0.4cm,0cm) $)
(linopt.east) edge[bend right=20] (fullopt.south)
(fullopt.east) edge (keyscan.west)
(keyscan.east) edge (marg.west)
(marg.east) edge (map.west)
(marg.south) edge (output_pose.north)
(map.south) edge (output_map.north);
\begin{scope}[on background layer]
\node[BIGBOX,label=above:{\fullref{sec:feats}}] (big_feature) at (extract) {};
\node[BIGBOX,label=above:{\fullref{sec:icp}}] (big_match) at (middle) {};
\node[BIGBOX,label=above:{\fullref{sec:marg}}] (big_marg) at (marg) {};
\end{scope}
\draw[rounded corners, ->, thick] (output.west) -- (corner.center) -- (big_match.south);
\end{tikzpicture}
\caption{The components that make up the \ac{ours} pipeline as described in Section~\ref{sec:methods}. First, point and planar features are extracted. Next, the pose is initialized, \ac{icp} iterations are performed utilizing a semi-linearized dense optimization over past poses, and a full nonlinear optimization follows. Finally, keyscan management is done, poses are marginalized, and a new map is generated from the smoothed poses.}\label{fig:flow}
\end{figure*}
\begin{figure*}[t]
\centering
\begin{tikzpicture}[
node distance = 3mm,
font = \small,
% variable nodes
VAR/.style={circle, thick, minimum size=8mm},
VARKEY/.style={circle, thick, minimum size=1.5mm, inner sep=0pt},
KEY/.style={draw=color0!60, fill=color0!5},
LAST/.style={draw=color2!60, fill=color2!5},
RECENT/.style={draw=color1!60, fill=color1!5},
% factor nodes
FAC/.style={rectangle, minimum size=1.5mm, inner sep=0pt},
MARG/.style={fill=color4!25, draw=color4!100},
NEW/.style={fill=color3!25, draw=color3!100},
OLD/.style={fill=color7!10, draw=color7!55},
]
\pgfmathsetmacro{\numkey}{4}
\pgfmathsetmacro{\numrecent}{3}
\pgfmathsetmacro{\spacing}{1.5}
\pgfmathsetmacro{\numkeyhalf}{\numkey / 2 - 1}
% Variable nodes
\node[VAR, KEY] (x_k_0) {\(X\)};
\node[VAR, KEY] (x_k_1) [right=of x_k_0] {\(X\)};
\node (key_dots) [right=of x_k_1] {\(\cdots\)};
\node[VAR, KEY] (x_k_2) [right=of key_dots] {\(X\)};
\node[VAR, KEY] (x_k_3) [right=of x_k_2] {\(X\)};
\node[VAR, LAST] (x_r_0) [right=6mm of x_k_3] {\(X\)};
\node[VAR, RECENT] (x_r_1) [right=of x_r_0] {\(X\)};
\node (recent_dots) [right=of x_r_1] {\(\cdots\)};
\node[VAR, RECENT] (x_r_2) [right=of recent_dots] {\(X\)};
\node[VAR, RECENT] (x_r_3) [right=of x_r_2] {\(X\)};
% Marg Factor
\node[FAC, MARG] (marg) [below=5mm of x_k_3] {};
\begin{scope}[on background layer]
\draw (x_k_0) -- (marg);
\draw (x_k_1) -- (marg);
\draw (x_k_2) -- (marg);
\draw (x_k_3) -- (marg);
\draw (x_r_0) -- (marg);
\draw (x_r_1) -- (marg);
\draw (x_r_2) -- (marg);
\end{scope}
% Random connections
\begin{scope}[on background layer]
\path[OLD] (x_r_0) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_r_2);
\path[OLD] (x_k_0) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_r_2);
\path[OLD] (x_k_2) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_r_2);
\path[OLD] (x_k_0) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_r_1);
\path[OLD] (x_k_1) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_r_1);
\path[OLD] (x_k_2) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_r_1);
\path[OLD] (x_k_3) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_r_0);
\path[OLD] (x_r_0) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_r_1);
\path[OLD] (x_k_1) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_k_2);
\path[OLD] (x_k_2) edge[bend left=25] node[FAC, OLD, pos=0.5] {} (x_r_0);
\end{scope}
% Connected factors to most recent frame
\begin{scope}[on background layer]
\path (x_k_0) edge[bend left=25] node[FAC, NEW, pos=0.35] {} (x_r_3);
\path (x_k_1) edge[bend left=25] node[FAC, NEW, pos=0.35] {} (x_r_3);
\path (x_k_2) edge[bend left=25] node[FAC, NEW, pos=0.35] {} (x_r_3);
\path (x_k_3) edge[bend left=25] node[FAC, NEW, pos=0.35] {} (x_r_3);
\path (x_r_0) edge[bend left=25] node[FAC, NEW, pos=0.35] {} (x_r_3);
\path (x_r_1) edge[bend left=25] node[FAC, NEW, pos=0.35] {} (x_r_3);
\path (x_r_2) edge[bend left=0] node[FAC, NEW, pos=0.5] {} (x_r_3);
\end{scope}
% Make the key on the left
\node[rectangle, draw=color7!80, fill=color7!3, semithick, rounded corners=2pt, minimum width=33mm, minimum height=25mm] (legend) [above right=-14.5mm and 7mm of x_r_3] {};
\begin{scope}[font=\footnotesize, node distance=2mm]
\node[VARKEY, KEY, label=right:Keyscans] (keyscan) [above left=-4mm and -4mm of legend] {};
\node[VARKEY, LAST, label=right:Potential Keyscan] (oldest) [below=of keyscan] {};
\node[VARKEY, RECENT, label=right:Recent Scans] (recent) [below=of oldest] {};
\node[FAC, MARG, label=right:Marginalization Prior] (marg_key) [below=of recent] {};
\node[FAC, NEW, label=right:Newest Matches] (new_match) [below=of marg_key] {};
\node[FAC, OLD, label=right:Previous Matches] (old_match) [below=of new_match] {};
\end{scope}
\end{tikzpicture}
\caption{Example of the dense factor graph used in \ac{ours}. Shown are keyscans, recent scans, and the recent scan that is being considered to become a keyscan. During semi-linearized optimization, all the previous matches are linearized to use less compute. The newest matches are also recomputed at every \ac{icp} step.}\label{fig:graph}
\end{figure*}
@contagon
Copy link
Author

Also, there's a number of macros that'll probably cause this to not compile as is. Most of them aren't important for the tikz stuff. Here's the colors though (copied from seaborn's colorblind palette),

% Colors - seaborn colorblind palette
\definecolor{color0}{HTML}{0173b2} % blue
\definecolor{color1}{HTML}{de8f05} % orange
\definecolor{color2}{HTML}{029e73} % green
\definecolor{color3}{HTML}{d55e00}
\definecolor{color4}{HTML}{cc78bc}
\definecolor{color5}{HTML}{ca9161}
\definecolor{color6}{HTML}{fbafe4}
\definecolor{color7}{HTML}{949494}
\definecolor{color8}{HTML}{ece133}
\definecolor{color9}{HTML}{56b4e9}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment