fav-ros-intro-slides/main.tex
2024-10-24 11:15:22 +02:00

269 lines
7.9 KiB
TeX

\documentclass[aspectratio=169]{beamer}
\usepackage{mum-theme-beamer/mum-theme}
\usepackage{graphicx}
\input{code-block-settings.tex}
\input{tikz-settings.tex}
\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{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}