Robotics2024-11-108 min readBy Abhishek Nair - Fractional CTO für Deep Tech & AI

Erste Schritte mit ROS 2 für die Roboterentwicklung

#ROS2#Robotik#Python
Loading...

Umfassender Leitfaden zu ROS 2 für die professionelle Roboterentwicklung

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.

Warum ROS 2? Eine Perspektive aus der Praxis

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:

Die DDS-Revolution

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:

  • Kein Single Point of Failure: Der Master-Knoten von ROS 1 ist weg. Ihr Roboter kommt nicht zum Stillstand, wenn ein Prozess abstürzt.
  • Echtzeitfähig: DDS bietet Quality-of-Service (QoS)-Profile für bessere Echtzeitkommunikation, Skalierbarkeit und Sicherheitsleistung
  • Sicherheit durch Design: DDS bietet Sicherheitsgarantien, die in ROS 1 nicht vorhanden waren und für kommerzielle Einsätze entscheidend sind
  • Plattformübergreifend: Einsatz auf Linux, Windows, macOS und eingebetteten Systemen ohne Codeänderungen

Was sich gegenüber ROS 1 geändert hat

Verbesserungen der Architektur:

  • Mehrere Knoten können in einem einzigen Prozess laufen, was eine flexible Zusammensetzung zum Zeitpunkt der Bereitstellung ermöglicht
  • Startdateien verwenden nun Python für komplexe Logik und Bedingungen
  • Integriertes Lebenszyklusmanagement für Knoten
  • Echte parallele Ausführung auf Multi-Core-Prozessoren

Kommunikationsmodell:

  • Verteilte Erkennung (kein zentraler Master)
  • Konfigurierbare QoS-Richtlinien pro Thema
  • Unterstützung sowohl für zuverlässige (TCP-ähnliche) als auch für Best-Effort-Kommunikation (UDP-ähnlich)

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.

Installation & Einrichten der Umgebung

Ubuntu 22.04 (für den Produktionsbetrieb empfohlen)

# 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

Best Practices für die Einrichtung des Arbeitsbereichs

# 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.

DDS-Middleware verstehen: Eine entscheidende Wahl

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.

Vergleich der DDS-Middleware

CycloneDDS (Standard):

  • Bietet im Allgemeinen eine bessere Leistung in Szenarien mit hohem Durchsatz und geringerer Latenz
  • Sauberere Codebasis, aber weniger Funktionen
  • Am besten geeignet für: Standard-Mobilroboter, Forschungsanwendungen
  • Installation: sudo apt install ros-humble-rmw-cyclonedds-cpp

FastDDS:

  • Zeigt auf dem Raspberry Pi eine schnellere Leistung im Vergleich zu anderen Middlewares
  • Mehr Konfigurationsoptionen und Funktionen
  • Native Unterstützung für 5G-Netzwerkflüsse und Zero-Copy-Übertragung
  • Am besten geeignet für: Industrielle Anwendungen, Multi-Roboter-Systeme, ressourcenbeschränkte Plattformen
  • Installation: sudo apt install ros-humble-rmw-fastrtps-cpp

Wechseln 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

DDS-Konfiguration für den Produktiveinsatz

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

Quality of Service (QoS): Das Geheimnis zuverlässiger Kommunikation

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.

Wesentliche QoS-Richtlinien

Zuverlässigkeit:

  • RELIABLE: Garantiert die Zustellung (wie TCP) – Verwendung für Befehle, Karten
  • BEST_EFFORT: Schnell, kann Nachrichten verlieren (wie UDP) – Verwendung für Sensor-Streams

Beständigkeit:

  • TRANSIENT_LOCAL: Der Publisher speichert Samples für spät hinzukommende Abonnements
  • VOLATILE: Keine Persistenz, nur aktuelle Daten

Verlauf:

  • KEEP_LAST(n): Speichert die letzten n Nachrichten
  • KEEP_ALL: Speichert alle Nachrichten (mit Bedacht verwenden!)

Praktische QoS-Muster

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, &#x27;/camera/image&#x27;, 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.

Unverzichtbare ROS 2-Pakete

Navigations-Stack (Nav2)

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:

  • Verhaltensbäume für die Orchestrierung komplexer Aufgaben
  • Unterstützung für holonomische, Differential-Drive-, beinebasierte und Ackermann-Basistypen
  • Einbündelbare Planer, Steuerungen und Wiederherstellungsverhalten
  • Dynamische Hindernisvermeidung
  • Wegpunktverfolgung

Konfigurationsstruktur:

  • Globale Costmap für die Pfadplanung
  • Lokale Costmap für die dynamische Hindernisvermeidung
  • Planer-Server (NavFn, Theta*, Smac Hybrid-A*)
  • Regler-Server (DWB, TEB, Regulated Pure Pursuit)
  • Wiederherstellungsverhalten (Drehen, Rückwärtsfahren, Warten)

Manipulation (MoveIt 2)

Für Roboterarme ist MoveIt 2 der Industriestandard:

sudo apt install ros-humble-moveit

Funktionen:

  • Bewegungsplanung (OMPL, Pilz, CHOMP)
  • Kollisionsprüfung
  • Kinematik (KDL, IKFast, TRAC-IK)
  • Trajektorienausführung
  • Wahrnehmungseinbindung

SLAM-Toolbox

Für Kartierung und Lokalisierung:

sudo apt install ros-humble-slam-toolbox

Modi:

  • Online-Async-SLAM (Echtzeit-Kartierung)
  • Offline-SLAM (Nachbearbeitung)
  • Lokalisierungsmodus (Karte bereits vorhanden)
  • Lifelong-SLAM (kontinuierliche Kartenaktualisierungen)

Wichtige Debugging-Tools

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

Aufbau Ihres ersten Systems: Ein praktisches Beispiel

Lassen Sie uns ein vollständiges Publisher-Subscriber-System mit ordnungsgemäßer Fehlerbehandlung und QoS aufbauen:

Paketerstellung

cd ~/ros2_ws/src ros2 pkg create --build-type ament_python robot_control \ --dependencies rclpy std_msgs geometry_msgs sensor_msgs

Erweiterter Publisher-Knoten

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): &quot;&quot;&quot; Produktionsreifer Geschwindigkeitsregler mit Hindernisvermeidung &quot;&quot;&quot; def __init__(self): super().__init__(&#x27;velocity_controller&#x27;) # Parameter mit Standardwerten deklarieren self.declare_parameter(&#x27;max_linear_vel&#x27;, 0.5) self.declare_parameter(&#x27;max_angular_vel&#x27;, 1.0) self.declare_parameter(&#x27;obstacle_distance&#x27;, 0.5) # Parameter abrufen self.max_linear = self.get_parameter(&#x27;max_linear_vel&#x27;).value self.max_angular = self.get_parameter(&#x27;max_angular_vel&#x27;).value self.obstacle_dist = self.get_parameter(&#x27;obstacle_distance&#x27;).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, &#x27;/scan&#x27;, self.scan_callback, sensor_qos) # Publisher self.cmd_pub = self.create_publisher( Twist, &#x27;/cmd_vel&#x27;, cmd_qos) # Status self.min_distance = float(&#x27;inf&#x27;) self.obstacle_detected = False # Timer für Regelkreis (10 Hz) self.timer = self.create_timer(0.1, self.control_loop) self.get_logger().info(&#x27;Geschwindigkeitsregler initialisiert&#x27;) def scan_callback(self, msg): &quot;&quot;&quot;Laserscandaten verarbeiten&quot;&quot;&quot; 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 &lt; r &lt; msg.range_max] if valid_ranges: self.min_distance = min(valid_ranges) self.obstacle_detected = self.min_distance &lt; self.obstacle_dist else: self.min_distance = float(&#x27;inf&#x27;) self.obstacle_detected = False except Exception as e: self.get_logger().error(f&#x27;Scan-Verarbeitungsfehler: {str(e)}&#x27;) def control_loop(self): &quot;&quot;&quot;Hauptregelkreis&quot;&quot;&quot; cmd = Twist() if self.obstacle_detected: # Not-Aus cmd.linear.x = 0.0 cmd.angular.z = 0.0 self.get_logger().warn( f&#x27;Hindernis erkannt bei {self.min_distance:.2f} m – STOPPEN&#x27;) 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&#x27;Fehler: {e}&#x27;) finally: controller.destroy_node() rclpy.shutdown() if __name__ == &#x27;__main__&#x27;: main()

Startdatei (Python)

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( &#x27;max_vel&#x27;, default_value=&#x27;0.5&#x27;, description=&#x27;Maximale lineare Geschwindigkeit&#x27; ), # Controller-Knoten starten Node( package=&#x27;robot_control&#x27;, executable=&#x27;velocity_controller&#x27;, name=&#x27;velocity_controller&#x27;, output=&#x27;screen&#x27;, parameters=[{ &#x27;max_linear_vel&#x27;: LaunchConfiguration(&#x27;max_vel&#x27;), &#x27;obstacle_distance&#x27;: 0.5 }], remappings=[ (&#x27;/scan&#x27;, &#x27;/robot/scan&#x27;), (&#x27;/cmd_vel&#x27;, &#x27;/robot/cmd_vel&#x27;) ] ), # RViz zur Visualisierung starten ExecuteProcess( cmd=[&#x27;ros2&#x27;, &#x27;run&#x27;, &#x27;rviz2&#x27;, &#x27;rviz2&#x27;], output=&#x27;screen&#x27; ) ])

Simulation mit Gazebo

Gazebo ist über das Paket ros_gz_bridge in ROS 2 integriert und ermöglicht realistische Physiksimulationen.

Installation

# Gazebo Garden (für Humble) sudo apt install ros-humble-ros-gz sudo apt install ros-humble-gazebo-ros-pkgs

Einfache mobile Robotersimulation

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

Best Practices für die Systemarchitektur

Domänen-IDs

DDS-Domänen haben Teilnehmerbeschränkungen (120 für RTI Connext v4.2e+). Verwenden Sie unterschiedliche Domänen-IDs für:

  • Mehrere Roboter: export ROS_DOMAIN_ID=1, export ROS_DOMAIN_ID=2
  • Entwicklung vs. Produktion: export ROS_DOMAIN_ID=10 (dev)
  • Netzwerkisolierung: Getrennte Domänen verhindern Übersprechen

Knotenzusammensetzung

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()

Lebenszyklusverwaltung

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(&#x27;Konfigurieren...&#x27;) # Ressourcen initialisieren return TransitionCallbackReturn.SUCCESS def on_activate(self, state: State): self.get_logger().info(&#x27;Aktivierung...&#x27;) # Operationen starten return TransitionCallbackReturn.SUCCESS def on_deactivate(self, state: State): self.get_logger().info(&#x27;Deaktivieren...&#x27;) # Vorgänge sicher beenden return TransitionCallbackReturn.SUCCESS def on_cleanup(self, state: State): self.get_logger().info(&#x27;Aufräumen...&#x27;) # Ressourcen freigeben return TransitionCallbackReturn.SUCCESS

VPN und Fernsteuerung

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=&#x27;wg0&#x27;

Checkliste für die Produktionsbereitstellung

Leistungsoptimierung

  1. Mit Optimierungen kompilieren:

    colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
  2. CPU-Pinning für Echtzeitknoten:

    taskset -c 0,1 ros2 run my_pkg critical_node
  3. Speichersperre:

    sudo setcap cap_ipc_lock+ep /path/to/node

Überwachung und Protokollierung

# Protokollierung konfigurieren self.get_logger().set_level(rclpy.logging.LoggingSeverity.INFO) # Strukturierte Protokollierung verwenden self.get_logger().info(f&#x27;Position: x={x:.3f}, y={y:.3f}&#x27;) self.get_logger().warn(f&#x27;Batterie schwach: {battery_pct}%&#x27;) self.get_logger().error(f&#x27;Motorfehler: {error_code}&#x27;)

Testen

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

Sicherheit (SROS2)

# 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

Häufige Fallstricke und Lösungen

  1. QoS-Inkompatibilität: Publisher und Subscriber müssen über kompatible QoS-Profile verfügen. Überprüfen Sie dies mit ros2 topic info -v /topic.

  2. DDS-Leistung: Wechseln Sie zwischen CycloneDDS und FastDDS, wenn Netzwerkprobleme auftreten.

  3. Probleme mit Transform (TF): Veröffentlichen Sie immer mit >10 Hz. Verwenden Sie ros2 run tf2_tools view_frames zur Fehlerbehebung.

  4. Speicherlecks: Löschen Sie Knoten in Shutdown-Handlern immer ordnungsgemäß.

  5. Uhrensynchronisation: Verwenden Sie in der Simulation durchgehend use_sim_time:=true.

Ressourcen und Community

Fazit

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.

Abhishek Nair - Fractional CTO für Deep Tech & AI
Abhishek Nair - Fractional CTO für Deep Tech & AI
Robotics & AI Engineer
About & contact
Why trust this guide?

Follow Me