diff --git a/.latexmkrc b/.latexmkrc index cf7eea3..e1054b5 100644 --- a/.latexmkrc +++ b/.latexmkrc @@ -3,8 +3,8 @@ $out_dir = 'build'; $pdflatex = 'lualatex -pdflatex=lualatex -interaction=nonstopmode -halt-on-error -shell-escape -synctex=1 %O %S'; $default_files = ('main.tex'); $pdf_previewer = 'zathura'; -$pdf_update_method = 1; -$preview_continuous_mode = 1; -$sleep_time = 1; +#$pdf_update_method = 1; +#$preview_continuous_mode = 1; +#$sleep_time = 1; ensure_path( 'TEXINPUTS', './mum-theme-beamer//' ); diff --git a/code-block-settings.tex b/code-block-settings.tex new file mode 100644 index 0000000..51b3a5a --- /dev/null +++ b/code-block-settings.tex @@ -0,0 +1,27 @@ +% !TEX root=main.tex +\usepackage[outputdir=build]{minted} +\usepackage{tcolorbox} +\usepackage{etoolbox} +\tcbuselibrary{minted,skins,breakable} + +\makeatletter +\renewenvironment{minted@colorbg}[1]{ +\setlength{\fboxsep}{\z@} +\def\minted@bgcol{#1} +\noindent +\begin{lrbox}{\minted@bgbox} +\begin{minipage}{\linewidth}} +{\end{minipage} +\end{lrbox}% +\colorbox{\minted@bgcol}{\usebox{\minted@bgbox}}} +\makeatother + +\usemintedstyle{friendly} +\newminted[pythoncode]{python}{frame=single, framesep=2pt, numbersep=1mm, linenos, fontsize=\tiny, escapeinside=||, bgcolor=black!10} + +\def\theFancyVerbLine{% + \ttfamily\tiny\arabic{FancyVerbLine}% + %{\tikz[remember picture,overlay]\node(minted-\arabic{FancyVerbLine}){};}% +} + + diff --git a/images/robot-bin.png b/images/robot-bin.png new file mode 100644 index 0000000..0942b04 Binary files /dev/null and b/images/robot-bin.png differ diff --git a/main.tex b/main.tex index fb7188b..e3855ff 100644 --- a/main.tex +++ b/main.tex @@ -1,18 +1,269 @@ \documentclass[aspectratio=169]{beamer} \usepackage{mum-theme-beamer/mum-theme} +\usepackage{graphicx} +\input{code-block-settings.tex} +\input{tikz-settings.tex} -\title{My Title} -\subtitle{My Subtitle} -\date{03.11.2023} -\author[Short Author Name]{Long Author Name} +\title{Getting Started} +\subtitle{Introduction into ROS} +\date{24.10.2024} +\author[\textbf{L. Alff}, N. Bauschmann, D.A. Duecker]{Lennart Alff, Nathalie Bauschmann, Daniel Duecker} \institute[TUHH]{Hamburg University of Technology} +\renewcommand<>{\fbox}[1]{\alt#2{\fcolorbox{black}{white}{#1}}{\fcolorbox{white}{white}{#1}}} + \begin{document} \begin{frame} \titlepage \end{frame} + \begin{frame} - \frametitle{First Slide Title} - Content of First Slide + \frametitle{Motivation} + + \fbox<3->{\begin{minipage}[]{0.45\textwidth} + \onslide<1->{\textbf{Scenario:}} + \begin{itemize} + \item<1-> Mobile robot with a camera + \end{itemize} + \onslide<2->{\textbf{Goal:}} + \begin{itemize} + \item<2-> Autonomous navigation + \end{itemize} +\end{minipage}} + \begin{minipage}[]{0.45\textwidth} + \centering + \includegraphics<1->[width=0.7\textwidth]{images/robot-bin.png} +\end{minipage}\\ +\vspace{2mm} + +\visible<4->{\begin{minipage}{\textwidth} + \begin{tikzpicture}[node distance=8mm and 10mm] + \node[fill=white] (camera) [rosnode]{Camera}; + \node[fill=white] (image) [rostopic, right=of camera, xshift=10mm]{Image}; + \node[fill=white] (localization) [rosnode, right=of image]{Localization}; + \node[fill=white] (position) [rostopic, right=of localization]{Position}; + \node[fill=white] (controller) [rosnode, below=of position]{Controller}; + \node[fill=white] (motor-commands) [rostopic, left=of controller]{Motor Commands}; + \node[fill=white] (motor-drivers) [rosnode, below=of camera]{Motor Drivers}; + \begin{scope}[on background layer] + \draw[thick, rounded corners=2mm, fill=blue!10] ($(camera.north west)+(-1.5, 0.5)$) rectangle ($(motor-drivers.south east)+(1.0,-0.5)$); + \draw[thick, rounded corners=2mm, fill=green!10] ($(image.north west)+(-0.5, 0.5)$) rectangle ($(controller.south east)+(0.5,-0.5)$); + \end{scope} + \path[-latex, thick] (camera) edge (image) + (image) edge (localization) + (localization) edge (position) + (position) edge (controller) + (controller) edge (motor-commands) + (motor-commands) edge (motor-drivers) + ; + % align needed for line break + \node (env)[above=of camera, yshift=-4.5mm, align=center]{Interaction with\\Environment}; + \node (env)[above=of localization, yshift=-4mm, align=center]{Internal Processing}; + + \end{tikzpicture} + \end{minipage} +} \end{frame} + +\begin{frame} + \frametitle{How to do it?} + + \onslide<1->\textbf{\Large Naive Approach:} + \begin{itemize} + \item<2->Single monolithic program + \begin{itemize} + \item<3-> scales badly + \item<3->hard to introspect/debug + \item<3->what if multiple computers/devices used? + \end{itemize} + \end{itemize} + \vspace{5mm} + + \onslide<4->\textbf{\Large Better Approach:} + \begin{itemize} + \item<5-> Use ROS $\Rightarrow$ write small programs + \begin{itemize} + \item<6->solve big problems by subdividision (divide \& conquer) + \item<7->ROS brings a lot of utility + \begin{itemize} + \item<8-> plotting + \item<9-> logging + \item<10-> ... + \end{itemize} + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame} + \frametitle{From Theory to Practice} + \centering + \textbf{\Huge From zero to depth!} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Hello World} + \begin{pythoncode} +print('hello world!') + \end{pythoncode} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Hello World With Good Practice} + \begin{pythoncode} +def main(): + print('hello world') + + +if __name__ == '__main__': + main() + \end{pythoncode} + \pause + \vspace{5mm} + \begin{itemize}[<+->] + \item not strictly required but a convention + \item analog to the \texttt{int main()} in C/C++ + \end{itemize} +\end{frame} + +\begin{frame}[fragile] + \frametitle{First Contact with ROS} + \begin{minipage}{0.48\textwidth} + \begin{pythoncode} +import rclpy +from rclpy.node import Node + + +def main(): + rclpy.init() + node = Node(node_name='my_node') + rclpy.spin(node) + + +if __name__ == '__main__': + main() + \end{pythoncode} + \end{minipage} + \begin{minipage}{0.48\textwidth} + \begin{itemize}[<+->] + \item minimal ROS program + \item the generic \texttt{Node} does nothing on its own + \begin{itemize}[<+->] + \item not particularly useful + \end{itemize} + \end{itemize} + \vspace{10mm} + \pause + \textbf{\Large $\Rightarrow$ we implement our own node!} + \end{minipage} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Our First Custom Node} + \begin{minipage}{0.48\textwidth} + \begin{pythoncode} +import rclpy +from rclpy.node import Node + +class DepthCalculatorNode(Node): + + def __init__(self): + super().__init__(node_name='my_node') + + +def main(): + rclpy.init() + node = DepthCalculatorNode() + rlcpy.spin(node) + + +if __name__ == '__main__': + main() + \end{pythoncode} + \end{minipage} + \begin{minipage}{0.48\textwidth} + \pause + \begin{itemize}[<+->] + \small + \item our custom node does exactly the same as the generic \texttt{rclpy.Node} $\rightarrow$ \textbf{nothing} + \item this is the boilerplate code we need for every node we write + \item doing nothing $\neq$ not beeing able to do something + \end{itemize} + \vspace{5mm} + \pause + \textbf{Provides us with these functions:} + \begin{itemize}[<+->] + \small + \item \texttt{create\_publisher} + \item \texttt{create\_subscription} + \item \texttt{create\_timer} + \item \texttt{get\_logger} + \item \texttt{get\_clock} + \end{itemize} + \end{minipage} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Receiving Pressure Measurements} + \begin{pythoncode} +import rclpy +from rclpy.node import Node +from sensor_msgs.msg import FluidPressure + +class DepthCalculatorNode(Node): + + def __init__(self): + super().__init__(node_name='my_node') + + self.pressure_sub = self.create_subscription(FluidPressure, 'pressure', + self.on_pressure, 1) + + def on_pressure(self, msg: FluidPressure): + self.get_logger().info('Pressure measurement received') + + +def main(): + ... + \end{pythoncode} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Publishing Depth Values} + + \begin{pythoncode} +import rclpy +from hippo_msgs.msg import DepthStamped +from rclpy.node import Node +from sensor_msgs.msg import FluidPressure + +class DepthCalculatorNode(Node): + + def __init__(self): + super().__init__(node_name='my_node') + + self.pressure_sub = self.create_subscription(FluidPressure, 'pressure', + self.on_pressure, 1) + self.depth_pub = self.create_publisher(DepthStamped, 'depth', 1) + + def on_pressure(self, msg: FluidPressure): + self.get_logger().info('Pressure measurement received') + pressure = msg.fluid_pressure + depth = self.pressure_to_detph(pressure) + + depth_msg = DepthStamped() # do not forget the parentheses! + depth_msg.header.stamp = msg.header.stamp + depth_msg.depth = depth + + self.depth_pub.publish(depth_msg) + + def pressure_to_depth(self, pressure): + depth = ??? + return depth + + + +def main(): + ... + \end{pythoncode} +\end{frame} + \end{document} diff --git a/tikz-settings.tex b/tikz-settings.tex new file mode 100644 index 0000000..964be80 --- /dev/null +++ b/tikz-settings.tex @@ -0,0 +1,13 @@ +\usepackage{tikz} +\usetikzlibrary{shapes.geometric, arrows, calc, chains, backgrounds} +\usetikzlibrary{overlay-beamer-styles} + +\tikzset{ + rosbase/.style={minimum width=20mm, minimum height=7.5mm, text centered, draw=black, align=center}, + startstop/.style={rectangle, rounded corners, rosbase, fill=red!30}, + rostopic/.style={rectangle, rosbase}, + rosnode/.style={ellipse,rosbase}, + arrow/.style={thick,->,>=stealth}, +} + +