deadprogrammer / flying_robot_blimpduino

Implementation of flying_robot add-ons for Ruby Arduino Development (RAD) to support the Blimpduino

flying_robot_blimpduino / flying_robot_blimpduino.rb
100644 415 lines (360 sloc) 11.713 kb
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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# flying_robot_blimpduino
#
# Implementation of flying_robot for the blimpduino
#
# Based on flying_robot library (http://flyingrobo.com)
# Uses Ruby Arduino Development to create Unmanned Aerial Vehicles
# Written by Ron Evans (http://deadprogrammersociety.com)
#
# This sketch that uses the flying_robot parser, you just need to implement the methods that make up its interface
# so that it will respond to the standard command set.
#
# The following commands are supported:
# (h)ail - See if the UAV can still respond. Should send "Roger" back.
# (s)tatus - Grab a snapshot of all instrument readings plus any other status info that the UAV can support
# (e)levators - Set the elevators. The command supports two parameters:
# direction - enter 'u' for up, 'c' for centered, or 'd' for down
# deflection - enter an angle between 0 and 90 degrees
# (r)udder - Set the rudder. This command supports two parameters:
# direction - enter 'l' for left, 'c' for centered, or 'r' for right
# deflection - enter an angle between 0 and 90 degrees
# (t)hrottle - Set the throttle. This command supports two parameters:
# direction - enter 'f' for forward, or 'r' for reverse
# speed - enter a percentage from 0 to 100
# (i)nstruments - Read the current data for one of the installed instruments on the UAV. This command supports one parameter:
# id - enter an id for which instrment readings should be returned from. If there is not an instrument installed
# for that id 'Invalid instrument' should be returned
# (a)utopilot - Turns on/off the autopilot. This command supports one parameter:
# id - enter an id for which autopilot routine should be turned on. Entering '0' is reserved to turn Off the autopilot.
#
class FlyingRobotBlimpduino < ArduinoSketch
  serial_begin :rate => 38400
  
  # read battery voltage, to protect our expensive LiPo from going below minimum power.
  input_pin 0, :as => :battery
  
  # L293D motor controller
  output_pin 2, :as => :right_motor
  output_pin 3, :as => :right_speed
  
  output_pin 4, :as => :left_motor
  output_pin 5, :as => :left_speed
  
  # vectoring servo
  output_pin 10, :as => :vectoring_servo, :device => :servo
  
  # IR sensors
  input_pin 8, :as => :ir_front
  input_pin 6, :as => :ir_right
  input_pin 7, :as => :ir_rear
  input_pin 9, :as => :ir_left
  
  # ultrasonic sensor
  input_pin 16, :as => :range_finder
  output_pin 15, :as => :range_finder_reset
  @dist = "0, long"
  
  # optional LEDs
  # output_pin 12, :as => :led_forward
  # output_pin 17, :as => :led_right
  # output_pin 11, :as => :led_back
  # output_pin 13, :as => :led_left
  
  # optional digital compass
  output_pin 19, :as => :wire, :device => :i2c, :enable => :true
  
  define "MINIMUM_ALTITUDE 36"
  define "MAX_SPEED 127"
  define "AUTOPILOT_THRUST_FACTOR 6"
  @forward = "1, byte"
  @reverse = "0, byte"
  @direction = "1, byte"
  @left_direction = "1, byte"
  @right_direction = "1, byte"
  @left_motor_speed = "0, long"
  @right_motor_speed = "0, long"
  @deflection = "0, byte"
  @deflection_percent = "0, long"
  @deflection_val = "0, long"
  @autopilot_update_frequency = "500, unsigned long"
  @last_autopilot_update = "0, unsigned long"
  @led_update_frequency = "500, unsigned long"
  @last_led_update = "0, unsigned long"
  
  def setup
    # this should zero out the L293
    activate_thrusters
  end
  
  # main command loop, required for any arduino program
  def loop
    be_flying_robot
    battery_test
    range_finder.update_maxsonar(range_finder_reset)
    update_ir_receiver(ir_front, ir_right, ir_rear, ir_left)
    #update_leds
    
    handle_autopilot_update
    
    process_command
    servo_refresh
  end
 
  # flying robot interface
  def hail
    serial_println "Roger"
  end
  
  def status
    serial_println "Status: operational"
    serial_print "Battery: "
    check_battery_voltage
    serial_print "IR: "
    check_ir
    serial_print "Altitude: "
    check_altitude
    serial_print "Compass: "
    check_compass
  end
  
  def elevators
    print_current_command("Elevators", current_elevator_deflection)
    if current_elevator_direction == 'c'
      @deflection = 22
    end
    if current_elevator_direction == 'u'
      @deflection = 22 - (current_elevator_deflection / 4)
    end
    if current_elevator_direction == 'd'
      @deflection = 22 + (current_elevator_deflection / 4)
    end
 
    if @deflection < 0
      @deflection = 0
    end
    if @deflection > 45
      @deflection = 45
    end
    
    vectoring_servo.position @deflection
    servo_refresh
  end
    
  def rudder
    print_current_command("Rudder", current_rudder_deflection)
    set_thrusters
  end
  
  def throttle
    print_current_command("Throttle", current_throttle_speed)
    set_thrusters
  end
    
  def instruments
    if current_command_instrument == 'b'
      check_battery_voltage
    end
    
    if current_command_instrument == 'i'
      check_ir
    end
 
    if current_command_instrument == 'a'
      check_altitude
    end
 
    if current_command_instrument == 'c'
      check_compass
    end
  end
  
  def autopilot
    if current_command_autopilot == '0'
      # autopilot cancel, so we should shutoff motors
      throttle_speed = 0
      set_thrusters
      serial_println "Autopilot Is Off"
    end
    
    if current_command_autopilot == '1'
      # follow IR beacon
      autopilot_on
      serial_println "Autopilot 1 On"
    end
 
    if current_command_autopilot == '2'
      # use digial compass to set orientation to North
      autopilot_on
      serial_println "Autopilot 2 On"
    end
  end
  
  # motor control
  def set_thrusters
    if current_throttle_direction == 'f'
      @left_direction = @forward
      @right_direction = @forward
    else
      @left_direction = @reverse
      @right_direction = @reverse
    end
    
    calculate_motor_speeds
    activate_thrusters
  end
  
  def activate_thrusters
    left_motor.L293_send_command(left_speed, @left_direction, @left_motor_speed)
    right_motor.L293_send_command(right_speed, @right_direction, @right_motor_speed)
  end
  
  def calculate_motor_speeds
    if current_rudder_direction == 'c'
      @left_motor_speed = current_throttle_speed / 100.0 * MAX_SPEED
      @right_motor_speed = current_throttle_speed / 100.0 * MAX_SPEED
    end
    if current_rudder_direction == 'l'
      if current_rudder_deflection >= 45
        if (current_throttle_direction == 'f')
          @left_direction = @reverse
        else
          @left_direction = @forward
        end
                
        @left_motor_speed = hard_turn_throttle_speed / 10000
        @right_motor_speed = current_throttle_speed / 100.0 * MAX_SPEED
      else
        @left_motor_speed = adjusted_throttle_speed / 10000
        @right_motor_speed = current_throttle_speed / 100.0 * MAX_SPEED
      end
    end
    if current_rudder_direction == 'r'
      if current_rudder_deflection >= 45
        if (current_throttle_direction == 'f')
          @right_direction = @reverse
        else
          @right_direction = @forward
        end
                
        @left_motor_speed = current_throttle_speed / 100.0 * MAX_SPEED
        @right_motor_speed = hard_turn_throttle_speed / 10000
      else
        @left_motor_speed = current_throttle_speed / 100.0 * MAX_SPEED
        @right_motor_speed = adjusted_throttle_speed / 10000
      end
    end
  end
  
  def adjusted_throttle_speed
    @deflection_percent = (current_rudder_deflection * 100 / 90)
    @deflection_val = 100 - @deflection_percent
    return @deflection_val * current_throttle_speed * MAX_SPEED
  end
  
  def hard_turn_throttle_speed
    @deflection_percent = (current_rudder_deflection * 100 / 90)
    return @deflection_percent * current_throttle_speed * MAX_SPEED
  end
  
  # LEDs
  # def update_leds
  # if millis() - @last_led_update > @led_update_frequency
  # leds_off
  #
  # if ir_beacon_forward
  # led_forward.digitalWrite( HIGH );
  # end
  #
  # if ir_beacon_right
  # led_right.digitalWrite( HIGH );
  # end
  #
  # if ir_beacon_back
  # led_back.digitalWrite( HIGH );
  # end
  #
  # if ir_beacon_left
  # led_left.digitalWrite( HIGH );
  # end
  #
  # if maxsonar_distance > 0 && maxsonar_distance < MINIMUM_ALTITUDE
  # leds_on
  # end
  #
  # @last_led_update = millis()
  # end
  # end
  #
  # def leds_on
  # led_forward.digitalWrite( HIGH );
  # led_right.digitalWrite( HIGH );
  # led_back.digitalWrite( HIGH );
  # led_left.digitalWrite( HIGH );
  # end
  #
  # def leds_off
  # led_forward.digitalWrite( LOW );
  # led_right.digitalWrite( LOW );
  # led_back.digitalWrite( LOW );
  # led_left.digitalWrite( LOW );
  # end
  
  # instruments
  # check LiPo battery votage
  def check_battery_voltage
    serial_println int(battery.voltage)
  end
  
  # check state of infrared sensor array
  def check_ir
    serial_println current_ir_beacon_direction
  end
  
  # check reading on ultrasonic range finder
  def check_altitude
    serial_println maxsonar_distance
  end
  
  # check reading on digital compass
  def get_compass
    prepare_compass
    read_compass
  end
 
  def check_compass
    get_compass
    serial_print heading
    serial_print "."
    serial_println heading_fractional
  end
  
  # autopilot
  def handle_autopilot_update
    if is_autopilot_on && millis() - @last_autopilot_update > @autopilot_update_frequency
      if current_command_autopilot == '1'
        navigate_using_ir
      end
 
      if current_command_autopilot == '2'
        navigate_using_compass
      end
    
      @last_autopilot_update = millis()
    end
  end
    
  def navigate_using_ir
    if ir_beacon_not_detected
      @left_motor_speed = 0
      @right_motor_speed = 0
      @left_direction = @forward
      @right_direction = @forward
    end
    
    if ir_beacon_forward
      @left_motor_speed = 0
      @right_motor_speed = 0
      @left_direction = @forward
      @right_direction = @forward
    end
    
    if ir_beacon_right
      @left_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
      @left_direction = @forward
      @right_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
      @right_direction = @reverse
    end
    
    if ir_beacon_back
      @left_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
      @left_direction = @forward
      @right_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
      @right_direction = @reverse
    end
    
    if ir_beacon_left
      @left_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
      @left_direction = @reverse
      @right_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
      @right_direction = @forward
    end
    activate_thrusters
  end
  
  # the Honeywell digital compass is installed by reverse on the Blimpduino by default
  def navigate_using_compass
    get_compass
    
    if heading <= 210 && heading >= 150
      # in front of us, so do nothing
      @left_direction = @forward
      @right_direction = @forward
      @left_motor_speed = 1
      @right_motor_speed = 1
    end
 
    if heading < 150 && heading >= 0
      # facing west, turn right
      @left_direction = @forward
      @right_direction = @reverse
      @left_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
      @right_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
    end
 
    if heading < 360 && heading > 210
      # facing east, turn left
      @left_direction = @reverse
      @right_direction = @forward
      @left_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
      @right_motor_speed = MAX_SPEED / AUTOPILOT_THRUST_FACTOR
    end
 
    activate_thrusters
  end
  
end