P04 EFSM Modules
P04 EFSM Modules
modularity
• Simulation motor
– Module setup
– Main loop
• EFSM behavior
– init() to setup variables and outputs
– read_inputs() to read primary inputs
– step() to compute next state and variable values
– write_outputs() to write primary outputs
– forward() to update current state and variables
– monitor() to have internal data shown on stdout
– active() to indicate whether EFSM is not at a sink state
Software counter model, 1
begin’ C<B
Counter = {
state = {}, -- state is an object
M = 4, -- counter limit, with default value WAIT
begin COUNT
/ C+ = 0,
begin = nil, -- input signal to start counting B+ = M – 1,
/ C+ = C+1,
B = {}, -- internal boundary limit end = 0
end = 1
C = {}, -- internal counter
c_end = nil, -- output event to signal counting ends C=B
init = function( self, n )
self.state.curr = "WAIT"; self.state.next = "WAIT"
self.M = n or self.M -- if n is nil, then self.M takes default value
self.B.curr = -1; self.B.next = -1; self.C.curr = -1; self.C.next = -1
end -- function init()
,
read_inputs = function( self )
local begin, M = nil, nil
io.write( "> Counter: begin(0/1), M(integer) [whitespace=keep] = " )
--[[ ... --]]
self.begin, self.M = begin, M
end -- function read_inputs()
,
--[[ cont’ed --]]
Software counter model, 2
step = function( self )
if self.begin == nil then
self.state.next = "STOP"; self.state.curr = "STOP“
end -- if
if self.state.curr == "WAIT" then
self.C.next = 0; self.B.next = self.M - 1
if self.begin then self.state.next = "COUNT" end
elseif self.state.curr == "COUNT" then
self.C.next = self.C.curr + 1
if self.C.curr == self.B.curr then self.state.next = "WAIT" end
else -- Stop state or error
begin’ C<B
end -- if chain
end -- function step()
,
WAIT
monitor = function( self ) / C+ = 0, begin COUNT
io.write( "# Counter: state = " ) / C+ = C+1,
B+ = M – 1,
end = 0
--[[ ... --]] end = 1
end -- function monitor()
, C=B
write_outputs = function( self )
io.write( "< Counter: end = " )
if self.c_end then io.write( "1\n" ) else io.write( "0\n" ) end
end -- function write_outputs()
,
--[[ cont’ed --]]
Software counter model, 3
forward = function( self )
self.state.curr = self.state.next
self.B.curr = self.B.next; self.C.curr = self.C.next
if self.state.curr == "WAIT" then self.c_end = true
elseif self.state.curr == "COUNT" then self.c_end = false
else --[[state=="STOP" or error --]] self.c_end = true
end -- if chain
end -- function forward()
,
active = function( self )
return self.state.curr == "WAIT" or self.state.curr == "COUNT"
end -- function active()
} -- Counter
begin’ C<B
WAIT
begin COUNT
/ C+ = 0,
/ C+ = C+1,
B+ = M – 1,
end = 0
end = 1
C=B
Software counter simulator
--SETUP
Counter:init(2)
-- MAIN LOOP
cycle = 0
while Counter:active() do
io.write( string.format( "Cycle = %04i:\n", cycle ) )
Counter:write_outputs()
Counter:monitor()
Counter:read_inputs()
Counter:step()
Counter:forward()
cycle = cycle + 1
end -- while
io.write( "Program exited!\n" )
EFSM execution model
Exercise 4
• Create a timer module
– Timer generates a pulse every M cycles
– Use P04_counter.lua
• Create a module for the odometer
– Use the Lua program from the last exercise
• Deliverables
– Draw of EFSM of the counter with changes to make it
behave as a timer, as described
– List of changes into corresponding code
– Full listing of odometer program
(Code repetitions can be indicated with references)
Exercise 4, cont’d
• For testing
– Create a simulator for the models
– Example with P04_counter.lua
• P04_counter.lua:
local Counter = { --[[ ... --]] }
return Counter
• P04_counter_sim.lua:
local Counter = require “P04_counter”
CoppeliaSim
• Each object can be attached to a Lua script
– Kind of a module without setup & main loop
– Setup and main loop are within CoppeliaSim
• Lots of functions to interact with simulator
– sim.FunctionName
– Scripts can include a setup for the module:
function sysCall_init()
-- setup
end -- sysCall_init()
Robot motion control (1/6)
• NinjUAB / CoppeliaSim case
sim.tubeRead(tubeL1L0handle)
sim.tubeWrite(tubeL0L1handle, stringMessage)
• Characterization INIT/
B+= T TESTING /
– Spinning L+=PWL −
R+=PWR
• Make the robot turn for a while T‒ B ≥ D
• Measure end angle and
NULL/ STOP /
compute rotational speed L+=0, L+=1500,
R+=0 R+=1500
– Moving forward
if self.state.curr == "INIT" then
• Make the robot move self.B.next = sim.getSimulationTime() -- B+ = begin time
ahead for a while self.L.next = PWL -- L+ = pulse width for right motor
self.R.next = PWR -- R+ = pulse width for right motor
• Measure travelled self.state.next = "TESTING"
distance and elseif self.state.curr == "TESTING" then
if (T – self.B.curr) >= 5.0 then -– D = 5.0 seconds
compute linear speed self.state.next = "STOP"
end -- if
elseif self.state.curr == "STOP" then
– State machines self.L.next = 1500; self.R.next = 1500
self.state.next = "NULL"
function sysCall_actuation() elseif self.state.curr == "NULL" then
-- actuating & self.L.next = 0; self.R.next = 0
-- publishing outputs, else -- error
-- i.e. write_outputs() self.state.next = "STOP"
end -- if chain
end
function sysCall_sensing() then
-- sensing (broad ‘sense’), i.e. read_inputs()
-- compute next state and variable values, i.e. step()
-- update current state and variable values, i.e. forward()
end
© Lluís Ribas Xirgo, Univ. Autònoma de Barcelona, 2020
Robot motion control (4/6)
• GO angle distance
– Open-loop mode:
only the vehicle dynamics model is used
T‒ B < D
SPIN/ *
B+=T,
INIT/ WAIT/
<go α r>**
+
L =width***, SPINNING/ *
L+=1500, L+=0, R+=width***, L+=0,
R+=1500 R+=0 D+= |α|/w, R+=0
not A+= α, S+= r
(<go α r>)
T‒ B ≥ D
MOVE/
T‒ B ≥ D MOVING/ * B+=T, *
L+=0, L+=1660,
R+=0 R+=1340,
D +=S/v
T‒ B < D
– Safety distance
– Options
• Stop movement (as HALTed)
• Suspend movement until
– Distance to obstacles > safety distance
– Timeout
© Lluís Ribas Xirgo, Univ. Autònoma de Barcelona, 2020