crubyflie 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c53014aa0e2af1f0f401853e934227e5f2950f38
4
- data.tar.gz: 6729d96811026479a1a0984aa1316506be35af75
3
+ metadata.gz: 9f8c948aed7ee8dc5776a9d2b26ceb5ce6673792
4
+ data.tar.gz: 6314537e66334c3e403142c0c217af9bb832d54a
5
5
  SHA512:
6
- metadata.gz: 81452b7995d89199f233b55c132bded7b8790875754d020ada0831bef8cf9b100b4aa439c4ae340b197e8a3a3add072d9ee9cfb19ffbabc8d278b7bb35641cfe
7
- data.tar.gz: a4ca9a64c904644c1dd756c77a9f47875da126cc1ea72844b2a5a191ec2d849481645c33e64f733054df54425c04e1faeb3fe6dbd84f0d361f6e4f90fb7ca01f
6
+ metadata.gz: cbc016920486d8c234bf3efdc78f895dd70d38ad61f128afca3a80521efb8cfc758ab07441dec8e54e9168285f71f2b0d1d9c48f97f36ab0a1b0f4f6376bbc60
7
+ data.tar.gz: 2478f8cdcf14a77fdfbab4eaad8b6477916c9c4cee8cc92af55afe34214b48caba7a7aa8aa6a1ffb72f8074f705c64bbdd9fe62cb73c8b3a102c96c357c9e469
data/README.md CHANGED
@@ -7,6 +7,12 @@ Crubyflie is a Ruby rewrite of the [Crazyflie quadcopter](http://www.bitcraze.se
7
7
 
8
8
  The Crazyflie is awesome, but I did not know where to start contributing. Therefore I thought that rewriting the code in Ruby would be one way of knowing what is going on and how it works. Along the way I took the time to document all the code so that others can understand it and create tests.
9
9
 
10
+ You may be also interested in some other unofficial Crazyflie clients:
11
+
12
+ * C++: https://github.com/fairlight1337/libcflie
13
+ * Node.js: https://github.com/ceejbot/aerogel
14
+ * Haskell: https://github.com/orclev/crazyflie-haskell
15
+
10
16
  Disclaimer
11
17
  ----------
12
18
 
@@ -17,7 +23,8 @@ Features
17
23
 
18
24
  * Crubyflie can be used to fly a Crazyflie device using a Joystick and the Crazyradio USB dongle
19
25
  * Crubyflie exposes an API that allows to control the copter, read logging, parameters and console easily
20
- * Crubyflie runs headless
26
+ * Crubyflie runs headless
27
+ * Lightweight: If you just want to fly, Crubyflie consumes around 1/2 memory and 1/3 CPU compared to the original Python `cfheadless` utility.
21
28
 
22
29
  Not included...
23
30
  ----------------
@@ -45,6 +52,16 @@ If you are wondering about your Joystick's axis IDs, ranges etc, you will find a
45
52
 
46
53
  If you need help just open an issue or contact me.
47
54
 
55
+ Raspberri Pi
56
+ ------------
57
+
58
+ If you want to use Crubyflie in your Raspberry Pi you need to:
59
+
60
+ sudo apt-get install ruby ruby-dev libsdl-dev
61
+ sudo gem install crubyflie
62
+
63
+ This should provide everything you need to run the `crubyflie` command. Of course you might need to put your user in the `input` group and modify `udev` rules as explained in the [Crazyflie wiki](http://wiki.bitcraze.se/projects:crazyflie:hacks:rasberrypi).
64
+
48
65
  Using the Crazyflie API
49
66
  -----------------------
50
67
 
data/bin/crubyflie CHANGED
@@ -72,11 +72,16 @@ Signal.trap("SIGINT") do
72
72
  end
73
73
 
74
74
  while cf.active? && !exit do
75
- # We should be good sending 10 ticks per second
76
- # it says that somewhere in the docs
75
+ start_time = Time.now.to_f
77
76
  joystick.read_input()
78
77
  joystick.apply_input(cf)
79
- sleep 0.1
78
+ # We should be good sending 10 ticks per second,
79
+ # as it says that somewhere in the docs, so we have
80
+ # 1/10 secs of time per loop. If we are fast, we can sleep
81
+ # a little bit
82
+ consumed_time = Time.now.to_f - start_time
83
+ sleep_time = 0.1 - (consumed_time)
84
+ sleep sleep_time if sleep_time > 0
80
85
  end
81
86
 
82
87
  joystick.quit()
@@ -147,7 +147,7 @@ module Crubyflie
147
147
 
148
148
  if pitch && roll && yaw && thrust
149
149
  m = "Sending R: #{roll} P: #{pitch} Y: #{yaw} T: #{thrust}"
150
- #logger.debug(m)
150
+ logger.debug(m)
151
151
  crazyflie.commander.send_setpoint(roll, pitch, yaw, thrust,
152
152
  @xmode)
153
153
  end
@@ -36,6 +36,8 @@ module Crubyflie
36
36
  DEFAULT_INPUT_RANGE = "-32768:32767"
37
37
  # Default Crazyflie min/max angles in degrees
38
38
  DEFAULT_OUTPUT_RANGE = "-30:30"
39
+ # Default dead zone range
40
+ DEFAULT_DEAD_ZONE = "0:0"
39
41
  # Default configuration file
40
42
  DEFAULT_CONFIG_PATH = File.join(File.dirname(__FILE__), "..","..","..",
41
43
  "configs", "joystick_default.yaml")
@@ -87,12 +89,36 @@ module Crubyflie
87
89
  end
88
90
 
89
91
  axis[id] = action
92
+
93
+ # Parse and fill in ranging values
94
+ [[:input_range, DEFAULT_INPUT_RANGE],
95
+ [:output_range, DEFAULT_OUTPUT_RANGE],
96
+ [:dead_zone, DEFAULT_DEAD_ZONE]].each do |id, default|
97
+ range_s = axis_cfg[id] || default
98
+ start, rend = range_s.split(':')
99
+ start = start.to_i; rend = rend.to_i
100
+ range = {
101
+ :start => start.to_f,
102
+ :end => rend.to_f,
103
+ :width => (Range.new(start,rend).to_a.size() - 1).to_f
104
+ }
105
+ axis_cfg[id] = range
106
+ end
107
+
108
+ # output value max jump per second. We covert to rate/ms
109
+ max_chrate = axis_cfg[:max_change_rate] || 10000
110
+ axis_cfg[:max_change_rate] = max_chrate.to_f / 1000
111
+
112
+ axis_cfg[:last_poll] ||= 0
113
+ axis_cfg[:last_value] ||= 0
114
+ axis_cfg[:invert] ||= false
115
+ axis_cfg[:calibration] ||= 0
116
+
90
117
  end
91
118
 
92
119
  buttons = {}
93
- if config_h[:buttons].nil?
94
- raise JoystickException.new("No buttons section")
95
- end
120
+ config_h[:buttons] = buttons if config_h[:buttons].nil?
121
+
96
122
  config_h[:buttons].each do |id, button_cfg|
97
123
  action = button_cfg[:action]
98
124
  if action.nil?
@@ -141,44 +167,37 @@ module Crubyflie
141
167
  return 0 if axis_conf.nil?
142
168
  is_thrust = axis_conf[:action] == :thrust
143
169
 
170
+ last_poll = axis_conf[:last_poll]
171
+ last_value = axis_conf[:last_value]
172
+ invert = axis_conf[:invert]
173
+ calibration = axis_conf[:calibration]
144
174
 
145
- last_poll = axis_conf[:last_poll] || 0
146
- last_value = axis_conf[:last_value] || 0
147
- invert = axis_conf[:invert] || false
148
- calibration = axis_conf[:calibration] || 0
149
-
150
- input_range_s = axis_conf[:input_range] || DEFAULT_INPUT_RANGE
151
- ir_start, ir_end = input_range_s.split(':')
152
- input_range = Range.new(ir_start.to_i, ir_end.to_i)
153
-
154
- output_range_s = axis_conf[:output_range] || DEFAULT_OUTPUT_RANGE
155
- or_start, or_end = output_range_s.split(':')
156
- output_range = Range.new(or_start.to_i, or_end.to_i)
175
+ input_range = axis_conf[:input_range]
176
+ output_range = axis_conf[:output_range]
157
177
 
158
- # output value max jump per second. We covert to rate/ms
159
- max_chrate = axis_conf[:max_change_rate] || 10000
160
- max_chrate = max_chrate.to_f / 1000
178
+ max_chrate = axis_conf[:max_change_rate]
161
179
 
162
- dead_zone = axis_conf[:dead_zone] || "0:0" # % deadzone around 0
163
- dz_start, dz_end = dead_zone.split(':')
164
- dead_zone_range = Range.new(dz_start.to_i, dz_end.to_i)
180
+ dead_zone = axis_conf[:dead_zone]
165
181
 
166
182
  value = @joystick.axis(axis_id)
183
+
167
184
  value *= -1 if invert
168
185
  value += calibration
169
186
 
170
- # Make sure input falls with the expected range
171
- if value > input_range.last then value = input_range.last end
172
- if value < input_range.first then value = input_range.first end
173
- # Dead zone
174
187
 
175
- if dead_zone_range.first < value && dead_zone_range.last > value
188
+ # Make sure input falls with the expected range and take care of
189
+ # the dead zone
190
+ if dead_zone[:start] < value && dead_zone[:end] > value
176
191
  value = 0
192
+ elsif value > input_range[:end]
193
+ value = input_range[:end]
194
+ elsif value < input_range[:start]
195
+ value = input_range[:start]
177
196
  end
197
+
178
198
  # Convert
179
199
  if is_thrust
180
- value = pre_normalize_thrust(value, input_range, output_range)
181
- value = normalize_thrust(value)
200
+ value = normalize_thrust(value, input_range, output_range)
182
201
  else
183
202
  value = normalize(value, input_range, output_range)
184
203
  end
@@ -210,24 +229,34 @@ module Crubyflie
210
229
  return value
211
230
  end
212
231
 
213
- # The thrust axis is disabled for values < 0. What we do here is to
214
- # convert it to a thrust 0-100% value first and then make sure it
215
- # stays within the limits provided for output range
216
- def pre_normalize_thrust(value, input_range, output_range)
217
- value = normalize(value, input_range, (-100..100))
232
+
233
+ # Returns integer from 9.500 to 60.000 which is what the crazyflie
234
+ # expects
235
+ def normalize_thrust(value, input_range, output_range)
218
236
  value = 0 if value < 0
219
- if value > output_range.last then value = output_range.last
220
- elsif value < output_range.first then value = output_range.first
237
+ range = {
238
+ :start => -100.0,
239
+ :end => 100.0,
240
+ :width => 200.0
241
+ }
242
+ value = normalize(value, input_range, range)
243
+
244
+ if value > output_range[:end] then value = output_range[:end]
245
+ elsif value < output_range[:start] then value = output_range[:start]
221
246
  end
222
- return value
223
- end
224
- private :pre_normalize_thrust
225
247
 
226
-
227
- # Returns integer from 10.000 to 60.000 which is what the crazyflie
228
- # expects
229
- def normalize_thrust(value)
230
- return normalize(value, (0..100), (9500..60000)).round
248
+ range = {
249
+ :start => 0.0,
250
+ :end => 100.0,
251
+ :width => 100.0
252
+ }
253
+
254
+ cf_range = {
255
+ :start => 9500.0,
256
+ :end => 60000.0,
257
+ :width => 50500.0
258
+ }
259
+ return normalize(value, range, cf_range).round
231
260
  end
232
261
  private :normalize_thrust
233
262
 
@@ -263,15 +292,16 @@ module Crubyflie
263
292
 
264
293
  # Linear-transforms a value in one range to a different range
265
294
  # @param value [Fixnum, Float] the value in the original range
266
- # @param from_range [Range] the range from which we want to normalize
267
- # @param to_range [Range] the destination range
295
+ # @param from_range [Hash] the range from which we want to normalize.
296
+ # a range must have :start, :end, :width keys
297
+ # @param to_range [Hash] the destination range
268
298
  # @return [Float] the linear-corresponding value in the destination
269
299
  # range
270
300
  def normalize(value, from_range, to_range)
271
- from_w = from_range.to_a.size.to_f - 1
272
- to_w = to_range.to_a.size.to_f - 1
273
- from_min = from_range.first.to_f
274
- to_min = to_range.first.to_f
301
+ from_min = from_range[:start]
302
+ to_min = to_range[:start]
303
+ to_w = to_range[:width]
304
+ from_w = from_range[:width]
275
305
  # puts "#{to_min}+(#{value.to_f}-#{from_min})*(#{to_w}/#{from_w})
276
306
  r = to_min + (value.to_f - from_min) * (to_w / from_w)
277
307
  return r.round(3)
@@ -18,5 +18,5 @@
18
18
 
19
19
  module Crubyflie
20
20
  # Current gem version
21
- VERSION = "0.1.1"
21
+ VERSION = "0.1.2"
22
22
  end
@@ -91,12 +91,12 @@ describe Joystick do
91
91
  it "should raise exception if the axis or buttons are missing" do
92
92
  cfg = {
93
93
  :type => "Joystick",
94
- :axis => {0 => {:action => :yaw}},
94
+ :buttons => {0 => {:action => :yaw}},
95
95
  }
96
96
  expect(YAML).to receive(:load_file).and_return(cfg)
97
97
  expect {
98
98
  @joystick.read_configuration('baa')
99
- }.to raise_exception(JoystickException, "No buttons section")
99
+ }.to raise_exception(JoystickException, "No axis section")
100
100
  end
101
101
 
102
102
  it "should raise exception if an axis has no action" do
@@ -131,93 +131,178 @@ describe Joystick do
131
131
 
132
132
  describe "#read_axis" do
133
133
  it "should read the axis value with all the defaults" do
134
- @joystick.config[:axis][8] = {:action => :test}
134
+ config = {
135
+ :type => "Joystick",
136
+ :axis => {
137
+ 8 =>{
138
+ :action => :test,
139
+ }
140
+ }
141
+ }
142
+ expect(YAML).to receive(:load_file).and_return(config)
135
143
  expect(@sdl_joystick).to receive(:axis).with(8).and_return(-32768)
144
+ @joystick.read_configuration('baa')
136
145
  value = @joystick.read_axis(8)
137
146
  value.should == -30
138
147
  end
139
148
 
140
149
  it "should put values out of range in range" do
141
- @joystick.config[:axis][8] = {:action => :test}
150
+ config = {
151
+ :type => "Joystick",
152
+ :axis => {
153
+ 8 =>{
154
+ :action => :test,
155
+ }
156
+ }
157
+ }
158
+ expect(YAML).to receive(:load_file).and_return(config)
142
159
  expect(@sdl_joystick).to receive(:axis).with(8).and_return(-40000)
160
+ @joystick.read_configuration('baa')
143
161
  value = @joystick.read_axis(8)
144
162
  value.should == -30
145
163
  end
146
164
 
147
165
  it "should invert values if requested" do
148
- @joystick.config[:axis][8] = {:action => :test, :invert => true}
166
+ config = {
167
+ :type => "Joystick",
168
+ :axis => {
169
+ 8 =>{
170
+ :action => :test,
171
+ :invert => true
172
+ }
173
+ }
174
+ }
175
+ expect(YAML).to receive(:load_file).and_return(config)
149
176
  expect(@sdl_joystick).to receive(:axis).with(8).and_return(-32767)
177
+ @joystick.read_configuration('baa')
150
178
  value = @joystick.read_axis(8)
151
179
  value.should == 30
152
180
  end
153
181
 
154
182
  it "should put values in the deadzone as 0" do
155
- @joystick.config[:axis][8] = {
156
- :action => :test,
157
- :dead_zone => "-40:40"
183
+ config = {
184
+ :type => "Joystick",
185
+ :axis => {
186
+ 8 =>{
187
+ :action => :test,
188
+ :dead_zone => "-40:40"
189
+ }
190
+ }
158
191
  }
192
+ expect(YAML).to receive(:load_file).and_return(config)
159
193
  expect(@sdl_joystick).to receive(:axis).with(8).and_return(23)
194
+ @joystick.read_configuration('baa')
160
195
  value = @joystick.read_axis(8)
161
196
  value.should == 0
162
197
  end
163
198
 
164
199
  it "should normalize the thrust correctly" do
165
- @joystick.config[:axis][8] = {
166
- :action => :thrust,
167
- :output_range => "0:100"
200
+ config = {
201
+ :type => "Joystick",
202
+ :axis => {
203
+ 8 =>{
204
+ :action => :thrust,
205
+ :output_range => "0:100"
206
+ }
207
+ }
168
208
  }
209
+ expect(YAML).to receive(:load_file).and_return(config)
169
210
  expect(@sdl_joystick).to receive(:axis).with(8).and_return(32767)
211
+ @joystick.read_configuration('baa')
170
212
  value = @joystick.read_axis(8)
171
213
  value.should == 60000
172
214
  end
173
215
 
174
216
  it "should throotle the change rate correctly" do
175
- @joystick.config[:axis][8] = {
176
- :action => :test,
177
- :output_range => "100:200",
178
- :input_range => "0:100",
179
- :max_change_rate => 1,
180
- :last_poll => 0.500,
181
- :last_value => 150
217
+ config = {
218
+ :type => "Joystick",
219
+ :axis => {
220
+ 8 => {
221
+ :action => :thrust,
222
+ :output_range => "0:100",
223
+ :input_range => "100:200",
224
+ :max_change_rate => 1,
225
+ :last_poll => 0.500,
226
+ :last_value => 34750
227
+ }
228
+ }
182
229
  }
230
+ expect(YAML).to receive(:load_file).and_return(config)
231
+
183
232
  allow(Time).to receive(:now).and_return(1.0) # 0.5 secs after
184
- expect(@sdl_joystick).to receive(:axis).with(8).and_return(60)
233
+ expect(@sdl_joystick).to receive(:axis).with(8).and_return(140)
234
+ @joystick.read_configuration('baa')
185
235
  value = @joystick.read_axis(8)
186
- value.should == 150.5
236
+ value.should == 34749.5
187
237
  end
188
238
 
189
239
  it "should not throotle the change rate when increasing thrust" do
190
- @joystick.config[:axis][8] = {
191
- :action => :thrust,
192
- :output_range => "0:100",
193
- :input_range => "0:100",
194
- :max_change_rate => 1,
195
- :last_poll => 0.500,
196
- :last_value => 50
240
+ config = {
241
+ :type => "Joystick",
242
+ :axis => {
243
+ 8 =>{
244
+ :action => :thrust,
245
+ :output_range => "0:100",
246
+ :input_range => "0:100",
247
+ :max_change_rate => 1,
248
+ :last_poll => 0.500,
249
+ :last_value => 50
250
+ }
251
+ }
197
252
  }
253
+ expect(YAML).to receive(:load_file).and_return(config)
254
+
198
255
  allow(Time).to receive(:now).and_return(1.0) # 0.5 secs after
199
256
  expect(@sdl_joystick).to receive(:axis).with(8).and_return(100)
257
+ @joystick.read_configuration('baa')
200
258
  value = @joystick.read_axis(8)
201
259
  value.should == 60000
202
260
  end
203
261
  end
204
262
 
205
- describe "#pre_normalize_thrust" do
206
- it "should discard negative axis values" do
207
- v = @joystick.send(:pre_normalize_thrust, -12, (-20..20), (0..100))
208
- v.should == 0
263
+ describe "#normalize_thrust" do
264
+ it "should return values within the range expected by the CF" do
265
+ v = @joystick.send(:normalize_thrust, 5000,
266
+ {
267
+ :start => -10000.0,
268
+ :end => 10000.0,
269
+ :width => 20000.0
270
+ },{
271
+ :start => 0.0,
272
+ :end => 80.0,
273
+ :width => 80.0
274
+ })
275
+ v.should == 34750
276
+ v.should be_an_instance_of Fixnum
209
277
  end
210
278
 
211
- it "should pre-normalize to the expected" do
212
- v = @joystick.send(:pre_normalize_thrust, 5, (-10..10), (0..50))
213
- v.should == 50
279
+ it "should set values under the given range to the min" do
280
+ v = @joystick.send(:normalize_thrust, 0,
281
+ {
282
+ :start => -10000.0,
283
+ :end => 10000.0,
284
+ :width => 20000.to_f
285
+ },{
286
+ :start => 30.0,
287
+ :end => 80.0,
288
+ :width => 50.0
289
+ })
290
+ v.should == 24650 # 30% of 9500-60000
291
+ v.should be_an_instance_of Fixnum
214
292
  end
215
- end
216
293
 
217
- describe "#normalize_thrust" do
218
- it "should return values within the range expected by the CF" do
219
- v = @joystick.send(:normalize_thrust, 50)
220
- v.should == 34750
294
+ it "should set values over the given range to the max" do
295
+ v = @joystick.send(:normalize_thrust, 32767,
296
+ {
297
+ :start => -10000.0,
298
+ :end => +10000.0,
299
+ :width => 20000.to_f
300
+ },{
301
+ :start => 0.0,
302
+ :end => 80.0,
303
+ :width => 80.0
304
+ })
305
+ v.should == 49900 # 80% of 9500-60000
221
306
  v.should be_an_instance_of Fixnum
222
307
  end
223
308
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crubyflie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hector Sanjuan