ROS Arduino Serial

Please refer to this tutorial of the ultrasound sensor HC-SR04.

Ultrasound sensors measures distance by sending sound waves against objects in front of it and receiving the echos (so it doesn’t work well against objects that absorb sound, e.g. furs).

This code runs in the Arduino, and communicates the message serially through the USB cable to the roscore on the laptop.

#define trigPin 12
#define echoPin 13

// 12 and 13 refers to the pin numbers on Arduino
// You can choose to use any pins on the Arduino for digital 
// signal read and write (which sensor uses) 

#include <ros.h>
#include <std_msgs/Int16.h>

ros::NodeHandle nh;

std_msgs::Int16 int_msg;
ros::Publisher arduino("distance", &int_msg);

// Standard ROS node intialization

void setup() { // Setup code runs only once
  //Serial.begin (9600);  
  // You can choose to see output on a serial monitor
  // on the computer 
  // But from what I found, Arduino can't seem to maintain
  // both serial communication to ROS and serial monitor
  pinMode(trigPin, OUTPUT); // marking pins to be read and write 
  pinMode(echoPin, INPUT); 
  nh.initNode();
  nh.advertise(arduino); 
}
void loop() { // This part of the code runs repeatedly
  long duration;
  int distance;

  digitalWrite(trigPin, LOW); // This is just setup code 
  delayMicroseconds(2);       // So that ultrasound sensor would
  digitalWrite(trigPin, HIGH);// start reading
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW); 

  duration = pulseIn(echoPin, HIGH); 
  // The data coming in from ultrasound sensor is just 
  // digital data (0, 1), but it signifies distance data 
  // (0 ~ 200) by sending "pulses" of certain length 

  distance = (duration/2) / 29.1;
  int_msg.data = distance;

  if (distance >= 2000 ) {
    //Serial.println("Out of range");
  }
  else {
    //Serial.print(distance);
    //Serial.println(" cm");
    arduino.publish(&int_msg);
    nh.spinOnce();
    // Standard publish routine for ROS
  }
  delay(500); // Wait 500 ms before running again
}
~

 

ROS and Serial Communication

Recommended reading: Serial Communication, ROS Serial – Arduino tutorial, Arduino Basics

Beneath all the software abstractions, much of the communications between the devices we use daily are electrical signals, in either analog or serial form.

Analog communication utilizes all range of the voltage allotted by the medium (e.g. 0v – 5v for Arduino). The range is used to deliver a granular form of data; for example, an analog temperature sensor might indicate a low temperature with a low voltage and high temperature with high voltage. While an analog input might simplify processing of the communication (just pass the voltage levels through an equation), it is also error-prone in many of the cases due to the possibility of electrical noises.

Serial/digital communication is a form of communication that essentially utilizes bits to communicate – sending 2 kinds of voltages, no voltage indicating a low, or off, signal and some high voltage indicating an on signal. While the serial communication prevents much of the noise (by using two range of disparate volts), the devices that send and receive the communication would need some sort of a processor to process and retrieve the data that has been abstracted away.

Also, by its encoded nature, serial communication can also reduce miscommunication by using encoding schemes that guarantee that the both parties are communicating properly.

Given that the web pages above present a much better explanation of how analog and serial communications work, I’ll stop my post here. Please contact me if you need any topics clarified.

Setting up ROS client over network

Client code

#!/usr/bin/env python

from service_tutorial.srv import *
import rospy
import sys

def concat_things_client(string1, string2, num1, num2):
 rospy.wait_for_service('concat_things')
 try:
   concat_things = rospy.ServiceProxy('concat_things',   ConcatThings)
   resp = concat_things(string1, string2, num1, num2)
   return resp.result
 except rospy.ServiceException, e:
   print "Service call failed: %s" % e

if __name__ == "__main__":
 if len(sys.argv) == 5:
   string1 = sys.argv[1]
   string2 = sys.argv[2]
   num1 = int(sys.argv[3])
   num2 = int(sys.argv[4])
 else:
   print "Please provide all args"
   sys.exit(1)
   print "Requesting service"
   print "%s" % concat_things_client(string1, string2, num1, num2)

/etc/hosts on the client

127.0.0.1 localhost comp1
127.0.1.1 guest
10.99.0.12 server

# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

127.0.0.1 vultr.guest
::1 vultr.guest

/etc/hosts on the server

127.0.0.1 localhost server
127.0.1.1 guest
10.99.0.10 comp1
10.99.0.11 comp2

# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
#!/usr/bin/env python

from service_tutorial.srv import *
import rospy
import sys

Commands to debug

Are all the network ports necessary to communicate open?

On server – netcat -l 1234 : echo all things received from port 1234

On client – netcat server 1234 : send input to server through port 1234

Can I connect to ROS core through network?

Try running ROS commands, like rostopic

Am I even connected to the server?

ping server

Is the service available?

rossrv list

Is my environmental variables set to what I think they should be?

echo $ROS_MASTER_URI or ${Environmental variable name}

What is my IP?

ifconfig and check under wlan0 and wlan1.

Modifying Sensor Data with ROS

 

Here is a sample code that modifies sensor data with ROS:

import rospy
from sensor_msgs.msg import Imu

def imu_callback(msg, pub):
 msg.linear_acceleration.z += 9.3
 pub.publish(msg)

def main():
 rospy.init_node('imu_without_g')
 pub = rospy.Publisher('imu_without_g', Imu, queue_size=10)
 rospy.Subscriber('imu/data', Imu, imu_callback,callback_args=pub) 
 rospy.spin()

if __name__ == '__main__':
 main()
def imu_callback(msg, pub):
 msg.linear_acceleration.z += 9.3
 pub.publish(msg)

The callback is whenever Imu msg arrive, with an additional argument of pub (which is specified in the rospy.Subscriber call). I can modify the msg using the dot notation (with names you can find out with command rosmsg show sensor_msgs/Imu).

def main():
 rospy.init_node('imu_without_g')
 pub = rospy.Publisher('imu_without_g', Imu, queue_size=10)
 rospy.Subscriber('imu/data', Imu, imu_callback,callback_args=pub) 
 rospy.spin()

I initialize a publisher to publish modified IMU data. Since this modified data will be published onto a different topic, you’ll have to redirect the topic of the imu for each nodes that use IMU data in the future.

 

ROS and Sensors

Recommended reading: ROS Sensor msgs, RViz (wiki.ros.org/rviz), Camera on ROS

One of the main goals ROS had set out to achieve was to remove redundant and unnecessary work in robotics. By providing sensible interfaces (ROS messages) and tools that utilize the interfaces, ROS removes lots of unnecessary work that would have been required with sensors.

Please refer to the camera section of ROS.

Observe that the sections are divided into multiple parts. First, there are software and tools that complement or enhance the sensing capabilities of the camera. These packages are generally, if not all, device-independent – that is, they function from the inputs of messages (topics).

Then, there are hardware drivers specific to each camera, or more generally, types of camera. These packages are either written by the manufacturers themselves or other programmers who wanted to connect the devices to ROS. These packages take raw data from the sensors and publishes it to ROS topics, or allows the sensors to be configured using ROS API (services or something called dynamic parameters).

Since the software tools and hardware drivers communicate through the common ROS interface, you can choose to only change one of them if there is an issue. For example, you can swap the cameras and hardware drivers, but still use the same software package for the large part, or vice versa.

Another thing to note is that devices are listed by the connection type (USB, Serial, Ethernet), as the devices also go through the common Linux interfaces for each types as well, generally through the ports (not the network ports discussed below).

The ports (with names like /dev/ttyUSB0) allows the users on Linux to read and write from the devices. Some notable characteristics of the ports are that they are …

  • allocated dynamically unless specified otherwise (the first USB device is allocated under /dev/ttyUSB0, the second USB device /dev/ttyUSB1, …)
  • Are treated as a file in Linux (which means that all the things that come with files are with ports, like read and write permissions)

Generally, the issue of reading and writing data off these ports will be abstracted away by the drivers (by their purpose).

However, in the cases in which drivers are not provided, the read and write would have to be performed in a specific fashion. For example, the port names specified above indicate (/dev/”tty”) that they are serial ports, which means that you have to communicate them with a specific baud rate (rate of communication), stop bit (signifying end of message), and delimiter (character that indicates the end of message).

For more information on serial port, skim through https://en.wikipedia.org/wiki/Serial_port.

However, as I stated before, we can skip over these complexities and deal with the abstractions of ROS instead.

Please refer to: http://docs.ros.org/api/sensor_msgs/html/msg/Image.html

Like with all ROS messages we have dealt with before, we can do likewise with the ROS image messages. We can assess the header (which contains timestamp, which is necessary to sync up all the sensor data together), put in image data as arrays of ints, and specify the encoding as a string (encoding schemes specify how the row of ints should be read to produce an image).

Other ROS interfaces, as mentioned before, are services (which we can write clients for) and parameters (which can easily be specified when launching the node).

Utilizing ROS interfaces are as simple as they sound – in the package usb_cam (http://wiki.ros.org/usb_cam), you can get images from topic ~<camera_name>/image, set parameters by alongside rosrun like “image_width:=500”, and request that ROS node do somethings by writing clients as specified by the srv files.

 

Networking for Linux and ROS – Week 7

Recommended material: SSH essentials, ROS on multiple machines, ROS Network Setup , Turtlebot setup

Robots generally require lots of networking, due to the constraints of portability, limited computational power, and robot’s usual multi-part composition. This unfortunately meant that programmers were often required to deal with the fun, easy networking problems of networking.

Thankfully, ROS, with its approach of treating everything as a node and its publisher-subscriber model, removes much of the difficulty that comes with networking – in fact, there is hardly a difference between the node on the computer and the node elsewhere in the network.

As with anything in life, there are some caveats. One, you still have to deal with the underlying networking for Linux/OSes. Second, you do need to setup the environment so that everything may recognize one another.

First, please refer to the ROS on multiple machines tutorial, referenced above. Below are basic information/definition about networking below.

I.P. Address (Internet protocol address) – the number that is used for the network devices (routers, internet service providers) to identify you. There are several different IPs – there is one specific to your machine, generated by your machine (127.0.0.1), the local address generated by the router (or school network in our case, and the address used over the internet (e.g. the address I provided for the virtual Linux instance).

DNS (Domain Name Resolution) – a server that translates an IP address to a rememberable name (google.com); if you look at the ROS network setup, there is a local alternative to this by changing the configuration file.

Static/Dynamic address – Once declared static, IP addresses are each dedicated to a machine, not changing once it comes on and off. For dynamic addressing, each machine in the network is given an available address when they log in to the network, using method called DHCP. Typically, most large networks and routers do dynamic addressing, and static address is used generally for small, private networks or tools that require constant address (e.g. websites).

Port – Necessary appendage to IP addresses for there to be a connection; it generally indicates the type of the connection – 80 is used for HTTP, 25 for SMTP (email), and 22 for SSH. Beyond these commonly used ports, you can use the ports for your own communications. Network admins or routers with strict security policy tend to block all the ports that are not used, as restricting ports restricts malicious people accessing the network in via brute force.

Firewall –As mentioned above, routers can block ports and specific IP addresses from being used for outside communications. Some ways to get around this is to use something called VPN (Virtual Private Network) and pass that data you were share through one port through a port already open (Port 80 of HTTP generally).

MAC Address – Hardware-specific (to network card) addresses used for – you guessed it – hardware identification. Note that MAC addresses are only known to each once they are connected; you can’t connect to another computer using MAC Address.

Now, back to the page. To summarize the page, let me emphasize that once you set up the ROS_MASTER_URI environmental variable (variables that are stored and used in the shell like bash) and the connections, the ROS nodes on separate computers can communicate immediately using ROS methods like topics and services.

Once those things are done, you should be set – that is, of course, if you were able to setup everything without trouble.

If you are indeed in trouble, refer to the tools below to troubleshoot the issue.

ssh – as always, you could use it to access the other computer through a network. You can also scp to copy files through ssh.

ifconfig – reveals network information, like MAC address, device name (wlan0, etc), and IP address

ping – checks if the computer is connected to a specific IP address. To test connection to local computers, use ping <local_address>. To test internet connection, use ping 8.8.8.8 – which is one of the Google’s addresses. You can also verify things like network latency, if there is any network “packets” being dropped (not getting response).

tracert – Use it to check what devices are between your network connection.

Router address – The router configurations are accessible by typing its address into a browser, and are generally addresses with two one’s at the end of the addresses of the computer (i.e. 192.168.1.103 computer address generally translates to 192.168.1.1 for routers).

nmap / ping broadcasting – To find the IP address of the devices of same network, broadcasting a message in the network and waiting for a response is one of the ways to find it.