Skip to content

Commit 11747d4

Browse files
committed
Revise L06
1 parent a209bea commit 11747d4

File tree

13 files changed

+282
-42
lines changed

13 files changed

+282
-42
lines changed

06_EinfuehrungROS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ https://fkromer.github.io/awesome-ros2/
243243

244244
**Node** - Ein Knoten ist Teilnehmer im ROS-Graphen. ROS-Knoten verwenden eine ROS-Clientbibliothek, um mit anderen Knoten zu kommunizieren. Knoten können ein *Subject* veröffentlichen oder abonnieren. *Nodes* können auch einen Dienst bereitstellen oder verwenden. Einem Knoten sind konfigurierbare Parameter zugeordnet. Verbindungen zwischen Knoten werden durch einen verteilten Erkennungsprozess hergestellt. Knoten können sich im selben Prozess, in unterschiedlichen Prozessen oder auf unterschiedlichen Rechnern befinden.
245245

246+
**Messages** - To enable the communication of data packets between the nodes, their structure and content format must be specified. Which data formats are used, where is the sending sensor located, which units of measurement represent the information? ROS defines abstract message types for this purpose.
247+
246248
![RoboterSystem](./image/06_EinfuehrungROS/rosgraph.png)<!-- width="100%" -->
247249
*Screenshot der Knoten eines umfangreicheren Projektes. Die Ellipsen repräsentieren die Knoten, die rechteckigen Boxen die "Datenkanäle" dazwischen.*
248250

@@ -666,8 +668,7 @@ Auf der ROS2 Seite nutzen wir nun das [image_tools](https://github.com/ros2/demo
666668
#ros2 run image_tools showimage # zum testen
667669
```
668670

669-
Im folgenden wird ein Knoten aus dem Paket [opencv_app](http://wiki.ros.org/opencv_apps) aktiviert, dass für die Erkennung der Gesichter verantwortlich ist. Diese ist
670-
bisher nicht für ROS2 verfügbar.
671+
Im folgenden wird ein Knoten aus dem Paket [opencv_app](http://wiki.ros.org/opencv_apps) aktiviert, dass für die Erkennung der Gesichter verantwortlich ist. Diese war für die verwendete Version `foxy` im November 2021 nicht für ROS2 verfügbar.
671672

672673
```bash ConsoleD
673674
# ROS1_Shell die ein Kamerasignal erfasst

07_ROS_Pakete.md

Lines changed: 120 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,28 @@ import: https://github.com/liascript/CodeRunner
3131

3232
## ROS2 auf mehreren Rechnern
3333

34+
> Frage: Was hat es mit dem folgenden Befehl auf sich, den Sie in den Übungen verwenden?
35+
36+
```bash
37+
export ROS_DOMAIN_ID=5
38+
```
39+
40+
{{1-2}}
41+
********************************************
42+
43+
> This number will be used as the DDS Domain ID, which is a number that identifies the DDS domain to which the nodes belong. Nodes in the same domain can communicate with each other, while nodes in different domains cannot. The default value is 0, which means that all nodes belong to the same domain. This is fine if you are running all nodes on the same machine, but if you want to run nodes on different machines, you need to use different domain IDs for each machine.
44+
45+
3446
Während die Organisation von Anwendungen über mehreren Rechnern unter ROS1 aufwändiger war, ist die Realsierung in ROS2 außerordentlich einfach.
3547

36-
https://roboticsbackend.com/ros2-multiple-machines-including-raspberry-pi/
48+
vgl. Beschreibung der Einrichtung einer Verbindung zwischen einem RaspberryPi und einem Entwicklungsrechner [Link](https://roboticsbackend.com/ros2-multiple-machines-including-raspberry-pi/)
49+
3750

51+
********************************************
3852

3953
## Konzept
4054

41-
ROS1 und 2 sind in Pakten organisiert, diese kann man als Container für zusammengehörigen Code betrachten.
55+
ROS1 und 2 sind in Paketen organisiert, diese kann man als Container für zusammengehörigen Code betrachten.
4256

4357
Softwareengineering Ziele:
4458

@@ -127,7 +141,7 @@ Für ein ganzes Set von Paketen ist deutlich mehr Aufwand erforderlich:
127141

128142
+ Voraussetzungen:
129143

130-
+ Verfügbarkeit von System-Abhängigkeiten (abhängige Pakete) [rosdep](http://wiki.ros.org/rosdep/Tutorials/How%20to%20add%20a%20system%20dependency)
144+
+ Verfügbarkeit von System-Abhängigkeiten (abhängige Pakete)
131145
+ Setzen der notwendigen Umgebungsvariablen
132146

133147
+ Eingabe: Code und Konfigurationsdateien der Pakete
@@ -141,13 +155,13 @@ Für ein ganzes Set von Paketen ist deutlich mehr Aufwand erforderlich:
141155

142156
> **Merke:** ROS / ROS2 umfasst eine Fülle von Tools für die Organisation dieses Prozesses.
143157
144-
## Realisierung eines eigenen Paketes
158+
## Realisierung eigener Pakete
145159

146160
Wir wollen die Funktionalität der `minimal_subscriber`/`minimal_publisher` Beispiel erweitern und einen neuen Knoten implementieren, der den Zählwert nicht als Bestandteil eines strings kommuniziert sondern als separaten Zahlenwert.
147161

148162
Sie finden den Beispielcode im Repository dieses Kurses unter [Link](https://github.com/TUBAF-IfI-LiaScript/VL_SoftwareprojektRobotik/tree/master/examples/07_ROS_Pakete/src)
149163

150-
**Stufe 1: Individuelles Msg-Format**
164+
### Stufe 1: Individuelles Msg-Format
151165

152166
```
153167
> ros2 pkg create my_msg_package --build-type ament_cmake --dependencies rclcpp std_msgs
@@ -230,7 +244,11 @@ my_msg_package/msg/MyMsg
230244
> ros2 topic echo /tralla
231245
```
232246

233-
**Schritt 2: Integration einer Methode**
247+
Einen guten Überblick zur Behandlung von eigenen Datentypen im originären Paket oder aber in einem anderen bietet:
248+
249+
https://github.com/ros2/rosidl
250+
251+
### Schritt 2: Integration in einen C++ Knoten
234252

235253
Nunmehr wollen wir die neu definierte Nachricht auch in einem Node verwenden.
236254
Entsprechend nutzen wir den `minimal_publisher` Beispiel aus der vergangenen Vorlesung und ersetzen die `String` Message gegen unsere `My_Msg` Implementierung. Dafür muss für den Knoten eine Abhängigkeit zum Paket `my_msg_package` spezifiziert werden. Dies kann während der Inititalisierung des Paketes oder im Anschluss anhand der 'package.xml' und 'CMakeList.txt' erfolgen. Schauen Sie sich noch mal die Definition der Abhängigkeiten in unserem `my_msg_package` an.
@@ -337,7 +355,7 @@ class MinimalPublisher : public rclcpp::Node
337355
MinimalPublisher()
338356
: Node("minimal_publisher"), count_(0)
339357
{
340-
publisher_ = this->create_publisher<my_msg_package::msg::MyMsg>("topic", 10);
358+
publisher_ = this->create_publisher<my_msg_package::msg::MyMsg>("interestingTopic", 10);
341359
timer_ = this->create_wall_timer(
342360
500ms, std::bind(&MinimalPublisher::timer_callback, this));
343361
}
@@ -364,12 +382,100 @@ class MinimalPublisher : public rclcpp::Node
364382
}
365383
```
366384

367-
Nach einem weiteren Build-Prozess können wir das Paket nun im erwarteten Funktionsumfang starten.
368-
Einen guten Überblick zur Behandlung von eigenen Datentypen im originären Paket oder aber in einem anderen bietet:
385+
### Schritt 3: Integration in einen Python Knoten
386+
387+
Die Integration in einen Python Knoten ist analog zu der C++ Implementierung. Wir nutzen das `minimal_subscriber` Beispiel aus der vergangenen Vorlesung und ersetzen die `String` Message gegen unsere `My_Msg` Implementierung. Dafür muss für den Knoten eine Abhängigkeit zum Paket `my_msg_package` spezifiziert werden. Dies kann während der Inititalisierung des Paketes oder im Anschluss anhand der 'package.xml' und 'CMakeList.txt' erfolgen. Schauen Sie sich noch mal die Definition der Abhängigkeiten in unserem `my_msg_package` an.
388+
389+
```bash
390+
> ros2 pkg create --build-type ament_python my_py_subscriber --node-name py_subscriber --dependencies rclpy std_msgs my_msg_package
391+
going to create a new package
392+
package name: my_py_subscriber
393+
destination directory: /home/sebastian/Desktop/Vorlesungen/WiSe_2023-24/VL_SoftwareprojektRobotik/examples/07_ROS_Pakete/src
394+
package format: 3
395+
version: 0.0.0
396+
description: TODO: Package description
397+
maintainer: ['sebastian <[email protected]>']
398+
licenses: ['TODO: License declaration']
399+
build type: ament_python
400+
dependencies: ['rclpy', 'std_msgs', 'my_msg_package']
401+
node_name: py_subscriber
402+
creating folder ./my_py_subscriber
403+
creating ./my_py_subscriber/package.xml
404+
creating source folder
405+
creating folder ./my_py_subscriber/my_py_subscriber
406+
creating ./my_py_subscriber/setup.py
407+
creating ./my_py_subscriber/setup.cfg
408+
creating folder ./my_py_subscriber/resource
409+
creating ./my_py_subscriber/resource/my_py_subscriber
410+
creating ./my_py_subscriber/my_py_subscriber/__init__.py
411+
creating folder ./my_py_subscriber/test
412+
creating ./my_py_subscriber/test/test_copyright.py
413+
creating ./my_py_subscriber/test/test_flake8.py
414+
creating ./my_py_subscriber/test/test_pep257.py
415+
creating ./my_py_subscriber/my_py_subscriber/py_subscriber.py
416+
```
417+
418+
> In dem wir den Node-Namen explizit angeben, wird dieser auch in der `setup.py` Datei integriert. Dies ist notwendig, da Python Pakete nicht über einen `main` Eintrag gestartet werden.
419+
420+
```python setup.py
421+
entry_points={
422+
'console_scripts': [
423+
'py_subscriber = my_py_subscriber.py_subscriber:main'
424+
],
425+
},
426+
```
427+
428+
Die eigentliche Implementierung gestaltet sich dann wie folgt:
429+
430+
```python py_subscriber.py
431+
import rclpy
432+
from rclpy.node import Node
433+
434+
from std_msgs.msg import String
435+
from my_msg_package.msg import MyMsg # Include the new message type
436+
437+
class MinimalSubscriber(Node):
438+
439+
def __init__(self):
440+
super().__init__('minimal_subscriber')
441+
self.subscription = self.create_subscription(
442+
MyMsg, # Adapting message type
443+
'/interestingTopic', # Adapting topic name
444+
self.listener_callback,
445+
10)
446+
self.subscription # prevent unused variable warning
369447

370-
https://index.ros.org/doc/ros2/Tutorials/Rosidl-Tutorial/
448+
def listener_callback(self, msg):
449+
self.get_logger().info(f'I heard: "{msg.counter} - {msg.comment}')
371450

372451

452+
def main(args=None):
453+
rclpy.init(args=args)
454+
455+
minimal_subscriber = MinimalSubscriber()
456+
457+
rclpy.spin(minimal_subscriber)
458+
minimal_subscriber.destroy_node()
459+
rclpy.shutdown()
460+
461+
if __name__ == '__main__':
462+
main()
463+
```
464+
465+
Werfen wir noch einen kurzen Blick auf die Abhängigkeiten, die `colcon` nunmehr erkennt.
466+
467+
```text @plantUML.png
468+
@startuml
469+
digraph graphname {
470+
"my_tutorial_package";
471+
"my_py_subscriber";
472+
"my_msg_package";
473+
"my_tutorial_package" -> "my_msg_package" [color="#0000ff:#ff0000"];
474+
"my_py_subscriber" -> "my_msg_package" [color="#0000ff:#ff0000"];
475+
}
476+
@enduml
477+
```
478+
373479
## Aufzeichnen des Prozesses
374480

375481
Die Aufzeichnung von ganzen Datensätzen ist einer der zentralen Debug-Techniken
@@ -441,31 +547,6 @@ Unter anderem sollten zwei Fehlkonfigurationen vermieden werden:
441547

442548
Ein Lösungsansatz ist die zeitliche Filterung der Informationen, in dem zum Beispiel nur jede 10te Nachricht gespeichert wird. Dies wiederum kann dann aber einen Einfluss auf das Verhalten des Algorithmus haben!
443549

444-
An dieser Stelle wird schon deutlich, wie der unter ROS1 erreichte
445-
Komfort noch nicht unter ROS2 realisiert ist. Das `rosbag` Tool unter ROS1 erreicht
446-
ein weit größeres Spektrum an Konfigurierbarkeit.
447-
448-
!?[rosbagInfo](https://www.youtube.com/watch?time_continue=70&v=pwlbArh_neU&feature=emb_logo)<!-- height="500px" width="800px" -->
449-
450-
Beispiel des Einsatzes eines Bagfiles anhand der Scan-Daten im deutschen Museum in München [Link](https://google-cartographer-ros.readthedocs.io/en/latest/demos.html).
451-
Laden Sie einen zugehörigen Datensatz mittels
452-
453-
```
454-
wget -P ~/Downloads https://storage.googleapis.com/cartographer-public-data/bags/backpack_2d/b2-2016-04-05-14-44-52.bag
455-
```
456-
457-
auf Ihren Rechner. Es handelt sich dabei um ein ROS1-bagfile!
458-
459-
1. Visualisierung der Dateninhalte mittels `rqt_bag`. Zuvor sollten Sie die notwendigen `rqt_bag_plugins` installieren.
460-
2. Starten des Bagfiles unter `ros2` mittels eines Plugins für die Konvertierung
461-
462-
```
463-
> source /opt/ros/noetic/setup.zsh
464-
> source /opt/ros/foxy/setup.zsh
465-
ROS_DISTRO was set to 'noetic' before. Please make sure that the environment does not mix paths from different distributions.
466-
> ros2 bag play -s rosbag_v2 b2-2016-04-05-14-44-52.bag
467-
```
468-
469550
## Steuerung des Startprozesses
470551

471552
Sie sehen, dass wir immer weitere Konsolen öffnen, um einzelne Knoten zu starten
@@ -602,7 +683,7 @@ export RCUTILS_COLORIZED_OUTPUT=0 # 1 for forcing it or, on Windows:
602683
rcutils_logging_set_logger_level("logger_name", RCUTILS_LOG_SEVERITY_DEBUG);
603684
```
604685
605-
Das Loggingsystem unter ROS2 bildet folgende Makros ab, die in der rclcpp API enthalten sind ([Link](http://docs.ros2.org/latest/api/rclcpp/logging_8hpp.html)). Dabei zielt die Neuimplementierung aber darauf ab, ein Interface zu definieren, das es erlaubt Logging-Bibliotheken allgemein einzubetten:
686+
Das Loggingsystem unter ROS2 bildet folgende Makros ab, die in der rclcpp API enthalten sind ([Link](http://docs.ros2.org/latest/api/rclcpp/logging_8hpp.html)). Dabei zielt die Neuimplementierung aber darauf ab, ein Interface zu definieren, das es erlaubt Logging-Bibliotheken allgemein einzubetten:
606687
607688
| Makro | Signatur | Bedeutung |
608689
| --------------------- | ----------------------- | -------------------------------------------------------------------------------------------------- |
@@ -640,13 +721,13 @@ Das wichtigste Konzept dieses Dokuments ist, dass ein verwalteter Knoten eine be
640721

641722
ROS2 definiert vier Zustände `Unconfigured`, `Inactive`, `Active`, `Finalized` und insgesamt 7 Transitionen.
642723

643-
![STL Container](./image/07_ROSPakete/life_cycle_sm.png)<!-- width="100%" -->
644-
Autor: Geoffrey Biggs Tully Foote, https://design.ros2.org/articles/node_lifecycle.html
724+
![STL Container](./image/07_ROSPakete/life_cycle_sm.png "Autor: Geoffrey Biggs Tully Foote, https://design.ros2.org/articles/node_lifecycle.html")
725+
645726

646727
Für die Interaktion mit einem *managed node* stehen Ihnen unterschiedlichen Möglichkeiten offen. Auf der Kommandozeile kann zwischen den States mittels
647728

648729
```
649730
ros2 lifecycle set /nodename X #State Id
650731
```
651732

652-
gewechselt werden. Komfortabler ist die Spezifikation in den launch-Files. Ein Beispiel für die entsprechend Realisierung findet sich unter folgendem [Link](https://www.stereolabs.com/docs/ros2/lifecycle/#the-life-cycle-state-machine)
733+
gewechselt werden. Komfortabler ist die Spezifikation in den launch-Files.

examples/07_ROS_Pakete/src/my_msg_package/package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
<build_depend>builtin_interfaces</build_depend>
1616
<build_depend>rosidl_default_generators</build_depend>
17+
1718
<exec_depend>builtin_interfaces</exec_depend>
1819
<exec_depend>rosidl_default_runtime</exec_depend>
1920

examples/07_ROS_Pakete/src/my_py_subscriber/my_py_subscriber/__init__.py

Whitespace-only changes.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import rclpy
2+
from rclpy.node import Node
3+
4+
from std_msgs.msg import String
5+
from my_msg_package.msg import MyMsg # Include the new message type
6+
7+
class MinimalSubscriber(Node):
8+
9+
def __init__(self):
10+
super().__init__('minimal_subscriber')
11+
self.subscription = self.create_subscription(
12+
MyMsg, # Adapting message type
13+
'/interestingTopic', # Adapting topic name
14+
self.listener_callback,
15+
10)
16+
self.subscription # prevent unused variable warning
17+
18+
def listener_callback(self, msg):
19+
self.get_logger().info(f'I heard: "{msg.counter} - {msg.comment}')
20+
21+
22+
def main(args=None):
23+
rclpy.init(args=args)
24+
25+
minimal_subscriber = MinimalSubscriber()
26+
27+
rclpy.spin(minimal_subscriber)
28+
minimal_subscriber.destroy_node()
29+
rclpy.shutdown()
30+
31+
if __name__ == '__main__':
32+
main()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
<name>my_py_subscriber</name>
5+
<version>0.0.0</version>
6+
<description>TODO: Package description</description>
7+
<maintainer email="[email protected]">sebastian</maintainer>
8+
<license>TODO: License declaration</license>
9+
10+
<depend>rclpy</depend>
11+
<depend>std_msgs</depend>
12+
<depend>my_msg_package</depend>
13+
14+
<test_depend>ament_copyright</test_depend>
15+
<test_depend>ament_flake8</test_depend>
16+
<test_depend>ament_pep257</test_depend>
17+
<test_depend>python3-pytest</test_depend>
18+
19+
<export>
20+
<build_type>ament_python</build_type>
21+
</export>
22+
</package>

examples/07_ROS_Pakete/src/my_py_subscriber/resource/my_py_subscriber

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[develop]
2+
script_dir=$base/lib/my_py_subscriber
3+
[install]
4+
install_scripts=$base/lib/my_py_subscriber
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from setuptools import find_packages, setup
2+
3+
package_name = 'my_py_subscriber'
4+
5+
setup(
6+
name=package_name,
7+
version='0.0.0',
8+
packages=find_packages(exclude=['test']),
9+
data_files=[
10+
('share/ament_index/resource_index/packages',
11+
['resource/' + package_name]),
12+
('share/' + package_name, ['package.xml']),
13+
],
14+
install_requires=['setuptools'],
15+
zip_safe=True,
16+
maintainer='sebastian',
17+
maintainer_email='[email protected]',
18+
description='TODO: Package description',
19+
license='TODO: License declaration',
20+
tests_require=['pytest'],
21+
entry_points={
22+
'console_scripts': [
23+
'py_subscriber = my_py_subscriber.py_subscriber:main'
24+
],
25+
},
26+
)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright 2015 Open Source Robotics Foundation, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from ament_copyright.main import main
16+
import pytest
17+
18+
19+
# Remove the `skip` decorator once the source file(s) have a copyright header
20+
@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
21+
@pytest.mark.copyright
22+
@pytest.mark.linter
23+
def test_copyright():
24+
rc = main(argv=['.', 'test'])
25+
assert rc == 0, 'Found errors'

0 commit comments

Comments
 (0)