crubyflie 0.1.0

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.
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