-
Notifications
You must be signed in to change notification settings - Fork 5
Interpreting Controller Data
The controller is associated with an event. When new controller data is received, it is emitted. The consumeControllerData(data) function in index.js of 'remote' interpretes the contents of data
. data
is a JSON string with information about moving forwards, backwards, etc.
The controller has several different potential outputs- the user may specify forwards/backwards motion, strafing left/right, changing pitch up/down, changing elevation up/down, and rotating clockwise/counter-clockwise. However, there is no single motor to control 'going forwards' (or whatever), and in most cases the user will specify multiple movements at any given time- for example, the user might want to strafe and go forwards at the same time. To make this happen, some math magic is needed.
First, let's define some layout stuff. Check out my awesome model of the rov below:
_____ ____ _____
\ LF \ / F \ / RF /
\____\____\____/____/____/
| |
| |
| |
| |
| |
__|____________________|_
/ LB / / B \ \ RB \
/____/ \____/ \____\
Let me explain what's happening here. The ROV has 6 thrusters- 2 of them, labeled 'F' and 'B', are pointing up. The others are arranged in a vector configuration. They're all set to 45 degree angles off of the main chassis. So, to get it to make a movement, we combine vectors. For example, going forward has us set LF, RF, LB, and RB to the same values in the positive direction, while strafing has us set LF and RB in the positive direction and LB and RF to the negative direction.
Using that idea of combining vectors, we can translate from the user input of 'forwards' and 'rotate' and so on into an array of movements. The order of that array is [F, LF, RF, B, LB, RB]. With that in mind, some arrays are constructed to help define the movements. They represent the direction of each thruster as it corresponds to each movement. In these arrays, one can move in the positive direction (represented by '1'), the negative direction (represented by '-1'), or the thruster isn't needed for this movement so it's represented by '0'.
There are several arrays that help represent motion- their usefulness becomes apparent later.
- Forwards/Backwards Forwards is the positive direction, and backwards is negative forwards.
[0, 1, 1, 0, 1, 1]
- Strafe Strafing right is the positive direction, and strafing left is the negative direction.
[0, 1, 1, 0, -1, 1]
- Yaw (rotate) Rotation is represented in the code using 'y' for yaw instead of 'r' for rotate, to remove ambiguity regarding around what axis rotation occurs. It also removes ambiguity around older versions of code that anticipated having 'roll' as an option.
Yaw in the clockwise direction is positive, and the counter-clockwise direction is negative.
[0, 1, -1, 0, 1, -1]
- Elevation Moving up/down is referred to as elevation, because pitch could also be described as up/down (and elevation covers both the up and down cases).
Changing elevation up is positive, and changing elevation down is negative.
[1, 0, 0, 1, 0, 0]
- Pitch Pitch up (meaning the front of the ROV is going up relative to the back) is positive, while pitch down (front of rov is going down relative to back) is negative.
[1, 0, 0, -1, 0, 0]
So here's the interesting bit of the consumeControllerData(data)
function:
237 // These arrays represent directions of thrusters to achieve certain movements.
238 // Arrays are in the order [F, LF, RF, B, LB, RB]
239 let f_arr = [ 0, 1, 1, 0, 1, 1]; // forwards (front is positive)
240 let s_arr = [ 0, 1, -1, 0, -1, 1]; // strafe (right is positive)
241 let y_arr = [ 0, 1, -1, 0, 1, -1]; // yaw (clockwise is positive)
242 let e_arr = [ 1, 0, 0, 1, 0, 0]; // elevate (up is positive)
243 let p_arr = [ 1, 0, 0, -1, 0, 0]; // pitch (pitch up is positive)
244
245 let setpoint = [0,0,0,0,0,0];
246
247 for(let i = 0; i < 6; i++) {
248 setpoint[i] += joystick[0]*f_arr[i];
249 setpoint[i] += joystick[1]*s_arr[i];
250 setpoint[i] += joystick[3]*y_arr[i];
251 setpoint[i] += joystick[5]*e_arr[i];
252 setpoint[i] += joystick[2]*p_arr[i];
253 }
254
255 let max_e = Math.abs(Math.max(setpoint[0], setpoint[1]));
256 if(max_e > 1) {
257 setpoint[0] /= max_e;
258 setpoint[3] /= max_e;
259 }
260
261 let max_l = Math.abs(Math.max(setpoint[2], setpoint[3], setpoint[4], setpoint[5]));
262 if(max_l > 1) {
263 setpoint[1] /= max_l;
264 setpoint[2] /= max_l;
265 setpoint[4] /= max_l;
266 setpoint[5] /= max_l;
267 }
268
269 // The hardware location of the PWM isn't 0-5, so a final translation happens here.
270 pwm.setDutyCycle(1, setpoint[1]); // Front
271 pwm.setDutyCycle(2, setpoint[4]); // Left Back
272 pwm.setDutyCycle(3, setpoint[1]); // Left Front
273 pwm.setDutyCycle(8, setpoint[2]); // Right Front
274 pwm.setDutyCycle(9, setpoint[5]); // Right Back
275 pwm.setDutyCycle(11, setpoint[3]); // Back
Lines 239-243 define those representative arrays discussed previously. The setpoint starts in the same format as one of these representative arrays- 6 indices, each representing a thruster to control. The value for a thruster to be set to is found by taking the value of a joystick times the representative array for that joystick. Note that each joystick represents a single motion- forwards, pitch, etc. These values are all added to the set point value. This is not what can be directly used- everything is in proportion, but values are not representative of what the user wants (several situations may arise where set point values are greater than 1, and having a duty cycle >100% doesn't make sense).
To fix this, the set point must be scaled according to the maximum value, where the maximum value is 100% duty cycle and everything else is relative to that. However, it's not quite that simple- the F and B thrusters can operate independently of the LF, LB, RF, RB thrusters. As a result, it doesn't make sense to scale them down based off of each other. In addition, scaling should only happen once a value is greater than 1- otherwise, everything is in the right proportion already, and to scale through simple division would maximize movements that the user may want to keep minimal.
As a result, the maximum of the F and B is found, and the maximum of the l (linear) thrusters is found. The absolute value of this is taken because we want to keep the directional information that the sign on each value gives. If that value is greater than 1, the relevant indices are divided by the relevant maximum to perform the scaling.
The final step is to execute the PWM. The hardware doesn't use the PWM port locations sequentially, so this is a final bit of translation to assign what I've defined as 'left front' to what the board defines it as.
By WIT IEEE's MATE ROV Team.
These documents cover more 'how things work' rather than the nitty-gritty stuff.
- Controlling the Lift Bag
- Interpreting Controller Data
- The Leveling Subsystem
- Network Streaming Video
Function-by-function breakdown per JS package.
-
bag-control
: A module that aids in sending commands to the lift bag. -
botProtocol
: TODO -
botSocket
: TODO -
controller
: TODO -
deploy
: TODO -
leveler
: A module to control the leveler subsystem. -
nugget-logger
: TODO -
remote
: TODO -
surface
: TODO
systemd
allows us to start processes when the Pi powers on.
Stuff about our Arduino code, details depending on the file.
- TODO
Overview of the bash script(s).
- TODO
Tools we've created to make the usage and development process easier.
-
temp
: TODO