crubyflie 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|