Skip to content

Commit

Permalink
Inter-robot communication
Browse files Browse the repository at this point in the history
  • Loading branch information
jfcloutier committed May 1, 2016
1 parent 35ea55b commit e55a3de
Show file tree
Hide file tree
Showing 16 changed files with 471 additions and 236 deletions.
5 changes: 3 additions & 2 deletions config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ use Mix.Config
# watchers to your application. For example, we use it
# with brunch.io to recompile .js and .css sources.
config :ev3, Ev3.Endpoint,
# url: [host: "192.168.1.100"],
# url: [host: "192.168.1.100"],
# url: [host: "10.10.10.97"],
http: [port: 4000],
debug_errors: true,
code_reloader: true,
cache_static_lookup: false,
# watchers: []
# watchers: []
watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin"]]

# Watch static and templates for browser reloading.
Expand Down
7 changes: 7 additions & 0 deletions lib/ev3/body/communicating.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule Ev3.Communicating do
use Behaviour

@doc "Communicate info to other robots in a team"
defcallback communicate(info :: any, team :: atom) :: :any

end
28 changes: 28 additions & 0 deletions lib/ev3/body/device.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,34 @@ defmodule Ev3.Device do
end
end

#### TODO

def mode(device_type, :nxt) do
case device_type do
:infrared -> "nxt-i2c"
:touch -> "nxt-analog" # works
:gyro -> "nxt-analog"
# :color -> "???" # NOT SUPPORTED but EV3 Color sensor works on brickpi
:ultrasonic -> "nxt-i2c"
:large -> "tacho-motor" # automatically detected
# :medium -> "tacho-motor" # does not exist
end
end

def device_code(device_type, :nxt) do
case device_type do
:infrared -> "ht-nxt-ir-receiver 0x01" # HiTechnic NXT IRReceiver Sensor
:touch -> "lego-nxt-touch"
:gyro -> "ht-nxt-gyro"
# :color -> "???" # NXT Color sensor not supported
:ultrasonic -> "lego-nxt-us"
:large -> "lego-ev3-l-motor" # automatically detected as Lego EV3 Large motors
# :medium -> "lego-ev3-m-motor" # does not exist
end
end



def self_loading_on_brickpi?(device_type) do
device_type == :touch
end
Expand Down
55 changes: 55 additions & 0 deletions lib/ev3/body/lego_communication.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
defmodule Ev3.LegoCommunication do
@moduledoc "Lego inter-robot communication"

require Logger
alias Ev3.Device

@doc "Get all available communicator"
def communicators() do
[:pg2]
|> Enum.map(&(init_communicator("#{&1}", module_for(&1))))
end

@doc"Find a communicator device by type"
def communicator(type: type) do
communicators()
|> Enum.find(&(type(&1) == type))
end

@doc "Get the type of the communicator device"
def type(communicator) do
communicator.type
end

@doc "Execute a cound command"
def execute_command(communicator, command, params) do
apply(Ev3.LegoCommunication, command, [communicator | params])
communicator
end

@doc "Communicate through a communicator"
def communicate(communicator_device, %{info: info, team: team}) do
apply(communicator_device.path, :communicate, [communicator_device, info, team])
end

### Private

defp init_communicator(type, module) do
%Device{class: :comm,
path: module,
port: nil,
type: type
}
end

defp module_for(type) do
case type do
:pg2 -> Ev3.PG2Communicator
other ->
error = "Unknown type #{type} of communicator"
Logger.error(error)
raise error
end
end

end
6 changes: 3 additions & 3 deletions lib/ev3/body/lego_sound.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ defmodule Ev3.LegoSound do

@sys_path "/sound"

@doc "Get the available sound players"
@doc "Get the available sound player devices"
def sound_players() do
[:speech]
|> Enum.map(&(init_sound_player("#{&1}", "#{@sys_path}/#{&1}")))
end

@doc "Find a sound player by type"
@doc "Find a sound player device by type"
def sound_player(type: type) do
sound_players()
|> Enum.find(&(type(&1) == type))
end

@doc "Get the type of the sound player"
@doc "Get the type of the sound player device"
def type(sound_player) do
sound_player.type
end
Expand Down
52 changes: 52 additions & 0 deletions lib/ev3/body/pg2_communicator.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
defmodule Ev3.PG2Communicator do
@moduledoc "Communicating with other robots via pg2"

@behaviour Ev3.Communicating
use GenServer
alias Ev3.Percept
alias Ev3.CNS
require Logger

@name __MODULE__

### API

@doc "Star the communication server"
def start_link() do
Logger.info("Starting #{@name}")
GenServer.start_link(@name, [], [name: @name])
end

@doc "Communicate a percept to the team"
def communicate(device, info, team) do
GenServer.cast(@name, {:communicate, device, info, team})
end


### CALLBACK

def init([]) do
group = Application.get_env(:ev3, :group)
:pg2.start()
:pg2.create(group)
:pg2.join(group, self())
{:ok, %{group: group}}
end

def handle_cast({:communicate, device, info, team}, state = %{group: group}) do
:pg2.get_members(group)
|> Enum.each(&(GenServer.cast(&1, {:communication, Node.self(), info, team, device.props.ttl})))
Logger.info("COMMUNICATOR communicated #{inspect info} to team #{team}")
{:noreply, state}
end

def handle_cast({:communication, source, info, team, ttl}, state) do # ttl for what's communicated
Logger.info("COMMUNICATOR heard #{inspect info} for team #{team} from #{inspect source}")
percept = Percept.new(about: :heard, value: %{source: source, team: team, info: info})
CNS.notify_perceived(%{percept |
ttl: ttl,
source: @name})
{:noreply, state}
end

end
162 changes: 95 additions & 67 deletions lib/ev3/mind/actuation/actuation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,78 +7,90 @@ defmodule Ev3.Actuation do
alias Ev3.MotorSpec
alias Ev3.LEDSpec
alias Ev3.SoundSpec
alias Ev3.CommSpec
alias Ev3.Activation
alias Ev3.Script

@doc "Give the configurations of all actuators to be activated"
def actuator_configs() do
[
ActuatorConfig.new(name: :locomotion,
type: :motor,
specs: [ # to find and name motors from specs
%MotorSpec{name: :left_wheel, port: "outA"},
%MotorSpec{name: :right_wheel, port: "outB"}
],
activations: [ # scripted actions to be taken upon receiving intents
%Activation{intent: :go_forward,
action: going_forward()},
%Activation{intent: :go_backward,
action: going_backward()},
%Activation{intent: :turn_right,
action: turning_right()},
%Activation{intent: :turn_left,
action: turning_left()},
%Activation{intent: :stop,
action: stopping()}
]),
ActuatorConfig.new(name: :manipulation,
type: :motor,
specs: [
%MotorSpec{name: :mouth, port: "outC"}
],
activations: [
%Activation{intent: :eat,
action: eating()}
]),
ActuatorConfig.new(name: :leds,
type: :led,
specs: [
%LEDSpec{name: :lr, position: :left, color: :red}, #ev3
%LEDSpec{name: :lg, position: :left, color: :green}, #ev3
%LEDSpec{name: :lb, position: :left, color: :blue}, #brickpi
%LEDSpec{name: :rr, position: :right, color: :red}, #ev3
%LEDSpec{name: :rg, position: :right, color: :green}, #ev3
%LEDSpec{name: :rb, position: :right, color: :blue} #brickpi
],
activations: [
%Activation{intent: :green_lights,
action: green_lights()},
%Activation{intent: :red_lights,
action: red_lights()},
%Activation{intent: :orange_lights,
action: orange_lights()}
]),
ActuatorConfig.new(name: :sounds,
type: :sound,
specs: [
%SoundSpec{name: :loud_speech, type: :speech, props: %{volume: :loud, speed: :normal, voice: "en-sc"}}
],
activations: [
%Activation{intent: :say_hungry,
action: say_hungry()},
%Activation{intent: :say_scared,
action: say_scared()},
%Activation{intent: :say_curious,
action: say_curious()},
%Activation{intent: :say_uh_oh,
action: say_uh_oh()},
%Activation{intent: :say_stuck,
action: say_stuck()},
%Activation{intent: :say_food,
action: say_food()},
%Activation{intent: :eating_noises,
action: eating_noises()}
])
ActuatorConfig.new(name: :locomotion,
type: :motor,
specs: [ # to find and name motors from specs
%MotorSpec{name: :left_wheel, port: "outA"},
%MotorSpec{name: :right_wheel, port: "outB"}
],
activations: [ # scripted actions to be taken upon receiving intents
%Activation{intent: :go_forward,
action: going_forward()},
%Activation{intent: :go_backward,
action: going_backward()},
%Activation{intent: :turn_right,
action: turning_right()},
%Activation{intent: :turn_left,
action: turning_left()},
%Activation{intent: :stop,
action: stopping()}
]),
ActuatorConfig.new(name: :manipulation,
type: :motor,
specs: [
%MotorSpec{name: :mouth, port: "outC"}
],
activations: [
%Activation{intent: :eat,
action: eating()}
]),
ActuatorConfig.new(name: :leds,
type: :led,
specs: [
%LEDSpec{name: :lr, position: :left, color: :red}, #ev3
%LEDSpec{name: :lg, position: :left, color: :green}, #ev3
%LEDSpec{name: :lb, position: :left, color: :blue}, #brickpi
%LEDSpec{name: :rr, position: :right, color: :red}, #ev3
%LEDSpec{name: :rg, position: :right, color: :green}, #ev3
%LEDSpec{name: :rb, position: :right, color: :blue} #brickpi
],
activations: [
%Activation{intent: :green_lights,
action: green_lights()},
%Activation{intent: :red_lights,
action: red_lights()},
%Activation{intent: :orange_lights,
action: orange_lights()}
]),
ActuatorConfig.new(name: :sounds,
type: :sound,
specs: [
%SoundSpec{name: :loud_speech, type: :speech, props: %{volume: :loud, speed: :normal, voice: "en-sc"}}
],
activations: [
%Activation{intent: :say_hungry,
action: say_hungry()},
%Activation{intent: :say_scared,
action: say_scared()},
%Activation{intent: :say_curious,
action: say_curious()},
%Activation{intent: :say_uh_oh,
action: say_uh_oh()},
%Activation{intent: :say_stuck,
action: say_stuck()},
%Activation{intent: :say_food,
action: say_food()},
%Activation{intent: :eating_noises,
action: eating_noises()},
%Activation{intent: :say,
action: say()}
]),
ActuatorConfig.new(name: :communicators,
type: :comm,
specs: [
%CommSpec{name: :marvins, type: :pg2} # could set props.ttl to something other than 30 secs default
],
activations: [
%Activation{intent: :communicate, # intent value = %{info: info, team: team}
action: communicate()}
])
]
end

Expand All @@ -92,7 +104,7 @@ defmodule Ev3.Actuation do
|> Script.add_step(:right_wheel, :set_speed, [:rps, rps_speed])
|> Script.add_step(:left_wheel, :set_speed, [:rps, rps_speed])
|> Script.add_step(:all, :run_for, [how_long] )
# |> Script.add_wait(how_long)
# |> Script.add_wait(how_long)
end
end

Expand Down Expand Up @@ -272,11 +284,27 @@ defmodule Ev3.Actuation do
end
end

defp say() do
fn(intent, sound_players) ->
Script.new(:say, sound_players)
|> Script.add_step(:loud_speech, :speak, [intent.value])
end
end

defp eating_noises() do
fn(_intent, sound_players) ->
Script.new(:say_eating, sound_players)
|> Script.add_step(:loud_speech, :speak, ["Nom de nom de nom"])
end
end

# communications

defp communicate() do
fn(intent, communicators) ->
Script.new(:communicate, communicators)
|> Script.add_step(:marvins, :communicate, [intent.value])
end
end

end
Loading

0 comments on commit e55a3de

Please sign in to comment.