Thursday, May 22, 2014

Robotic Remote Controller Protocol Design

http://blog.oscarliang.net/robotic-remote-controller-protocol-design/

Robotic Remote Controller Protocol Design

Remote Controller Protocol

The Communication Protocol

Remote Controller Protocol design is the core part of the DIY Remote Controller Project, which can also be the most difficult part if you are aiming for a sophisticated design. I have had similar design experience in my past project designingcommunication protocol.
The protocol I am talking about is the format of a series of values we send from the remote controller to the client, each value is one byte packet sent using Serial.write(). I also call this series of values “command” and it looks something like this:
   1      122       93       28       19        1       0        1
| type | value1 | value2 | value3 | value4 | value5 | value6 | value7 |
“Type” is the type of protocol. I am going to design various type of protocols, each type uses different control values and precisions. The “Type” number tells the client how many and what kind of value we are expecting, and how to use these values.
This is a demo video using the protocol, controlling a quadruped robot.

A Trick To Encode Button/Toggle States

Since the buttons and toggles states only have values of 0 or 1 (boolean value), so it would not be very wise to transfer each of the input using a full byte. What I have done is to treat each button/toggle as 1 bit, and I have 8 of them so it’s just enough to make 1 byte (8 bits). That way I can save 7 bytes each time I send a command! I call this process of converting Button States “Button States Encoding”. When it reaches the client side, we have to do the reverse process to convert 1 byte value into 8 boolean values, and I call it “Button States Decoding”.

Button States Encoding

This is an example on how it works. Imagine we align the inputs like this
 | toggle1 | toggle2 | toggle3 | toggle4 | button1 | button2 | button3 | button4 |
(1) If Button3 is pressed and not any others, we have 0000 0010 in binary, which is 2 in decimal, and that’s the number we are going to send using this Serial.write(2);
(2) Another example if Toggle2 is on, and button2 is pressed and not others, we have 01000100, which is 2^6 + 2^2 = 64 + 4 = 68.
Notice the maths (+ additions and ^ powers) we have to do when doing the button encodings, this is quite computationally expensive. To improve this, we can manipulate what we call “Bit Shift Operator“. It can help reduce calculation time.
For example (1), we can now do button3 << 1, which is 2
For example (2), toggle2 << 6 + button2 << 2 = 68
1// convert 4 toggle and 4 buttons state into binary, and then convert binary to byte number for transmission
2// expect bit1 - bit8 are zeros or ones
3byte EncodeButton(bool bit1, bool bit2, bool bit3, bool bit4, bool bit5, boolbit6, bool bit7, bool bit8){
4 
5    byte sum = 0;
6    sum += bit1 << 7;
7    sum += bit2 << 6;
8    sum += bit3 << 5;
9    sum += bit4 << 4;
10    sum += bit5 << 3;
11    sum += bit6 << 2;
12    sum += bit7 << 1;
13    sum += bit8 << 0;
14 
15    return sum;
16 
17}

Button States Decoding

To do the reverse at the client side, we need to test each bit of the received byte value to see if they are 0 or 1 and assign it to a variable that represents each button. Or you can also have a switch case statement to look it up, it’s up to you. From above examples:
(1) If we received 2, we need to check each bit start from most significant bit. Assuming Toggle1 is on, we should have received a value 10000000 = 2^7 = 128. But 2/128 < 1 thus Toggle1 = 0. Just like this, we work all the way down to Button3.
Assume Button3 is pressed, we should have 00000010 = 2, 2/2 >= 1, thus button3 = 1 is true!
Assume Button4 is pressed, we should have 00000001 = 1, (2-2)/2 < 1, thus button4 = 0!
So, all variables are 0′s, except button3.
Again, like Button Encoding, we can explode the “Bit Shift Operator” trick. From the above example, we need to test each bit starting from the most significant bit.
We received 2, 2 >> 7 = 0, thus toggle1 = 0; And work your way down.
2 >> 1 >= 1, so button3 = 1, (2-2) >> 0 < 1, thus button4 = 0;
1void DecodeButton(byte byte1, bool *bit1, bool *bit2, bool *bit3, bool *bit4,bool *bit5, bool *bit6, bool *bit7, bool *bit8){
2// Usage: DecodeButton(buttonByte, &toggle1, &toggle2, &toggle3, &toggle4, &button3, &button4, &button5, &button6);
3    byte sum = 0;
4 
5    *bit1 = byte1 >> 7;
6 
7    sum += (*bit1)*128;
8    *bit2 = (byte1-sum) >> 6;
9 
10    sum += (*bit2)*64;
11    *bit3 = byte1-sum >> 5;
12 
13    sum += (*bit3)*32;
14    *bit4 = byte1-sum >> 4;
15 
16    sum += (*bit4)*16;
17    *bit5 = byte1-sum >> 3;
18 
19    sum += (*bit5)*8;
20    *bit6 = byte1-sum >> 2;
21 
22    sum += (*bit6)*4;
23    *bit7 = byte1-sum >> 1;
24 
25    sum += (*bit7)*2;
26    *bit8 = byte1-sum >> 0;
27 
28}

Types of Remote Controller Protocols

As I have discussed in the last post, we are going to have multiple protocols that transmit data in different format and level of accuracy, so users are backed by these choices of different protocols depend on the situation. Sometimes you might want the smallest latency protocol (less accurate but faster transmission), and sometimes you might prefer high resolution commands (high accuracy but slower transmission). At the moment I have implemented these protocols, these examples give you an idea what the commands look loke.

1. All Controls, Short Version

All the values are made up as an example. Each value is a byte which has a max value of 255. Same applies to all four examples.
   0     122     93    28     19     60     55    199   250      50
| type | pot1 | pot2| pot3 | pot4 | js1x | js1y| js2x | js2y | buttons |

2. All Controls, Long Version (full accuracy)

    0         2           93           0           19           3
| type | pot1(high) | pot1(low) | pot2(high) | pot2(low) | pot3(high)
     0           155          1           155           3          0
| pot3(low) | pot4(high) | pot4(low) | js1x(high) | js1x(low) | js1y(high)
      3          51            1           12           25         21
| js1y(low) | js2x(high) | js2x(low) | js2y(high) | js2y(low) | buttons |

3. Selected Controls, Short Version

For this example, I selected a potentiomete, a joystick and all buttons and toggles.
   0     129     93     0        19
| type | pot1 | js1x | js1y | buttons |

4. Selected Controls, Long Version (full accuracy)

For this example, I selected a potentiomete, a joystick and all buttons and toggles.
   
   0      2          93         0        19       3       20         0 
|type| pot1(high)| pot1(low)| js1x(h)| js1x(l)| js1y(h)| js1y(l)| buttons

Channel

The term “channel” is used quite a lot when it comes to commercial RC transmitters. Channel can mean 2 totally different things:
1. the number of “things” you can control, for example for a joystick you need at least 2 channel, one for left right, one for up down (1 Degree of freedom each channel).
2. the number of different transmission frequency you can use to avoid conflicts with other remote controllers. (pretty much like Radio channels)
Remote Controller Protocol
Fortunately none of these would be relevant to my remote controller. For (1), because the way I send data, I can choose to send input data from each control one by one if I want, so no matter how many thing I need to control, it would be do-able (although the more control means more data to transmit thus takes longer). For (2), I can use something called “controller identifier” in the command I send to differentiate different controllers, so the commands will only be picked up at the client side with the pre-defined identifier. This is only an idea but totally do-able.
If you want to discuss or share your ideas, you can post something in our forum here.

No comments:

Post a Comment