Einen ROS-2-Roboter bauen, der sieht und zu einem Marker fährt
Ein Roboter, der einen Marker quer durch den Raum erkennt, ausrechnet, wo er relativ zu ihm liegt, und frontal darauf zufährt – strukturiert als echter ROS-2-Nodegraph.
Was du baust
Einen Roboter, der irgendwo im Raum abgesetzt wird, sich dreht, bis er einen gedruckten Marker an einer Wand findet, und darauf zufährt – einen halben Meter davor stoppend, rechtwinklig zur Markerfläche statt bloß nahe dran.
Jeder andere Bau dieser Seite fährt ein Programm auf einem Mikrocontroller. Dieser hier ist ein Rechner mit Rädern – und das ändert, worum es im Projekt geht. Die schweren Teile sind nicht mehr Timing und Verdrahtung; sie heißen: zu wissen, wo Dinge relativ zueinander sind, und mehrere Softwarestücke ehrlich zueinander zu halten, während alle zugleich laufen.
Warum dieser Roboter einen Rechner braucht
Ein Arduino kann das nicht – und der Grund lohnt Präzision. Es ist nicht rohe Geschwindigkeit: Ein Kamerabild ist ein Viertel-Megabyte, ArUco-Erkennung will Gleitkomma-lineare-Algebra, und das alles muss geschehen, während die Räder weiter mit 50 Hz befohlen werden. Das ist ein Job für ein Betriebssystem mit echtem Speicher und echtem Scheduling.
Was man im Tausch für diese Linux-Kiste bekommt, ist die Fähigkeit, in einen laufenden Roboter hineinzuschauen. Jede Nachricht zwischen jedem Teil ist ein Topic, das man vom Laptop abonnieren kann. Fährt der Roboter am Marker vorbei statt darauf zu, fügt man keine Print-Statements hinzu und flasht neu – man lässt ros2 topic echo die Pose ausgeben und sieht ihr live beim Falschsein zu.
Die Form des Nodegraphen
Fünf Verantwortlichkeiten, eine Node pro Stück:
| Node | Publiziert | Job |
|---|---|---|
camera |
/image_raw, /camera_info |
Bilder vom CSI-Bus holen |
aruco_detector |
TF camera_link → marker_7 |
Das Tag finden, seine Pose lösen |
docking_controller |
/cmd_vel |
Entscheiden, wohin zu fahren ist |
diff_drive |
seriell an den Arduino | Twist zu Raddrehzahlen |
odometry |
TF odom → base_link |
Encoderimpulse integrieren |
Die Schnittstellen machen die Strukturierung lohnend. Der Controller fasst nie Kamera oder Motor an – er verbraucht einen Transform und erzeugt einen Twist. Heißt: Man kann denselben Roboter per Tastatur-Teleop fahren, den Controller gegen einen Fake-Transform ganz ohne Hardware testen und später einen Navigationsstack einsetzen, der dasselbe Topic publiziert.
NODES, TOPICS, SERVICES UND ACTIONS lesen, bevor man eine eigene Version dieser Tabelle zeichnet. Der häufigste Fehler in dieser Phase: Den Andockregler zum Service machen – es ist ein langlaufendes Ziel, das man abbrechen können muss, was exakt das ist, wofür eine Action da ist.
Stückliste
Das ist der Zwei-Gehirne-Roboter, und die Stückliste spiegelt das: einen Rechner für Wahrnehmung und einen Mikrocontroller für die Dinge, die ein Rechner nicht verlässlich kann.
| Teil | Menge | Ungefähre Kosten | Hinweise |
|---|---|---|---|
| Raspberry Pi 4, 4 GB | 1 | 55 $ | 4 GB sind die praktische Untergrenze für ROS 2 plus Vision. 1 GB lagert aus und stirbt |
| Pi-Kamera Modul 3 | 1 | 25 $ | Die Weitwinkelvariante ist besser zur Navigation – 102° statt 66° |
| A2-microSD, 32 GB | 1 | 8 $ | Oder von USB-SSD booten, was wirklich schneller ist |
| Kühlkörper oder Lüfter | 1 | 5 $ | Ein Pi 4 drosselt bei 80 °C – und ein gedrosselter Pi verliert Frames |
| Arduino Uno oder Nano | 1 | 5 $ | Encoder und Motorsteuerung – die Echtzeithälfte |
| N20-Encodermotoren | 2 | 16 $ | |
| TB6612FNG | 1 | 3 $ | |
| 2WD-Chassis | 1 | 12 $ | Platz für Pi, Kameramast und Akku |
| Akkupack | 1 | 15 $ | 3S mit schaltendem 5-V-Regler für 3 A |
| Gedruckte ArUco-Marker | — | 0 $ | Drucken, die gedruckte Größe messen und die Messung verwenden |
Gesamt: grob 145–175 $. Das ist mit Abstand das teuerste Projekt der Seite – und das meiste davon ist das Pi.
Versorgung ist der Ort, wo diese Roboter scheitern – und der Fehler ist teuer. Ein Brownout während eines SD-Kartenschreibens korrumpiert das ganze Systemabbild. Einen Schaltregler deutlich über dem Pi-Bedarf verwenden, die Schiene nie mit den Motoren teilen, und vcgencmd get_throttled prüfen – alles ungleich null heißt: Versorgung unzureichend und eine korrumpierte Karte kommt.
Die gedruckten Marker vermessen. Eine ArUco-Posenlösung skaliert direkt mit der deklarierten Markergröße. Ein Marker, gedruckt mit 95 % per Drucker-„An Seite anpassen“, liefert Posen, die bei jeder Entfernung 5 % falsch sind – und nichts in der Pipeline verrät es.
Baureihenfolge
1 – ROS 2 zum Laufen bringen und das Netz beweisen. ROS 2 auf einem Raspberry Pi installieren folgen. Nicht weitergehen, bis ros2 topic echo auf deinem Laptop ein Topic zeigt, das auf dem Roboter publiziert wird. Discovery über WLAN frisst am ehesten einen Abend – und soll ihn jetzt fressen statt beim Debuggen von Vision.
2 – Die Räder einen Twist gehorchen lassen. Arduino, Encoder und TB6612FNG verdrahten und die cmd_vel-Brücke bauen. Meilenstein: Tastatur-Teleop vom Laptop fährt den echten Roboter. Hier die Radseparation kalibrieren – alles Nachgelagerte erbt sie.
3 – Die Kamera kalibrieren. Ordentlich, mit echtem Target, nach Kamerakalibrierung und ArUco-Pose. Eine unkalibrierte Kamera erzeugt Posen, die plausibel aussehen und an den Bildrändern um Dutzende Prozent falsch sind – die schlimmste verfügbare Fehlermode.
4 – Den Marker als Transform publizieren. Nicht als eigene Nachricht mit einem x und y darin. camera_link → marker_7 in TF2 zu publizieren heißt: Der Controller kann fragen „wo ist der Marker, in base_link?“, und die Antwort berücksichtigt exakt die eigene Kameramontage – inklusive dass sie geneigt ist, was sie ist.
5 – Auf den Marker zufahren. Ein Proportionalregler auf Peilung: Darauf zudrehen, vorwärts fahren, im Sollabstand stoppen. Das funktioniert – und kommt schräg an, was der Sinn des nächsten Schritts ist.
6 – Entlang der Normalen anfahren. Statt auf den Marker zu lenken, einen Wegpunkt berechnen, der vor der Markerfläche entlang ihrer Normalen steht, dorthin fahren und erst dann gerade hineinfahren. Das ist der Unterschied zwischen Dock erreichen und Andocken. Hier beginnt die Markerorientierung – der Poseteil, den man bis jetzt nicht brauchte – zu zählen, und hier begegnet man Posenmehrdeutigkeit.
7 – In eine Zustandsmaschine wickeln. Suchen, Anfahren, Ausrichten, final, fertig – plus die Übergänge aus jedem, wenn der Marker verloren geht. Endliche Zustandsmaschinen für Roboterverhalten deckt die Struktur ab; das Spezielle, das dieser Roboter braucht, ist ein Marker-verloren-Timeout, das zur Suche zurückkehrt statt auf letzter bekannter Pose weiterzufahren.
Hardwarenotizen, die zählen
Die Kamera steif montieren, und wie vermessen. Jede Pose, die der Roboter rechnet, ist relativ zur Kamera – und TF2 verwandelt das per deinem deklarierten Transform von base_link zu camera_link in eine roboterrelative Pose. Eine Kamera an biegsamer Halterung macht diesen deklarierten Transform zur Lüge, die mit Beschleunigung variiert.
Die Kamera nach unten neigen, und das angeben. Eine Kamera zum Horizont verliert den Marker exakt, wenn der Roboter nahe kommt. Zehn bis fünfzehn Grad nach unten halten ihn durch die Endanfahrt im Bild – und diese Neigung gehört in den statischen Transform, nicht in einen Fudge-Faktor im Regler.
Den Marker mit Messschieber messen. Posenskalierung ist direkt proportional zur dem Löser genannten Markergröße. Mit 96 % zu drucken, weil der Drucker auf Passend skalierte, macht jede Entfernung 4 % falsch – lautlos und konsistent.
Das Pi getrennt von den Motoren speisen. Ein blockierender Motor drückt die Schiene – und wo ein Arduino harmlos zurücksetzen würde, korrumpiert ein Pi sein Dateisystem. Entweder ein separater Regler oder ein separater Pack – hier spart man kein Bauteil.
Das Kameraband von Motorleitungen fernhalten. Es ist eine ungeschirmte Hochgeschwindigkeitsverbindung. Eingekoppeltes Motorrauschen erscheint als korrumpierte Bilder – die als Detektionen erscheinen, die ohne sichtbaren Grund flackern.
Wie gut aussieht
| Messung | Typisch auf Pi 4 |
|---|---|
| Kameraerfassung bei 640×480 | 30 fps |
| ArUco-Erkennung, 640×480 | 25–30 fps, bei 40–60 % eines Kerns |
| ArUco-Erkennung, 1920×1080 | 5–8 fps – und jedes Bild ist alt |
| Markererkennungsreichweite (100-mm-Marker, 640×480) | 1,5–2,5 m |
| Posengenauigkeit bei 1 m | ±2–5 cm Position, ±3–5° Orientierung |
| Gesamt Bild-zu-Entscheidung-Latenz | 60–120 ms |
| Motorschleife, auf dem Arduino | 100–200 Hz |
| ROS-2-Topiclatenz, Pi zu Arduino über seriell | 5–15 ms |
Zwei Zeilen tragen die Lektion des ganzen Projekts.
Auflösung ist nicht gratis, und höher ist oft schlechter. Von 640×480 auf 1080p kostet 4–6× Verarbeitung für grob 1,7× Erkennungsreichweite. Wenn man nicht gezielt einen Marker weiter entfernt sehen muss, liefert das kleinere Bild eine schnellere, frischere Antwort – und Frische zählt in einer Regelschleife mehr als Präzision.
Die Latenzzeile erklärt die Zwei-Gehirne-Aufteilung. Bei 0,3 m/s sind 100 ms Visionslatenz 3 cm Fahrt; bei 90°/s Drehung 9° Rotation. Jede Visionsmessung beschreibt, wo der Roboter war. Darauf direkt eine schnelle Schleife zu schließen schwingt. Die funktionierende Struktur ist die, die dieser Roboter nutzt: Der Arduino fährt die schnelle Schleife auf Encodern, und Vision korrigiert sie langsam – exakt dieselbe Form wie GPS mit Odometrie auf dem Wegpunktrover zu fusionieren, aus exakt demselben Grund.
Rolling Shutter, wenn der Roboter schaut während er fährt
Die Standard-Pi-Kameramodule haben Rolling Shutter: Zeilen werden nacheinander belichtet – die Bildoberkante wird also wenige Millisekunden vor der Unterkante erfasst. Stationär unsichtbar. Bei 90°/s Drehung wurden die vier Ecken eines ArUco-Markers bei vier verschiedenen Roboterorientierungen erfasst – und die daraus gelöste Pose ist auf eine Art falsch, die keine Kalibrierung behebt.
Muss der Roboter Vision während der Fahrt betreiben, dafür ist das Global-Shutter-Modul da. Kann er es sich leisten zu stoppen, zu schauen und dann zu fahren, reicht das billigere Modul völlig – und Stoppen-zum-Schauen ist eine legitime Entwurfsentscheidung, kein Workaround.
Wenn es schiefgeht
| Symptom | Meist |
|---|---|
| Laptop sieht keine Topics | Multicast vom Access Point blockiert oder ROS_DOMAIN_ID unpassend |
| Marker erkannt, Roboter bewegt sich nicht | Twist gegen TwistStamped-Subscriber publiziert |
| Entfernung konsistent wenige Prozent daneben | Markergröße falsch oder der Drucker skalierte die Seite |
| Pose zittert wild auf Distanz | Marker zu klein im Bild – näher heran oder größer drucken |
| Markernormale kippt hin und her | Posenmehrdeutigkeit – außerachsig anfahren oder ChArUco-Board |
| Roboter kommt nahe am Marker an, aber schief | Auf den Marker gelenkt statt entlang seiner Normalen |
| Überschießt in der Endanfahrt | Auf einer 50–100 ms alten Pose gelenkt – gegen Ende langsamer |
| Fährt weiter, nachdem der Marker das Bild verlässt | Kein Marker-verloren-Timeout in der Zustandsmaschine |
Die Verteilung in dieser Tabelle lohnt Bemerken vor Beginn: Fast nichts davon ist Bildverarbeitung. Es sind Vernetzung, Frames, Einheiten und Regelung – was der Bau eines ROS-2-Roboters wirklich lehrt, und warum der Lernpfad ROS-2-Grundlagen seine Zeit mit Kommunikationsmustern und Koordinatenframes statt OpenCV verbringt.
Ist das ein Schritt zu weit, lehrt der Micromouse Kartierung und Planung auf Mikrocontroller ohne Linux, und der handygesteuerte Roboter deckt die „Befehle kommen übers Netz“-Hälfte dieses Problems für einen Bruchteil der Teile ab.
Projekt-Roadmap
Der Baupfad
Folge dem Technikbaum von den Bauteilen bis zum funktionierenden Roboter. Jeder Knoten wird freigeschaltet, sobald seine Voraussetzungen erledigt sind, und dein Fortschritt wird auf diesem Gerät gespeichert.
0 / 19 erledigt
Bauteile
- ControllerRaspberry PiEin vollwertiger Linux-Rechner in Kartengröße – das Gehirn für Visions- und ROS-Roboter.
- SensorRaspberry-Pi-KameraEine Kamera auf eigener Datenspur, damit Vision nicht mit dem USB-Bus kämpft.
- ControllerArduino UnoDas gutmütige 8-Bit-Board, über das die meisten die Robotik kennenlernen.
- TreiberTB6612FNG-MotortreiberEine MOSFET-H-Brücke, die die Volt behält, für die du wirklich bezahlt hast.
- AktorN20-Getriebemotor mit EncoderMikromotor mit Metallgetriebe und Encoder hinten: So weiß der Roboter, wie weit er wirklich gefahren ist.
- Chassis2WD Robot ChassisDie Plattform, auf die zwei Motoren, eine freie Rolle und die Elektronik geschraubt werden.
- StromversorgungRobot Battery & Power PackDer Unterschied zwischen einem Roboter, der läuft, und einem, der ständig neu startet.
Tutorials in diesem Pfad
- Einsteiger · 1 StundeROS 2 auf einem Raspberry Pi installieren und Workspace bauenDas OS wählen, ROS 2 aus apt installieren, einen colcon-Workspace bauen und einen Laptop mit dem Roboter sprechen lassen.
- Einsteiger · 19 Min. LesezeitROS 2 Nodes, Topics, Services und ActionsWähle das richtige ROS-2-Kommunikationsmuster und inspiziere, wie Nodes Roboterdaten und Kommandos tauschen.
- Mittelstufe · 20 Min. LesezeitROS 2 TF2-Koordinatensysteme für mobile RoboterBaue und debugge einen TF2-Framebaum von map und odom über die Roboterbasis zu jedem Sensor.
- Mittelstufe · 22 Min. LesezeitKamerakalibrierung und ArUco-Pose mit OpenCVKameraintrinsik kalibrieren, Reprojektionsfehler prüfen und die 3D-Pose eines ArUco-Markers schätzen.
- Mittelstufe · 45 Min.Von cmd_vel zu Raddrehzahlen: ROS 2 DifferenzialantriebWandle einen Twist in linke und rechte Raddrehzahlen – Kinematik, Sättigung, Totband und Watchdog.
- Einsteiger · 17 Min. LesezeitQuadratur-Encoder lesen für Distanz und TempoVerwandle Quadratur-Encoderpulse in zuverlässige Radrichtung, Distanz und Geschwindigkeit.
- Einsteiger · 17 Min. LesezeitEndliche Zustandsmaschinen für RoboterverhaltenVerhedderte Roboterbedingungen durch explizite Zustände, Ereignisse, Übergänge, Timeouts und Erholung ersetzen.
Häufig gestellte Fragen
Warum ROS 2 dafür statt eines einzigen Python-Skripts?
Für diesen Roboter allein würde ein einziges Skript funktionieren – und es wäre kürzer. Was es nicht überleben würde, ist das zweite Feature. Sobald Kameraschleife, Antriebsschleife und Entscheidungslogik mit verschiedenen Raten laufen und weiterlaufen müssen, wenn eine davon hängt, wird ein einziges Skript zum Schedulingproblem, das man selbst lösen muss. ROS 2 liefert Prozesstrennung, ein Drahtformat zwischen den Teilen – und den Teil, den man sofort spürt: die Fähigkeit, jedes Topic vom Laptop aus zu inspizieren, während der Roboter läuft. Das Projekt lohnt sich, weil es diese Struktur an einem Problem lehrt, das klein genug bleibt, um es zu verstehen.
Brauche ich einen Arduino zusätzlich zum Raspberry Pi?
Man braucht ihn nicht strikt – aber der Roboter ist mit deutlich besser. Ein Pi fährt Linux – die eigene Motorschleife wird also neben allem anderen geschedult und ist gelegentlich Millisekunden zu spät. Das ist für Vision unsichtbar und für ein Rad-PID tödlich. Den Roboter aufzuteilen – Pi für Wahrnehmung und Entscheidungen, Mikrocontroller für Schleifen, die nicht zittern dürfen – ist aus gutem Grund die Standardarchitektur, und genau die Aufteilung fördert ROS 2 ohnehin.
Warum ein ArUco-Marker statt ein echtes Objekt zu erkennen?
Weil der Marker aus einem Bild eine volle 6-DOF-Pose liefert – ohne Trainingsdaten und ohne Mehrdeutigkeit, welches Objekt man fand. Einen Stuhl zu erkennen sagt grob, wo ein Stuhl ist; ein ArUco-Tag zu erkennen sagt seine Position und Orientierung in Metern – was ein Regler wirklich braucht. Es ist außerdem der ehrliche Startpunkt: Echte Andockstationen, Lagerroboter und Drohnenlandeplätze nutzen aus demselben Grund Referenzmarker.
Warum kippt die Markerorientierung hin und her?
Das ist Posenmehrdeutigkeit – eine Eigenschaft der Geometrie statt ein Fehler im Code. Ein flaches Quadrat fast frontal betrachtet hat zwei 3D-Orientierungen, die auf fast dieselben Pixel projizieren – winziges Rauschen kippt den Löser also zwischen beiden. Die Entfernung bleibt stabil, während die geschätzte Normale springt. Einen physisch größeren Marker verwenden, leicht außerachsig anfahren, wo sich beide Lösungen trennen, oder zu ChArUco-Board oder mehreren Markern am Dock wechseln – alles gibt dem Löser mehr Geometrie, worüber er mit sich selbst uneins sein kann.
Wie genau ist das Andocken?
Mit kalibrierter Kamera und millimetergenau vermessenem Marker sind ein, zwei Zentimeter bei halbem Meter Abstand realistisch. Die dominierenden Fehler sind nicht die erwarteten: Falsch eingetragene Markergröße skaliert jede Entfernungsschätzung proportional – und Kamerapipeline-Latenz heißt: Die Pose, nach der man lenkt, beschreibt, wo der Marker vor 50 bis 100 Millisekunden war. Über die letzten 30 cm langsamer werden beseitigt den Großteil des zweiten Problems gratis.
Geht das auf einem Raspberry Pi Zero?
Für dieses Projekt nein – nicht bequem. Ein Zero 2 W kann ROS 2 fahren und einen Kamerastrom dekodieren – doch ArUco-Erkennung mit brauchbarer Bildrate obendrauf lässt nichts übrig, und einen Workspace darauf zu bauen ist schmerzhaft. Ein Pi 4 mit 4 GB oder Pi 5 nehmen. Der Zero ist ein gutes Board für einen Roboter, dessen Pi nur Befehle weiterleitet.