Nach jahrelanger Entwicklung von AMRs, AGVs und Robotermanipulatoren in Produktionsumgebungen habe ich gelernt, dass der Einstieg in ROS 2 mehr erfordert, als nur Installationsanleitungen zu befolgen. Dieser Leitfaden fasst praktisches Wissen für den Aufbau robuster, produktionsreifer Robotersysteme zusammen.
Nachdem ich mehrere Produktionssysteme von ROS 1 migriert habe, kann ich Ihnen sagen, dass ROS 2 nicht nur ein Upgrade ist – es ist eine grundlegende Neugestaltung der Roboter-Middleware. Hier ist, worauf es in der Praxis ankommt:
ROS 2 nutzt DDS (Data Distribution Service) als Middleware und ersetzt damit das benutzerdefinierte Protokoll von ROS 1 durch einen in der Industrie bewährten Standard, der in Kriegsschiffen und Raumfahrtsystemen zum Einsatz kommt. Das bedeutet:
Verbesserungen der Architektur:
Kommunikationsmodell:
Warum dies für AMRs/AGVs wichtig ist: Aus meiner Arbeit mit Lagerrobotern weiß ich, dass die deterministische Echtzeit-Reaktion und die verteilte Architektur bahnbrechend sind. Sie können nun zuverlässig Reaktionszeiten von 2–5 ms für kritische Regelkreise erzielen, was für eine sichere Mensch-Roboter-Zusammenarbeit unerlässlich ist.
# Locale einrichten sudo apt update && sudo apt install locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 export LANG=en_US.UTF-8 # ROS 2-Repository hinzufügen sudo apt install software-properties-common sudo add-apt-repository universe sudo apt update && sudo apt install curl -y sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null # ROS 2 Humble (LTS) installieren sudo apt update sudo apt install ros-humble-desktop # Entwicklungswerkzeuge installieren sudo apt install python3-colcon-common-extensions python3-rosdep # rosdep initialisieren sudo rosdep init rosdep update
# Arbeitsbereichsstruktur erstellen mkdir -p ~/ros2_ws/src cd ~/ros2_ws # Mit Optimierungen kompilieren colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release # Zu .bashrc hinzufügen für automatisches Laden echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc
Profi-Tipp: Verwenden Sie während der Entwicklung --symlink-install. Dadurch werden symbolische Links erstellt, anstatt Python-Dateien zu kopieren, sodass Sie Skripte bearbeiten können, ohne neu kompilieren zu müssen.
Die Standard-DDS-Implementierung seit ROS 2 Galactic ist CycloneDDS, aber auch FastDDS und RTI Connext werden unterstützt. Ihre Wahl hat erhebliche Auswirkungen auf die Leistung.
CycloneDDS (Standard):
sudo apt install ros-humble-rmw-cyclonedds-cppFastDDS:
sudo apt install ros-humble-rmw-fastrtps-cppWechseln der DDS-Implementierungen:
# Umgebungsvariable setzen export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp # oder export RMW_IMPLEMENTATION=rmw_fastrtps_cpp # Überprüfen ros2 doctor --report | grep middleware
Für Roboter mit mehreren Schnittstellen (WLAN + Ethernet):
CycloneDDS XML:
<CycloneDDS> <Domain> <General> <NetworkInterfaceAddress>eth0,wlan0</NetworkInterfaceAddress> </General> </Domain> </CycloneDDS>
Setzen: export CYCLONEDDS_URI=file:///path/to/cyclonedds.xml
FastDDS XML:
<profiles> <transport_descriptors> <transport_descriptor> <transport_id>udp_transport</transport_id> <type>UDPv4</type> </transport_descriptor> </transport_descriptors> <participant profile_name="default"> <rtps> <userTransports> <transport_id>udp_transport</transport_id> </userTransports> </rtps> </participant> </profiles>
Set: export FASTRTPS_DEFAULT_PROFILES_FILE=/path/to/fastdds.xml
Bei den QoS-Profilen zeigt ROS 2 seine wahre Stärke. ROS 2 kann so zuverlässig wie TCP oder so „best-effort“ wie UDP sein, mit vielen möglichen Zuständen dazwischen.
Zuverlässigkeit:
RELIABLE: Garantiert die Zustellung (wie TCP) – Verwendung für Befehle, KartenBEST_EFFORT: Schnell, kann Nachrichten verlieren (wie UDP) – Verwendung für Sensor-StreamsBeständigkeit:
TRANSIENT_LOCAL: Der Publisher speichert Samples für spät hinzukommende AbonnementsVOLATILE: Keine Persistenz, nur aktuelle DatenVerlauf:
KEEP_LAST(n): Speichert die letzten n NachrichtenKEEP_ALL: Speichert alle Nachrichten (mit Bedacht verwenden!)Für Sensordaten (Kamera, LiDAR):
from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy sensor_qos = QoSProfile( reliability=ReliabilityPolicy.BEST_EFFORT, history=HistoryPolicy.KEEP_LAST, depth=1 # Nur der aktuellste Frame ist relevant ) subscription = self.create_subscription( Image, '/camera/image', callback, sensor_qos)
Für Karten (SLAM):
map_qos = QoSProfile( reliability=ReliabilityPolicy.RELIABLE, durability=DurabilityPolicy.TRANSIENT_LOCAL, history=HistoryPolicy.KEEP_LAST, depth=1 )
Für Steuerbefehle:
cmd_qos = QoSProfile( reliability=ReliabilityPolicy.RELIABLE, history=HistoryPolicy.KEEP_LAST, depth=1 # Nur der letzte Befehl )
Wichtiger Hinweis: Die Verwendung von „Reliable QoS“ mit tiefen Verlaufswarteschlangen führt zu Befehlsrückstau und Latenz. Verwenden Sie für die Fernsteuerung immer BEST_EFFORT mit depth=1.
Nav2 ist ein produktionsreifes Navigations-Framework, auf das weltweit über 100 Unternehmen vertrauen. Es bietet Wegplanung, Steuerung, Lokalisierung und Verhaltenskoordination.
Installation:
sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup
Wichtige Funktionen:
Konfigurationsstruktur:
Für Roboterarme ist MoveIt 2 der Industriestandard:
sudo apt install ros-humble-moveit
Funktionen:
Für Kartierung und Lokalisierung:
sudo apt install ros-humble-slam-toolbox
Modi:
RViz2 – 3D-Visualisierung:
ros2 run rviz2 rviz2
RQt – GUI-Plugins:
ros2 run rqt_console rqt_console # Log-Viewer ros2 run rqt_graph rqt_graph # Knotengrafik ros2 run rqt_plot rqt_plot # Echtzeit-Plotten
Befehlszeilen-Tools:
ros2 topic list # Alle Themen auflisten ros2 topic echo /topic_name # Nachrichten ausgeben ros2 topic hz /topic_name # Frequenz prüfen ros2 node info /node_name # Knotendetails ros2 param list # Parameter auflisten ros2 service list # Dienste auflisten ros2 bag record -a # Alle Themen aufzeichnen
Lassen Sie uns ein vollständiges Publisher-Subscriber-System mit ordnungsgemäßer Fehlerbehandlung und QoS aufbauen:
cd ~/ros2_ws/src ros2 pkg create --build-type ament_python robot_control \ --dependencies rclpy std_msgs geometry_msgs sensor_msgs
import rclpy from rclpy.node import Node from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy from geometry_msgs.msg import Twist from sensor_msgs.msg import LaserScan import math class VelocityController(Node): """ Produktionsreifer Geschwindigkeitsregler mit Hindernisvermeidung """ def __init__(self): super().__init__('velocity_controller') # Parameter mit Standardwerten deklarieren self.declare_parameter('max_linear_vel', 0.5) self.declare_parameter('max_angular_vel', 1.0) self.declare_parameter('obstacle_distance', 0.5) # Parameter abrufen self.max_linear = self.get_parameter('max_linear_vel').value self.max_angular = self.get_parameter('max_angular_vel').value self.obstacle_dist = self.get_parameter('obstacle_distance').value # QoS für Sensordaten (Best-Effort, nur die neuesten) sensor_qos = QoSProfile( reliability=ReliabilityPolicy.BEST_EFFORT, history=HistoryPolicy.KEEP_LAST, depth=1 ) # QoS für Befehle (zuverlässig, nur die neuesten) cmd_qos = QoSProfile( reliability=ReliabilityPolicy.RELIABLE, history=HistoryPolicy.KEEP_LAST, depth=1 ) # Abonnenten self.scan_sub = self.create_subscription( LaserScan, '/scan', self.scan_callback, sensor_qos) # Publisher self.cmd_pub = self.create_publisher( Twist, '/cmd_vel', cmd_qos) # Status self.min_distance = float('inf') self.obstacle_detected = False # Timer für Regelkreis (10 Hz) self.timer = self.create_timer(0.1, self.control_loop) self.get_logger().info('Geschwindigkeitsregler initialisiert') def scan_callback(self, msg): """Laserscandaten verarbeiten""" try: # Minimalen Abstand im vorderen Sektor ermitteln (±30 Grad) front_angles = 30 start_idx = len(msg.ranges) // 2 - front_angles end_idx = len(msg.ranges) // 2 + front_angles front_ranges = msg.ranges[start_idx:end_idx] valid_ranges = [r for r in front_ranges if msg.range_min < r < msg.range_max] if valid_ranges: self.min_distance = min(valid_ranges) self.obstacle_detected = self.min_distance < self.obstacle_dist else: self.min_distance = float('inf') self.obstacle_detected = False except Exception as e: self.get_logger().error(f'Scan-Verarbeitungsfehler: {str(e)}') def control_loop(self): """Hauptregelkreis""" cmd = Twist() if self.obstacle_detected: # Not-Aus cmd.linear.x = 0.0 cmd.angular.z = 0.0 self.get_logger().warn( f'Hindernis erkannt bei {self.min_distance:.2f} m – STOPPEN') else: # Normalbetrieb – vorwärts fahren cmd.linear.x = self.max_linear * 0.5 cmd.angular.z = 0.0 self.cmd_pub.publish(cmd) def main(args=None): rclpy.init(args=args) try: controller = VelocityController() rclpy.spin(controller) except KeyboardInterrupt: pass except Exception as e: print(f'Fehler: {e}') finally: controller.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, ExecuteProcess from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ # Argumente deklarieren DeclareLaunchArgument( 'max_vel', default_value='0.5', description='Maximale lineare Geschwindigkeit' ), # Controller-Knoten starten Node( package='robot_control', executable='velocity_controller', name='velocity_controller', output='screen', parameters=[{ 'max_linear_vel': LaunchConfiguration('max_vel'), 'obstacle_distance': 0.5 }], remappings=[ ('/scan', '/robot/scan'), ('/cmd_vel', '/robot/cmd_vel') ] ), # RViz zur Visualisierung starten ExecuteProcess( cmd=['ros2', 'run', 'rviz2', 'rviz2'], output='screen' ) ])
Gazebo ist über das Paket ros_gz_bridge in ROS 2 integriert und ermöglicht realistische Physiksimulationen.
# Gazebo Garden (für Humble) sudo apt install ros-humble-ros-gz sudo apt install ros-humble-gazebo-ros-pkgs
URDF/SDF-Modell (vereinfacht):
<?xml version="1.0"?> <robot name="simple_agv"> <link name="base_link"> <visual> <geometry> <box size="0.5 0.3 0.1"/> </geometry> </visual> <collision> <geometry> <box size="0.5 0.3 0.1"/> </geometry> </collision> <gazebo> <plugin name="differential_drive" filename="libgazebo_ros_diff_drive.so"> <update_rate>50</update_rate> <left_joint>left_wheel_joint</left_joint> <right_joint>right_wheel_joint</right_joint> <wheel_separation>0.3</wheel_separation> <wheel_diameter>0.1</wheel_diameter> <command_topic>/cmd_vel/</command_topic> <odometry_topic>odom</odometry_topic> <command_topic>/</command_topic> </plugin> <plugin name="lidar" filename="libgazebo_ros_ray_sensor.so"> <topic>scan</topic> <frame_name>lidar_link</frame_name> </plugin> </gazebo> </robot>
Simulation starten:
# Gazebo-Welt starten ros2 launch gazebo_ros gazebo.launch.py # Roboter erstellen ros2 run gazebo_ros spawn_entity.py \ -entity simple_agv -file simple_agv.urdf
DDS-Domänen haben Teilnehmerbeschränkungen (120 für RTI Connext v4.2e+). Verwenden Sie unterschiedliche Domänen-IDs für:
export ROS_DOMAIN_ID=1, export ROS_DOMAIN_ID=2export ROS_DOMAIN_ID=10 (dev)Für leistungskritische Anwendungen:
from rclpy.node import Node import rclpy.executors class SensorNode(Node): pass class ControlNode(Node): pass def main(): rclpy.init() executor = rclpy.executors.MultiThreadedExecutor() # Mehrere Knoten in einem Prozess ausführen sensor = SensorNode() control = ControlNode() executor.add_node(sensor) executor.add_node(control) try: executor.spin() finally: executor.shutdown() sensor.destroy_node() control.destroy_node() rclpy.shutdown()
Für Produktionssysteme, die einen kontrollierten Start/Abschluss erfordern:
from rclpy.lifecycle import Node, State, TransitionCallbackReturn class LifecycleRobot(Node): def on_configure(self, state: State): self.get_logger().info('Konfigurieren...') # Ressourcen initialisieren return TransitionCallbackReturn.SUCCESS def on_activate(self, state: State): self.get_logger().info('Aktivierung...') # Operationen starten return TransitionCallbackReturn.SUCCESS def on_deactivate(self, state: State): self.get_logger().info('Deaktivieren...') # Vorgänge sicher beenden return TransitionCallbackReturn.SUCCESS def on_cleanup(self, state: State): self.get_logger().info('Aufräumen...') # Ressourcen freigeben return TransitionCallbackReturn.SUCCESS
Für die Flottenverwaltung über Netzwerke hinweg:
VPN-Einrichtung mit WireGuard:
# WireGuard installieren sudo apt install wireguard # Schnittstelle konfigurieren sudo nano /etc/wireguard/wg0.conf
Konfiguration:
[Interface] PrivateKey = <robot_private_key>Address = 10.0.0.2/24 ListenPort = 51820 [Peer] PublicKey =<base_station_public_key> Endpoint = base.example.com:51820 AllowedIPs = 10.0.0.0/24 PersistentKeepalive = 25</base_station_public_key></robot_private_key>
DDS-Erkennung über VPN:
# Sicherstellen, dass DDS die VPN-Schnittstelle verwendet export CYCLONEDDS_URI='wg0'
Mit Optimierungen kompilieren:
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
CPU-Pinning für Echtzeitknoten:
taskset -c 0,1 ros2 run my_pkg critical_node
Speichersperre:
sudo setcap cap_ipc_lock+ep /path/to/node
# Protokollierung konfigurieren self.get_logger().set_level(rclpy.logging.LoggingSeverity.INFO) # Strukturierte Protokollierung verwenden self.get_logger().info(f'Position: x={x:.3f}, y={y:.3f}') self.get_logger().warn(f'Batterie schwach: {battery_pct}%') self.get_logger().error(f'Motorfehler: {error_code}')
import unittest from launch import LaunchDescription from launch_ros.actions import Node import launch_testing class TestVelocityController(unittest.TestCase): def test_obstacle_avoidance(self): # Integrationstest pass
# Sicherheitsschlüssel generieren ros2 security create_keystore demo_keys ros2 security create_key demo_keys /my_robot_node # Mit Sicherheit ausführen export ROS_SECURITY_KEYSTORE=demo_keys export ROS_SECURITY_ENABLE=true ros2 run my_pkg my_node
QoS-Inkompatibilität: Publisher und Subscriber müssen über kompatible QoS-Profile verfügen. Überprüfen Sie dies mit ros2 topic info -v /topic.
DDS-Leistung: Wechseln Sie zwischen CycloneDDS und FastDDS, wenn Netzwerkprobleme auftreten.
Probleme mit Transform (TF): Veröffentlichen Sie immer mit >10 Hz. Verwenden Sie ros2 run tf2_tools view_frames zur Fehlerbehebung.
Speicherlecks: Löschen Sie Knoten in Shutdown-Handlern immer ordnungsgemäß.
Uhrensynchronisation: Verwenden Sie in der Simulation durchgehend use_sim_time:=true.
ROS 2 stellt ein ausgereiftes, produktionsreifes Framework für moderne Robotik dar. Der Übergang von ROS 1 erfordert ein Überdenken Ihrer Architektur, aber die Vorteile – Echtzeitleistung, Sicherheit und Skalierbarkeit – machen es für jedes ernsthafte Robotikprojekt unverzichtbar.
Beginnen Sie mit einer Simulation, beherrschen Sie die QoS-Konfiguration und bauen Sie schrittweise auf. Das Ökosystem ist reichhaltig, die Community ist aktiv, und die Zukunft der Robotik basiert auf ROS 2.
Über den Autor: Dieser Leitfaden spiegelt mehr als 7 Jahre Erfahrung bei der Implementierung von ROS-basierten Systemen in den Bereichen Lagerautomatisierung, autonome Fahrzeuge und industrielle Handhabung wider. Die hier gewonnenen Erkenntnisse stammen aus realen Produktionsumgebungen, in denen Zuverlässigkeit, Leistung und Sicherheit unverzichtbar sind.