ROS 1 Noetic-basis

Dit hoofdstuk introduceert de ROS 1 Noetic-ontwikkelworkflow op de reComputer Jetson, inclusief workspaces, packages, veelgebruikte tools, topic-/service-communicatie, aangepaste berichten en TF.

Langlopende, uitvoerbare voorbeelden staan onder ./code/, en de bijbehorende afbeeldingen staan onder ./images/.

Inhoud

7.2.1.1 Introduction to ROS 1

ROS 1 (Robot Operating System 1) is een opensource softwareframework voor robotica, onderhouden door Open Robotics. Het is geen besturingssysteem in de traditionele zin, maar biedt robottoepassingen communicatiemechanismen, toolchains en een gemeenschappelijke functiebibliotheek, wat de ontwikkeling van robotsoftware aanzienlijk vereenvoudigt. Het levert de diensten die een besturingssysteem normaal biedt, waaronder hardware-abstractie, aansturing van apparatuur op laag niveau, implementatie van veelgebruikte functies, berichtuitwisseling tussen processen en packagebeheer. Daarnaast biedt het de tools en bibliotheekfuncties die nodig zijn om code op te halen, te compileren, voor te bereiden en op meerdere computers uit te voeren.

ROS 1 Release

De meest voorkomende ROS 1-versies zijn als volgt:

Version NameUbuntuMaintenance status
Kinetic16.04Stopgezet
Melodic18.04Stopgezet
Noetic20.04Laatste ROS 1-versie (LTS)

De vervolgvoorbeelden in dit hoofdstuk zijn gebaseerd op de noetic-versie van ROS 1.

Het belangrijkste doel van ROS is om hergebruik van code bij robotica-onderzoek en -ontwikkeling te ondersteunen. ROS is een gedistribueerd framework van processen (ofwel "nodes") die zijn verpakt in packages die eenvoudig te delen en te publiceren zijn. ROS ondersteunt ook een gezamenlijk systeem vergelijkbaar met een coderepository, waarmee eveneens samenwerking en verspreiding binnen projecten mogelijk zijn. Dit ontwerp maakt het mogelijk om binnen een engineeringproject volledig onafhankelijke keuzes te maken (zonder beperkingen vanuit ROS), van het bestandssysteem tot de gebruikersinterface. Tegelijkertijd kan al het werk worden geïntegreerd in de basistools van ROS.

Belangrijkste kenmerken van ROS 1

(1) Een gedistribueerde structuur (elk werkproces wordt gezien als een node, beheerd via een node manager),

(2) Ondersteuning voor meerdere talen (bijv. C++, Python, enz.),

(3) Goede flexibiliteit (je kunt zowel één node schrijven als via roslaunch meerdere nodes tot een groter project organiseren),

(4) Opensource code (ROS volgt de BSD-licentie en is volledig gratis voor persoonlijke en commerciële toepassingen en aanpassingen).

Algemene architectuur van ROS 1

Opensource-communityniveau: dit omvat onder meer kennisdeling tussen ontwikkelaars, code en algoritmes.

Bestandssysteemniveau: een beschrijving van de code en de uitvoerbare bestanden die op de harde schijf te vinden zijn.

Rekenniveau: weerspiegelt de communicatie tussen proces en proces, en tussen proces en systeem.

De ROS 1-ontwikkelomgeving starten

De SeeedStudio Jetson Orin Nano Super DevKit draait lokaal op Ubuntu 22.04 en biedt daarmee zelf geen ondersteuning voor ROS 1, maar dit is wel vooraf geëvalueerd binnen JetPack 6. Als je onze BSP JetPack 6 gebruikt, kun je met het volgende commando in een terminalvenster van het Jetson-apparaat een Docker-container met ROS 1 starten, gebaseerd op Ubuntu 22.04:

bash
xhost +
sudo docker run -it \
  --net=host \
  --privileged \
  -v /dev:/dev \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  -e DISPLAY=$DISPLAY \
  -e QT_X11_NO_MITSHM=1 \
  ros:noetic

Als je Jetson-apparaat niet vooraf is voorzien van een ROS 1-ontwikkelomgeving, raadpleeg dan hier voor de installatie.

Configuratieprofiel van de computatiegraaf

Nodes

De node is de meest fundamentele computationele bouwsteen in ROS 1 en komt doorgaans overeen met een zelfstandig draaiend proces. Een ROS-systeem is geen enkel programma, maar een gedistribueerd systeem waarin meerdere nodes samenwerken.

In ROS 1 heeft elke node meestal een relatief enkelvoudige en duidelijk afgebakende functie, bijvoorbeeld:

Sensordata verzamelen (camera, radar, IMU)

Algoritmische verwerking (lokalisatie, mapping, padplanning)

Besturingsuitvoer (snelheidsregeling, elektrische aansturing)

Data doorsturen en debuggen (logging, visualisatie)

Kenmerken van ROS 1-nodes

Onafhankelijk proces Elke node is doorgaans een zelfstandig Linux-proces, en nodes communiceren onderling via ROS-communicatiemechanismen.

Losgekoppeld ontwerp Nodes roepen elkaars functies niet direct aan, maar communiceren via Topic, Service, Action, Parameter, enz., wat systeemuitbreiding en onderhoud vergemakkelijkt.

Unieke naam Elke node moet een unieke naam hebben binnen het ROS-computationele systeem, bijvoorbeeld: /turtle_velocity_publisher

Distribueerbaar uitvoerbaar Nodes kunnen op verschillende hosts draaien, zolang ze verbonden zijn met dezelfde ROS Master.

Levenscyclus beheerd door ROS Master Een node registreert bij het opstarten zijn eigen informatie (naam, publicatie/abonnement, enz.) bij ROS Master, en draait vervolgens onder beheer daarvan.

Veelgebruikte communicatiemethoden tussen ROS 1-nodes

Topic. Nodes gebruiken dit voor kant-en-klare communicatie via het publish/subscribe-model voor hoogfrequente datastromen.

Service Gesynchroniseerde communicatie op basis van request-response.

Action Geschikt voor tijdrovende taken, met ondersteuning voor feedback en annulering.

Parameter Server (parameterserver) Voor het opslaan van runtime-parameters van het systeem.

Een node is het belangrijkste computationele proces. ROS bestaat uit veel van dit soort nodes.

Vul een onderdeel gewoon aan met [Tab] wanneer je het invoert.

Hieronder staat een voorbeeld van een nodegraaf:

Wanneer we op de commandoregel [rosnode] invoeren en vervolgens dubbel op Tab drukken, vinden we deze trefwoorden onder de commandoregel.

ROS-commandoregeltool rosnode:

Bij het ontwikkelen en debuggen heb je vaak informatie over de huidige node en nodes nodig, dus onthoud deze veelgebruikte commando's. Lukt dat niet, dan kun je het gebruik van het rosnode-commando ook opzoeken via rosnode help.

Message

Logische koppelingen en data-uitwisseling tussen nodes worden via messages gerealiseerd.

Wanneer we op de commandoregel [rosmsg] invoeren en dubbel op de Tab-toets drukken, vinden we deze trefwoorden onder de commandoregel.

ROS-commandoregeltool rosmsg:

Topic

Een topic is een manier om informatie te verspreiden (publiceren/abonneren). Elk bericht wordt op het bijbehorende topic gepubliceerd, en elk topic is sterk getypeerd.

Het topic-bericht van ROS kan via TCP/IP of UDP worden verzonden, waarbij ROS standaard TCP/IP gebruikt. Op TCP gebaseerde transmissie, zoals TCPROS, is een langdurige verbinding; op UDP gebaseerde UDPROS is een low-latency, efficiënte transmissiewijze, maar verliest gemakkelijker data en is geschikt voor teleoperatie.

Wanneer we op de commandoregel [rostopic] invoeren en vervolgens dubbel op de Tab-toets drukken, vinden we deze trefwoorden onder de commandoregel.

ROS-commandoregeltool rostopic:

Services

Ook de service moet een unieke naam hebben binnen het request-response-model. Wanneer een node een service aanbiedt, kunnen alle nodes ermee communiceren met behulp van code die met de ROS-client is ontwikkeld.

Wanneer we op de commandoregel [rosservice] invoeren en vervolgens dubbel op de Tab-toets drukken, vinden we deze trefwoorden onder de commandoregel.

ROS-commandoregeltool rosservice:

Message Log Package

De message-logpackage is een bestandsformaat voor het opslaan en afspelen van ROS-berichtdata en wordt opgeslagen in een .bag-bestand. Het is een belangrijk mechanisme voor het opslaan van data.

Wanneer we op de commandoregel [rosbag] invoeren en dubbel op Tab drukken, vinden we deze trefwoorden onder de commandoregel.

ROS-commandoregeltool rosbag:

Parameter Server

De parameterserver is een gedeeld, online toegankelijk multivariabel woordenboek dat via sleutels op de node manager wordt opgeslagen.

Wanneer we op de commandoregel [rosparam] invoeren en dubbel op de Tab-toets drukken, vinden we deze trefwoorden onder de commandoregel.

ROS-commandoregeltool rosparam:

Node Manager (Master)

Node Manager wordt gebruikt voor de registratie en opzoeking van topics, servicenamen, enz. Zonder node manager kan er in het hele ROS-systeem geen communicatie tussen nodes plaatsvinden.

Bestandssysteemniveau

De afhankelijkheden tussen packages kunnen worden geconfigureerd. Als package A afhankelijk is van package B, moet B in het ROS-buildsysteem ouder zijn dan A, en kan A de header- en librarybestanden in B gebruiken.

Het concept van het bestandssysteemniveau is als volgt:

Functionele packagelijst:

Deze lijst geeft de afhankelijkheden van het package aan, de documentatie van het brondocument, enz. Het package.xml-bestand van het package is een lijst van packages.

Functioneel package:

Het package is de basisvorm van softwareorganisatie in het ROS-systeem en bevat draaiende nodes, configuratiebestanden, enz.

Gerelateerd ROS-packagecommando

Geïntegreerde functionele kit

Er kan een combinatie van meerdere packages worden gevormd.

Berichttype

Om een bericht tussen ROS-nodes te versturen, is vooraf een informatieannotatie nodig. In ROS worden standaardtypeberichten aangeboden, maar er kunnen ook eigen types worden gedefinieerd. De beschrijving van het berichttype wordt opgeslagen in het msg-bestand onder het package.

Servicetype

Definieert de datastructuur van de request en response die door elk proces in ROS als service wordt aangeboden.

Opensource-communityniveau

Distributie: een ROS-release is een reeks geïntegreerde packages die onafhankelijk met een versienummer kunnen worden geïnstalleerd. De ROS-release speelt een vergelijkbare rol als een Linux-distributie. Dit maakt het installeren van ROS-software eenvoudiger en zorgt dat consistente versies via een softwarepool onderhouden kunnen worden.

Repository: ROS steunt op gedeelde opensource- en software-repositorywebsites of hostingdiensten, waar verschillende organisaties hun eigen robotsoftware en -programma's kunnen publiceren en delen.

ROSWiki: ROSWiki is het belangrijkste forum voor het vastleggen van informatie over ROS-systemen. Iedereen kan een account aanmaken, eigen documenten bijdragen, correcties of updates aanleveren, cursusmateriaal voorbereiden en meer.

Bug Ticket System: als je een probleem vindt of een nieuwe functie wilt voorstellen, biedt ROS hiervoor de nodige middelen.

Mailing list (mailinglijst): de ROS-gebruikersmailinglijst is het belangrijkste communicatiekanaal voor ROS en maakt de uitwisseling mogelijk van vragen of informatie, van ROS-software-updates tot ROS-softwaregebruik; hetzelfde geldt voor het forum.

ROS Answer: gebruikers kunnen deze bron gebruiken om vragen te stellen.

Overzicht van communicatiemechanismen

Topic

Het publish-subscribe-communicatiepatroon wordt in ROS veelvuldig gebruikt. Topic wordt over het algemeen gebruikt voor eenrichtings-streamingcommunicatie. Een topic heeft doorgaans een sterke typedefinitie: een topic van een bepaald type kan alleen berichten van een specifiek datatype accepteren/verzenden. De publisher wordt niet gedwongen tot typeconsistentie, maar de subscriber controleert bij ontvangst het md5-type, en pas dan treedt er een fout op.

Service

Service wordt gebruikt voor het afhandelen van synchrone communicatie binnen ROS-communicatie, via het semantische server/client-model. Elk servicetype bestaat uit twee delen: request en response. Voor de serviceserver controleert ROS geen aliassen; alleen de laatst geregistreerde server is geldig en wordt met de client verbonden.

Action

Action gebruikt meerdere topics om een taak te definiëren, waaronder goal (Goal), feedback (feedback) en resultaat (result). Bij het compileren van een action worden automatisch zeven structuren gegenereerd: Action, ActionGoal, ActionFeedback, ActionResult, Goal, Feedback en Result.

Kenmerken van action:

Een vraag-antwoord-communicatiemechanisme

Met continue feedback

Kan tijdens de taak worden beëindigd.

Gerealiseerd op basis van het ROS-informatiemechanisme

Interface voor Action:

Goal: publiceert het taakdoel

Cancel: verzoek tot annulering

Status: informeert de client over de huidige status

Feedback: besturingsdata die periodiek worden teruggekoppeld tijdens het uitvoeren van de taak

Result: stuurt het resultaat van de opdracht, slechts één keer, naar de client.

Vergelijking van communicatiepatronen

Algemene componenten

Het launch-bestand; TF-coördinatentransformatie; Rviz; Gazebo; QT-toolbox; Navigation; MoveIt!

Launch: het Launch-bestand is een manier om in ROS meerdere nodes tegelijk te activeren. Het activeert ook automatisch de ROS Master Node Manager en maakt de configuratie van elke node mogelijk, wat het werken met meerdere nodes sterk vereenvoudigt.

TF-coördinatentransformatie: in de werkomgeving van een robot bevinden zich vaak veel componenten, en de positie en oriëntatie van de verschillende componenten spelen een rol bij robotontwerp en robottoepassingen. TF is een package waarmee gebruikers meerdere coördinaten in de tijd kunnen volgen, met behulp van een boomvormige datastructuur; het buffert tijd en onderhoudt de coördinatenrelaties tussen meerdere frames, wat ontwikkelaars helpt om op elk moment coördinaten te wijzigen, tussen coördinaten om te rekenen, vectoren te bepalen, enz.

QT Toolbox: om visueel debuggen en weergeven te vergemakkelijken, biedt ROS een grafisch achtergrondtoolpakket op basis van de Qt-architectuur — rqt common plugins — met veel praktische tools: een logoutputtool (rqt console), een tool voor computationele visualisatie (rqt graph), een tool voor datavisualisatie (rqt plot), en een tool voor dynamische parameterconfiguratie (rqt reconfigure)

Rviz: rviz is een 3D-visualisatietool gebaseerd op het ROS-softwareframework, die goed samenwerkt met verschillende robotplatforms. In rviz kan XML worden gebruikt om de afmetingen, massa, positie, materiaal, gewrichten, enz. van robots en omliggende objecten te beschrijven en in de interface weer te geven. Tegelijkertijd kan rviz in real time grafisch informatie tonen over robotsensoren, de bewegingsstatus van de robot, veranderingen in de omgeving, enz.

Gazebo: Gazebo is een krachtig 3D-fysicasimulatieplatform met een krachtige physics engine, hoogwaardige grafische rendering, gebruiksvriendelijke programmering en een grafische interface, en bovenal: gratis en opensource. Hoewel de robotmodellen in Gazebo dezelfde zijn als die in rviz, moeten fysieke eigenschappen van de robot en de omgeving, zoals massa, wrijvingscoëfficiënt, elasticiteitscoëfficiënt, enz., aan de modellen worden toegevoegd. Door middel van plugins wordt de simulatieomgeving toegevoegd, en ook de sensorinformatie van de robot kan visueel worden weergegeven.

Navigation: navigation is de 2D-navigatiekit van ROS, die kort gezegd, op basis van de informatiestroom en de algemene positie van de robot (bijvoorbeeld de ingevoerde odometrie), een veilige en betrouwbare snelheidsbesturingsopdracht voor de robot berekent.

MoveIt: MoveIt! is de meest gebruikte toolkit en wordt vooral gebruikt voor trajectplanning. Bij MoveIt! zijn configuratiehulpmiddelen voor bepaalde documenten die tijdens het plannen nodig zijn, van cruciaal belang.

Alle ROS 1-releases

Referentielink: http://wiki.ros.org/Distributions

Een ROS-release (ROS publication) verwijst naar een ROS-softwarepakket, vergelijkbaar met een Linux-distributie (bijv. Ubuntu). Het uitbrengen van ROS-versies is bedoeld om ontwikkelaars een relatief stabiele coderepository te laten gebruiken totdat ze klaar zijn om alles te upgraden. Daarom repareren ROS-ontwikkelaars bugs in een bepaalde versie doorgaans pas na het uitbrengen van die versie, terwijl ze ondertussen een klein aantal verbeteringen aan de kernpackages leveren. Vanaf oktober 2019 staan de namen van de belangrijkste ROS-distributieversies, hun publicatiedatum en hun levenscyclus in de onderstaande tabel:

Reference Connection

ROS Official wiki:

ROS Official guidance: http://wiki.ros.org/ROS/Tutorials

ROS installation: https://wiki.ros.org/noetic/Installation/Ubuntu (sla deze stap over als ROS al vooraf is geïnstalleerd)

Figures

7.2.1.1 Introduction to ROS 1 figure 1

7.2.1.1 Introduction to ROS 1 figure 2

7.2.1.1 Introduction to ROS 1 figure 3

7.2.1.1 Introduction to ROS 1 figure 4

7.2.1.1 Introduction to ROS 1 figure 5

7.2.1.1 Introduction to ROS 1 figure 6

7.2.1.1 Introduction to ROS 1 figure 7

7.2.1.1 Introduction to ROS 1 figure 8

7.2.1.1 Introduction to ROS 1 figure 9

7.2.1.1 Introduction to ROS 1 figure 10

7.2.1.1 Introduction to ROS 1 figure 11

7.2.1.1 Introduction to ROS 1 figure 12

7.2.1.2 Preparing the Workspace

Workspace-directory

De documentstructuur van ROS is niet verplicht voor elke map; deze wordt naar behoefte van het project ontworpen.

Over de workspace

De workspace is de plek waar de documenten van een ROS-project worden beheerd en georganiseerd. Visueel voorgesteld is het een repository met de verschillende projectonderdelen van ROS, wat het beheer van het systeem vergemakkelijkt. Het is een map in een grafische interface. Onze eigen ROS-code staat doorgaans in de workspace. Er zijn vier belangrijke topniveau-directory's:

src: bronruimte; ROS Catkin-package (broncode-package)

build: compilatieruimte; Catkin (CMake) cache-informatie en tussenliggende bestanden

devel: ontwikkelruimte; output van doelbestanden (inclusief headers, dynamic-link libraries, static-link libraries, uitvoerbare documenten, enz.), omgevingsvariabelen

install: installatieruimte

De topniveau-workspace (die vrij benoemd mag worden) en de src-map (moet src heten) moeten zelf worden aangemaakt;

de build- en devel-mappen worden automatisch aangemaakt door het commando catkin_make;

de install-map wordt automatisch aangemaakt door het commando catkin_make install, dat vrijwel nooit wordt gebruikt en doorgaans niet wordt aangemaakt.

Let op: voordat catkin_make wordt gebruikt, moet je vanuit de workspace terug naar het topniveau gaan. Binnen dezelfde workspace mogen geen packages met dezelfde naam bestaan; in verschillende workspaces is dat wel toegestaan.

bash
mkdir -p ~/catkin_ws/src  # create
cd catkin_ws/             # enter the workspace
catkin_make               # build
source devel/setup.bash   # source the workspace environment

Packages

Een package is een specifieke bestandsstructuur en mapindeling. Programmacode die dezelfde functie realiseert, wordt doorgaans in één package geplaatst. Alleen CMakeLists.txt en package.xml zijn [verplicht]; de rest van het pad hangt af van wat het package nodig heeft.

Functioneel package aanmaken

bash
cd ~/catkin_ws/src
catkin_create_pkg my_pkg rospy rosmsg roscpp

[rospy], [rosmsg], [roscpp] zijn afhankelijkheidsbibliotheken die naar behoefte van het project kunnen worden toegevoegd, of je kunt er andere toevoegen; dit hoeft niet opnieuw geconfigureerd te worden op het moment van aanmaken, maar als je vergeet ze toe te voegen, moet je dat later alsnog configureren.

Bestandsstructuur

bash
|-- CMakeLists.txt  # (required) build rules for the current package.
|—— package.xml     # (required) package metadata and ROS dependencies.
|—— include directory    # stores C++ header files
|—— config directory     # stores parameter files
|—— launch directory     # stores launch files (.launch or .xml)
|—— meshes directory     # stores robot or simulation 3D models (.sda, .stl, .dae, etc.)
|—— urdf directory       # stores robot model descriptions (.urdf or .xacro)
|—— rviz directory       # rviz files
|—— src directory        # C++ source code
|—— scripts directory    # executable scripts, such as shell scripts (.sh) and Python scripts (.py)
|—— srv directory        # custom services
|—— msg directory        # custom topics
|—— action directory     # custom actions

Inleiding tot CMakeLists.txt

Algemeen

CMakeLists.txt was oorspronkelijk een regelgebaseerd document voor het CMake Build System, en Catkin-builds volgen grotendeels de CMake-buildstijl, maar voegen enkele macrodefinities toe specifiek voor het ROS-project. Bij het schrijven ervan is Catkin's CMakeLists.txt dus in feite hetzelfde als CMake.

Dit document definieert direct het proces waarvan het package afhankelijk is, welke doelen het compileert, hoe het compileert, enz. CMakeLists.txt is dus erg belangrijk, omdat het de regels vastlegt van broncode tot doelbestand, en catkin zoekt bij het bouwen eerst naar de CMakeLists.txt onder elk package en compileert en bouwt vervolgens volgens die regels.

Formaat

De basissyntaxis van CMakeLists.txt is dezelfde als die van CMake, waarbij Catkin een klein aantal macro's heeft toegevoegd; de algehele structuur ziet er als volgt uit:

A typical catkin CMakeLists.txt includes these parts:

cmake
cmake_minimum_required(VERSION 3.0.2)
project(package_name)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs)
catkin_package()
include_directories(${catkin_INCLUDE_DIRS})
add_executable(node_name src/node_name.cpp)
target_link_libraries(node_name ${catkin_LIBRARIES})

Packages that define custom messages, services, or actions also use add_message_files(), add_service_files(), add_action_files(), and generate_messages().

Boost inschakelen

Als je C++ en Boost gebruikt, moet je find_package() aanroepen voor Boost en aangeven welke onderdelen van Boost als componenten worden gebruikt. Als je bijvoorbeeld de Boost-thread wilt gebruiken, zou je dit zeggen:

Find package

catkin_package()

catkin_package() is een CMake-macro die door catkin wordt aangeboden. Dit is nodig om catkin-specifieke informatie voor het bouwen van het systeem toe te wijzen, wat wordt gebruikt om pkg-config- en CMake-bestanden te genereren.

Deze functie moet worden aangeroepen voordat een object wordt gedeclareerd via add_library() of add_executable(). Deze functie heeft vijf optionele parameters:

INCLUDE_DIRS - exporteert include-paden

LIBRARIES - geëxporteerde library vanuit het project

CATKIN_DEPENDS - andere catkin-projecten waar dit project op is gebaseerd

DEPENDS - een niet-catkin CMake-project waar dit project van afhankelijk is. Voor een beter begrip, bekijk deze toelichting.

CFG_EXTRAS - overige configuratieopties

Het volledige macrodocument is hier te vinden.

Bijvoorbeeld:

bash
catkin_package(
   INCLUDE_DIRS include
   LIBRARIES ${PROJECT_NAME}
   CATKIN_DEPENDS roscpp nodelet
   DEPENDS eigen opencv)

Dit geeft aan dat de map "include" in de packagemap het punt is vanwaar het headerbestand wordt geëxporteerd. De CMake-omgevingsvariabele ${PROJECT_NAME} evalueert naar wat eerder aan de functie project() is doorgegeven, in dit geval "robot brain". "roscpp" + "nodelet" zijn packages die aanwezig moeten zijn om dit package te bouwen/uit te voeren, en "eigen" + "opencv" zijn systeemafhankelijkheden die aanwezig moeten zijn om dit package te bouwen/uit te voeren.

Include-paden en libraries

Voordat je een target specificeert, moet je aangeven waar de benodigde resources voor dat doel te vinden zijn, met name headerbestanden en libraries:

Include-pad - waar het headerbestand (meestal C/C++) te vinden is

Library-pad - waar de libraries voor het actieve target zich bevinden

Include-directory's

Link-directory's

include_directories()

De parameters voor include_directories() moeten de package-aanroep zelf zijn plus elke andere map die moet worden meegenomen. Als je catkin en Boost gebruikt, zou je include_directories() er als volgt uit moeten zien:

Include-directory's

De eerste parameter "include" betekent dat include/directory in het package eveneens deel uitmaakt van het pad.

link_directories()

Voorbeeld:

link_directories (~)

De CMake-functie link_directories() kan worden gebruikt om extra librarypaden toe te voegen, maar dit wordt afgeraden. Alle catkin- en CMake-packages voegen automatisch linkinformatie voor de library toe in target_link_libraries() zodra je het package hebt gevonden.

Zie de lijst bij cmake voor een gedetailleerd voorbeeld van het gebruik van target_link_libraries() ten opzichte van link_directories().

Uitvoerbare doelen

Om het uitvoerbare bestand te specificeren dat gebouwd moet worden, gebruiken we de CMake-functie add_executable().

bash
edd executeable

Hiermee wordt een doel-executable met de naam MyProgram gebouwd, opgebouwd uit drie bronbestanden: src/main.cpp, src/some_file.cpp en src/other_file.cpp.

Library-doelen

Use add_library() when the package needs to build a reusable library target. Many simple tutorial packages only need executables.

cmake
add_library(${PROJECT_NAME} src/library_file.cpp)

target_link_libraries

Use target_link_libraries() after add_executable() or add_library() to link the target against catkin and other required libraries.

cmake
target_link_libraries(node_name ${catkin_LIBRARIES})

Voorbeeld:

bash
(foo src/foo.cpp)
Add library (moo src/moo.cpp)
This links fly against libmoo.so

Let op: in de meeste gevallen is het gebruik van link_directories() niet nodig, omdat deze informatie automatisch via find_package() wordt geïntroduceerd.

Messages, services en actions

Message- (.msg), service- (.srv) en action-bestanden (.action) vereisen een speciale preprocessor-buildstap voordat ROS-packages worden gebouwd en gebruikt. Het belangrijkste van deze macro's is het genereren van taalspecifieke documenten, zodat messages, services en actions in de programmeertaal van keuze gebruikt kunnen worden. Het buildsysteem koppelt dit met alle beschikbare generators (bijv. gencpp, genpy, genlisp, enz.).

Er zijn drie macro's voorzien om messages, services en actions afzonderlijk te verwerken:

add_message_files()

add_service_files()

add_action_files()

Op deze macro's moet altijd de resulterende macro volgen:

generate_messages()

Lees CMake Practice: https://github.com/Akagi201/learning-cmake/blob/master/docs/cmake-practice.pdf als je nog nooit met de syntaxis van CMake in aanraking bent gekomen. Het beheersen van CMake helpt enorm bij het begrijpen van een ROS-project.

Introductie tot Package.xml

Overzicht

De packagelijst is een rootmap in een XML-bestand genaamd package.xml dat elk compatibiliteitspackage moet bevatten. package.xml is ook een verplicht package voor catkin's package, een beschrijving van het package, wat in een eerdere ROS-versie (het Rosbuild-buildsysteem) "manifest.xml" werd genoemd om basisinformatie over het package te beschrijven. Als je op internet nog ROS-projecten tegenkomt met manifest.xml, dan zijn die waarschijnlijk van vóór de hydro-versie. package.xml bevat informatie over de naam, het versienummer, een beschrijving van de inhoud, de onderhouder(s), softwarelicenties, buildtools voor compilatie, builddependencies en run-dependencies van het package.

package.xml-bestanden moeten message-generatie bevatten, run_depend moet message-runtime bevatten.

Formaat

A typical package.xml contains the package metadata and dependency declarations:

xml
<package format="2">
  <name>package_name</name>
  <version>0.0.0</version>
  <description>Package description</description>
  <maintainer email="user@example.com">Maintainer Name</maintainer>
  <license>BSD</license>
  <buildtool_depend>catkin</buildtool_depend>
  <depend>roscpp</depend>
  <depend>rospy</depend>
  <depend>std_msgs</depend>
</package>

Afhankelijkheidsrelaties

De packagelijst met minimale tags specificeert geen enkele afhankelijkheid van andere packages. Het package kent zes soorten afhankelijkheden:

build_depend specificeert het package dat nodig is om dit package te bouwen. Dit is het geval wanneer bestanden uit die packages nodig zijn tijdens het bouwen. Dit kan het headerbestand tijdens compilatie omvatten, een link naar het librarybestand van die packages, of andere resources die nodig zijn om te bouwen (met name wanneer de packages worden gevonden via find_package() in CMake). In een cross-compilatiescenario wordt de afhankelijkheidsrelatie opgebouwd richting het doelsysteem.

build_export_depend specificeert het package dat nodig is om de library van dit package te bouwen. Dit is het geval wanneer je de header van dit package opneemt in het publieke headerbestand van dit package (met name wanneer dit in CMake wordt gedeclareerd via catkin_package (CATKIN_DEPENDS)).

exec_depend specificeert het softwarepackage dat nodig is om de code in dit package uit te voeren. Dit is het geval wanneer je afhankelijk bent van de shared library in dit package (met name wanneer dit in CMake wordt gedeclareerd via catkin_package()).

test_depend specificeert alleen extra afhankelijkheden voor unittests. Deze mogen geen dubbeling zijn van afhankelijkheden die al als build- of run-dependency zijn genoemd.

buildtool_depend specificeert dat dit package zijn eigen buildsysteemtool nodig heeft om te bouwen. Meestal is de enige buildtool catkin. In een cross-compilatiescenario zorgt de buildtool-afhankelijkheidsrelatie voor de implementatie van de compilatiearchitectuur.

doc_depend specificeert de documentatietool die het package gebruikt om documentatie te genereren.

Overige tags

- URL voor informatie over het package, meestal de wikipagina op ros.org.

  • Auteur van het package

Bijvoorbeeld:

"Website"

Seed.

Workspace voorbereiden met officiële voorbeelden

Let op: ROS 1-operaties moeten worden uitgevoerd binnen een bestaande docker-container.

Download na het betreden van de docker-container het officiële ROS-voorbeeld (indien beschikbaar):

bash
git clone https://github.com/ros/ros_tutorials.git -b noetic-devel

Dit is een voorbeeld van ROS 1 gebaseerd op C++:

bash
cd ros_tutorials/
mkdir  src
cp roscpp_tutorials/ src/ -r
catkin_make # start building

De compilatie wordt als volgt afgerond:

Na afronding van de compilatie moet de workspace worden geïnitialiseerd voordat je verder kunt:

bash
source devel/setup.bash

Figures

7.2.1.2 Preparing the Workspace figure 1

7.2.1.2 Preparing the Workspace figure 2

7.2.1.3 Common Commands and Tools

Manier om een node te starten

launch-documenten

Er zijn minstens twee manieren om een launch-bestand met het commando roslaunch te starten:

  1. Starten via het ROS-packagepad

Het formaat is als volgt:

bash
roslaunch package_name launch_file_name
roslaunch pkg_name launchfile_name.launch
  1. Absoluut pad naar het launch-bestand

Het formaat is als volgt:

bash
roslaunch path_to_launchfile

Ongeacht welke manier je gebruikt om het launch-bestand te starten, kun je er parameters aan toevoegen; de meest voorkomende zijn:

--screen: maakt het makkelijker om debug-informatie van de ROS-node (indien aanwezig) op het scherm te tonen in plaats van in een logbestand op te slaan

arg:=waarde: als er een variabele in het launch-bestand moet worden meegegeven, kan de waarde op deze manier worden opgegeven, bijvoorbeeld:

bash
roslaunch pkg_name launchfile_name model:=urdf/myfile.urdf # the launch file has a `model` argument that must be set

of

bash
roslaunch pkg_name launchfile_name model:='$(find urdf_pkg)/urdf/myfile.urdf' # use `find` to provide the path

Het commando roslaunch detecteert bij uitvoering eerst of de rosmaster van het systeem al draait; zo ja, dan wordt de bestaande rosmaster gebruikt. Als deze niet is gestart, wordt eerst de rosmaster gestart, waarna de instellingen in het launch-bestand worden uitgevoerd, en kunnen er meerdere nodes op basis van onze vooraf ingestelde configuratie worden gestart.

Let op dat het launch-bestand niet gecompileerd hoeft te worden en direct kan worden uitgevoerd zoals hierboven beschreven.

rosrun

De node manager (master) moet actief zijn; master wordt gebruikt om de vele processen in het systeem te beheren, en elke node registreert zich bij het starten en beheert de communicatie tussen node en node. Nadat master is gestart, registreert elke node zich via master. Voer het volgende commando in de Ubuntu-terminal in:

bash
roscore

Node starten: rosrun + packagenaam + nodenaam; de methode rosrun voert slechts één node tegelijk uit.

bash
rosrun [--prefix cmd] [--debug] pkg_name node_name [ARGS]

rosrun zoekt naar een uitvoerbaar programma binnen het package, waaraan optionele ARGS kunnen worden meegegeven.

Python

Als de code in Python is geschreven, kun je deze direct starten vanuit de map waar het py-bestand zich bevindt; let daarbij op het verschil tussen Python 2 en Python 3.

Een schildpad starten

bash
roscore    # start roscore in the first terminal
rosrun turtlesim turtlesim_node     # start the turtlesim node in the second terminal
rosrun turtlesim turtle_teleop_key  # start keyboard teleoperation in the third terminal

Na het opstarten kun je via toetsenbordinvoer de beweging van de schildpad aansturen; de cursor moet de beweging van de schildpad besturen door in het venster van [rosrun turtlesim turtle_teleop_key] op het toetsenbord op [omhoog], [omlaag], [links], [rechts] te klikken.

En de terminal van rosrun turtlesim turtlesim_node zal enkele logregels van de schildpad printen.

bash
[ INFO] [1607648666.226328691]: Starting turtlesim with node name /turtlesim
[ INFO] [1607648666.229275030]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]

Een tweede schildpad starten

Start in de eerste terminal een node via het launch-bestand:

bash
roslaunch turtle_tf turtle_tf_demo.launch

Houd de vorige node voor toetsenbordbesturing actief

Druk nu op het toetsenbord op [omhoog], [omlaag], [links], [rechts] om de beweging van de eerste schildpad aan te sturen; je zult zien dat een tweede schildpad die beweging volgt.

launch-documenten

Algemeen

Een nodeprogramma in ROS voert doorgaans maar één enkele functie uit, maar een volledige ROS-robot draait typisch gelijktijdig met veel nodeprogramma's die samenwerken om complexe taken uit te voeren. Dit betekent dat er bij het activeren van een robot veel nodeprogramma's gestart moeten worden, wat lastiger wordt als je elke node één voor één start. Het launch-bestand en het commando roslaunch maken het mogelijk om meerdere nodes in één keer te activeren, wat "one-key"-opstarten en rijke parameterconfiguratie mogelijk maakt.

Documentformaat

Het launch-bestand is in essentie een xml-bestand, dat in sommige editors gemarkeerd (highlighted) kan worden weergegeven, leesbaar is, en waarbij de header wel of niet wordt toegevoegd

What? "0"? >

Net als andere bestanden in xml-formaat worden launch-bestanden opgebouwd uit tags; de belangrijkste tags zijn als volgt:

Code file: 7-2-1-3-common-commands-and-tools-example-01.xml

xml
<launch>                <!-- root tag -->
<node>                  <!-- node and parameters to start -->
<include>               <!-- include another launch file -->
<machine>               <!-- target machine -->
<env-loader>            <!-- set environment variables -->
<param>                 <!-- define a parameter on the parameter server -->
<rosparam>              <!-- load YAML parameters into the parameter server -->
<arg>                   <!-- define an argument -->
<remap>                 <!-- set topic remapping -->
<group>                 <!-- set a group -->
</launch>               <!-- root tag -->
  1. Tag [node]

De tag [node] is het kernonderdeel van het launch-bestand.

Code file: 7-2-1-3-common-commands-and-tools-example-02.xml

xml
<launch>
    <node pkg="package_name" type="executable_file" name="node_name"/>
    <node pkg="another_package" type="another_executable" name="another_node"></node>...
</launch>

waarbij

pkg de packagenaam van de node is

type het uitvoerbare document in het package is, wat bij Python .py kan zijn, of bij C++ de naam van het uitvoerbare document nadat het bronbestand gecompileerd is.

name de naam is nadat de node is gestart; elke node heeft zijn eigen unieke naam.

Let op: roslaunch kan de startvolgorde van nodes niet garanderen, dus alle nodes in het launch-bestand zouden zo min mogelijk van de startvolgorde afhankelijk moeten zijn.

Er kunnen meer parameters worden ingesteld, zoals hieronder:

Code file: 7-2-1-3-common-commands-and-tools-example-03.xml

xml
<launch>
    <node
        pkg=""
        type=""
        name=""
        respawn="true"
        required="true"
        launch-prefix="xterm -e"
        output="screen"
        ns="namespace"
    />
</launch>

In de bovenstaande volgorde,

respawn: als deze node wordt afgesloten, wordt deze dan automatisch herstart?

required: als deze node wordt afgesloten, worden dan alle andere nodes ook afgesloten

launch-prefix: of er een nieuw venster wordt geopend voor de uitvoering. Bijvoorbeeld: er moet een nieuw venster worden geopend voor de besturing van een node wanneer robotbeweging via een venster bestuurd moet worden; of wanneer een node bepaalde uitvoer heeft die je niet met de uitvoer van andere nodes wilt vermengen.

output: standaard schrijft launch node-informatie naar een logbestand; door hier parameters in te stellen kan dit op het scherm worden getoond

ns: integreert de node in een andere namespace, d.w.z. voegt een ns-prefix toe vóór de nodenaam. Om dit type gedrag te bereiken, worden nodenaam en topicnaam in het bronbestand van de node met een relatieve naam gedefinieerd, d.w.z. zonder het symbool /.

De naam van de computationele bron is onderverdeeld in:

  1. Basisnaam, bijv. topic

  2. Globale naam, bijv.: /A/topic

  3. Relatieve naam, bijv. A/topic

  4. Private namen, bijv. ~topic

Deze regel code komt voor bij het publiceren of abonneren.

bash
 ros::init(argc, argv, "publish_node");
 ros::NodeHandle nh;
 ros::Publisher pub = nh.advertise<std_msgs::string>("topic",1000);
  1. Tag [remap]

Komt vaak voor als sub-tag van een node-tag om een topic aan te passen. In veel rosnode-bestanden is het te ontvangen of te versturen topic mogelijk niet gespecificeerd, maar wordt het enkel vervangen door een input-topic en output-topic, zodat abstracte topicnamen worden gebruikt in plaats van topicnamen voor een specifieke scène.

Kortom, de functie van remap is om het toepassen van hetzelfde nodebestand in een andere omgeving te vergemakkelijken, door van buitenaf de remap-topic te gebruiken zonder het bronbestand te wijzigen.

Veelgebruikte formaten voor remap zijn als volgt:

Code file: 7-2-1-3-common-commands-and-tools-example-04.xml

xml
<node pkg="some" type="some" name="some">
    <remap from="origin" to="new" />
</node>
  1. Include

Deze tag wordt gebruikt om een ander launch-bestand aan dit launch-bestand toe te voegen, vergelijkbaar met het nesten van launch-bestanden. Basisformaat:

"Path-to-launch-file"

Het pad naar het bovenliggende bestand kan als een specifiek pad worden opgegeven, maar voor de overdraagbaarheid van het programma is het over het algemeen beter om het bestandspad met een find-commando op te geven:

<include file=$(find package-name)"/>

Voor het bovenstaande commando is de waarde van $(find package-name) gelijk aan het pad van het bijbehorende package op deze machine. Zo kan het bijbehorende pad ook worden gevonden wanneer een andere master hetzelfde package gebruikt.

Soms heeft een andere node die via launch wordt geïntroduceerd een uniforme naam nodig, of een nodenaam met vergelijkbare kenmerken, zoals /my/gps, /my/lidar, /my/imu, of moet de node een uniform prefix hebben dat makkelijk te doorzoeken is. Dit kan worden bereikt door de eigenschap ns (namespace) in te stellen, met het volgende commando:

<include file=$(find package-name) "ns= "my"/>

  1. Tag

Herhaling van parameters via [arg] is mogelijk en kan eenvoudig op meerdere plekken worden aangepast. Drie veelgebruikte methoden:

: een declaratie van [arg] zonder waarde. Op een later moment kun je een waarde toewijzen via de commandoregel of via de tag [include].

: standaardwaarde: een vaste waarde.

Waarde meegeven via de commandoregel

roslaunch pakketnaam bestandsnaam.launch arg1:=waarde1 arg2:=waarde2

  1. Vervanging van variabelen

Er zijn twee veelgebruikte variabelevervangingen in launch-bestanden

$(find pkg): for example, $(find rospy)/manifest.xml. Package-based paths are strongly recommended when possible.

$(arg arg_name): set a default value; use it when no override is provided

Bijvoorbeeld:

Code file: 7-2-1-3-common-commands-and-tools-manifest.xml

xml
<arg name="gui" default="true" />
<!-- set a default value; use it when no override is provided -->
<param name="use_gui" value="$(arg gui)"/>

Nog een voorbeeld:

cpp
<node pkg="package_name" type="executable_file" name="node_name" args="$(arg a) $(arg b)" />

Na het instellen kan deze waarde bij het starten van roslaunch aan de args-parameters worden meegegeven

bash
roslaunch package_name file_name.launch a:=1 b:=5
  1. Tag

In tegenstelling tot [arg] is [param] gedeeld, en de waarde ervan is niet beperkt tot een enkele waarde; het kan een bestand zijn, zelfs een regel commando.

Formaat

Code file: 7-2-1-3-common-commands-and-tools-example-06.xml

bash
<param name="param_name" type="type1" value="val"/>                         # type can be omitted; ROS infers it
<param name="param_name" textfile="$(find pkg)/path/file"/>                 # read file content as a string
<param name="param_name" command="$(find pkg)/exe '$(find pkg)/arg.txt'"/>
Example:
<param name="param" type="yaml" command="cat '$(find pkg)/*.yaml'"/>        # store command output in the parameter

[param] kan zowel in de globale context worden gebruikt, waarbij de naam de oorspronkelijke naam is, als in een kleiner bereik, zoals binnen Node, waarbij de volledige naam node/param is.

Bijvoorbeeld, in de globale context als volgt gedefinieerd:

bash
<param name="publish_frequency" type="double" value="10.0" />

Als volgt gedefinieerd binnen het bereik van een node

Code file: 7-2-1-3-common-commands-and-tools-example-07.xml

xml
 <node name="node1" pkg="pkg1" type="exe1">
    <param name="param1" value="False"/>
 </node>

Als je de [param]-lijst opvraagt met rosparam list, is dat

bash
/publish_frequency
/node1/param1   # namespace prefix is added automatically

Let op: hoewel de namespace aan de naam van [param] is toegevoegd, blijft deze nog steeds globaal.

[rosparam]

[param] kan slechts op één enkele [param] tegelijk werken, en alleen in drie vormen: value, textfile, command, waarbij het de inhoud van dat afzonderlijke [param] teruggeeft. [rosparam] maakt batchbewerkingen mogelijk en bevat commando's voor het instellen van parameters, bijv. dump, delete, enz.

load: laadt een batch parameters uit een YAML-bestand, in het volgende formaat:

bash
<rosparam command="load" file="$(find rosparam)/example.yaml" />

Delete: verwijdert een bepaalde param

bash
<rosparam command="delete" param="my_param" />

Toewijzing zoals bij [param]

bash
<rosparam param="my_param">[1,2,3,4]</rosparam>

Of...

Code file: 7-2-1-3-common-commands-and-tools-example-08.xml

bash
<rosparam>
a: 1
b: 2
</rosparam>

[rosparam] kan ook binnen [node] worden geplaatst, waarbij het dan onder de namespace van die node valt.

  1. Group

Als je dezelfde configuratie voor meerdere nodes wilt, bijvoorbeeld binnen dezelfde namespace, of hetzelfde topic wilt remappen, kun je [group] gebruiken. Alle gangbare tags kunnen binnen [group] worden gebruikt, bijvoorbeeld

Code file: 7-2-1-3-common-commands-and-tools-example-09.xml

xml
<group ns="rosbot">
    <remap from="chatter" to="talker"/>       # applies to following nodes in this group
    <node... />
    <node... >
        <remap from="chatter" to="talker1"/>  # each node can override the remap
    </node>
</group>

TF-coördinatentransformatie

tf is een package waarmee gebruikers op elk moment meerdere coördinaten kunnen volgen. tf onderhoudt de relatie tussen de coördinaten in een realtime-bufferstructuur en stelt gebruikers in staat om op elk gewenst moment punten, vectoren, enz. tussen twee willekeurige frames om te rekenen.

Het tf-package is degene die de coördinaten van een punt in het ene coördinatensysteem omzet naar de coördinaten in een ander coördinatensysteem. De sensor kan een coördinatensysteem "zien", de machine kan een coördinatensysteem "zien" en het obstakel kan een punt "zien".

Voer, nadat je twee schildpadden hebt geactiveerd, de volgende handelingen uit.

Veelgebruikte tf-tools

  1. View frames-tool

Deze kan alle op dat moment via ROS uitgezonden tf-coördinaten beluisteren en boomdiagrammen tekenen om de relatie tussen de coördinaten weer te geven; hierbij wordt een bestand met de naam frame.pdf gegenereerd en opgeslagen op de huidige lokale locatie.

bash
rosrun tf view_frames
  1. rqt_tf_tree-tool

Hoewel view_frames de huidige coördinatenrelatie in een offline-bestand kan opslaan, geeft het de coördinatenrelatie niet in real time weer, dus kun je de coördinatenrelatie met rqt_tf_tree wel in real time bijwerken

bash
rosrun rqt_tf_tree rqt_tf_tree
  1. tf_echo-tool

Met de tf_echo-tool kun je de relatie tussen twee referentiesystemen bekijken.

bash
rosrun tf tf_echo <source_frame> <target_frame>

Print de rotatietransformatie van source_frame naar target_frame; bijvoorbeeld:

bash
rosrun tf tf_echo turtle1 turtle2
  1. Static transform publisher

Publiceert statische coördinaten tussen twee coördinaten, waarvan de relatieve positie niet verandert. Commandoformaat:

bash
static_transform_publisher x y z yaw pitch roll frame_id child_frame_id period_in_ms
static_transform_publisher x y z qx qy qz qw frame_id child_frame_id period_in_ms

Gebruik binnen launch:

Code file: 7-2-1-3-common-commands-and-tools-example-10.xml

xml
<launch>
<node pkg="tf" type="static_transform_publisher" name="link1_broadcaster" args="1 0 0 0 0 0 1 link1_parent link1 100" />
</launch>
  1. Roswtf-plugin

Een plugin om je huidige tf-configuratie te analyseren en te proberen veelvoorkomende problemen te identificeren.

bash
roswtf

Veelgebruikte coördinatensystemen

De gebruikelijke coördinaten zijn de frame_id, met map, odom, base_link, base_footprint, base_laser, enz.

Wereldcoördinaten (map)

De map-coördinaten vormen een vast wereldcoördinatensysteem met de Z-as omhoog gericht. De houding van het mobiele platform ten opzichte van het map-systeem zou in de loop van de tijd niet significant moeten verschuiven. map-coördinaten zijn niet continu, wat betekent dat de houding van het bewegende platform in het map-systeem op elk moment los kan staan. Bij een typische opstelling herberekenen lokalisatiemodules op basis van sensorwaarnemingen continu de positie van de robot in wereldcoördinaten, waardoor afwijkingen worden geëlimineerd, maar er kunnen sprongen optreden wanneer nieuwe sensorinformatie binnenkomt. map-coördinaten zijn nuttig als langetermijn-globale referentie, maar door deze sprongen zijn ze een slechte referentie voor lokale sensoren.

odom

odom is een globaal coördinatensysteem dat de huidige bewegingshouding van de robot via een odometrie vastlegt. De positie van het bewegende platform in odom-coördinaten kan vrij en zonder grenzen bewegen, wat voorkomt dat odom-coördinaten als langetermijn-globale referentie kunnen dienen. Dit onderscheidt zich van het concept van coördinaten en verplaatsing die op basis van een encoder (of visueel, enz.) worden berekend. Er is echter wel een relatie: de transformatiematrix van het odom-topic is de tf-relatie odom->base_link. De odom- en map-coördinaten vallen samen bij het begin van de robotbeweging. Na verloop van tijd is er echter geen overlap meer, en de afwijking is de cumulatieve fout van de odometrie. In sommige zelfcorrigerende packages, zoals amcl, wordt een positieschatting (localization) gegeven, die kan worden verkregen via de tf van map->base_link, dus het verschil tussen deze positie en de odom-positie is het verschil tussen de odom- en map-coördinaten. Als je odom-berekening geen fouten bevat, is de map-odom-tf nul. Het odom-coördinatensysteem is nuttig als kortetermijn-lokale referentie, maar de afwijking maakt het ongeschikt als langetermijnreferentie.

Basiscoördinaten (base_link)

De coördinaten van het robot-basissysteem vallen samen met het centrum van de robot, wat doorgaans het rotatiecentrum van de robot is.

base_footprint: de oorsprong is de projectie van de oorsprong van base_link op de grond, met een klein verschil (z-waarden).

Relatie tussen coördinaten

In robotsystemen gebruiken we een boomstructuur om alle coördinaten te verbinden, zodat elke coördinaat een ouder heeft en willekeurig veel kindcoördinaten kan hebben, als volgt: map -> odom -> base_link — het wereldcoördinatensysteem is de ouder van het odom-coördinatensysteem, en het odom-coördinatensysteem is de ouder van base_link. Hoewel het intuïtief lijkt dat zowel map als odom rechtstreeks met base_link verbonden zouden moeten zijn, is dat niet toegestaan, omdat elk systeem maar één ouder mag hebben.

Rechten binnen het coördinatensysteem

De transformatie van odom naar base_link wordt berekend en gepubliceerd door de odometriebron. De lokalisatiemodule publiceert echter niet de transformatie (transform) van map naar base_link. In plaats daarvan ontvangt de lokalisatiemodule de transformatie van odom naar base_link en gebruikt deze informatie om de transformatie van map naar odom te publiceren.

rqt (QT-tool)

Open het commandoregelvenster, voer rosrun rqt in en druk dubbel op de Tab-toets om te zien wat er allemaal in de QT-tool van ROS zit, zoals in de onderstaande afbeelding:

Laten we, aan de hand van het voorbeeld van de schildpadden, kort een aantal van de gebruikte QT-tools introduceren:

  1. rqt_graph — visualisatie van de computatiegraaf

Open het commandoregelvenster, voer het volgende commando in, en er verschijnt een dialoogvenster.

Rosrun rqt gram rqt gram

Uit de afbeeldingen blijkt duidelijk dat de node /teleop_turtle via het topic /turtle1/cmd_vel naar de node /turtlesim zendt.

/teleop_turtle is de node met de publisher-functie.

/turtlesim is de node met de subscriber-functie.

This post is part of our special coverage Zambia.

  1. rqt_topic — topics bekijken

rosrun rqt topic rqt topic

Met deze tool kunnen we duidelijk realtime informatie zien over de veranderingen van de schildpad.

  1. rqt_publisher

rqt_publisher biedt een GUI-plugin om elk bericht te publiceren met vaste of berekende veldwaarden. Open het commandoregelvenster, voer het volgende commando in, en er verschijnt een dialoogvenster.

bash
rosrun rqt_publisher rqt_publisher

Klik op het selectievak rechts van Topic om het gewenste topic /turtle1/cmd_vel te vinden en klik rechts om de waarde als volgt toe te voegen:

  1. rqt_plot — datavisualisatie

De referentie-instructies zijn als volgt:

bash
rosrun rqt_plot rqt_plot
  1. rqt_console — loguitvoer

Het ROS-logsysteem heeft als functie om logberichten te genereren die op het scherm worden weergegeven, naar een specifiek topic worden gestuurd of in een specifiek logbestand worden opgeslagen, om debuggen, registreren, alarmeren, enz. te vergemakkelijken.

Het logbericht in ROS kan naar ernst worden onderverdeeld in 5 niveaus: DEBUG, INFO, WARN, ERROR, FATAL. Zolang het programma kan draaien, hoef je hier geen aandacht aan te besteden, maar de aanwezigheid van ERROR en FATAL geeft aan dat er ernstige problemen met het programma zijn waardoor het niet kan draaien.

bash
rosrun rqt_console rqt_console

De loguitvoertool maakt deel uit van het ROS-logframework, dat uitvoerinformatie voor nodes toont; op de kaart kunnen we zien dat de schildpad tegen de muur is gebotst.

Veelgebruikte API

  1. rqt_reconfigure — dynamische parameterconfiguratie

De referentie-instructies zijn als volgt:

bash
rosrun rqt_reconfigure rqt_reconfigure

Afbeelding afkomstig van ROS wiki:

Rviz

rviz is een grafische tool waarmee je eenvoudig het ros-programma grafisch kunt uitvoeren. Het is ook eenvoudiger in gebruik.

[Set initial pose], [Set target pose]

De rviz-interface bestaat voornamelijk uit:

1: 3D-weergavegebied voor het visueel weergeven van data, dat op dit moment niet beschikbaar is en daarom zwart is.

2: Werkbalk, die tools biedt zoals perspectiefregeling, doelinstelling, positiebepaling, enz.

3: Toont een lijst met items om de momenteel geselecteerde weergaveplugin te tonen, waarbij de eigenschappen van elke plugin geconfigureerd kunnen worden.

4: Perspectiefinstelling, met meerdere beschikbare weergavepunten.

5: Tijdweergavegebied dat de huidige systeemtijd en de ROS-tijd toont.

Weergave toevoegen

Stap 1: klik op de knop [Add]. Er verschijnt een venster.

Stap 2: toevoegen op basis van weergavetype [By display type], hoewel de coördinaten alleen getoond kunnen worden als het bijbehorende topic wordt aangepast; je kunt ook rechtstreeks toevoegen door het topic te selecteren [by topic], zodat het correct wordt weergegeven.

Stap 3: klik op [OK].

Veelgebruikte ROS-commando's

Figures

7.2.1.3 Common Commands and Tools figure 1

7.2.1.3 Common Commands and Tools figure 2

7.2.1.3 Common Commands and Tools figure 3

7.2.1.3 Common Commands and Tools figure 4

7.2.1.3 Common Commands and Tools figure 5

7.2.1.3 Common Commands and Tools figure 6

7.2.1.3 Common Commands and Tools figure 7

7.2.1.3 Common Commands and Tools figure 8

7.2.1.3 Common Commands and Tools figure 9

7.2.1.3 Common Commands and Tools figure 10

7.2.1.3 Common Commands and Tools figure 11

7.2.1.3 Common Commands and Tools figure 12

7.2.1.3 Common Commands and Tools figure 13

7.2.1.4 Publisher

Publisher

De publisher fungeert, zoals de naam al zegt, als uitgever. Dit bericht — dat bijvoorbeeld door de onderliggende machine naar de sensorinformatie op de machine gestuurd kan worden — wordt vervolgens verpakt en verzonden naar de subscriber van het topic; het is ook mogelijk om data op het vliegtuig (aircraft) te berekenen, te verpakken en te versturen naar de subscriber die zich op het topic heeft geabonneerd.

Workspace en topic-kit aanmaken

Workspace aanmaken

bash
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace

Workspace compileren

bash
cd ~/catkin_ws/
catkin_make

Omgevingsvariabelen bijwerken

bash
source devel/setup.bash

Omgevingsvariabelen controleren

bash
echo $ROS_PACKAGE_PATH

Package aanmaken

bash
cd ~/catkin_ws/src
catkin_create_pkg learning_topic std_msgs rospy roscpp geometry_msgs turtlesim

Toelichting: learning_topic is de naam van het functionele package

Package bouwen

bash
cd ~/catkin_ws
catkin_make
source ~/catkin_ws/devel/setup.bash

Een publisher aanmaken

Stappen

Initialisatie van ROS-nodes

  1. Handles aanmaken
  1. Registreer nodeinformatie bij ROS Master, inclusief de naam en het type van het gepubliceerde bericht en de lengte van de queue
  1. Berichtdata aanmaken en initialiseren

Vijf, herhaal het versturen van het bericht met een bepaalde frequentie

C++-implementatie

  1. Maak een C++-bestand (bestand met extensie .cpp) aan in de src-map van het package, genaamd turtle_velocity_publisher.cpp (basisgebruik van vim ter herinnering: 14 met de Vim-editor)
bash
touch turtle_velocity_publisher.cpp # create the file
vim turtle_velocity_publisher.cpp # edit the file
  1. Kopieer onderstaande programmacode naar het bestand turtle_velocity_publisher.cpp

Code file: 7-2-1-4-publisher-turtle_velocity_publisher.cpp

cpp
/*Create a turtlesim velocity publisher.*/
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
int main(int argc, char **argv){

    ros::init(argc, argv, "turtle_velocity_publisher");//Initialize the ROS node.

    ros::NodeHandle n;//Create a node handle.

    //Create a publisher for /turtle1/cmd_vel with geometry_msgs::Twist messages and queue size 10.
    ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);

    ros::Rate loop_rate(10);//Set the loop rate.

    while (ros::ok()){
            //Initialize a message with the same type as the publisher.
        geometry_msgs::Twist turtle_vel_msg;
        turtle_vel_msg.linear.x = 0.8;
        turtle_vel_msg.angular.z = 0.6;

        turtle_vel_pub.publish(turtle_vel_msg);// Publish the velocity message.

        //Print the published velocity.
        ROS_INFO("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]", turtle_vel_msg.linear.x, turtle_vel_msg.angular.z);

        loop_rate.sleep();//Sleep according to the loop rate.
    }
    return 0;
}

De bewerkte projectdirectory ziet er als volgt uit:

bash
catkin_ws/
├── CMakeLists.txt
└── src/
    ├── CMakeLists.txt
    └── learning_topic/
        ├── CMakeLists.txt
        ├── package.xml
        └── src/
            └── turtle_velocity_publisher.cpp
  1. Stroomschema van het programma, overeenkomend met de inhoud van 1.3.1

  2. Voeg in catkin_ws/src/learning_topic/CMakeLists.txt, onder het buildgedeelte, het volgende toe:

(basisgebruik van vim ter herinnering: 14 met de Vim-editor)

Code file: 7-2-1-4-publisher-example-02.cmake

cmake
add_executable(turtle_velocity_publisher src/turtle_velocity_publisher.cpp)
target_link_libraries(turtle_velocity_publisher ${catkin_LIBRARIES})

Let op: pas CMakeLists.txt aan op het juiste pad

  1. Code opnieuw compileren onder de workspace-directory
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program

Uitvoeringsprocedure

Open de eerste terminal waarin roscore draait:

bash
roscore

Voer de node van de schildpad uit

bash
rosrun turtlesim turtlesim_node

Voer de publisher uit, die continu snelheid naar de schildpad blijft sturen.

bash
rosrun learning_topic turtle_velocity_publisher
  1. Verwacht resultaat

  2. Beschrijving van de werking van het programma

Wanneer je op de terminal de lijst met topics opvraagt, vind je het topic /turtle1/cmd_vel.

We vinden dit met rostopic info /turtle1/cmd_vel

Dit betekent dat de schildpad een subscriber is op het snelheidstopic /turtle1/cmd_vel, dus de publisher blijft snelheidsdata versturen, en zodra de schildpad deze ontvangt, begint deze met de opgegeven snelheid te bewegen.

Python-implementatie

  1. Maak in de packagedirectory een nieuwe map scripts aan, en maak vervolgens in de map scripts een nieuw Python-bestand (bestand met extensie .py) aan, genaamd turtle_velocity_publisher.py

  2. Kopieer de volgende programmacode naar het bestand turtle_velocity_publisher.py

Code file: 7-2-1-4-publisher-suffix.py

python
#!/usr/bin/env python3

import rospy
from geometry_msgs.msg import Twist

def turtle_velocity_publisher():

    rospy.init_node('turtle_velocity_publisher', anonymous=True) # Initialize the ROS node.

    # Create a turtlesim velocity publisher on /turtle1/cmd_vel. The message type is geometry_msgs/Twist and the queue size is 8.
    turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=8)


    rate = rospy.Rate(10) # Set the loop rate.

    while not rospy.is_shutdown():
        # Initialize a geometry_msgs::Twist message.
        turtle_vel_msg = Twist()
        turtle_vel_msg.linear.x = 0.8
        turtle_vel_msg.angular.z = 0.6

        # Publish the message.
        turtle_vel_pub.publish(turtle_vel_msg)
        rospy.loginfo("linear is:%0.2f m/s, angular is:%0.2f rad/s",
                turtle_vel_msg.linear.x, turtle_vel_msg.angular.z)


        rate.sleep()# Sleep according to the loop rate.

if __name__ == '__main__':
    try:
        turtle_velocity_publisher()
    except rospy.ROSInterruptException:
        pass
  1. Projectstroomschema

  2. Uitvoeringsprocedure

Open de eerste terminal waarin roscore draait

bash
roscore

Voer de node van de schildpad uit

bash
rosrun turtlesim turtlesim_node

Voer de publisher uit, die continu snelheid naar de schildpad blijft sturen.

bash
rosrun learning_topic turtle_velocity_publisher.py

Let op: voordat je dit uitvoert, moet je turtle_velocity_publisher.py uitvoerbaar maken door de terminal te openen in de map van turtle_velocity_publisher.py.

bash
sudo chmod a+x turtle_velocity_publisher.py

Alle Python-bestanden moeten uitvoeringsrechten krijgen, anders krijg je een foutmelding!

Figures

7.2.1.4 Publisher figure 1

7.2.1.4 Publisher figure 2

7.2.1.4 Publisher figure 3

7.2.1.4 Publisher figure 4

7.2.1.4 Publisher figure 5

7.2.1.4 Publisher figure 6

7.2.1.5 Subscriber

Subscriber

De subscriber ontvangt de door de publisher gepubliceerde data en geeft deze vervolgens door aan zijn callbackfunctie, waar de ontvangen data wordt verwerkt. De kern is een callbackfunctie, en elke subscriber abonneert zich op een topic.

Een subscriber aanmaken

Stappen

Initialisatie van ROS-nodes

  1. Handles aanmaken

  2. Abonneren op topics

  3. Het topicbericht doorlopen en dit teruggeven aan de callbackfunctie

  1. De berichtverwerking afronden in een callbackfunctie.

De workspace van dit hoofdstuk bouwt voort op de workspace die in sectie IV is aangemaakt.

C++-implementatie

  1. Maak een nieuw C++-bestand aan in de src-map van het package uit de vorige "publishing"-tutorial, genaamd turtle_pose_subscriber.cpp

  2. Kopieer onderstaande programmacode naar het bestand turtle_pose_subscriber.cpp

Code file: 7-2-1-5-subscriber-subscriber.cpp

cpp
/*Create a subscriber for the current turtlesim pose.*/
#include <ros/ros.h>
#include "turtlesim/Pose.h"
// The callback runs when a subscribed message is received.
void turtle_poseCallback(const turtlesim::Pose::ConstPtr& msg){
    // Print the received message.
    ROS_INFO("Turtle pose: x:%0.3f, y:%0.3f", msg->x, msg->y);
}

int main(int argc, char **argv){

    ros::init(argc, argv, "turtle_pose_subscriber");// Initialize the ROS node.

    ros::NodeHandle n;//Create a node handle.

    // Create a subscriber for /turtle1/pose and register poseCallback.
    ros::Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, turtle_poseCallback);

    ros::spin(); // Wait for callbacks.

    return 0;
}
bash
catkin_ws/
├── CMakeLists.txt
└── src/
    ├── CMakeLists.txt
    └── learning_topic/
        ├── CMakeLists.txt
        ├── package.xml
        └── src/
            └── turtle_velocity_publisher.cpp
            └── turtle_pose_subscriber.cpp
  1. Stroomschema van het programma, overeenkomend met de inhoud van 5.2.1

  2. Voeg in catkin_ws/src/learning_topic/CMakeLists.txt, onder het buildgedeelte, het volgende toe:

(basisgebruik van vim ter herinnering: 14 met de Vim-editor)

bash
Add executeable (turtle pose subscriber src/turtle_pose_subscriber.cpp)
{\cHFFFFFF}{\cH00FFFF}
  1. Code compileren onder de workspace-directory
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program

Uitvoeringsprocedure

Open de eerste terminal waarin roscore draait

bash
roscore

De tweede terminal voert de node van de schildpad uit.

bash
rosrun turtlesim turtlesim_node

De derde terminal voert de subscriber-node uit en blijft data over de positie van de schildpad ontvangen

bash
rosrun learning_topic turtle_pose_subscriber
  1. Beschrijving van de werking van het programma

Na het starten van de node van de schildpad blijft deze zijn positiebericht versturen, op het topic:

/turtle1/pose

En bij het draaien ontvangt het de databerichten die door de schildpad worden verstuurd, en print deze vervolgens via de callbackfunctie.

Python-implementatie

  1. Maak in de packagedirectory een nieuwe map scripts aan en maak vervolgens in de map scripts een nieuw Python-bestand (bestand met extensie .py) aan, genaamd turtle_pose_subscriber.py

  2. Kopieer de volgende programmacode naar turtle_pose_subscriber.py

Code file: 7-2-1-5-subscriber-suffix.py

python
#!/usr/bin/env python3

import rospy
from turtlesim.msg import Pose

def poseCallback(msg):
    rospy.loginfo("Turtle pose: x:%0.3f, y:%0.3f", msg.x, msg.y)

def turtle_pose_subscriber():

    rospy.init_node('turtle_pose_subscriber', anonymous=True)# Initialize the ROS node.

    # Create a subscriber for /turtle1/pose and register poseCallback.
    rospy.Subscriber("/turtle1/pose", Pose, poseCallback)


    rospy.spin()# Wait for callbacks.

if __name__ == '__main__':
    turtle_pose_subscriber()
  1. Projectstroomschema

  2. Uitvoeringsprocedure

Voer roscore uit

bash
roscore

Voer de node van de schildpad uit

bash
rosrun turtlesim turtlesim_node

Voer de subscriber uit, die continu data over de positie van de schildpad blijft ontvangen

bash
rosrun learning_topic turtle_pose_subscriber.py

Figures

7.2.1.5 Subscriber figure 1

7.2.1.5 Subscriber figure 2

7.2.1.5 Subscriber figure 3

7.2.1.5 Subscriber figure 4

7.2.1.5 Subscriber figure 5

7.2.1.6 Custom Topic Messages and Usage

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

This section creates and uses a custom topic message named Information.msg. The example continues with the learning_topic package created earlier.

Create the Message File

Create the msg directory and define the custom message:

bash
cd ~/catkin_ws/src/learning_topic
mkdir -p msg
vim msg/Information.msg

Code file: 7-2-1-6-custom-topic-messages-and-usage-Information.msg

msg
string company
string city

Update package.xml

Add message generation/runtime dependencies to package.xml:

xml
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

Update CMakeLists.txt

Add message generation to CMakeLists.txt:

cmake
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

add_message_files(
  FILES
  Information.msg
)

generate_messages(
  DEPENDENCIES
  std_msgs
)

catkin_package(
  CATKIN_DEPENDS message_runtime
)

Build the workspace:

bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash

C++ Publisher and Subscriber

Create the following files under ~/catkin_ws/src/learning_topic/src.

Code file: 7-2-1-6-custom-topic-messages-and-usage-Information_publisher.cpp

cpp
/**
 * Publish /company_info with the custom learning_topic::Information message type.
 */
#include <ros/ros.h>
#include "learning_topic/Information.h"

int main(int argc, char **argv)
{
    ros::init(argc, argv, "company_information_publisher");
    ros::NodeHandle nh;

    ros::Publisher info_pub = nh.advertise<learning_topic::Information>("/company_info", 10);
    ros::Rate loop_rate(1);

    while (ros::ok())
    {
        learning_topic::Information info_msg;
        info_msg.company = "Seeed";
        info_msg.city = "Shenzhen";

        info_pub.publish(info_msg);
        ROS_INFO("Information: company:%s city:%s", info_msg.company.c_str(), info_msg.city.c_str());
        loop_rate.sleep();
    }
    return 0;
}

Code file: 7-2-1-6-custom-topic-messages-and-usage-Information_subscriber.cpp

cpp
/**
 * Subscribe to /company_info with the custom learning_topic::Information message type.
 */
#include <ros/ros.h>
#include "learning_topic/Information.h"

void companyInfoCallback(const learning_topic::Information::ConstPtr& msg)
{
    ROS_INFO("Company: %s, city: %s", msg->company.c_str(), msg->city.c_str());
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "company_information_subscriber");
    ros::NodeHandle nh;
    ros::Subscriber sub = nh.subscribe("/company_info", 10, companyInfoCallback);
    ros::spin();
    return 0;
}

Add the executables to CMakeLists.txt:

cmake
add_executable(Information_publisher src/Information_publisher.cpp)
target_link_libraries(Information_publisher ${catkin_LIBRARIES})
add_dependencies(Information_publisher ${PROJECT_NAME}_generate_messages_cpp)

add_executable(Information_subscriber src/Information_subscriber.cpp)
target_link_libraries(Information_subscriber ${catkin_LIBRARIES})
add_dependencies(Information_subscriber ${PROJECT_NAME}_generate_messages_cpp)

Build and run:

bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash
roscore
rosrun learning_topic Information_publisher
rosrun learning_topic Information_subscriber

Python Publisher and Subscriber

Create the following files under ~/catkin_ws/src/learning_topic/scripts, then make them executable.

Code file: 7-2-1-6-custom-topic-messages-and-usage-Information_publisher.py

python
#!/usr/bin/env python3
import rospy
from learning_topic.msg import Information

def information_publisher():
    rospy.init_node('information_publisher', anonymous=True)
    info_pub = rospy.Publisher('/company_info', Information, queue_size=10)
    rate = rospy.Rate(1)

    while not rospy.is_shutdown():
        info_msg = Information()
        info_msg.company = 'Seeed'
        info_msg.city = 'Shenzhen'
        info_pub.publish(info_msg)
        rospy.loginfo('Information: company:%s city:%s', info_msg.company, info_msg.city)
        rate.sleep()

if __name__ == '__main__':
    information_publisher()

Code file: 7-2-1-6-custom-topic-messages-and-usage-Information_subscriber.py

python
#!/usr/bin/env python3
import rospy
from learning_topic.msg import Information

def company_info_callback(msg):
    rospy.loginfo('Company: %s, city: %s', msg.company, msg.city)

def information_subscriber():
    rospy.init_node('information_subscriber', anonymous=True)
    rospy.Subscriber('/company_info', Information, company_info_callback)
    rospy.spin()

if __name__ == '__main__':
    information_subscriber()
bash
chmod +x scripts/Information_publisher.py scripts/Information_subscriber.py
roscore
rosrun learning_topic Information_publisher.py
rosrun learning_topic Information_subscriber.py

Figures

7.2.1.6 Custom Topic Messages and Usage figure 1

7.2.1.6 Custom Topic Messages and Usage figure 2

7.2.1.6 Custom Topic Messages and Usage figure 3

7.2.1.6 Custom Topic Messages and Usage figure 4

7.2.1.6 Custom Topic Messages and Usage figure 5

7.2.1.6 Custom Topic Messages and Usage figure 6

7.2.1.6 Custom Topic Messages and Usage figure 7

7.2.1.6 Custom Topic Messages and Usage figure 8

7.2.1.7 Client

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

In addition to the topic communication, there is a service communication. A client sends a request, and a server returns a response. This section focuses on the client, showing how to implement a client in C++ and Python.

Voorbereidend werk

Continue using the learning_server package created in this section.

Package aanmaken

  1. Ga naar ~/catkin_ws/src en voer in de terminal uit:
bash
catkin_create_pkg learning_server std_msgs rospy roscpp geometry_msgs turtlesim

Ga naar de ~-map om te compileren:

bash
catkin_make

C++-implementatie

Stappen naar realisatie

Initialisatie van ROS-nodes

  1. Handles aanmaken
  1. Een voorbeeld van een client aanmaken
  1. Service-requestdata initialiseren en versturen

  2. Response ontvangen van server

Maak a_new_turtle.cpp aan onder ~/catkin_ws/src/learning_server/src en plak de volgende code erin.

a_new_turtle.cpp

Code file: 7-2-1-7-client-new.cpp

cpp
/**
This example calls the turtlesim /spawn service to create a new turtle at the specified position.
*/

#include <ros/ros.h>
#include <turtlesim/Spawn.h>

int main(int argc, char** argv)
{

    ros::init(argc, argv, "a_new_turtle");// Initialize the ROS node.

    ros::NodeHandle node;

    ros::service::waitForService("/spawn"); // Wait for the /spawn service.

    ros::ServiceClient new_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");//Create a service client for /spawn.

    // Initialize the turtlesim::Spawn request.
    turtlesim::Spawn new_turtle_srv;
    new_turtle_srv.request.x = 6.0;
    new_turtle_srv.request.y = 8.0;
    new_turtle_srv.request.name = "turtle2";

    // Call the service with x/y position and name parameters.
    ROS_INFO("Call service to create a new turtle name is %s,at the x:%.1f,y:%.1f", new_turtle_srv.request.name.c_str(),
        new_turtle_srv.request.x,
        new_turtle_srv.request.y);

    new_turtle.call(new_turtle_srv);


    ROS_INFO("Spawn turtle successfully [name:%s]", new_turtle_srv.response.name.c_str());// Display the service call result.

    return 0;
};
  1. Stroomschema van de procedure
  1. Voeg in de configuratie van CMakeLists.txt, onder het buildgedeelte, het volgende toe:

(basisgebruik van vim ter herinnering: 14 met de Vim-editor)

Code file: 7-2-1-7-client-example-02.cmake

cmake
add_executable(a_new_turtle src/a_new_turtle.cpp)
target_link_libraries(a_new_turtle ${catkin_LIBRARIES})
  1. Code opnieuw compileren onder de workspace-directory
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program
  1. Open drie terminals en voer de programma's uit
bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_server a_new_turtle
  1. Verwacht resultaat

  2. Verloop

Zodra de node van de schildpad actief is, verschijnt er bij het opnieuw uitvoeren van a_new_turtle een tweede schildpad in de weergave, omdat de node van de schildpad de service /spawn aanbiedt, die een tweede schildpad turtle2 creëert; dit kun je bekijken via het commando rosservice list, zoals hieronder getoond.

De parameters die deze service nodig heeft, kun je bekijken via rosservice info /spawn, zoals in de onderstaande afbeelding.

Je ziet dat er vier parameters nodig zijn: x, y, theta, name, die worden geïnitialiseerd in a_new_turtle.cpp

Code file: 7-2-1-7-client-turtle.cpp

cpp
srv.request.x = 6.0;
srv.request.y = 8.0;
srv.request.name = "turtle2";

Let op: theta wordt niet toegewezen, de standaardwaarde is 0

Python-implementatie

Maak scripts/a_new_turtle.py aan onder ~/catkin_ws/src/learning_server en plak de volgende code erin.

a_new_turtle.py

Code file: 7-2-1-7-client-a_new_turtle.py

python
#!/usr/bin/env python3

import rospy
from turtlesim.srv import Spawn


def turtle_spawn():
    rospy.init_node('new_turtle')
    rospy.wait_for_service('/spawn')

    try:
        spawn_client = rospy.ServiceProxy('/spawn', Spawn)
        response = spawn_client(2.0, 2.0, 0.0, 'turtle2')
        return response.name
    except rospy.ServiceException as exc:
        rospy.logerr('Failed to call /spawn: %s', exc)
        return None


if __name__ == '__main__':
    name = turtle_spawn()
    if name:
        rospy.loginfo('Created a new turtle named %s.', name)
  1. Stroomschema van de procedure

  2. Open drie terminals en voer de programma's uit

bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_server a_new_turtle.py
  1. Het resultaat en de beschrijving van de werking komen overeen met wat met C++ is bereikt; hier zie je hoe Python de parameters aan de service meegeeft,

response = spawn_client(2.0, 2.0, 0.0, "turtle2")

De bijbehorende parameters zijn x, y, theta, name.

Figures

7.2.1.7 Client figure 1

7.2.1.7 Client figure 2

7.2.1.7 Client figure 3

7.2.1.7 Client figure 4

7.2.1.7 Client figure 5

7.2.1.7 Client figure 6

7.2.1.7 Client figure 7

7.2.1.8 Server

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

Wanneer we het hebben over de client die een request stuurt en vervolgens de service, hebben we het over het leveren van een service.

Continue using the learning_server package created in this section.

C++-implementatie

Stappen naar realisatie

Initialisatie van ROS-nodes

  1. Voorbeeld van het aanmaken van een Server
  1. Wachten in een lus op service-requests, en het aanroepen van een callbackfunctie

  2. De functionele verwerking van de service afronden in de callbackfunctie en response-data terugkoppelen

Maak turtle_vel_command_server.cpp aan onder ~/catkin_ws/src/learning_server/src en plak de volgende code erin.

Code file: 7-2-1-8-server-new.cpp

cpp
/**
This example provides /turtle_vel_command with the std_srvs/Trigger service type.
*/
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <std_srvs/Trigger.h>

ros::Publisher turtle_vel_pub;
bool pubvel = false;

// Service callback: req is the request and res is the response.
bool pubvelCallback(std_srvs::Trigger::Request  &req,
                    std_srvs::Trigger::Response &res)
{
    pubvel = !pubvel;

        ROS_INFO("Do you want to publish the vel?: [%s]", pubvel==true?"Yes":"No");// Print the client request.

    // Set response data.
    res.success = true;
    res.message = "The status is changed!";

    return true;
}

int main(int argc, char **argv)
{

    ros::init(argc, argv, "turtle_vel_command_server");


    ros::NodeHandle n;

    // Create the /turtle_vel_command server and register pubvelCallback.
    ros::ServiceServer command_service = n.advertiseService("/turtle_vel_command", pubvelCallback);

    // Create a publisher for /turtle1/cmd_vel. The message type is geometry_msgs::Twist and the queue size is 8.
    turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 8);

    ros::Rate loop_rate(10);// Set the loop rate.

    while(ros::ok())
    {

        ros::spinOnce();// Process callbacks once.

        // Publish turtle velocity commands when pubvel is true.
        if(pubvel)
        {
            geometry_msgs::Twist vel_msg;
            vel_msg.linear.x = 0.6;
            vel_msg.angular.z = 0.8;
            turtle_vel_pub.publish(vel_msg);
        }

        loop_rate.sleep();//Sleep according to the loop rate.
    }

    return 0;
}
  1. Stroomschema van de procedure
  1. Voeg in CMakeLists.txt, onder het buildgedeelte, het volgende toe:

Code file: 7-2-1-8-server-example-02.cmake

cmake
add_executable(turtle_vel_command_server src/turtle_vel_command_server.cpp)
target_link_libraries(turtle_vel_command_server ${catkin_LIBRARIES})
  1. Code compileren onder de workspace-directory
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program
  1. Vier terminals met programma's starten
bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_server turtle_vel_command_server
rosservice call /turtle_vel_command
  1. Verwacht resultaat

  2. Verloop

Wanneer je eerst de node van de schildpad uitvoert, kun je in de terminal rosservice list invoeren om te zien welke services er op dat moment beschikbaar zijn, als volgt:

Vervolgens voeren we het programma turtle_vel_command_server uit, en als we rosservice list invoeren, zien we een extra turtle_vel_command_server, zoals in de onderstaande afbeelding.

Vervolgens roepen we deze service aan door deze in de terminal in te voeren, en we zien de schildpad continu ronddraaien; als we de service opnieuw aanroepen, stopt deze. Dit komt doordat we in de callback van de service de waarde van pubvel omkeren en deze vervolgens terugkoppelen; de hoofdfunctie beoordeelt de waarde van pubvel, en als deze True is, geeft die snelheidsinstructies, en niet als deze False is.

Python-implementatie

Maak scripts/turtle_vel_command_server.py aan onder ~/catkin_ws/src/learning_server en plak de volgende code erin.

turtle_vel_command_server.py

Code file: 7-2-1-8-server-turtle_vel_command_server.py

python
#!/usr/bin/env python3

import threading

import rospy
from geometry_msgs.msg import Twist
from std_srvs.srv import Trigger, TriggerResponse

pubvel = False
turtle_vel_pub = None


def publish_velocity_loop():
    rate = rospy.Rate(10)
    while not rospy.is_shutdown():
        if pubvel:
            vel_msg = Twist()
            vel_msg.linear.x = 0.6
            vel_msg.angular.z = 0.8
            turtle_vel_pub.publish(vel_msg)
        rate.sleep()


def pubvel_callback(req):
    global pubvel
    pubvel = not pubvel
    rospy.loginfo('Publish turtle velocity: %s', pubvel)
    return TriggerResponse(success=True, message='Velocity publishing toggled.')


def turtle_pubvel_command_server():
    global turtle_vel_pub
    rospy.init_node('turtle_vel_command_server')
    turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=8)
    rospy.Service('/turtle_vel_command', Trigger, pubvel_callback)
    threading.Thread(target=publish_velocity_loop, daemon=True).start()
    rospy.loginfo('Ready to receive /turtle_vel_command requests.')
    rospy.spin()


if __name__ == '__main__':
    turtle_pubvel_command_server()
  1. Stroomschema van de procedure
  1. Open drie terminals en voer de programma's uit:
bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_server turtle_vel_command_server.py
  1. Het resultaat en de beschrijving van de werking komen overeen met wat met C++ is bereikt.

Figures

7.2.1.8 Server figure 1

7.2.1.8 Server figure 2

7.2.1.8 Server figure 3

7.2.1.8 Server figure 4

7.2.1.8 Server figure 5

7.2.1.8 Server figure 6

7.2.1.8 Server figure 7

7.2.1.9 Custom Service Messages and Usage

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

This section defines a custom service named IntPlus.srv and implements a server/client pair in C++ and Python.

Create the Service File

bash
cd ~/catkin_ws/src/learning_server
mkdir -p srv
vim srv/IntPlus.srv

Code file: 7-2-1-9-custom-service-messages-and-usage-IntPlus.srv

srv
int64 a
int64 b
---
int64 result

Update package.xml and CMakeLists.txt

Add these dependencies to package.xml:

xml
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

Add service generation to CMakeLists.txt:

cmake
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

add_service_files(
  FILES
  IntPlus.srv
)

generate_messages(
  DEPENDENCIES
  std_msgs
)

catkin_package(
  CATKIN_DEPENDS message_runtime
)

Build the workspace:

bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash

C++ Server and Client

Code file: 7-2-1-9-custom-service-messages-and-usage-IntPlus_server.cpp

cpp
#include <ros/ros.h>
#include "learning_server/IntPlus.h"

bool intPlusCallback(learning_server::IntPlus::Request &req,
                     learning_server::IntPlus::Response &res)
{
    ROS_INFO("number 1 is:%ld, number 2 is:%ld", req.a, req.b);
    res.result = req.a + req.b;
    return true;
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "IntPlus_server");
    ros::NodeHandle nh;
    ros::ServiceServer service = nh.advertiseService("/Two_Int_Plus", intPlusCallback);
    ROS_INFO("Ready to calculate two integers.");
    ros::spin();
    return 0;
}

Code file: 7-2-1-9-custom-service-messages-and-usage-IntPlus_client.cpp

cpp
#include <ros/ros.h>
#include "learning_server/IntPlus.h"

int main(int argc, char **argv)
{
    ros::init(argc, argv, "IntPlus_client");
    ros::NodeHandle nh;
    ros::service::waitForService("/Two_Int_Plus");
    ros::ServiceClient client = nh.serviceClient<learning_server::IntPlus>("/Two_Int_Plus");

    learning_server::IntPlus srv;
    srv.request.a = 8;
    srv.request.b = 6;

    if (client.call(srv)) {
        ROS_INFO("Result: %ld", srv.response.result);
    } else {
        ROS_ERROR("Failed to call /Two_Int_Plus");
    }
    return 0;
}

Add the executables to CMakeLists.txt:

cmake
add_executable(IntPlus_server src/IntPlus_server.cpp)
target_link_libraries(IntPlus_server ${catkin_LIBRARIES})
add_dependencies(IntPlus_server ${PROJECT_NAME}_generate_messages_cpp)

add_executable(IntPlus_client src/IntPlus_client.cpp)
target_link_libraries(IntPlus_client ${catkin_LIBRARIES})
add_dependencies(IntPlus_client ${PROJECT_NAME}_generate_messages_cpp)

Run the example:

bash
roscore
rosrun learning_server IntPlus_server
rosrun learning_server IntPlus_client

You can also call the service directly:

bash
rosservice call /Two_Int_Plus 5 6

Python Server and Client

Code file: 7-2-1-9-custom-service-messages-and-usage-IntPlus_server.py

python
#!/usr/bin/env python3
import rospy
from learning_server.srv import IntPlus, IntPlusResponse

def int_plus_callback(req):
    rospy.loginfo('Ints: a:%d b:%d', req.a, req.b)
    return IntPlusResponse(req.a + req.b)

if __name__ == '__main__':
    rospy.init_node('IntPlus_server')
    rospy.Service('/Two_Int_Plus', IntPlus, int_plus_callback)
    rospy.loginfo('Ready to calculate two integers.')
    rospy.spin()

Code file: 7-2-1-9-custom-service-messages-and-usage-IntPlus_client.py

python
#!/usr/bin/env python3
import rospy
from learning_server.srv import IntPlus

if __name__ == '__main__':
    rospy.init_node('IntPlus_client')
    rospy.wait_for_service('/Two_Int_Plus')
    plus_client = rospy.ServiceProxy('/Two_Int_Plus', IntPlus)
    response = plus_client(22, 20)
    rospy.loginfo('Result: %d', response.result)
bash
chmod +x scripts/IntPlus_server.py scripts/IntPlus_client.py
roscore
rosrun learning_server IntPlus_server.py
rosrun learning_server IntPlus_client.py

Figures

7.2.1.9 Custom Service Messages and Usage figure 1

7.2.1.9 Custom Service Messages and Usage figure 2

7.2.1.9 Custom Service Messages and Usage figure 3

7.2.1.9 Custom Service Messages and Usage figure 4

7.2.1.9 Custom Service Messages and Usage figure 5

7.2.1.9 Custom Service Messages and Usage figure 6

7.2.1.9 Custom Service Messages and Usage figure 7

7.2.1.10 Publishing and Listening with TF

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

tf-packages

tf is een package waarmee gebruikers meerdere coördinatensystemen in de tijd kunnen volgen, met behulp van boomvormige datastructuren die ontwikkelaars helpen om op elk moment coördinaten te wijzigen, tussen coördinaten om te rekenen, vectoren te bepalen, enz., door gebruik te maken van tijdbuffers en het onderhouden van coördinatenrelaties tussen meerdere frames.

Gebruiksstappen

  1. tf-transformatie onderscheppen

Ontvangt alle in het cachesysteem gepubliceerde coördinaten, transformeert de data, en zoekt daarin de gewenste coördinaten op.

  1. tf-transformatie uitzenden

Zendt de coördinatenwisseling tussen de coördinaten in het systeem uit. Er kunnen in meerdere onderdelen van het systeem tf-aanpassingen worden uitgezonden. Elke uitzending kan direct in de tf-boom worden ingevoegd, zonder verdere synchronisatie.

Implementatie van tf-coördinaten uitzenden en luisteren via programmeren

Package aanmaken en compileren

bash
cd ~/catkin_ws/src
catkin_create_pkg learning_tf rospy roscpp turtlesim tf
cd..
catkin_make

Hoe je een tf-broadcaster realiseert

  1. Definitie van de TF-broadcaster (Transform Broadcaster);

  2. Initialisatie van tf-data en het aanmaken van coördinaten;

  1. publicatie van de coördinatentransformatie (sendTransform);

Hoe je een tf-listener realiseert

  1. Definitie van de TF-listener (TransformListener);
  1. Coördinaten opzoeken (waitForTransform, lookupTransform)

Realisatie van de tf-broadcaster in C++

  1. Maak een C++-bestand (bestand met extensie .cpp) aan in de src-map van het package

  2. Kopieer onderstaande programmacode naar het bestand turtle_tf_broadcaster.cpp

Code file: 7-2-1-10-publishing-and-listening-with-tf-with.cpp

cpp
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>

std::string turtle_name;

void poseCallback(const turtlesim::PoseConstPtr& msg)
{

    static tf::TransformBroadcaster br;// Create a TF broadcaster.

    // Initialize TF data.
    tf::Transform transform;
    transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );//Set xyz coordinates.
    tf::Quaternion q;
    q.setRPY(0, 0, msg->theta);//Set Euler angles for x, y, and z rotation.
    transform.setRotation(q);

    br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));// Broadcast TF data between world and the turtle frame.
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "turtle_world_tf_broadcaster");// Initialize the ROS node.

    if (argc != 2)
    {
        ROS_ERROR("Missing a parameter as the name of the turtle!");
        return -1;
    }

    turtle_name = argv[1];// Use the input argument as the turtle name.

    // Subscribe to the turtle pose topic.
    ros::NodeHandle node;
    ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);

        // Wait for callbacks.
    ros::spin();

    return 0;
};
  1. Projectstroomschema

  2. Codetoelichting

Allereerst wordt er geabonneerd op de /pose-positie van de schildpad, en zodra het topic wordt gepubliceerd, wordt de callbackfunctie aangeroepen. Vervolgens gaat het terug naar de broadcaster van tf, waarna de tf-data wordt geïnitialiseerd, waarvan de waarde afkomstig is van het geabonneerde /pose-topic. Tot slot wordt de transformatie van de coördinaten van de wereld naar de schildpad gepubliceerd via br.sendTransform, een functie genaamd sendTransform. Deze heeft vier parameters: de eerste vertegenwoordigt de tf: (d.w.z. de eerder geïnitialiseerde tf-data) coördinaten van het type Transform, de tweede parameter is een tijdstempel, en de derde en vierde zijn respectievelijk de bron- en doelcoördinaten.

Realisatie van de tf-listener in C++

  1. Maak een C++-bestand (bestand met extensie .cpp) aan in de src-map van het package

  2. Kopieer onderstaande programmacode naar het bestand turtle_tf_listener.cpp

Code file: 7-2-1-10-publishing-and-listening-with-tf-with.cpp

cpp
/**
This example listens to TF data, computes velocity commands, and publishes them to turtle2.
turtle2->turtle1 = world->turtle*world->turtle2
*/

#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>

int main(int argc, char** argv)
{

    ros::init(argc, argv, "turtle1_turtle2_listener");// Initialize the ROS node.


    ros::NodeHandle node; // Create a node handle.

    // Call the service to spawn turtle2.
    ros::service::waitForService("/spawn");
    ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
    turtlesim::Spawn srv;
    add_turtle.call(srv);

    // Create a publisher for turtle2 velocity commands.
    ros::Publisher vel = node.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 10);

    tf::TransformListener listener;// Create a TF listener.

    ros::Rate rate(10.0);

    while (node.ok())
    {
        // Get TF data between turtle1 and turtle2.
        tf::StampedTransform transform;
        try
        {
            listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));
            listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);
        }
        catch (tf::TransformException &ex)
        {
            ROS_ERROR("%s",ex.what());
            ros::Duration(1.0).sleep();
            continue;
        }

        // Compute angular and linear velocity from the relative pose between turtle1 and turtle2, then publish commands for turtle2.
        geometry_msgs::Twist turtle2_vel_msg;

        turtle2_vel_msg.angular.z = 6.0 * atan2(transform.getOrigin().y(),
                                        transform.getOrigin().x());
        turtle2_vel_msg.linear.x = 0.8 * sqrt(pow(transform.getOrigin().x(), 2) +
                                      pow(transform.getOrigin().y(), 2));
        vel.publish(turtle2_vel_msg);

        rate.sleep();
    }
    return 0;
};
  1. Projectstroomschema

  2. Codetoelichting

Ten eerste roept de service het aanmaken aan van nog een schildpad, turtle2, en maakt vervolgens een snelheidsregelaar voor turtle2 aan; daarna wordt een listener aangemaakt, die luistert naar en zoekt naar de relatie tussen turtle1 en turtle2, waarbij twee functies betrokken zijn:

Code file: 7-2-1-10-publishing-and-listening-with-tf-example-03.cpp

cpp
waitForTransform and lookupTransform
waitForTransform(target_frame,source_frame,time,timeout)

De twee frames vertegenwoordigen respectievelijk de doelcoördinaten en de broncoördinaten, en de tijd geeft aan hoe lang er gewacht wordt op een verandering tussen de twee coördinaten, aangezien het wijzigen van coördinaten een blokkerend proces is en er daarom een tijdslimiet moet worden ingesteld.

lookupTransform(target_frame, source_frame, transform): geeft, op basis van de broncoördinaten (source_frame) en de doelcoördinaten (target_frame), de transformatie tussen beide coördinaten op het opgegeven tijdstip terug (transform).

We verkregen het resultaat van de coördinatentransformatie via lookupTransform en vervolgens x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x

Aanpassingen aan CMakeLists.txt en compilaties

  1. CMakeLists.txt aanpassen

Pas src/learning_tf/CMakeLists.txt van het package aan door het volgende toe te voegen:

(basisgebruik van vim ter herinnering: 14 met de Vim-editor)

Code file: 7-2-1-10-publishing-and-listening-with-tf-example-04.cpp

cpp
add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})

add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})
  1. Uitvoerbare documenten compileren
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program

Demonstratie van opstarten en werking

  1. Open zes terminals en voer de volgende commando's uit:
bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1
rosrun learning_tf turtle_tf_broadcaster __name:=turtle2_tf_broadcaster /turtle2
rosrun learning_tf turtle_tf_listener
rosrun turtlesim turtle_teleop_key  # start keyboard teleoperation for the turtle
  1. Gedemonstreerd effect

III. TOELICHTING OP DE PROCEDURE

Wanneer roscore wordt geactiveerd, wordt de node van de schildpad geactiveerd, en er verschijnt een schildpad aan het einde; vervolgens publiceren we twee tf-transformaties, turtle1->world en turtle2->world, omdat we, als we de verandering tussen turtle2 en turtle1 willen kennen, eerst de verandering tussen elk van hen en world moeten kennen; vervolgens wordt het tf-luisterprogramma geopend, waarbij in de terminal te zien is dat er nog een schildpad verschijnt, en turtle2 zal richting turtle1 bewegen; vervolgens zetten we de toetsenbordbesturing aan, en door op de pijltjestoetsen te klikken sturen we de beweging van turtle1 aan, waarbij turtle2 de beweging van turtle1 zal volgen.

tf-broadcaster in Python

  1. Maak in het package een map scripts aan, ga naar deze directory, en maak een nieuw .py-bestand aan genaamd turtle_tf_broadcaster.py

  2. Kopieer onderstaande programmacode naar het bestand turtle_tf_broadcaster.py

Code file: 7-2-1-10-publishing-and-listening-with-tf-new.py

python
#!/usr/bin/env python3

import roslib
roslib.load_manifest('learning_tf')
import rospy

import tf
import turtlesim.msg

def handle_turtle_pose(msg, turtlename):
    br = tf.TransformBroadcaster()# Create a TF broadcaster.
    # Broadcast the TF transform between world and the named turtle.
    br.sendTransform((msg.x, msg.y, 0),
                     tf.transformations.quaternion_from_euler(0, 0, msg.theta),
                     rospy.Time.now(),
                     turtlename,
                     "world")

if __name__ == '__main__':

    rospy.init_node('turtle1_turtle2_tf_broadcaster')# Initialize the ROS node.

    turtlename = rospy.get_param('~turtle') # Get the turtle name from the parameter server.
    # Subscribe to the turtle pose topic.
    rospy.Subscriber('/%s/pose' % turtlename,
                     turtlesim.msg.Pose,
                     handle_turtle_pose,
                     turtlename)
    rospy.spin()
  1. Projectstroomschema

tf-listener in Python

  1. Maak een Python-bestand (bestand met extensie .py) aan in de map scripts van het package learning_tf, genaamd turtle_tf_listener.py

  2. Kopieer onderstaande programmacode naar het bestand turtle_tf_listener.py

Code file: 7-2-1-10-publishing-and-listening-with-tf-with.py

python
#!/usr/bin/env python3
import rospy
import math
import tf
import geometry_msgs.msg
import turtlesim.srv

if __name__ == '__main__':
    rospy.init_node('turtle_tf_listener')# Initialize the ROS node.

    listener = tf.TransformListener()# Initialize a TF listener.

    rospy.wait_for_service('spawn')
    # Call the service to create another turtle named turtle2.
    spawner = rospy.ServiceProxy('spawn', turtlesim.srv.Spawn)
    spawner(8, 6, 0, 'turtle2')
    # Declare a publisher for turtle2 velocity.
    turtle_vel = rospy.Publisher('turtle2/cmd_vel', geometry_msgs.msg.Twist,queue_size=1)

    rate = rospy.Rate(10.0)
    while not rospy.is_shutdown():
        try:
            # Look up the TF transform between turtle2 and turtle1.
            (trans,rot) = listener.lookupTransform('/turtle2', '/turtle1', rospy.Time(0))
        except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
            continue
        # Compute linear and angular velocity, then publish them.
        angular = 6.0 * math.atan2(trans[1], trans[0])
        linear = 0.8 * math.sqrt(trans[0] ** 2 + trans[1] ** 2)
        cmd = geometry_msgs.msg.Twist()
        cmd.linear.x = linear
        cmd.angular.z = angular
        turtle_vel.publish(cmd)
        rate.sleep()
  1. Projectstroomschema

Demonstratie van opstarten en werking

  1. Voorbereiding van een launch-document

Maak in de packagedirectory een nieuwe map launch aan, ga naar launch, maak een nieuw launch-bestand aan genaamd start_tf_demo_py.launch, en kopieer het volgende erin:

Code file: 7-2-1-10-publishing-and-listening-with-tf-py.xml

xml
<launch>

    <!-- turtlesim node-->
    <node pkg="turtlesim" type="turtlesim_node" name="sim"/>
    <!-- broadcast turtle1 -> world -->
    <node name="turtle1_tf_broadcaster" pkg="learning_tf" type="turtle_tf_broadcaster.py" respawn="false" output="screen" >
      <param name="turtle" type="string" value="turtle1" />
    </node>
    <!-- broadcast turtle2 -> world -->
    <node name="turtle2_tf_broadcaster" pkg="learning_tf" type="turtle_tf_broadcaster.py" respawn="false" output="screen" >
      <param name="turtle" type="string" value="turtle2" />
    </node>
    <!--listener-->
    <node pkg="learning_tf" type="turtle_tf_listener.py" name="listener" />
    <!--turtle keyboard control node-->
    <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
</launch>
  1. Starten
bash
roslaunch learning_tf start_tf_demo_py.launch

Wanneer de toepassing draait, klik je met de muis in het venster waarin launch draait, druk je op de pijltjestoets, en beweegt turtle2 mee met turtle1.

  1. De werking komt in grote lijnen overeen met C++

Figures

7.2.1.10 Publishing and Listening with TF figure 1

7.2.1.10 Publishing and Listening with TF figure 2

7.2.1.10 Publishing and Listening with TF figure 3

7.2.1.10 Publishing and Listening with TF figure 4

7.2.1.10 Publishing and Listening with TF figure 5

7.2.1.10 Publishing and Listening with TF figure 6

7.2.1.10 Publishing and Listening with TF figure 7