Overview

The Mjolnir robot was born on a dirty napkin during a robotics discussion at lunch one day. The original idea was to build a remote-controlled, stair-climbing robot with live video feed, and a deployable flying reconnaissance drone. We spent a lot of time researching the stair climbing design and many hours debating multi-wheels vs. tri-star wheels vs. tank treads vs. multi-tank treads and every combination in between. In the end we chose a tapering double track design for the sake of simplicity and versatility, but had our fabrication skills been more up to speed I think the coolness of the tri-star design would have carried the day.

The project itself is named after Mjolnir, the legendary hammer of Thor. Obviously, our thinking here was heavily influenced by our nerd mentality, but the primary reason for chosing Mjolnir was its parallel to the robot’s deployable flying reconnaissance drone feature. We wanted to build a sub-robot that could launch from the host robot platform and that, like Mjolnir, would always return to it’s origin. Unfortunately, we were forced to cut this feature from the final design due to complexity and cost. Instead we decided to keep the name and code a software return to home feature for the robot itself. This too, turned out to be overly ambitious given our the capabilities of our hardware, and although the feature is technically available, it has been disabled in the current version of the robot due to insufficient memory in the robot’s brain (the Arduino Uno).

Despite these several setbacks, we are very pleased with what we actually ended up building. The remote control design was inspired by Jonathan Bennet’s excellent Wifi Robot project. He uses a WRT54G router running OpenWRT to wirelessly interface into the drive circuitry of an RC car. The general  idea is that a software client will transmit navigation commands over TCP/IP to a CarServer program running on the WRT54G router. This CarServer program is responsible for relaying all navigation commands received from the TCP/IP client to the UART port of a PIC microcontroller via a hack enabled serial port on the router. The PIC microcontroller, in turn, interfaces directly to the drive circuitry of the RC car and simply sets the forward, backward, left, and right drive pins high or low depending on the navigation commands received over the serial port. While I have to give Bennett a lot of hacker kudos for such an impressive design (especially the serial port hack and WRT54G CarServer program), we decided to go with a simpler approach for our robot. After a bit of discussion, we realized we could accomplish the same level of wireless connectivity using an ethernet shielded Arduino Uno with a non-hacked WRT54G router (or any wireless router for that matter). That saved us the trouble of installing OpenWRT, enabling serial communication in the OS, writing a relay program, and hacking the router for access to the serial port. In some ways we missed out on a lot of fun because of this decision, but given the considerable amount of time it took us to complete this project in the first place, I think we made the right choice. Hopefully we’ll have the opportunity to do a cool Linux project sometime in the future.

As requested, here is a quick video that describes Mjolnir’s design and demonstrates some of its capabilities. Thanks again to my good friend Derrick Gibson for allowing us to use his song Building 7 as the soundtrack for our video.

Theory of Operation

The Mjolnir robot can be controlled by any client application capable of transmitting text based data over TCP/IP. In fact, our first client was ConnectBot, a simple telnet (and SSH) client for Android. We wanted something a little more polished so we ended up writing our own Android app (Mjolnir Control Panel) which allowed us to integrate video streaming, camera control and navigation into a single intuitive UI. At a very high level, Mjolnir is simply a client/server software bundle running over a wireless network. When you supply power to the robot, you are actually activating a wireless network with two attached servers–the IP Camera and the ethernet shielded Arduino. Specifically, the IP camera has a built-in web server with a set of CGI applications that allow it to be controlled over HTTP and the ethernet shielded Arduino accepts TCP/IP connections on port 23 for the purpose of receiving navigation commands and translating them into motion. Any client wishing to interface with the robot simply joins the robot’s network, establishes a TCP/IP connection to one or both servers, and then issues the appropriate navigation and/or camera commands. These primary components and their relationships are illustrated in the diagram below.

Construction

Admittedly, most of us here at section9 have a stronger background in software development than in fabrication or engineering and this fact is evident in Mjolnir’s physical design. In the past, we have experimented with welding our own robot frames but the process, while fun, has proven to be expensive, labor intensive, and time consuming. For these reasons we decided to do what any good programmer would do and avoid as much work as possible. The end result is that Mjolnir’s chassis, track, track assembly, motors and miscellaneous pieces were purchased from Vex Robotics, a supplier of modular robotics components. I’m not sure that we saved any money, but designing and constructing a satisfactory model required only few hours of effort.

We purchased the following material for the construction phase but had quite a few extra parts in the end. Our total expenses amounted to approximately $250 for the Vex parts alone. In hindsight, we really only required about $100 in parts, and I think if we were to build the same robot all over again, we could have cut our expenses in this category down to under $50 by utilizing basic hardware store components and a little ingenuity.

Electronics

The electrical system of Mjolnir is essentially two distinct circuits coupled together by a pair of optoisolators. The first circuit supplies power to all of the logical components of the robot while the second circuit supplies power to the motors. By electrically isolating the two circuits, we were able to prevent voltage irregularities introduced by the motors from damaging or otherwise affecting the logical components like the Arduino, router, and IP camera. In order for the Arduino (on the logic circuit) to safely control the tank tracks (on the motor circuit) we decided to use optoisolators to relay the control signal. The Arduino outputs two PWM motor control signals, one for each motor. Each signal is fed into an optoisolator which in turn allows current to flow into the respective motor controller input connection. One difficulty we encountered at this stage was that we did not realize the motor controllers required an input voltage very close to 5 volts. We spent several hours analyzing and fine tuning the circuit with with a scope before we stumbled upon the correct voltage range. Our solution was to add a 7805 voltage regulator to the motor circuit and use its output as input to the motor controller by way of the optoisolator’s internal transistor. The circuits are detailed in the diagrams below.

There are several problems with this design that we’d like to eventually address. The first is that two electrically isolated circuits are not really necessary. It should be possible to drive both the motors and all logical components safely using a single power supply. Unfortunately, that is beyond our electrical expertise at the moment. Another issue is that the logical circuit is not entirely free from motor induced voltage irregularities. The IP camera, which resides on the logical circuit, contains internal servos used to control panning and tilting. These motors draw relatively low amounts of current, but they do cause the LCD display to dim noticeably whenever they are in use. We also noticed that they will occasionally cause the Ethernet Shield to malfunction which results in the robot going haywire and racing off in a random direction. We kind of like this feature, but wish that the robot was equipped with whirling vibroblades when it did this.

Software

Two major software components were developed as part of this project–SARC (Simple Arduino Robot Control) and MCP (Mjolnir Control Panel). Both are Free Software and are available for download from our GitHub Mjolnir Repository. We decided to release MCP under the terms of the GNU GPLv3.0 and SARC under the terms of the GNU LGPLv2.1.

SARC is the software that operates all robot hardware, except the IP camera. It runs on the Arduino and is responsible for handling all TCP/IP communication, processing navigation commands, operating the LCD display, and controlling the motors. It was written to be modular and can be compiled with various directive definitions in order to enable/disable specific features and/or hardware. For example, defining the DEBUG directive causes the SARC to be compiled with support for debug output. There are many similar directives, but not all of them are compatible. Please see the SARC README for more information.

If you are just getting started in Arduino or are only interested in a basic understanding of how SARC works, we recommend the beta version of the program which we adapted from Lady Ada’s Arduino Ethernet+SD tutorial. This version works with the circuit defined above and a standard telnet client, but does not support the LCD display.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/* Prototype code for Simple Arduino Robotic Control.
* Control is via Ethernet and simple character-based commands.
* This is specialized for a tank (tracked) drive with 2 motors.
* Copyright (c) 2011 Leland Green... and Section9
*
* Version: 1.e (Beta)
*
* Adapted from Arduino Ethernet library example sketches
* for Section9 (http://section9.choamco.com/)
* By: Leland Green... Email: aboogieman (_at_) gmail.com
* Odysseus Email: odysseus.section9@gmail.com
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either the GNU General Public License version 2
* or the GNU Lesser General Public License version 2.1, both as
* published by the Free Software Foundation.
*
* To use, telnet to the Arduino and send commands. Commands are the
* same as used for many RPG's, with a few additions. They *are* case
* sensitive:
*
* w = Accelerate forward
* W = Full speed forward
* s = Accelerate backward
* S = Full speed backward
* a = Steer left
* d = Steer Right
* q = Stop (immediate)
*
* Note that the PULSE definitions are for Vex Robotics systems.
* Also note that if this is on a robot, you need either WiFi or
* a wireless router onboard so you can telnet to it. :)
*
* We (Section9) have great plans for an actual prototype. Initial
* plans are to use a wireless router. The project is called Mjolnir.
* See the Section9 URL above for blog updates and other plans.
*
*/


#include
#include "Ethernet.h"
#include

/************ VEX MOTOR CONTROL DEFINITIONS ************/
#define VEX_BRAKE 200
#define VEX_FULL_FORWARD 2000
#define VEX_NEUTRAL 1500
#define VEX_FULL_REVERSE 1000
#define VEX_SPEED_DELTA 50
#define MOVEMENT_TIMEOUT 2000

/************ ROBOT COMMAND DEFINITIONS ************/
#define CSTOP 'q'
#define CFORWARD 'w'
#define CREVERSE 's'
#define CLEFT 'a'
#define CRIGHT 'd'
#define CFORWARD_FULL 'W'
#define CREVERSE_FULL 'S'
#define CMAINTAIN 'm'

/************ PIN DEFINITIONS ************/
#define PIN_LEFT_SERVO 2
#define PIN_RIGHT_SERVO 3

/************ ETHERNET SHIELD CONFIG ************/
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x3B, 0xB2 }; // MAC address
byte ip[] = { 192, 168, 1, 99 }; // IP address
byte gateway[] = { 192, 168, 1, 1 }; // Gateway address
byte subnet[] = { 255, 255, 255, 0 }; // Subnet mask
unsigned int port = 23; // Port number (Telnet)
Server server = Server(port);

/************ SERVO CONFIG ************/
Servo leftTrackServo;
Servo rightTrackServo;
int leftTrackSpeed = VEX_NEUTRAL;
int rightTrackSpeed = VEX_NEUTRAL;
unsigned long lastMoveTime;
boolean isMoving = false;

void setup()
{
  // init serial communication for debugging
  Serial.begin(9600);

  // init servos
  Serial.println("Initializing servos.");
  leftTrackServo.attach(PIN_LEFT_SERVO);
  rightTrackServo.attach(PIN_RIGHT_SERVO);

  // init ethernet shield
  Serial.println("Initializing ethernet.");
  Ethernet.begin(mac, ip, gateway, subnet);
  server = Server(port);
  server.begin();
}

void loop()
{
  // output debug info
  Serial.println("Waiting for client...");

  // check for an incoming client
  Client client = server.available();

  // if client found, begin parsing robot control commands
  if (client) {

    // output debug info
    Serial.println("Client acquired...");

    // echo valid commands to user
    client.println("Movement commands:");
    client.println("------------------");
    client.println("Stop - q");
    client.println("Forward - w");
    client.println("Reverse - s");
    client.println("Left - a");
    client.println("Right - d");
    client.println("Full forward - W");
    client.println("Full Reverse - S");

    // process user input as long as connection persits
    while (client.connected()) {

      // check for user input
      if (client.available()) {

        // read the next character from the input buffer
        char c = client.read();

        // output debug info
        Serial.print("Received command: ");
        Serial.println(c);

        // process command
        switch (c)
        {
          case CSTOP:
            client.println("Full Stop");
            stopMovement();
            break;

          case CFORWARD:
            client.println("Accelerating Forward.");
            moveForward();
            break;

          case CREVERSE:
            client.println("Accelerating backward.");
            moveBackward();
            break;

          case CLEFT:
            client.println("Turning left.");
            turnLeft();
            break;

          case CRIGHT:
            client.println("Turning right.");
            turnRight();
            break;

          case CFORWARD_FULL:
            client.println("Full speed ahead!");
            leftTrackSpeed = VEX_FULL_FORWARD;
            rightTrackSpeed = VEX_FULL_FORWARD;
            updateServos();
            break;

          case CREVERSE_FULL:
            client.println("Full speed reverse!");
            leftTrackSpeed = VEX_FULL_REVERSE;
            rightTrackSpeed = VEX_FULL_REVERSE;
            updateServos();
            break;

          case CMAINTAIN:
            client.println("Maintaining current speed.");
            lastMoveTime = millis();
            break;

          default:
            client.print("Unrecognized command: ");
            client.println(c);
        }
      } else // no input from user to process
      {
        // stop movement if movement time limit exceeded
        if (isMoving) {
          if (millis() - lastMoveTime >= MOVEMENT_TIMEOUT) {
            Serial.println("Movement timeout.");
            stopMovement();
          }
        }
      }
    }

    Serial.println("Connection terminated.");
    stopMovement();
  }
}

void updateServos()
{
  leftTrackServo.writeMicroseconds(leftTrackSpeed);
  rightTrackServo.writeMicroseconds(rightTrackSpeed);
  lastMoveTime = millis();

  if ((leftTrackSpeed == VEX_NEUTRAL) && (rightTrackSpeed == VEX_NEUTRAL))
    isMoving = false;
  else
   isMoving = true;

  Serial.print("Left Track = ");
  Serial.println(leftTrackSpeed);
  Serial.print("Right Track = ");
  Serial.println(rightTrackSpeed);
  Serial.print("isMoving = ");
  Serial.println(isMoving, BIN);
}

void moveForward()
{
  leftTrackSpeed = min(VEX_FULL_FORWARD, leftTrackSpeed + VEX_SPEED_DELTA);
  rightTrackSpeed = min(VEX_FULL_FORWARD, rightTrackSpeed + VEX_SPEED_DELTA);
  updateServos();
}

void moveBackward()
{
  leftTrackSpeed = max(VEX_FULL_REVERSE, leftTrackSpeed - VEX_SPEED_DELTA);
  rightTrackSpeed = max(VEX_FULL_REVERSE, rightTrackSpeed - VEX_SPEED_DELTA);
  updateServos();
}

void turnLeft()
{
  leftTrackSpeed = max(VEX_FULL_REVERSE, leftTrackSpeed - VEX_SPEED_DELTA);
  rightTrackSpeed = min(VEX_FULL_FORWARD, rightTrackSpeed + VEX_SPEED_DELTA);
  updateServos();
}

void turnRight()
{
  leftTrackSpeed = min(VEX_FULL_FORWARD, leftTrackSpeed + VEX_SPEED_DELTA);
  rightTrackSpeed = max(VEX_FULL_REVERSE, rightTrackSpeed - VEX_SPEED_DELTA);
  updateServos();
}

void stopMovement()
{
  leftTrackSpeed = VEX_NEUTRAL;
  rightTrackSpeed = VEX_NEUTRAL;
  updateServos();
}

MCP is the software that interfaces to SARC and the IP camera’s embedded web server. MCP is an Android based client application that integrates Mjolnir’s video streaming, camera control, and navigation into a single touch based UI. Touching the Connect button will initiate TCP/IP connection attempts to Mjolnir’s IP camera (video subsystem) and Ethernet Shield (navigation subsystem). Both subsystems operate independently of one another and MCP is capable of fully operating one subsystem even if the other subsystem is unavailable. Interacting with the UI simply causes commands to be transmitted asyncronously to the appropriate subsystem. For example, tapping on the left edge of the video display will cause a “pan left” command to be transmitted to the video subsystem while tapping on the upper section of the directional graphic will cause the “accelerate forward” command to be transmitted to the navigation subsystem.

Since Android does not natively support the MJPEG video output of our IP camera, we had to write our own decoding and display routines. This was difficult due to limited availability of documentation on the subject. In the end, we were able to write a decoder that can be easily adapted for general purpose usage. This was largely possible because of the work of Padde at Anddev.org and Brian Peek at Coding4Fun Channel 9. Thanks to both of you for your excellent work!

Lastly, one of our objectives for this project from a software point of view was to write it so that the development for all software components could be performed from a single platform independent IDE. The original beta version of SARC was written using the excellent Arduino IDE which is platform indepent but limited to Arduino projects. Our solution to this problem was to use the Arduino Eclipse Plugin by Jantje. This allowed us to use Eclipse for both the Arduino based SARC component as well as the Android based MCP component. Unfortunately setup and configuration of this plugin can be a little problematic. In hindsight, I think it would have been better for us to use a more standard and stable AVR plugin for Eclipse and adapt it to work for Arduino, but the Arduino plugin get’s the job done and we are able to build and deploy both SARC and MCP from Eclipse on Windows, Mac or Linux. Please see the SARC README for information on setting up this plugin for use with our project.

Final Words

Mjolnir has been our most ambitious project to date and we’ve learned a lot of valuable lessons in the process. The first that comes to mind is that it is really hard to finish something as complex as this, especially if it’s a group effort. We worked on Mjolnir from April 2011 to August 2012–approximately 17 months. Admittedly, we didn’t work on it every day, or even every month, but there were many times when the temptation to delve into something else was difficult to resist.

Another valuable lesson was that cross-platform compatibility is a very high ideal indeed. We spent several weeks getting our environments set up correctly and restructuring our projects to achieve platform independence for development. Little hangups like this can be immensely frustrating when all you really want to do is code something that works, but being able to collaborate with other developers regardless of their background in or access to a certain platform is well worth the extra work.

Finally, we learned that robotics is expensive. Wow! The total cost for this project in parts alone approached $500. Of course, we could have reduced this significantly had low costs been a major concern for us, but it wasn’t. We deliberately chose to spend less time worrying about expenses so we could spend more time learning and having fun. This isn’t always an option in development and, in fact, can be a really bad habit. They say the cost of ignorance is often great, but that’s how we all start out. :)

So what’s next? Well, we probably won’t continue development for Mjolnir, but we’re not done building robots. We ran into a pretty hard limit with Arduino’s memory capacity and processing power. Although there are a number of ways we could do more with less on the Arduino, I think it is more likely that we will give the next robot a Raspberry Pi or Beagle{Board{,xM},Bone} brain. This would dramatically increase our processing and storage capability and allow us to build the robot operating software on top of a fully fledged Linux environment. The possibilities here are mind-blowing and we are very excited about the opportunity to work with these devices.

In addition to upgrading the brain, we’d also like to upgrade the power system in our next design. Specifically, we’d like to supply power to all elecrical components (both logical and motorized) from a single power source which can be recharged dynamically via onboard solor panels or a detachable tether.

And while we’re shooting for the moon, we also plan to code the robot operating software to use AI for localization and navigation. There will still be a client application, but it’s primary function will be for status monitoring and configuration and not strictly remote control as is currently the case with Mjolnir.

In the meantime, we’ll probably do a few software only projects and book reviews so stay posted and thanks very much for supporting us with your interest!