crubyflie 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +674 -0
  5. data/README.md +99 -0
  6. data/Rakefile +15 -0
  7. data/bin/crubyflie +85 -0
  8. data/configs/joystick_default.yaml +48 -0
  9. data/crubyflie.gemspec +50 -0
  10. data/examples/params_and_logging.rb +87 -0
  11. data/lib/crubyflie/crazyflie/commander.rb +54 -0
  12. data/lib/crubyflie/crazyflie/console.rb +67 -0
  13. data/lib/crubyflie/crazyflie/log.rb +383 -0
  14. data/lib/crubyflie/crazyflie/log_conf.rb +57 -0
  15. data/lib/crubyflie/crazyflie/param.rb +220 -0
  16. data/lib/crubyflie/crazyflie/toc.rb +239 -0
  17. data/lib/crubyflie/crazyflie/toc_cache.rb +87 -0
  18. data/lib/crubyflie/crazyflie.rb +282 -0
  19. data/lib/crubyflie/crazyradio/crazyradio.rb +301 -0
  20. data/lib/crubyflie/crazyradio/radio_ack.rb +48 -0
  21. data/lib/crubyflie/crubyflie_logger.rb +74 -0
  22. data/lib/crubyflie/driver/crtp_packet.rb +146 -0
  23. data/lib/crubyflie/driver/radio_driver.rb +333 -0
  24. data/lib/crubyflie/exceptions.rb +36 -0
  25. data/lib/crubyflie/input/input_reader.rb +168 -0
  26. data/lib/crubyflie/input/joystick_input_reader.rb +280 -0
  27. data/lib/crubyflie/version.rb +22 -0
  28. data/lib/crubyflie.rb +31 -0
  29. data/spec/commander_spec.rb +67 -0
  30. data/spec/console_spec.rb +76 -0
  31. data/spec/crazyflie_spec.rb +176 -0
  32. data/spec/crazyradio_spec.rb +226 -0
  33. data/spec/crtp_packet_spec.rb +79 -0
  34. data/spec/crubyflie_logger_spec.rb +39 -0
  35. data/spec/crubyflie_spec.rb +20 -0
  36. data/spec/input_reader_spec.rb +136 -0
  37. data/spec/joystick_cfg.yaml +48 -0
  38. data/spec/joystick_input_reader_spec.rb +238 -0
  39. data/spec/log_spec.rb +266 -0
  40. data/spec/param_spec.rb +166 -0
  41. data/spec/radio_ack_spec.rb +43 -0
  42. data/spec/radio_driver_spec.rb +227 -0
  43. data/spec/spec_helper.rb +51 -0
  44. data/spec/toc_cache_spec.rb +87 -0
  45. data/spec/toc_spec.rb +187 -0
  46. data/tools/sdl-joystick-axis.rb +69 -0
  47. metadata +222 -0
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ Crubyflie - A Ruby client and libraries for Crazyflie
2
+ =====================================================
3
+
4
+ Crubyflie is a Ruby rewrite of the [Crazyflie quadcopter](http://www.bitcraze.se/category/crazyflie/) Python [client libraries](https://bitbucket.org/bitcraze/crazyflie-pc-client), with some customizations.
5
+
6
+ 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.
7
+
8
+ Disclaimer
9
+ ----------
10
+
11
+ Crubyflie is in early stage of development, very untested.
12
+
13
+ Features
14
+ --------
15
+
16
+ * Crubyflie can be used to fly a Crazyflie device using a Joystick and the Crazyradio USB dongle
17
+ * Crubyflie exposes an API that allows to control the copter, read logging, parameters and console easily
18
+ * Crubyflie runs headless
19
+
20
+ Not included...
21
+ ----------------
22
+ * No fancy UI.
23
+ * No flash utility (yet?).
24
+ * No idea how this works in other OSs that are not Linux, but in theory it should work in all with some small fixes. I welcome you to take on this task if you are interested.
25
+ * No support for Ruby <= 1.8.7 (maybe it works who knows... I haven't tested but since Crubyflie relies heavily on threading probably it does not work so good).
26
+
27
+ Fyling the Crazyflie
28
+ --------------------
29
+
30
+ The easiest way to do it is to run the `crubyflie` command. This will connect to the first visible quadcopter using the first available joystick on your computer (you can modify this parameters with the appropiate flags):
31
+
32
+ > crubyflie2.0 -h
33
+ Options:
34
+ --joystick-id, -j <i>: Joystick ID (default: 0)
35
+ --cf-uri, -f <s>: Crazyflie URI (defaults to first one found in scan)
36
+ --config, -c <s>: Joystick configuration, defaults to default cfg in configs/ (default:
37
+ /usr/lib64/ruby/gems/2.0.0/gems/crubyflie-0.0.1/lib/crubyflie/input/../../../configs/joystick_default.yaml)
38
+ --help, -h: Show this message
39
+
40
+ A template/default configuration file (which works for me and my PS3-like controller :)) is provided with the gem (in the `configs/` folder). You should modify this file to fit it to your needs (configuration parameters are commented). The most tricky parameter in axis is the `:max_change_rate`. Depending on your controller, you will find the input is excessively throotled or not. I recommend that you play with this value.
41
+
42
+ If you are wondering about your Joystick's axis IDs, ranges etc, you will find a `sdl-joystick-axis.rb` script under `tools` that lets you open a joystick and check what the SDL library can read from it. It might come handy.
43
+
44
+ If you need help just open an issue or contact me.
45
+
46
+ Using the Crazyflie API
47
+ -----------------------
48
+
49
+ While both Python and Ruby APIs expose access to params, logging, console and commander facilities, Crubyflie does it in sligthly different way.
50
+
51
+ Crubyflie access to facilities is offered fully under the Crazyflie instance object, let's see it as code:
52
+
53
+ ```ruby
54
+ require 'crubyflie'
55
+ include Crubyflie
56
+
57
+ # Connect
58
+ @cf = Crazyflie.new()
59
+ @cf.open_link(@radio_uri)
60
+
61
+ # Interface to the logging facility
62
+ @cf.log.create_log_block(...)
63
+ @cf.log.start_logging(...) do |log_values|
64
+ ...
65
+ end
66
+ @cf.log.stop_logging(...)
67
+ @cf.log.delete_log(...)
68
+
69
+
70
+ # Interface to the param facility
71
+ @cf.param.get_value(...) do |value|
72
+ ...
73
+ end
74
+
75
+ @cf.param.get_value(...) do |value|
76
+ ...
77
+ end
78
+
79
+ @cf.param.set_value(...)
80
+
81
+ # Interface to the commander facility
82
+ @cf.commander.send_setpoint(...)
83
+
84
+ # Interface to the console facility
85
+ @cf.console.read do |value|
86
+ ...
87
+ end
88
+ ```
89
+
90
+ That's pretty much all. As you see, instead of declaring callbacks, registering them etc. We let the user pass blocks, which are run when the data is available.
91
+ In Crubyflie, params are read and set synchronously, while the block passed to `start_logging()` will be called repeteadly and asynchrnously until `stop_logging()` is invoked. Console offers both synchronous `read()` and asynchronous `start_reading()` options.
92
+
93
+ There are some examples in the `examples` folder. Read the gem documentation to get full information of the parameters for each function call.
94
+
95
+
96
+ Contributing
97
+ ------------
98
+
99
+ Contributions are awesome! :) I'd love some help here, so be invited to open issues, send pull requests or give me your opinion.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "yard"
4
+ require "yard/rake/yardoc_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |t|
7
+ t.pattern = 'spec/crubyflie_spec.rb'
8
+ end
9
+
10
+ YARD::Rake::YardocTask.new(:yard)
11
+
12
+ task :doc => :yard
13
+ task :test => :spec
14
+ task :default => [:spec, :doc]
15
+
data/bin/crubyflie ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright (C) 2013 Hector Sanjuan
4
+
5
+ # This file is part of Crubyflie.
6
+
7
+ # Crubyflie is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+
12
+ # Crubyflie is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
19
+
20
+ require 'crubyflie'
21
+ require 'trollop'
22
+
23
+ # This script allows to fly the Crazyflie usingi the Joystick controller
24
+ # and initializing a Crazyflie. It might need a bit of cleanup but it does
25
+ # the job.
26
+
27
+ opts = Trollop::options do
28
+ opt(:joystick_id, "Joystick ID",
29
+ :type => :int,
30
+ :default => 0,
31
+ :short => '-j')
32
+ opt(:cf_uri, "Crazyflie URI (defaults to first one found in scan)",
33
+ :type => :string,
34
+ :short => '-f')
35
+ opt(:config, "Joystick configuration, defaults to default cfg in configs/",
36
+ :type => :string,
37
+ :default => Crubyflie::Joystick::DEFAULT_CONFIG_PATH,
38
+ :short => '-c')
39
+ end
40
+
41
+ include Crubyflie
42
+ # Uncomment to turn on debugging
43
+ # $debug = true
44
+ cf = Crazyflie.new('/tmp/crubyflie')
45
+
46
+ # Before opening any link, scan for interfaces
47
+ uris = cf.scan_interface
48
+ if uris.empty?
49
+ logger.error("No crazyflies found")
50
+ exit 1
51
+ end
52
+ logger.info("Found copters at: #{uris}")
53
+ if uri = opts[:cf_uri] && !uris.include?(opts[:cf_uri])
54
+ logger.error("Provided URI not found")
55
+ exit 1
56
+ end
57
+
58
+ uri ||= uris.first
59
+
60
+ # Open a link to the copter
61
+ cf.open_link(uri)
62
+ # Make sure everything is still good
63
+ exit 1 if !cf.active?
64
+
65
+ # Initialize the joystick - ID defaults to 0
66
+ joystick = Joystick.new(opts[:config], opts[:joystick_id])
67
+ joystick.init()
68
+
69
+ exit = false
70
+ Signal.trap("SIGINT") do
71
+ exit = true
72
+ end
73
+
74
+ while cf.active? && !exit do
75
+ # We should be good sending 10 ticks per second
76
+ # it says that somewhere in the docs
77
+ joystick.read_input()
78
+ joystick.apply_input(cf)
79
+ sleep 0.1
80
+ end
81
+
82
+ joystick.quit()
83
+ cf.close_link()
84
+ warn "\n"
85
+ logger.info("Bye bye!")
@@ -0,0 +1,48 @@
1
+ # Default joystick configuration file. It mimics the PS3_Mode2 original
2
+ # configuration file
3
+ :type: "Joystick"
4
+ :axis:
5
+ 0:
6
+ :description: "Roll Axis"
7
+ :action: :roll
8
+ :input_range: "-32768:32767" # Optional - SDL dependant. Defaults to this.
9
+ :output_range: "-30:30" # Min/Max crazyflie angle in degrees.
10
+ :max_change_rate: 600 # Max angle change rate per second. Optional
11
+ :dead_zone: "-100:100" # Deadzone, within input range
12
+ :invert: false # Invert the axis
13
+ :calibration: 0 # This value is added to the raw value read from joystick
14
+ 1:
15
+ :description: "Pitch Axis"
16
+ :action: :pitch
17
+ :input_range: "-32768:32767"
18
+ :output_range: "-30:30"
19
+ :max_change_rate: 600
20
+ :dead_zone: "-100:100"
21
+ :invert: true
22
+ :calibration: 0
23
+ 2:
24
+ :description: "Yaw Axis"
25
+ :action: :yaw
26
+ :input_range: "-32768:32767"
27
+ :output_range: "-200:200"
28
+ :max_change_rate: 800
29
+ :dead_zone: "-1000:1000"
30
+ :invert: false
31
+ :calibration: 0
32
+ 3:
33
+ :description: "Thrust axis"
34
+ :action: :thrust
35
+ :input_range: "-32768:32767"
36
+ :output_range: "0:80" # Exception: Min/max thrust output is represented in 0-100%!
37
+ # Max change rate per second when lowering thrust. Note that this
38
+ # is a change ranging between ranges 9500 and 60000
39
+ :max_change_rate: 40000
40
+ :dead_zone: "-100:100" # Deadzone, within input range
41
+ :invert: true
42
+ :calibration: 0
43
+
44
+ :buttons:
45
+ 0:
46
+ :action: :switch_xmode
47
+ 1:
48
+ :action: :close_link
data/crubyflie.gemspec ADDED
@@ -0,0 +1,50 @@
1
+ # coding: utf-8
2
+ # Copyright (C) 2013 Hector Sanjuan
3
+
4
+ # This file is part of Crubyflie.
5
+
6
+ # Crubyflie is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+
11
+ # Crubyflie is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
18
+
19
+ lib = File.expand_path('../lib', __FILE__)
20
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
21
+ require 'crubyflie/version'
22
+
23
+ Gem::Specification.new do |spec|
24
+ spec.name = "crubyflie"
25
+ spec.version = Crubyflie::VERSION
26
+ spec.authors = ["Hector Sanjuan"]
27
+ spec.email = ["hector@convivencial.org"]
28
+ spec.description = <<EOF
29
+ Client library to control a Crazyflie. This library allows to talk to a
30
+ crazyflie using the USB radio dongle.
31
+ EOF
32
+ spec.summary = "Crazyflie ruby client"
33
+ spec.homepage = "https://github.com/hsanjuan/crubyflie"
34
+ spec.license = "GPLv3"
35
+
36
+ spec.files = `git ls-files`.split($/)
37
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
38
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
39
+ spec.require_paths = ["lib"]
40
+
41
+ spec.add_dependency "libusb"
42
+ spec.add_dependency "rubysdl"
43
+ spec.add_dependency "trollop"
44
+
45
+ spec.add_development_dependency "bundler", "~> 1.3"
46
+ spec.add_development_dependency "rake"
47
+ spec.add_development_dependency "rspec"
48
+ spec.add_development_dependency "yard"
49
+ spec.add_development_dependency "simplecov"
50
+ end
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (C) 2013 Hector Sanjuan
3
+
4
+ # This file is part of Crubyflie.
5
+
6
+ # Crubyflie is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+
11
+ # Crubyflie is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
18
+
19
+ # Require the Crubyflie gem
20
+ require 'crubyflie'
21
+ include Crubyflie # easy to use things in namespace
22
+
23
+ # Create a new Crazyflie with cache folder "cache"
24
+ cf = Crazyflie.new('cache')
25
+ # Before opening any link, scan for interfaces
26
+ ifaces = cf.scan_interface
27
+ if ifaces.empty?
28
+ logger.error("No crazyflies found")
29
+ exit 1
30
+ end
31
+ logger.info("Found interfaces: #{ifaces}")
32
+
33
+ # Open a link to the first interface
34
+ cf.open_link(ifaces.first)
35
+ # Make sure everything is still good
36
+ exit 1 if !cf.active?
37
+
38
+ # Write the TOCs to stdout
39
+ puts "Log TOC"
40
+ puts cf.log.toc.to_s
41
+ puts
42
+ puts "Param TOC"
43
+ puts cf.param.toc.to_s
44
+
45
+ # Read some parameters
46
+ puts "--------"
47
+ cf.param.get_value("attitudepid.kp_pitch") do |value|
48
+ puts "kp_pitch: #{value}"
49
+ end
50
+ cf.param.get_value("attitudepid.ki_pitch") do |value|
51
+ puts "ki_pitch: #{value}"
52
+ end
53
+ cf.param.get_value("attitudepid.kd_pitch") do |value|
54
+ puts "kd_pitch: #{value}"
55
+ end
56
+ puts "--------"
57
+
58
+
59
+ # We use 1 variable, is_toc = true
60
+ # The last two 7 means it is stored and fetched as float
61
+ log_conf_var = LogConfVariable.new("stabilizer.pitch", true, 7, 7)
62
+
63
+ # We create a configuration object
64
+ # We want to fetch it every 0.1 secs
65
+ log_conf = LogConf.new([log_conf_var], {:period => 10})
66
+
67
+ # With the configuration object, register a log_block
68
+ block_id = cf.log.create_log_block(log_conf)
69
+
70
+ # Start logging
71
+ # Counter on how many times we have logged the pitch
72
+ logged = 0
73
+ cf.log.start_logging(block_id) do |data|
74
+ warn "Pitch: #{data['stabilizer.pitch']}"
75
+ logged += 1
76
+ end
77
+
78
+ # Wait until we have hit the log_cb 10 times
79
+ while (logged < 10)
80
+ sleep 1
81
+ end
82
+
83
+ # Stop logging
84
+ cf.log.stop_logging(block_id)
85
+
86
+ # After finishing, close the link!
87
+ cf.close_link()
@@ -0,0 +1,54 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2013 Hector Sanjuan
3
+
4
+ # This file is part of Crubyflie.
5
+
6
+ # Crubyflie is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+
11
+ # Crubyflie is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
18
+
19
+ module Crubyflie
20
+ # The Commander facility is used to send control information to the
21
+ # Crazyflie. You want to use this class to fly your Crazyflie
22
+ class Commander
23
+ # Initialize the facility
24
+ def initialize(crazyflie)
25
+ @crazyflie = crazyflie
26
+ end
27
+
28
+ # Send a setpoint to the Crazyflie
29
+ #
30
+ # The roll, pitch, yaw values are floats with positive or negative
31
+ # values. The range should be the value read from the controller
32
+ # ([-1,1]) multiplied by the maximum angle change rate
33
+ # @param roll [float] the roll value
34
+ # @param pitch [float] the pitch value
35
+ # @param yaw [float] the yaw value
36
+ # @param thrust [Integer] thrust is an integer value ranging
37
+ # from 10001 (next to no power) to
38
+ # 60000 (full power)
39
+ def send_setpoint(roll, pitch, yaw, thrust, xmode=false)
40
+ if xmode
41
+ roll = 0.707 * (roll - pitch)
42
+ pitch = 0.707 * (roll + pitch)
43
+ end
44
+
45
+ packet = CRTPPacket.new()
46
+ packet.modify_header(nil, Crazyflie::CRTP_PORTS[:commander], nil)
47
+ data = [roll, -pitch, yaw, thrust]
48
+ # send 3 floats and one unsigned short (16 bits) (all little endian)
49
+ data = data.pack('eeeS<')
50
+ packet.data = data.unpack('C*')
51
+ @crazyflie.send_packet(packet, false)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,67 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2013 Hector Sanjuan
3
+
4
+ # This file is part of Crubyflie.
5
+
6
+ # Crubyflie is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+
11
+ # Crubyflie is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
18
+
19
+
20
+ module Crubyflie
21
+ # The Console facility is used to read characters that have been
22
+ # printer using printf in the crazyflie firmware
23
+ class Console
24
+
25
+ # Initialize the console
26
+ # @param crazyflie [Crazyflie]
27
+ def initialize(crazyflie)
28
+ @crazyflie = crazyflie
29
+ @in_queue = crazyflie.crtp_queues[:console]
30
+ @read_thread = nil
31
+ end
32
+
33
+ # Reads all the characters from the Crazyflie that are queued, until
34
+ # the queue is empty, and then return only after executing the
35
+ # given block for each packet that was in the queue.
36
+ # @param block [Proc] a block to call with the read information
37
+ def read(&block)
38
+ while @in_queue.size > 0 do
39
+ packet = @in_queue.pop() # block
40
+ yield(packet.data_repack) if block_given?
41
+ end
42
+ end
43
+
44
+
45
+ # Reads all the characters from the Crazyflie constantly
46
+ # and yields on the the given block is called with the payload.
47
+ # This call will return immediately, but the block will be
48
+ # called asynchronously until #stop_reading() is called.
49
+ # Use stop_read() to stop.
50
+ # @param block [Proc] a block to call with the read information
51
+ def start_reading(&block)
52
+ stop_reading()
53
+ @read_thread = Thread.new do
54
+ loop do
55
+ read(&block)
56
+ sleep 0.3 # no hurries?
57
+ end
58
+ end
59
+ end
60
+
61
+ # Stops reading characters from the Crazyflie
62
+ def stop_reading
63
+ @read_thread.kill() if @read_thread
64
+ @read_thread = nil
65
+ end
66
+ end
67
+ end