Script Examples

About

This tutorial demonstrates the use of custom scripts using ScriptController and ScriptMeasure.

High Jump

This example uses both a ScriptController and a ScriptMeasure to perform a standing high-jump optimization. The scone scenario is defined as followed:

CmaOptimizer {
	signature_prefix = DATE_TIME
 
	SimulationObjective {
		max_duration = 2
 
		# Model used in simulation
		OpenSimModel {
			model_file = data/Human0914.osim
			fixed_control_step_size = 0.005 # higher step sizes give better performance
		}
 
		# Controller based on lua script
		ScriptController {
			script_file = "data/ScriptControllerJump.lua"
		}
 
		# Measure based on lua script
		ScriptMeasure {
			minimize = 0 # let the optimizer know we want to maximize this measure
			target_body = "toes_r" # this parameter will be used in the script
			script_file = "data/ScriptMeasureJump.lua"
		}
	}
}

The actual controller is defined in data/ScriptControllerJump.lua:

-- SCONE script for a simple feed-forward controller.
-- See Tutorial 6a - Script - High Jump
 
function init( model, par )
	-- keep a list of offsets and slopes to compute the excitation
	offset = {}
	slope = {}
 
	-- keep a list of all actuators
	actuators = {}
 
	-- iterate over all actuators in the model
	for i = 1, model:actuator_count() do
		-- store the actuator in the list
		actuators[ i ] = model:actuator( i )
 
		-- create parameters for both slope and offset
		local name = actuators[ i ]:name()
		offset[ i ] = par:create_from_mean_std( name .. "-offset", 0.3, 0.1, 0, 1 )
		slope[ i ] = par:create_from_mean_std( name .. "-slope", 0, 1, -10, 10 )
	end
end
 
function update( model )
	-- get the current simulation time
	local t = model:time()
 
	-- iterate over all actuators
	for i = 1, #actuators do
		local excitation = offset[ i ] + t * slope[ i ]
		actuators[ i ]:add_input( excitation )
	end
end

The actual measure is defined in data/ScriptMeasureJump.lua:

-- SCONE script for a high-jump measure.
-- See Tutorial 6a - Script - High Jump
 
function init( model )
	-- get the 'target_body' parameter from ScriptMeasure, or set to "pelvis"
	target_body = scone.target_body or "pelvis"
 
	-- find the actual body with the same name
	body = model:find_body( target_body )
 
	-- global boolean that keeps track if we have moved upwards
	has_moved_up = false
end
 
function update( model )
	-- get current vertical position and velocity
	local pos = body:com_pos().y
	local vel = body:com_vel().y
 
	-- check if we are at peak height
	if has_moved_up then -- have we been moving up?
		if vel < 0 then -- are we moving down?
			-- we are at the peak!
			return true -- return true to end the simulation
		end
	else
		if vel > 0.01 then -- are we starting to move up?
			has_moved_up = true -- set the flag
		end
	end
 
	return false -- return false to keep going
end
 
function result( model )
	-- return the vertical position as the final result
	-- this is either peak height
	-- or the height at the end of the simulation
	return body:com_pos().y
end
 
function store_data( frame )
	-- store some values for analysis
	frame:set_value( "body_height", body:com_pos().y )
	frame:set_bool( "has_moved_up", has_moved_up )
end

Balance Device

This example demonstrates a possible implementation of a 'virtual balance device', which can generate a moment on a specific body if its orientation is outside a specific range.

The SCONE scenario uses a Geyer & Herr gait controller in combination with a ScriptController, and is defined as follows:

CmaOptimizer {
	signature_prefix = DATE_TIME
 
	SimulationObjective {
		max_duration = 10
 
		# Model used in simulation
		OpenSimModel {
			model_file = data/Human0914.osim
			fixed_control_step_size = 0.005 # higher step sizes give better performance
			enable_external_forces = 1 # Required when applying external forces
 
			# Optimize initial state parameters
			state_init_file = data/InitStateGait10.sto
			initial_state_offset =	0~0.01<-0.5,0.5>
			initial_state_offset_exclude = "*_tx;*_ty;*_u"
		}
 
		CompositeController {
			# Controller for gait, based on [Geyer & Herr 2010]
			<< data/ControllerGH2010.scone >>
 
			ScriptController {
				name = BalanceDevice
				target_body = torso
				min_deg = -20~2 # optimize this parameter
				max_deg = 5~0.5 # optimize this parameter
				moment = 50
				duration = 0.2
				script_file = "data/ScriptControllerBalanceDevice.lua"
			}
		}
 
		# Measure for gait
		<< data/MeasureGait05.scone >>
	}
}

The actual control script is defined in data/ScriptControllerBalanceDevice.lua:

-- SCONE script that simulates a device that generates an external moment at a specific condition.
-- See Tutorial 6b - Script - Balance Device
 
function init( model, par )
	-- get the 'target_body' parameter from ScriptController, or set to "pelvis"
	target_body = model:find_body( scone.target_body or "pelvis" )
 
	-- get 'min_deg' and 'max_deg' parameters from ScriptController (required!)
	if ( scone.min_deg and scone.min_deg ) then
		body_min = par:create_from_string( "min_deg", scone.min_deg )
		body_max = par:create_from_string( "max_deg", scone.max_deg )
	else
		-- produce an error and abort the simulation
		error( "Must set min_deg and max_deg parameters!" )
	end
 
	-- get 'device_moment' and 'device_duration' parameters from ScriptController (not required!)
	device_moment_mag = scone.moment and par:create_from_string( "device_moment", scone.moment ) or 100
	device_duration = scone.duration and par:create_from_string( "device_duration", scone.duration ) or 0.2
 
	-- initialize global variables that keep track of the device state
	device_start = 0
	device_moment = 0
end
 
function update( model )
	local t = model:time()
 
	-- check if the device is currently active
	if device_moment ~= 0 then
		-- check if the device should deactivate
		if t - device_start > device_duration then
			-- add opposite external moment to deactivate device
			target_body:add_external_moment( 0, 0, -device_moment )
			device_moment = 0
			-- print a message to the scone messages window
			scone.debug( "device deactivated at " .. t )
		end
	-- device is not active, check if we activate it
	elseif device_start == 0 then -- device was not activated before
		-- get the current body orientation in degrees and check with body_min / body_max
		local ori = 57.3 * target_body:ang_pos().z
		if ori < body_min then
			device_moment = device_moment_mag
		elseif ori > body_max then
			device_moment = -device_moment_mag
		end
 
		-- check if device_moment was set
		if device_moment ~= 0 then
			device_start = t
			target_body:add_external_moment( 0, 0, device_moment )
 
			-- print a message to the scone messages window
			scone.debug( "device activated at t=" .. t .. " ori=" .. ori .. " moment=" .. device_moment )
		end
	end
 
	-- return false to keep going
	return false;
end
  • by tgeijten