Tag: iOS SDK

Screenshot 2014.01.30 20.57.11As part of my 2014 challenge to become proficient at robotics, I’m building a robot. I’ll be documenting whatever I can here to share what I learn, and to start I’m looking at control systems. Since my day job is developing iOS applications, it was natural to look at ways of using my iPhone to control and monitor the robot and to do that I developed a library for communicating between iOS and ROS.

Robot Operating System (ROS) is an open source software package that works above the core operating system to provide communication and processing for the different components a robot might have. It has a very large user community and is used by a variety of robots in many different industries. It was also mentioned in the job description I was targeting.

ROS uses “nodes”, which are independent software blocks that connect to the ROS subsystem and communicate with each other. Many different nodes have been contributed by users in the community, from PID controllers to GPS signal analyzers, and one of those is ROSBridge. ROSBridge, like the name suggests, creates an interface to ROS using an industry-standard JSON format inside a websocket. This provides a very easy way for a variety of platforms to create robot controllers.

My library, called RBManager (the RB is for ROSBridge), is a wrapper for the SocketRocket websocket library in iOS. I’ve followed the standard publisher/subscriber/service call architecture to send packets compatible with the ROSBridge Protocol and make it easy to send packets and control your robot. The GitHub page provides a general overview of the structure, but I wanted to take this point to provide a more detailed example project.

To follow along, you’ll need to have a reasonable grasp of ROS concepts and know your way around Xcode and the iOS SDK. We’ll be using the standard turtlesim node. Some things I’ll skip through so it’s probably a good idea if you’ve built an iOS app before.


Download RBManager on GitHub


Open Xcode and create a new empty application, with a single view controller. We’ll keep it simple and contain everything into one view. Add the RBManager files to the project, along with the SocketRocket library and import RBManager.h to the header file of your view controller.

Set up header variables and methods

How the view is laid out is up to you, but below are some of the RBManager related objects and methods that should be added to the header file. UI objects are suggestions for outputting the subscriber data.

RBPublisher * twistPublisher;
RBSubscriber * turtleSubscriber;
UILabel * xLabel;
UILabel * yLabel;
UILabel * linearVelocityLabel;
UILabel * angularVelocityLabel;
RBServiceCall * clearTurtle;
RBServiceCall * resetTurtle;


The labels are largely self explanatory — they will be updated with the current position/velocity of the turtle simulator. The RBSubscriber and RBServiceCall objects are provided by the library and provide access to make those calls to ROSBridge.

Introduction to Messages

Messages are the core object that ROS returns from subscribers and RBManager has an RBMessage type that you override to be able to send and receive data in an object-oriented way. There are lots of messages included in ROS but you can also create your own. To see how to do that in RBManager, take a look at the header file VectorMessage.h

@interface VectorMessage : RBMessage {
    NSNumber * x;
    NSNumber * y;
    NSNumber * z;

@property (nonatomic, strong) NSNumber * x;
@property (nonatomic, strong) NSNumber * y;
@property (nonatomic, strong) NSNumber * z;


VectorMessage, which has the form std_msgs/Vector3 in ROS, is a basic object with properties for the keys that ROS expects. By doing it this way, it’s very easy to use Key-Value-Coding to populate a message object inside each subscriber. Some of the standard messages are included already but creating your own type is as easy as specifying the property type and name. You can even use nested messages like TwistMessage, which is what turtlesim uses and is shown below.

@interface TwistMessage : RBMessage {
    VectorMessage * linear;
    VectorMessage * angular;

@property (nonatomic, strong) VectorMessage * linear;
@property (nonatomic, strong) VectorMessage * angular;


By calling publish, you can get back the NSDictionary representation of the message object. This method is included in the top level RBMessage object and uses KVC to get the property list.

If your message object includes additional properties that are not in the ROS message type, you should override publish directly.

Connecting and Disconnecting

RBManager behaves as a singleton, by referencing [RBManager defaultManager]. This prevents multiple instances from being created. To connect to ROSBridge, it’s as easy as calling

[[RBManager defaultManager] connect:@"ws://robot.local:9090"];

where ws://robot.local:9090 is the IP of your robot. The default port of ROSBridge is 9090, but that can be changed. Disconnecting is just as simple, by calling

[[RBManager defaultManager] disconnect];

Adding Publishers and Subscribers

Handling the publishing process is RBPublisher. After this object is added to the manager, it is automatically advertised to ROS and you can push messages through. To create the object, in the case of turtlesim, use the following method

twistPublisher = [[RBManager defaultManager] addPublisher:@"/turtle1/cmd_vel" messageType:@"geometry_msgs/Twist"];
twistPublisher.label = @"Turtle controller";

In this case, the topic is /turtle1/cmd_vel and the message type is geometry_msgs/Twist, which are both expected by ROS. The label property is just a handy way of differentiating multiple publishers, if you have more than one created. Put this declaration inside the view controller init method and they will be automatically advertised when you connect the socket. If you want to manually un/advertise the publisher afterwards, you would use these methods

[twistPublisher advertise];
[twistPublisher unadvertise];

Subscribers behave in a similar fashion. They are added to the manager and subscribed automatically when the socket is opened. The primary difference is that subscribers also take a callback to receive messages that ROS publishes. In the case of turtlesim, that would look like this

turtleSubscriber = [[RBManager defaultManager] addSubscriber:self selector:@selector(turtlePoseUpdate:) name:@"/turtle1/pose" messageClass:[PoseMessage class]];
turtleSubscriber.throttleRate = 100;

-(void)turtlePoseUpdate:(PoseMessage*)message {
    // read the message and update the view here

Here turtlePoseUpdate: is the method in the view controller that will receive the message update, and it only has one parameter. The ROS topic of /turtle1/pose is generated by turtlesim and it provides a message of type PoseMessage. By providing the class type, RBManager can create and populate the correct message. As seen by the ROSBridge specification, the throttleRate is the interval at which ROS will send a message, in milliseconds. This is to prevent overloading the receiver.

Here is what a valid init and subscriber method would look like inside the view controller

- (id)init
    self = [super init];
    if (self) {
        // Custom initialization
        twistPublisher = [[RBManager defaultManager] addPublisher:@"/turtle1/cmd_vel" messageType:@"geometry_msgs/Twist"];
        twistPublisher.label = @"Turtle Controller";
        turtleSubscriber = [[RBManager defaultManager] addSubscriber:self selector:@selector(turtlePoseUpdate:) name:@"/turtle1/pose" messageClass:[PoseMessage class]];
        turtleSubscriber.throttleRate = 100;
    return self;

-(void)turtlePoseUpdate:(PoseMessage*)message {
    xLabel.text = [NSString stringWithFormat:@"%.5f", [message.x floatValue]];
    yLabel.text = [NSString stringWithFormat:@"%.5f", [message.y floatValue]];
    linearVelocityLabel.text = [NSString stringWithFormat:@"%.5f", [message.linear_velocity floatValue]];
    angularVelocityLabel.text = [NSString stringWithFormat:@"%.5f", [message.angular_velocity floatValue]];

Sending Messages

Using an iPhone with ROSBridge is advantageous because the iPhone has an abundance of sensors that are great for controlling robots. That means sending and receiving messages are a very important component. Doing that with RBManager is fairly straightforward. After connecting and advertising a publisher, create a new message object and submit it.

-(void)sendUpdate {
    CGFloat linearVelocity = xAngle * -4.0;
    CGFloat angularVelocity = zAngle * -4.0;
    TwistMessage * twist = [[TwistMessage alloc] init];
    twist.linear.x = [NSNumber numberWithFloat:linearVelocity];
    twist.angular.z = [NSNumber numberWithFloat:angularVelocity];
    [twistPublisher publish:twist];

In this case, xAngle and zAngle are calculated from the accelerometer. If everything is connected after this, you’ll start to see the turtle move.

Using Service Calls

Service calls are important components for monitoring and updating robot configurations. In the case of turtlesim, this includes changing the background, changing the colour of the pen and killing and spawning turtles. You can think of service calls like publishing a message without content, although some calls do return data. RBManager handles both cases, but doesn’t require adding the object to the queue.

// service call without parameters
resetTurtle = [[RBManager defaultManager] makeServiceCall:self selector:@selector(resetTurtleMessageResponse:) name:@"/reset"];
[resetTurtle send];

// service call with specific ROS message
SetPenMessage * penMessage = [[SetPenMessage alloc] init];
penMessage.r = [NSNumber numberWithFloat:red];
penMessage.g = [NSNumber numberWithFloat:green];
penMessage.b = [NSNumber numberWithFloat:blue];
penMessage.width = [NSNumber numberWithFloat:2];
penMessage.off = [NSNumber numberWithInteger:0];
changePenColour = [[RBManager defaultManager] makeServiceCall:self selector:@selector(changePenMessageResponse:) name:@"/turtle1/set_pen"];
[changePenColour setMessage:penMessage];
[changePenColour send];

The calls are not sent immediately because this allows you to assign additional parameters, like the id. The first example shows sending a call without any parameters. The selector is the callback when the response is returned. When there is no data expected back, the most useful thing to get from the response is a success true/false.

Related to service calls is the process for assigning parameters. These are essentially wrapped service calls that call directly to the ROS API. In turtlesim, this is used to change the background colour

// assigning specific parameters
RBServiceCall * backgroundService = [[RBManager defaultManager] setParam:@"background_r" value:[NSString stringWithFormat:@"%.0f", red]];
[backgroundService send];
backgroundService = [[RBManager defaultManager] setParam:@"background_g" value:[NSString stringWithFormat:@"%.0f", green]];
[backgroundService send];
backgroundService = [[RBManager defaultManager] setParam:@"background_b" value:[NSString stringWithFormat:@"%.0f", blue]];
[backgroundService send];

Although the actual service call uses arrays, it’s unfortunately not possible to include more than one parameter at a time so you have to make 3 separate calls.

That is a detailed, but still pretty open example about integrating RBManager into an iOS application. With the touchscreen and motion sensors, it’s pretty easy to now direct your robot to follow a path on the map or move in relation to the accelerometer position. Hopefully this library can make developing and building robots more efficient so please show me what you build with it! If you find an issue, create a ticket on GitHub or comment below and I’ll look into it. I’ll be continuing to develop it over time, particularly when my robot project actually makes use of it.

Tagged with: , , ,