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 +4 -4
- data/README.md +18 -1
- data/bin/crubyflie +8 -3
- data/lib/crubyflie/input/input_reader.rb +1 -1
- data/lib/crubyflie/input/joystick_input_reader.rb +79 -49
- data/lib/crubyflie/version.rb +1 -1
- data/spec/joystick_input_reader_spec.rb +124 -39
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f8c948aed7ee8dc5776a9d2b26ceb5ce6673792
|
4
|
+
data.tar.gz: 6314537e66334c3e403142c0c217af9bb832d54a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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()
|
@@ -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
|
-
|
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
|
-
|
146
|
-
|
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
|
-
|
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]
|
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
|
-
|
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 =
|
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
|
-
|
214
|
-
#
|
215
|
-
#
|
216
|
-
def
|
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
|
-
|
220
|
-
|
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
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
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 [
|
267
|
-
#
|
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
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
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)
|
data/lib/crubyflie/version.rb
CHANGED
@@ -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
|
-
:
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
156
|
-
:
|
157
|
-
:
|
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
|
-
|
166
|
-
:
|
167
|
-
:
|
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
|
-
|
176
|
-
:
|
177
|
-
:
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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(
|
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 ==
|
236
|
+
value.should == 34749.5
|
187
237
|
end
|
188
238
|
|
189
239
|
it "should not throotle the change rate when increasing thrust" do
|
190
|
-
|
191
|
-
:
|
192
|
-
:
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
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 "#
|
206
|
-
it "should
|
207
|
-
v = @joystick.send(:
|
208
|
-
|
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
|
212
|
-
v = @joystick.send(:
|
213
|
-
|
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
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|