T1ll13 robot step 2


As explained here:  http://www.cs.bham.ac.uk/internal/courses/int-robot/2015/notes/concepts.php

ROS is a message-passing framework which allows you to create fully-fledged robotic systems quickly and easily. Rather than writing a single, large program to control the robot, we run a number of smaller, process-specific programs (e.g. to analyse images from a camera) and run them side-by-side, passing messages between them to share data and other variables.

The core component in ROS is called a Node:


Each Node performs a particular process, such as processing sensor data or controlling a laser scanner. You can think of them as code modules. However, rather than calling a Java method to get the robot to do something, we publish messages.
Messages are published on topics, which are like separate channels.  Nodes subscribe to these topics:
Whilst one Node publishes messages on a certain topic (for example, robot movement commands), another Node may subscribe to this topic and act upon the messages it receives (you could think of it in terms of following someone on Twitter). Nodes can publish on any topic, or subscribe to any topic, or do both; and multiple nodes can each subscribe to the same topic.
Nodes can find each other using a program called roscore:

This acts a bit like a router in a network, and ensures that messages on each topic are being passed between nodes correctly.


I decided to make a Service to control the Servos.  "Hang on", I hear you say, "where did this concept of a Service come from?"....Well, let's take a look at what the ROS documentation says:

The publish / subscribe model is a very flexible communication paradigm, but its many-to-many one-way transport is not appropriate for RPC request / reply interactions, which are often required in a distributed system. 
Request / reply is done via a Service, which is defined by a pair of messages: one for the request and one for the reply. A providing ROS node offers a service under a string name, and a client calls the service by sending the request message and awaiting the reply. Client libraries usually present this interaction to the programmer as if it were a remote procedure call (RPC).
Services are defined using srv files, which are compiled into source code by a ROS client library.


A client can make a persistent connection to a service, which enables higher performance at the cost of less robustness to service provider changes.

I set about making a Service to specifically control the Servos (if this is the wrong thing to do, I'll no doubt find out shortly!).  Why did I choose to make this a service?  Well...I wanted a response to come back and tell me that the servo movement was valid and possibly what the status of the servo is.
I applied the following logic:
On the RPi3 I have Geany installed to edit/create the required python files.  As I'm still using the default folders from the initial walkthrough I'll be in the ~/catkin_ws folder for the following.
As is required for the service, I created the MoveServo.srv file in the /srv folder.  This contains a definition of the parameters to pass IN/OUT for the service.

Then, within the /scripts folder, I created the servo_control_server.py file.  This is the servo service that will listen on the topic /move_servo and will, well, move the servos!

Now, we need to create the servo_control_client.py file.  This will publish a message to /move_servo passing a couple of parameters ([which servo], [start degree], [end degree]) and will receive a response back that the action was successful. ie. the servo moved as was expected.  As you can see the code is very simple.

(Yes, I'll shortly upload the code to a github repo. so you can see the wonder for yourself)

Now that the python server/client code has been written, how do we make ROS aware of it and test it?  One thing to note already is that I've hooked up the RPi3 to an Adafruit PCA9685, that allows me to control 16 servos via the RPi3 I2C interface (here's an image of it being used with an Arduino, which may possibly also happen in the future)

As for servos, I have a couple of H-King 15298 servos as they are needed for the variant of the InMoov robot, that we'll be making.

Here's the initial setup.  As you can see I'm using an external power source for the PCA9685 (4xAAA batteries) as I read that the power draw for the servos will trip the RPi3, it's a good idea to power servos from their own power source anyway, (as I've previously done with ArcTuRus)

Now that we have the hardware ready.  Let's get ROS all setup and we can test this out.
$ cd ~/catkin_ws
$ catkin_make
//this will compile our .py code and make all the files needed for ROS
//as mentioned earlier, as I built on the tutorial code
//I've already done the modifications to CMake.txt and package.xml files
//so I'm not going to define it again here
$ source ./devel/setup.bash

Now, we need 3 Terminal windows to be opened:

TERM1: $ roscore

TERM2: $ rosrun beginner_tutorials servo_control_server.py
Ready to move servos.

TERM3: $ rosrun beginner_tutorials servo_control_client.py 0 150 600

The client will call /move_servo passing 0 150 600 that will trigger the code in (Service node) to then invoke the Servo[0] and move the servo accordingly 150 then 600 degrees.

(If you have to change anything in the .py files, just remember that you need to re-run $ catkin_make)

Of course, we have a little video of that doing it's thing (albeit, this video is from me writing a test app, before I merged it into the servo_control_server.py):


Yay!  Well, there we have a server Service that controls the servos and a client app that sends a request to move specific servos.  "Hang on", you say (btw - you say that a lot), "I could have just written a single app to do that.  I didn't need to use ROS.  Why do I need to add complexity for no perceived value?".  I am so glad you say that......


Let's now go one step further.  I happen to have a Sharp GP2Y0A21YK sensor knocking about.  I wire it up to the RPi3 and write a little bit of code that just does the detection of something breaking the IR beam.  You are meant to use the IR sensor to calculate the distance from the object by reviewing the voltage value, but for now, all we really care about is: "Did we detect something in the way?  If so, we need to trigger a reaction on a servo.....".  That sounds like a more realistic scenario.

As you can see below, this time we are going to create the pub/sub node concept:
We have a topic /servo_chatter listening and handled by the servo_listener.py node.
We have a servo_talker.py client app that is monitoring the IR sensor, if an object is detected it publishes a message to the topic /servo_chatter.
From the command line, we can also just run a command to manually publish to the topic /servo_chatter (demonstrating the many-to-many concept) - we could have multiple external sensors that would call the same topic just passing different parameters.  As we do not care about a response, this setup is valid.
Within the servo_listener.py code, we can then call the Service /move_servo.
As shown previously in the bottom right of this image:
We are now the servo_listener.py publishing to the /move_servo service that then performs an action on the servos.  cool, huh!  Now, you start to see why this setup makes sense.


Now, let's see all that in action:


Now we have the basics worked out, we can use this as the foundation framework to start building upwards from.  Obviously, there are ROS packages that we can re-use to do further things with, for instance, I am going to look into whether I can use this package to perform the REST API calls to the IBM Cloud Watson services.....for Conversation/Speech-to-Text/Text-To-Speech/Visual Recognition and Language Translation....



Right, now it's time to start making the InMoov finger....it's a start!

Comments