To install ROS2 on your Ubuntu follow this link.
Before you doing that you need to do
sudo apt install python3-vcstools python-vcstools python-pip
sudo pip install vcstools
In addition of ROS2, you need to setup a ROS2_workspace to compile library for ROS2. Just follow this tutorial
You can find the installation for turtlebot2 with ROS2 at this link
Note: All of my devellopement was done on Ubuntu 18.04.1 LTS with ROS2 Bouncy
Just before enter in Pharo side, you need to install RCLC
. RCLC
is jeust an adaptation of RCL
but with different simplification to handle more easly the different method of RCL
.
Here you have the link to Github of the RCLC.
To install this Library, juste clone this repo into your ros2_ws/src
.
Due to some difficulty and the implementation of ROS2 Type Support, a lybrary is use to handle the Type support between ROS2 and Pharo. This library is empty and dynamicly generated by Pharo.
The installation is the same as RCLC
, you juste need to clone the PhaROS_msgs
git into your ~/ros2_sw/src
.
To compile, you need to be in ros2 environment
.
ros2ify
cd ~/ros2_ws
src/ament_tools/scripts/ament.py build --build-tests --symlink-install
If the script run entirely, you can should except to have at least libpharosmsgs.so
and librclc.so
.
Try to perform
cd ~/ros2_ws/install/lib
ros2ify
ldd librclc.so
ldd libpharosmsgs.so
Both will have different Ros2
linked libraries
Use PharoLauncher follow this link. You only have to uncompress the folder to install it.
Once you have run pharolauncher
. Create and start a Pharo 7.0
empty image. Do nothing in this image because the Pharo-vm was not run inside ROS2 environment.
Pharo Image should be run in ROS2 environment.
To improve the simplicity, I've write a Shell-Script to run your PhaROS2 Image in ROS2 environment. To use this script, name the image in PharoLauncher: PhaROS2
#!/bin/bash
export ROS_DOMAIN_ID=10
source /opt/ros/bouncy/setup.bash
cd ~/Pharo/vms/70-x64/
./pharo ../../images/PhaROS2/PhaROS2
echo
echo
echo
echo '--------------------------'
echo '-- PhaRO2 --'
echo '-- Press enter to quit --'
echo '--------------------------'
read
Note: ROS_DOMAIN_ID permit to split the network in 256 (0->255) ROS2 domain. Each sub-domain is independant.
You need to use some depencie to use PhaROS2. We need to install OSProcess
and CommandShell
to execute PhaROS2 correctly.
Gofer new
squeaksource: 'OSProcess';
package: 'OSProcess';
load.
Gofer new
squeaksource: 'CommandShell';
package: 'CommandShell-Piping';
load.
With Iceberg
import PhaROS2 project host on GitHub: https://github.com/CARMinesDouai/PhaROS2.git
and load PhaROS2
package
Before run the test case, you have to change different library location according to your installation.
RCLC class>>ffiLibraryName
PhaROS_Msgs class >>ffiLibraryName
PhaROS_Msgs class >> Path categorie
To ensure everything work correctly, try to run the Test cases
.
ROS2 like ROS1 is based on type
so you need to create type before using it on PhaROS2
This is the way you have to get the correct structure. All subsctructure will be create, and a dictionary is used to optimize the creation.
The PharROS2TypeBrowser
is a singleton.
PhaROS2TypeBrowser instance ros2Type: 'geometry_msgs/Twist'.
PhaROS2TypeBrowser instance ros2Type: 'geometry_msgs/TwistWithCovarianceStamped'.
PhaROS2TypeBrowser instance ros2Type: 'std_msgs/String'.
All of your type is a subclass of PhaROS2_Type
.
If you want to reset the PhaROS2TypeBrowser
, you can execute the following code. They will delete all PhaROS2_type
and ROS2TypeSupport_struct
subclasses and the dictionary will be erase.
PhaROS2TypeBrowser reset.
Note: The subclasses of ROS2_TypeSupport_struct is not for users. Users only use PhaROS2_Type subclasses
To have an object for a message type, you have to use this method
PhaROS_Msgs typeSupport: aTypeSupport
PhaROS_Msgs typeSupport: 'std_msgs/String' "To have a String message type support"
PhaROS_Msgs
is based on the pharosmsgs
library in your ros2_ws
. This librarie is dynamicaly change by Pharo when it's needed.
Note: Unfortunetly the automatic recompilation does'nt work... you have yo run: src/ament_tools/scripts/ament.py build --symlink-install --only-packages pharosmsgs
manualy in you ros2_ws
The goal of this section is to able to have a fully functional Talker with phaROS2
.
In my implementation the talker need to have a thread to publish a message. But you can of course use the subscriber callback to publish an answer of each received messages.
Object subclass: #Talker
instanceVariableNames: 'node pub myThread active'
classVariableNames: ''
package: 'ROS2TalkerListener'
Explanation:
- Instance variables
- node: Contain the ROS2_Node
- pub: Contain the ROS_Publisher
- myThread: Will keep the thread to publish a message
- active: To stop the thread without
myThread terminate
- They will no have
class varibales
but you will be free to add as your convinence
Your initialize
will be as simple as possible.
Talker>>initialize
super initialize.
self createNode.
self createPublisher.
It's in this function, you will find the way to create a node
.
Talker>>createNode
node := ROS2_Node new.
node namespace: '/namespace'.
node nodeName: 'myTalker'.
node registerNode.
Note: You can place varibales instead of "/namespace"or "myTalker".
This function will create a publisher. You have more option on publisher.
Talker>createPublisher
pub := ROS2_Publisher new.
pub topicName: 'talker'.
pub parentNode: node.
pub typeSupportName: 'std_msgs/Float64'.
pub queueSize: 10.
pub registerPublisher.
Node: You can delete a publisher by using: pub destroyPublsher
As you see, you're not obliged to call PhaROS2TypeBrowser instance need: 'std_msgs/Float64'.
. It's done when you set the typeSupportName: aType
on a publisher or subscriber.
To stop a node, you juste have to call aNode destroyNode
. The destruction of the node will automaticaly destroy all publisher and subscribers attach to this node.
Add this function to your Talker class
Talker>>destroyNode
(node) ifNotNil: [
node destroyNode.
]
Talker>>loopFunction
| toPublish allocatedString |
[ active ] whileTrue: [
toPublish := PhaROS_Msgs typeSupport: 'std_msgs/String'.
allocatedString := PhaROS2_allocation stringToPointer: 'Hello World With phaROS2'.
toPublish rosidl_message_type_support data: allocatedString.
pub publish: toPublish .
(Delay forMilliseconds: 1000) wait.
]
For example: You juste have to get the TypeSupport
by calling the correct function.
This function is used to start the thread.
Talker>>start
active := true.
myThread := [ self loopFunction. ] fork.
This function is used to stop the Thread. By warn, the node and the publisher will not be delete by stopping the thread
Talker>>stop
active := false.
(myThread) ifNotNil: [myThread terminate].
To create and execute the pusliher, you have to play this playgound code
aTalker := Talker new.
aTalker start.
"Use to stop the thread"
aTalker stop.
"Use to delete the node"
aTalker destroyNode.
The listener node is more complex due to the callback needed.
As the same as the Talker
, the Listener
need some variables
Explanation:
- Instance variables
- node: Contain the ROS2_Node
- sub: Contain the ROS_Subscriber
- myThread: Will keep the thread to publish a message
- active: To stop the thread without
myThread terminate
- They will no have
class varibales
but you will be free to add as your convinence
Object subclass: #Listener
instanceVariableNames: 'node sub myThread active'
classVariableNames: ''
package: 'ROS2MyExperiment'
Listener>>initialize
super initialize.
self createNode.
self createSubscriber.
Same thing as precedent. Or you can use the same node.
Litener>>createNode
node := ROS2_Node new.
node namespace: '/namespace'.
node nodeName: 'myListener'.
node registerNode.
As you see, you found almost the same things as the publisher
. But they have a callback block.
As you see, you juste have to put a Pharo block of code with one parameter.
The parameter is a PhaROS2_Type
subsclass according to the typeSupport
. So you directly have access to the data.
Listener>>createSubscriber
| callbackBlock |
sub := ROS2_Subscriber new.
sub topicName: '/listener'.
sub parentNode: node.
sub typeSupportName: 'std_msgs/String'.
sub queueSize: 10.
sub ignoreLocalPublication: true.
callbackBlock := [ :data | self myCallback: data].
sub callback: callbackBlock.
sub registerSub.
Node: You can delete a subscriber by using: sub destroySubscriber
The callback function is used to treat the data send by ROS2
.
In my case I will juste print the data on the Transcript.
In general, the data is a PhaROS2_Type
subclasses according to the type the topic. For example, il you use std_msgs/String
type, data
will be Std_msgs_String
object.
Listener>>myCallback: data
| toPrint |
toPrint := data data.
Transcript crShow: toPrint.
As the same as the publisher
, you have to handle a Thread.
The fact of a node need to be spined, is laready handle by PhaROS2.
To handle this, you juste have to call the start
and stop
function.
To handle this, you can just add these two functions on you Listener class.
Listener>>start
node startSpin.
Listener>>stop
node stopSpin.
To stop the node on the ROS side, we wil add the same function as previous
Listeger>>destroyNode
(node) ifNotNil: [
node destroyNode.
]
To use the subscriber, you have to run this code un your playground
aListener := Listener new.
aListener start.
"Use to stop the thread"
aListener stop.
"Use to delete the node"
aListener destroyNode.
Once you have on PhaROS2
a talker and listener, you can see he result in your terminal.
I consider your terminal is in ROS2 environment
. If not do a ros2ify
Here you can find some command to know if your talker/listener
work correctly.
First you need to know if the node is created. Run: ros2 node list
to list all ROS2
node. Your node should appear.
To check oll the topic, you have to check the topic list with ros2 topic list
. Once you have the complete name and topic, you can retreive differents informations about the topic by running ros2 topic info /myTopicName
. You can see the number of publisher and subscriber to this topic.
Finaly to see the message send by the publisher
you have to run ros2 tpoic echo /myTopicName
. The message send by PhaROS2 will be print on your Terminal. To kill this terminal Listener, you simply have to use ctrl-c
.
To test the subcriber you can publish a message in your terminal. To do that juste run: ros2 topic pub /myTopicName std_msgs/String "data: 'Hello PhaROS2'"
. The message Hello PhaROS2
will triggered the callback in Pharo.
Note: Please ensure the thread used to spin the node is active in Pharo side
To perform this part, you need to have a Turtlebot2, and Two machine.
- Machine A:
- Will be placed on Trutlebot
- Need to have Ros2 and Ros1 installed to manage the Turtlebot
- They also received the joy information
- Machine B:
- You need to have Ros2
- You need to have PhaROS2.
Connect the Joystick receiver and the turtlebot to the computer.
ros1ify
ros2ify
ros2 run turtlebot2_drivers kobuki_node
After this code, the Turtlebot wil make some sound.
ros2ify
ros2 run joy joy_node
The joystick is now handle. The last step to move the robot is to transform the information from the joy_node
into cmd_vel
. And this part will be done in PhaROS2
When you have download the PhaROS2
git in Pharo, you have Turtlebot2
package.
turtleBotNode := Turtlebot2_Demo new.
turtleBotNode startAutoSpin.
turtleBotNode stopAutoSpin.
turtleBotNode defaultMove.
turtleBotNode stop.
First you create a simple Turtlebot2_Demo
object.
The defaultMove
will move forward the robot. You need to manuly call this function ctrl-d
to see the turtlebot move.
The two function startAutoSpin
and stopAutoSpin
handle a Thread to send the correct information receive by the joy_node
and publish in cmd_vel
.
The last function stop
will delete all publishers
, subscribers
and the node
.
ROS2
is a Middleware. So the node is broadcast on the network.
The informations send by the joy_node
is send throught the network to PhaROS2 turtlebot2_demo
. It's catch and treat by pharo. Once the cmd_vel
message is create, the information go on the network one more to the kobuki_node
. The turtlebot start moving.