first test push

This commit is contained in:
winterhalderp 2021-01-13 15:28:01 +01:00
commit c98972e4a9
21 changed files with 1156 additions and 0 deletions

4
Links.md Normal file
View File

@ -0,0 +1,4 @@
# Links for during ROS Workshop
ros_tutorials: https://github.com/ros/ros_tutorials.git

50
README.md Normal file
View File

@ -0,0 +1,50 @@
# pubsub - Publisher & Subscriber Package
Publisher & Subscriber package as template package and source code.
Created for ROS Workshop 2020
Roverentwicklung für Explorationsaufgaben
Institute for Space Systems
University of Stuttgart.
Created by Patrick Winterhalder,
[IRS](https://www.irs.uni-stuttgart.de/en/), University of Stuttgart.
## Workshop Prerequisites
* Install [Ubuntu 20.04]()
* Install Visual Studio Code using [Ubuntu Software](https://wiki.ubuntuusers.de/Ubuntu_Software/)
* Install [Git](https://linuxconfig.org/how-to-install-git-on-ubuntu-20-04-lts-focal-fossa-linux) (no account required yet)
* Install [ROS2](https://index.ros.org/doc/ros2/Installation/Foxy/Linux-Install-Debians/) ("desktop" on PC, "base" on Raspberry Pi). Do install _argcomplete_, no need for _ROS 1 bridge_ or _RMW implementations_.
* Install and update _rosdep_:
* `sudo apt install python3-rosdep2 -y`
* `rosdep update`
* (`sudo rosdep init`)
* Work through ["Beginner: CLI Tools"](https://index.ros.org/doc/ros2/Tutorials/) tutorial
* [Configuring your ROS 2 environment](https://index.ros.org/doc/ros2/Tutorials/Configuring-ROS2-Environment/):
* Source setup files (underlay, overlay)
* Configure .bashrc (shell startup script)
* Add colcon_cd to .bashrc (shell startup script)
* Check environment variables (check for correct installation)
* Configure ROS_DOMAIN_ID (DDS Network Number)
* Cover turtlesim, rqt, topics, services, actions
## Install Instructions
* Move to colcon workspace: `cd <workspace_path>`
* Clone repository: `git clone git://github.com/patrickw135/pubsub.git .` (include the . at the end)
* Build workspace: `colcon build`
__Note:__ Only the files inside _src/_ are of importance.
* Remember to run `source ~/<workspace_path>/install/local_setup.sh` after every build. Best would be to [add this command to _.bashrc_](https://github.com/patrickw135/pubsub/blob/master/bashrc_addons.txt) which is run everytime you start a new console.
## During Workshop
* Create workspace:
* Install [_colcon_](https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/#colcon): `sudo apt install python3-colcon-common-extensions -y`
* Create packag inside _~/{workspace_name}/src_:
* `ros2 pkg create --build-type [ament_cmake, ament-python] <package_name>`
* Go back up one layer: `cd ..`
* Build workspace: `colcon build --symlink-install`
* Add [this](https://github.com/patrickw135/pubsub/blob/main/bashrc_addons.txt) to end of .bashrc (`sudo nano .bashrc`), find all instances of "`~/ws_overlay_foxy`" and replace it with your local path to your colcon workspace
* [Instruction](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md) on how to create a custom message to interface between nodes

73
bashrc_addons.bash Normal file
View File

@ -0,0 +1,73 @@
# Add these lines to .bashrc, do this:
# Add the the following in between "#----" to the end of your .bashrc script
# To do this open .bashrc:
# sudo nano ~/.bashrc
# Move to end of .bashrc (arrow down)
# Highlight the lines below and copy (Ctrl+C)
# Paste into console at the end of .bashrc (Right click, paste or CTRL+LShift+V)
#----------------------------------------------------------------------------------
# ROS 2 SETUP
echo "ROS 2 Setup:"
# Source Underlay
cmd="/opt/ros/foxy/setup.bash"
source $cmd
echo "ROS Underlay: "$cmd
# Source Overlay
# Change to the path of your workspace's "install/local_setup.bash" file
cmd="$HOME/colcon_ws/install/local_setup.bash"
source $cmd
echo "ROS Overlay: "$cmd
# You can add other workspaces
#cmd="$HOME/colcon_libs/install/local_setup.bash"
#source $cmd
#echo "ROS Overlay: "$cmd
echo "******************************************************"
# ROS 2 Settings
# Print ROS Variables
#printenv | grep -i ROS
echo "ROS_VERSION: "$ROS_VERSION
echo "ROS_PYTHON_VERSION: "$ROS_PYTHON_VERSION
echo "ROS_DISTRO: "$ROS_DISTRO
echo "******************************************************"
# Define DDS Channel
# (this is the channel your ROS system communicates at over your local network)
# To change the channel open the ROS Underlay setup file:
# sudo nano /opt/ros/foxy/setup.bash
# Here, add the following command
# export ROS_DOMAIN_ID=<your_channel_nr>
# eg.: export ROS_DOMAIN_ID=69
# Important: no spaces before and after "=" (in bash)
# Print DDS Settings
echo "ROS 2 DDS Settings:"
export ROS_DOMAIN_ID=69
echo "ROS_DOMAIN_ID: "$ROS_DOMAIN_ID
echo "To change: sudo nano /opt/ros/foxy/setup.bash"
echo "******************************************************"
# Source colcon directory jump:
#source /usr/share/colcon_cd/function/colcon_cd.sh
#export _colcon_cd_root=~/colcon_ws
# Print active Node & Topics
echo "ROS 2 Topics active:"
ros2 topic list
echo "******************************************************"
#echo "ROS 2 Nodes active:"
#ros2 node list
echo "******************************************************"
printf "\n"
echo "If your package is not listed (ros2 pkg list):"
echo " * make sure you are sourcing the correct workspace: .bashrc"
echo " * delete build/, install/ and rebuild w/o errors"
#----------------------------------------------------------------------------------

View File

@ -0,0 +1,186 @@
# How to create custom ROS topics
This is a short instruction on how to create custom interfaces using ROS topics.
For this at least two packages will be required:
* The python package contains your scripts (eg. ROS nodes)
* The CMake package contains the custom msg/srv/act files
The CMake package is required because the custom msg/srv/act files cannot be created inside the python package as this is not supported yet.
__Table of Content__
* [CMake Package (eg. /pubsub_msg)](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#cmake-package-eg-pubsub_msg)
* [Create CMake Package](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#1-create-cmake-package)
* [Create Message Files](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#2-create-message-files)
* [Configure CMakeLists.txt](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#3-configure-cmakeliststxt)
* [Configure package.xml](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#4-configure-packagexml)
* [Build Package](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#5-build-package)
* [Source newly built workspace](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#6-source-newly-built-workspace)
* [Check functionality](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#7-check-functionality)
* [Python Package (eg. /pubsub)](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#python-package-eg-pubsub)
* [Create Python Package](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#1-create-python-package)
* [Write Python Scripts](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#2-write-python-scripts)
* [Configure package.xml](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#3-configure-packagexml)
* [Build Package](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#4-build-package)
* [Source newly built workspace](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#5-source-newly-built-workspace)
* [Run scripts](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#6-run-scripts)
* [Sources](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#sources)
## CMake Package (eg. /pubsub_msg)
This package makes up the basis for custom ROS interfaces and contains all custom msg/srv/act files. Additionally, the special files (_CMakeLists.txt_ and _package.xml_) describe how these interface files are to be used.
This package must be created as a CMake package: `ros2 pkg create --build-type ament-cmake <package_name>`
This will result in an empty package structure:
* msg/srv/act directory:
* This directory contains the custom msg files (eg. CustomMsg1.msg)
* CMakeLists.txt:
* This file describes how to build this package
* Configure this file according to this [instruction](https://index.ros.org/doc/ros2/Tutorials/Custom-ROS2-Interfaces/#cmakelists-txt)
* package.xml:
* This file contains meta information about this package
* Configure this file according to this [instruction](https://index.ros.org/doc/ros2/Tutorials/Custom-ROS2-Interfaces/#package-xml)
### 1. Create CMake Package
* Move to your colcon workspace's src directory: `cd <workspace_path>/src`
* (For example: `cd ~/colcon_ws/src`)
* Create CMake package: `ros2 pkg create --build-type ament_cmake <package_name>
* (Here: `ros2 pkg create --build-type ament_cmake pubsub_msg`)
### 2. Create Message Files
* If not already available create msg directory inside package directory.
Resulting structure: <workspace_name>/src/<package_name>/msg
* Move to newly created msg direcrory
* Create your own custom message files, eg. _CustomMsg1.msg_.
Give your files comprehensible names, eg. _Epossetvalues.msg_
### 3. Configure CMakeLists.txt
Open CMakeLists.txt and ad these lines before `if(BUILD_TESTING)`:
`find_package(builtin_interfaces REQUIRED)`
`find_package(rosidl_default_generators REQUIRED)`
`find_package(std_msgs REQUIRED)`
`find_package(rclcpp REQUIRED)`
Then also add custom lines depending your package, here the custom message/service files are added:
`rosidl_generate_interfaces(${PROJECT_NAME}`
` "msg/CustomMsg1.msg"`
` "msg/CustomMsg2.msg"`
` DEPENDENCIES builtin_interfaces`
` )`
### 4. Configure package.xml
In order to let the build system know what this package depends on add these lines to _package.xml_:
```xml
<!-- ADD THESE LINES: START HERE -->
<build_depend>builtin_interfaces</build_depend>
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>builtin_interfaces</exec_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
<!-- END HERE -->
```
### 5. Build Package
* Move back to the workspace's most top layer: `cd ~/<workspace_path>`
* Build workspace: `colcon build`
* Sucessful response:
_Starting >>> pubsub
Starting >>> pubsub_msg
Finished <<< pubsub [0.85s]
Finished <<< pubsub_msg [1.09s]
Summary: 2 packages finished [1.56s]_
### 6. Source newly built workspace
* Run: `source ~/<workspace_path>/install/local_setup.bash`
* If you already [updated your .bashrc file](https://github.com/patrickw135/pubsub/blob/master/bashrc_addons.txt) you can close all open consoles and start a new console (Ctrl+Alt+T). This will source your workspace automatically, as .bashrc is run every time you start a console.
__Important__: If you use multiple workspaces make sure you have the wanted workspace defined in .bashrc! Otherwise the changes introduced when building will not be available.
### 7. Check functionality
Check functionality of your messages by creataing a topic using your newly created message:
* CustomMsg1:
`ros2 topic pub /chatter1 pubsub_msg/CustomMsg1 "{temperature: {24.1234, 25.9876}, pressure: {1012.556, 1013.987}, humidity: {0.002, 0.001}}" --rate 1`
* Response:
_publisher: beginning loop
publishing #1: pubsub_msg.msg.CustomMsg1(temperature=[24.12339973449707, 25.987600326538086], pressure=[1012.5560302734375, 1013.9869995117188], humidity=[0.0020000000949949026, 0.0010000000474974513])..._
* CustomMsg2:
`ros2 topic pub /chatter2 pubsub_msg/CustomMsg2 "{pitch_ctrl: 33.33, yaw_ctrl: 0.5}" --rate 1`
* Response:
_publisher: beginning loop
publishing #1: pubsub_msg.msg.CustomMsg2(pitch_ctrl=33.33, yaw_ctrl=0.5)..._
## Python Package (eg. /pubsub)
This package contains your scripts, programs and libraries. After building the workspace (`colcon build`) the custom messages are available to all other packages.
This package can be created as a CMake (C++) package or as a python package depending on your coding preference.
* C++: `ros2 pkg create --build-type ament-cmake <package_name>`
* Python: `ros2 pkg create --build-type ament-python <package_name>`
<package_name> directory:
* This directory contains your python scripts (eg. listener.py)
* Also place the non-standard libraries in this directory and import the library in your python scripts
### 1. Create Python Package
* Move to your workspace's source directory: `cd <workspace_path>/src`
* Create python package: `rps2 pkg create --build-type ament_python <pkg_name>`
### 2. Write Python Scripts
When using custom interfaces in python scripts these must be imported into the python script first
```python
from <package_name>.msg import <message_name>
```
replacing `<package_name>` with the package containing the custom message and `<message_name>` with the message file name (excluding the file ending .msg).
However, in order to be able to import the custom message types, `<message_name>` must first be known to the ROS system. This was established when creating the [CMake package](https://github.com/patrickw135/pubsub/blob/master/instructions_custom_topics.md#cmake-package-eg-pubsub_msg) containing the custom message. Additionally, you must add this dependency to the _package.xml_ of this package as stated in the next chapter.
### 3. Configure package.xml
In addition to importing the message type into your python script you must also configure _package.xml_ adding the package dependency of where you inherite the custom message from. Add this line to _package.xml_:
```xml
<depend>std_msgs</depend>
<!-- CUSTOM LINE -->
<!-- This is custom for the package you depend on -->
<exec_depend>package_name</exec_depend>
```
exchanging _package_name_ with the source package of the custom message type (here _pubsub_msg_), e.g.:
```xml
<depend>std_msgs</depend>
<!-- CUSTOM LINE -->
<!-- This is custom for the package you depend on -->
<exec_depend>pubsub_msg</exec_depend>
```
### 4. Build Package
Now you can build the Python package.
* Move to your workspace's root: `cd ~/<workspace_path>`
* Build workspace: `colcon build --symlink-install`
### 5. Source newly built workspace
* Run: `source ~/<workspace_path>/install/local_setup.bash`
* If you already [updated your .bashrc file](https://github.com/patrickw135/pubsub/blob/master/bashrc_addons.txt) you can close all open consoles and start a new console (Ctrl+Alt+T). This will source your workspace automatically, as .bashrc is run every time you start a console.
__Important__: If you use multiple workspaces make sure you have the wanted workspace defined in .bashrc! Otherwise the changes introduced when building will not be available.
### 6. Run scripts
* Run Talker: `ros2 run pubsub talker`
* Run Listener: `ros2 run pubsub listener`
The talker console should print the sent data while the listener console should print the received data. These should match.
If anything is unclear, compare this instruction material to the files in `/pubsub` and `/pubsub_msg`.
## Sources
[ROS2 Tutorial](https://index.ros.org/doc/ros2/Tutorials/Custom-ROS2-Interfaces/#creating-custom-ros-2-msg-and-srv-files)
[theconstructsim custom messages](https://www.theconstructsim.com/ros2-tutorials-7-how-to-create-a-ros2-custom-message-new/)

125
rpi_install.bash Normal file
View File

@ -0,0 +1,125 @@
# Go through these bash commands one by one
# Prerequisite is a fresh Ubuntu 20.04 !! 64bit !! OS
# Only the password was changed after first boot
# Everything else is stock
# First Update
sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get dist-upgrade -y
# Install ROS2 Foxy Fitzroy Desktop
sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
sudo apt update && sudo apt install -y curl gnupg2 lsb-release
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
sudo sh -c 'echo "deb [arch=$(dpkg --print-architecture)] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2-latest.list'
sudo apt update && sudo apt install -y ros-foxy-ros-base
source /opt/ros/foxy/setup.bash
# Check to see if installed correctly
echo "ROS_VERSION: "$ROS_VERSION
# Install rosdep
sudo apt install -y python3-rosdep2
rosdep update
# Install colcon
sudo apt install -y python3-colcon-common-extensions
# Install pip and argcomplete
sudo apt install -y python3-pip
pip3 install -U argcomplete
# in between
sudo apt update && sudo apt upgrade -y
# Create workspace
mkdir -p ~/colcon_ws/src
cd colcon_ws
colcon build
# Configure .bashrc:
# The the following in between "#----" to the end of your .bashrc script
#----------------------------------------------------------------------
# ROS 2 SETUP
echo "ROS 2 Setup:"
# Source Underlay
cmd="/opt/ros/foxy/setup.bash"
source $cmd
echo "ROS Underlay: "$cmd
# Source Overlay
# Change to the path of your workspace's "install/local_setup.bash" file
cmd="$HOME/colcon_ws/install/local_setup.bash"
source $cmd
echo "ROS Overlay: "$cmd
# You can add other workspaces
#cmd="$HOME/colcon_libs/install/local_setup.bash"
#source $cmd
#echo "ROS Overlay: "$cmd
echo "******************************************************"
# ROS 2 Settings
# Print ROS Variables
#printenv | grep -i ROS
echo "ROS_VERSION: "$ROS_VERSION
echo "ROS_PYTHON_VERSION: "$ROS_PYTHON_VERSION
echo "ROS_DISTRO: "$ROS_DISTRO
export ROS_DOMAIN_ID=69
echo "ROS_DOMAIN_ID: "$ROS_DOMAIN_ID
echo "To change: sudo nano /opt/ros/foxy/setup.bash"
echo "******************************************************"
echo "Topics active:"
ros2 topic list
echo "******************************************************"
printf "\n"
echo "If your package is not listed (ros2 pkg list):"
echo " * make sure you are sourcing the correct workspace: .bashrc"
echo " * delete build/, install/ and rebuild w/o errors"
# Parsing of git branch
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'
}
export PS1="\u@\h \[\e[32m\]\w \[\e[91m\]\$(parse_git_branch)\[\e[00m\]$ "
#----------------------------------------------------------------------
# Install network tools
sudo apt install -y wireless-tools
# Install Git
sudo apt install -y git
# Setup remote git from inside workspace
cd ~/colcon_ws
git init
git remote add origin https://github.com/patrickw135/pubsub.git
git fetch --all
git reset --hard origin/master
# Install your project specific python packages
# You can use pip3 as the install tool, eg:
pip3 install picamera
# Or you can use the software installer:
sudo apt install python3-picamera
# again
sudo apt update && sudo apt upgrade -y

31
src/pubsub/package.xml Normal file
View File

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>pubsub</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="winterhalder.p@googlemail.com">patrick</maintainer>
<license>TODO: License declaration</license>
<!-- ADD THESE LINES -->
<!-- Package not listed when using this: <exec_depend>rclpy</exec_depend> -->
<!-- <exec_depend>std_msgs</exec_depend> -->
<!-- <build_depend>std_msgs</build_depend> -->
<depend>std_msgs</depend>
<!-- CUSTOM LINE -->
<!-- This is custom for the package you depend on -->
<exec_depend>pubsub_msg</exec_depend>
<!-- END HERE -->
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>

View File

View File

@ -0,0 +1,83 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#********************************************#
# Listener Template
# Creted for:
# ROS2 Workshop 2020
# Roverentwicklung für Explorationsaufgaben
# Institute for Space Systems
# University of Stuttgart
# Created by Patrick Winterhalder
# IRS, University of Stuttgart
#********************************************#
import rclpy
from rclpy.node import Node
# Import Subscriber Library
from .pubsub_library import CustomMsg1_sub
from .pubsub_library import CustomMsg2_sub
# Import Message Types
from pubsub_msg.msg import CustomMsg1
from pubsub_msg.msg import CustomMsg2
def main(args=None):
rclpy.init(args=args)
# Start nodes here, should create object <node_name> for every node
listener_1 = CustomMsg1_sub(NODE_NAME="pubsub_listener_1", TOPIC_NAME="/chatter1",MSG_TYPE=CustomMsg1, NUM_MSGS=0)
listener_2 = CustomMsg2_sub(NODE_NAME="pubsub_listener_2", TOPIC_NAME="/chatter2",MSG_TYPE=CustomMsg2, NUM_MSGS=0)
while rclpy.ok():
try:
# Insert main looping script here...
# Receive Topics by running nodes
rclpy.spin_once(listener_1, timeout_sec=0.01)
rclpy.spin_once(listener_2, timeout_sec=0.01)
# Do sth if message was received
if listener_1.topic_received is True:
listener_1.topic_received = False # zurücksetzen
msg_1 = listener_1.return_msg()
#*********************************
# Do sth based on received message
#*********************************
if listener_2.topic_received is True:
listener_2.topic_received = False # zurücksetzen
msg_2 = listener_2.return_msg()
#*********************************
# Do sth based on received message
#*********************************
# Check if "msg_1" or "msg_2" is available as a local variable
if 'msg_1' in locals():
# Print msg_1
listener_1.print_msg()
if 'msg_2' in locals():
# Print msg_2
listener_2.print_msg()
# Here, you can now also publish/pass on the results of this script by
# using the "MinimalPublisher" class. For this please refer to "talker.py".
except (KeyboardInterrupt, SystemExit):
print("\n\nShutting down...")
# Insert "<node_name>.destroy_node()" here for all running nodes in this script
# eg:
listener_1.destroy_node()
listener_2.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,284 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#********************************************#
# Publisher/Subscriber Class Library
# Creted for:
# ROS2 Workshop 2020
# Roverentwicklung für Explorationsaufgaben
# Institute for Space Systems
# University of Stuttgart
# Created by Patrick Winterhalder
# IRS, University of Stuttgart
#********************************************#
import rclpy
from rclpy.node import Node
# How to use:
# from pubsub_library import MinimalPublisher
# from pubsub_library import MinimalSubscriber
# minimal_publisher = MinimalPublisher(NODE_NAME='minimal_pub', TOPIC_NAME='user_controller', MSG_TYPE=Usercontroller, MSG_PERIOD=0.5)
# minimal_subscriber = MinimalSubscriber(NODE_NAME='minimal_sub', TOPIC_NAME='epos_feedback', MSG_TYPE=Eposreturn)
# See --> talker.py, listener.py
#******************************************************************************#
# Definition of Parent Classes
class MinimalPublisher(Node):
def __init__(self, NODE_NAME, TOPIC_NAME, MSG_TYPE, MSG_PERIOD):
self.PUBLISHER_NAME= NODE_NAME
self.TOPIC_NAME = TOPIC_NAME
self.CUSTOM_MSG = MSG_TYPE
self.timer_period = MSG_PERIOD # [seconds]
# Init above laying class Node
super().__init__(self.PUBLISHER_NAME)
print("\t- " + str(TOPIC_NAME) + "\n")
self.publisher_ = self.create_publisher(
self.CUSTOM_MSG,
self.TOPIC_NAME,
10)
self.new_msg = False
# Define Node Frequency, equivalent to ~rospy.rate()
self.timer = self.create_timer(self.timer_period, self.publisher_timer)
return
def publisher_timer(self):
if self.new_msg is True:
try:
#self.get_logger().info('Pub:')
self.publisher_.publish(self.msg)
self.new_msg = False
except TypeError:
print("[ERROR] Msg-Data-Types do not match")
return
# Publish using Timer
def timer_publish(self, msg):
self.msg=msg
self.new_msg = True
return
# Publish directly without Timer
def direct_publish(self, msg):
try:
#self.get_logger().info('Pub:')
self.publisher_.publish(msg)
except TypeError:
print("[ERROR] Msg-Data-Types do not match")
return
class MinimalSubscriber(Node):
def __init__(self, NODE_NAME, TOPIC_NAME, MSG_TYPE, NUM_MSGS):
self.NODE_NAME= NODE_NAME
self.TOPIC_NAME = TOPIC_NAME
self.CUSTOM_MSG = MSG_TYPE
self.NUM_MSGS = NUM_MSGS
self.topic_received = False
# Init above laying class Node
super().__init__(self.NODE_NAME)
self.subscription = self.create_subscription(
self.CUSTOM_MSG, # Message Type
self.TOPIC_NAME, # Topic Name
self.listener_callback, # Callback Function
self.NUM_MSGS) # List of saved messages
self.subscription # prevent unused variable warning
return
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
return
#***************************************************************************#
# Child Classes inherite through super().__init__(...) from parent class
# Child's purpose is to simplify subscribing to custom messages
# For this to work you must customize "listener_callback", "return_msg" (and "print_msg")
# Exemplary Subscriber Class: Inheriting from MinimalSubscriber Class
# This class is custom made for receiving the specific msg type but uses MinimalSubscriber as foundation
# Create a new class of this type for every custom msg you want to receive, customizing the listener_callback,
# return_msg (and print_msg) functions to correspond to your message
class example_subscriber(MinimalSubscriber):
def __init__(self, NODE_NAME, TOPIC_NAME, MSG_TYPE, NUM_MSGS):
# Init MinimalSubscriber:
super().__init__(NODE_NAME, TOPIC_NAME, MSG_TYPE, NUM_MSGS)
print("\t- " + str(TOPIC_NAME))
# !!! CUSTOMIZE THESE VARIABLES TO WORK WITH YOUR MESSAGE FILE !!!
self.pos_actual = None
self.vel_actual = None
self.baffle_switch = None
self.current = None
self.temp = None
# !!! CUSTOMIZE THIS FUNCTION TO WORK WITH YOUR MESSAGE FILE !!!
def listener_callback(self, msg): # Overwrites callback from inherited class
self.topic_received = True
self.pos_actual = msg.pos_actual
self.vel_actual = msg.vel_actual
self.baffle_switch = msg.baffle_switch
self.current = msg.epos_current
self.temp = msg.epos_temp
#print_msg(self) # activate to print the received data --> customize print_msg(self) !
return
# !!! CUSTOMIZE THIS FUNCTION TO WORK WITH YOUR MESSAGE FILE !!!
def return_msg(self): # Extract only msg variables from "self" object
msg = self.CUSTOM_MSG()
msg.pos_actual = self.pos_actual
msg.vel_actual = self.vel_actual
msg.baffle_switch = self.baffle_switch
msg.epos_current = self.current
msg.epos_temp = self.temp
return msg
# !!! CUSTOMIZE THIS FUNCTION TO WORK WITH YOUR MESSAGE FILE !!! (optional)
def print_msg(self):
print("[SUBSCRIBER] %s received topic:\t/%s" %(self.NODE_NAME, self.TOPIC_NAME))
string = "Received:\t"
# Check length of all transmitted value lists to be equal (equal number of parameters)
length = len(self.pos_actual)
if any(len(lst) != length for lst in [self.vel_actual, self.current, self.temp]):
print("[TRANSMISSION ERROR] Length of lists inside topic %s do not match" %(self.TOPIC_NAME))
else:
complete_list = []
complete_list.append(self.pos_actual)
complete_list.append(self.vel_actual)
complete_list.append(self.current)
complete_list.append(self.temp)
#print(complete_list)
for i in range(len(complete_list)):
for y in range(len(complete_list[0])):
string += "%.2f,\t" %(complete_list[i][y])
string += str(self.baffle_switch)
print(string)
return
#************************************************************************************#
# CustomMsg1 Subscriber Class
class CustomMsg1_sub(MinimalSubscriber):
def __init__(self, NODE_NAME, TOPIC_NAME, MSG_TYPE, NUM_MSGS):
# Init MinimalSubscriber:
super().__init__(NODE_NAME, TOPIC_NAME, MSG_TYPE, NUM_MSGS)
print("\t- " + str(TOPIC_NAME))
# CustomMsg1 Variables
self.temperature = None
self.pressure = None
self.humidity = None
return
# !!! CUSTOMIZE THIS FUNCTION TO WORK WITH YOUR MESSAGE FILE !!!
def listener_callback(self, msg): # Overwrites callback from inherited class
self.topic_received = True
self.temperature = msg.temperature
self.pressure = msg.pressure
self.humidity = msg.humidity
#print_msg(self) # activate to print the received data --> customize print_msg(self) !
return
# !!! CUSTOMIZE THIS FUNCTION TO WORK WITH YOUR MESSAGE FILE !!!
def return_msg(self): # Extract only msg variables from "self" object
msg = self.CUSTOM_MSG()
msg.temperature = self.temperature
msg.pressure = self.pressure
msg.humidity = self.humidity
return msg
# !!! CUSTOMIZE THIS FUNCTION TO WORK WITH YOUR MESSAGE FILE !!! (optional)
def print_msg(self):
print("[SUBSCRIBER] %s received topic:\t/%s" %(self.NODE_NAME, self.TOPIC_NAME))
string = "Received:\t"
# Check length of all transmitted value lists to be equal (equal number of parameters)
length = len(self.temperature)
if any(len(lst) != length for lst in [self.temperature, self.pressure, self.humidity]):
print("[TRANSMISSION ERROR] Length of lists inside topic %s do not match" %(self.TOPIC_NAME))
else:
complete_list = []
complete_list.append(self.temperature)
complete_list.append(self.pressure)
complete_list.append(self.humidity)
#print(complete_list)
for i in range(len(complete_list)):
for y in range(len(complete_list[0])):
string += "%.2f,\t" %(complete_list[i][y])
print(string)
return
#************************************************************************************#
# CustomMsg2 Subscriber Class
class CustomMsg2_sub(MinimalSubscriber):
def __init__(self, NODE_NAME, TOPIC_NAME, MSG_TYPE, NUM_MSGS):
# Init MinimalSubscriber:
super().__init__(NODE_NAME, TOPIC_NAME, MSG_TYPE, NUM_MSGS)
print("\t- " + str(TOPIC_NAME))
# CustomMsg1 Variables
self.pitch_ctrl = None
self.yaw_ctrl = None
return
# !!! CUSTOMIZE THIS FUNCTION TO WORK WITH YOUR MESSAGE FILE !!!
def listener_callback(self, msg): # Overwrites callback from inherited class
self.topic_received = True
self.pitch_ctrl = msg.pitch_ctrl
self.yaw_ctrl = msg.yaw_ctrl
#print_msg(self) # activate to print the received data --> customize print_msg(self) !
return
# !!! CUSTOMIZE THIS FUNCTION TO WORK WITH YOUR MESSAGE FILE !!!
def return_msg(self): # Extract only msg variables from "self" object
msg = self.CUSTOM_MSG()
msg.pitch_ctrl = self.pitch_ctrl
msg.yaw_ctrl = self.yaw_ctrl
return msg
# !!! CUSTOMIZE THIS FUNCTION TO WORK WITH YOUR MESSAGE FILE !!! (optional)
def print_msg(self):
print("[SUBSCRIBER] %s received topic:\t/%s" %(self.NODE_NAME, self.TOPIC_NAME))
string = "Received:\t"
# Check length of all transmitted value lists to be equal (equal number of parameters)
complete_list = []
complete_list.append([self.pitch_ctrl])
complete_list.append([self.yaw_ctrl])
#print(complete_list)
for i in range(len(complete_list)):
for y in range(len(complete_list[0])):
string += "%.2f,\t" %(complete_list[i][y])
print(string)
return

View File

@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#********************************************#
# Talker Template
# Creted for:
# ROS2 Workshop 2020
# Roverentwicklung für Explorationsaufgaben
# Institute for Space Systems
# University of Stuttgart
# Created by Patrick Winterhalder
# IRS, University of Stuttgart
#********************************************#
import rclpy
from rclpy.node import Node
# Import Publisher Library (Python Library)
from .pubsub_library import MinimalPublisher
# Import Message Types (Message Files)
from pubsub_msg.msg import CustomMsg1
from pubsub_msg.msg import CustomMsg2
def main(args=None):
rclpy.init(args=args)
# Start nodes here, should create object <node_name> for every node
talker_1 = MinimalPublisher(NODE_NAME="pubsub_talker_1", TOPIC_NAME="/chatter1", MSG_TYPE=CustomMsg1, MSG_PERIOD=0.25)
talker_2 = MinimalPublisher(NODE_NAME="pubsub_talker_2", TOPIC_NAME="/chatter2", MSG_TYPE=CustomMsg2, MSG_PERIOD=0.5)
while rclpy.ok():
try:
# Insert main looping script here...
# Eg. create custom msgs, fill and send them
# CustomMsg1 to talker_1
msg_1 = CustomMsg1()
msg_1.temperature = [24.5, 25.5, 26.5]
msg_1.pressure = [1011.5, 1012.55, 1010.11]
msg_1._humidity = [0.002, 0.0019, 0.0021]
talker_1.timer_publish(msg_1)
# CustomMsg2 to talker_2
msg_2 = CustomMsg2()
msg_2.pitch_ctrl = 0.0
msg_2.yaw_ctrl = 0.22
talker_2.timer_publish(msg_2)
# Insert "rclpy.spin_once(<node_name>, timeout_sec=0.1)" at the end of
# "try" for every node running in this script
rclpy.spin_once(talker_1, timeout_sec=0.1)
rclpy.spin_once(talker_2, timeout_sec=0.1)
except (KeyboardInterrupt, SystemExit):
print("\n\nShutting down...")
# Insert "<node_name>.destroy_node()" here once for every node running in this script
talker_1.destroy_node()
talker_2.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()

View File

4
src/pubsub/setup.cfg Normal file
View File

@ -0,0 +1,4 @@
[develop]
script-dir=$base/lib/pubsub
[install]
install-scripts=$base/lib/pubsub

27
src/pubsub/setup.py Normal file
View File

@ -0,0 +1,27 @@
from setuptools import setup
package_name = 'pubsub'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='patrick',
maintainer_email='winterhalder.p@googlemail.com',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'talker = pubsub.talker:main',
'listener = pubsub.listener:main'
],
},
)

View File

@ -0,0 +1,23 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ament_copyright.main import main
import pytest
@pytest.mark.copyright
@pytest.mark.linter
def test_copyright():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found errors'

View File

@ -0,0 +1,25 @@
# Copyright 2017 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ament_flake8.main import main_with_errors
import pytest
@pytest.mark.flake8
@pytest.mark.linter
def test_flake8():
rc, errors = main_with_errors(argv=[])
assert rc == 0, \
'Found %d code style errors / warnings:\n' % len(errors) + \
'\n'.join(errors)

View File

@ -0,0 +1,23 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ament_pep257.main import main
import pytest
@pytest.mark.linter
@pytest.mark.pep257
def test_pep257():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found code style errors / warnings'

View File

@ -0,0 +1,59 @@
cmake_minimum_required(VERSION 3.5)
project(pubsub_msg)
# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
# -->
# ADD THESE LINES: START HERE
find_package(builtin_interfaces REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rclcpp REQUIRED)
# CUSTOM LINES: CHANGE FOR YOUR FILENAMES
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/CustomMsg1.msg"
"msg/CustomMsg2.msg"
DEPENDENCIES builtin_interfaces
)
# END HERE
# <--
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()

View File

@ -0,0 +1,8 @@
# Header data, eg timestamp
# Problem: "header__struct.hpp: No such file or directory"
#Header header
# Sensor Data coming back from an array of atmospheric sensors
float32[] temperature
float32[] pressure
float32[] humidity

View File

@ -0,0 +1,7 @@
# Header data, eg timestamp
# Problem: "header__struct.hpp: No such file or directory"
#Header header
# User inputs, eg. for controlling a camera mast , eg. set angles [rad]
float32 pitch_ctrl
float32 yaw_ctrl

View File

@ -0,0 +1,34 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>pubsub_msg</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="winterhalder.p@googlemail.com">patrick</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<!-- ADD THESE LINES: START HERE -->
<build_depend>builtin_interfaces</build_depend>
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>builtin_interfaces</exec_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
<!-- END HERE -->
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

41
workshopinstall.sh Normal file
View File

@ -0,0 +1,41 @@
#!/bin/bash
sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get dist-upgrade -y
# Install Visual Studio Code
sudo apt install -y software-properties-common apt-transport-https wget
wget -q https://packages.microsoft.com/keys/microsoft.asc -O- | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main"
sudo apt update && sudo apt install -y code
# Install Git
sudo apt install -y git
# Install ROS2 Foxy Fitzroy Desktop
#sudo apt update && sudo apt install locales
#sudo locale-gen en_US en_US.UTF-8
#sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
sudo apt update && sudo apt install -y curl gnupg2 lsb-release
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
sudo sh -c 'echo "deb [arch=$(dpkg --print-architecture)] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2-latest.list'
sudo apt update && sudo apt install -y ros-foxy-desktop
source /opt/ros/foxy/setup.bash
# Install pip and argcomplete
sudo apt install -y python3-pip
pip3 install -U argcomplete
# If colcon does not build: Install pytest version>=5.0 ???
# Install rosdep
sudo apt install -y python3-rosdep2
rosdep update
# in between
sudo apt update && sudo apt upgrade -y
# Install colcon
sudo apt install -y python3-colcon-common-extensions
# At the end again
sudo apt update && sudo apt upgrade -y