This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Purple's profile picture

NAVIGATOR - Inertial Navigation for Turtles

Started by Purple, 19 March 2019 - 07:46 PM
Purple #1
Posted 19 March 2019 - 08:46 PM
Have you ever wished your turtles knew where they are in the world? Have you looked in vain for an API to do so only to end up staring down the ugly face of hyper complex and frankly overkill world of wireless GPS systems? Don't you just wish that there was something simple, easy and trivial to implement and use?


Well I did. And now I share my vision with you.

Navigator
Inertial navigation API for turtles.



Technical Description:
Navigator is a file backed storage based inertial navigation system for computercraft turtles. Inertial navigation is the process of calculating your current location in the world by recording your start position and than adding up your subsequent movement onto it. As such, Navigator is completely independent of any external input. It does not require rednet connections, other computers or anything else beyond the turtle and the API it uses.
The file backed storage aspect of it refers to the fact that Navigator stores its current position in a text file after each modification. This allows the program to remember and continue its work without resetting when the chunk or world is unloaded. You can save your game, exit and come back a week later and the turtle will remember where it left off as if you newer left.

This combination of functions makes Navigator ideal for automating repetitive jobs such as warehouses, delivery systems and item sorting. But it also opens up a whole world of operations outside of the range of player contact or infrastructure.

Quickstart Guide:
Navigator replaces the standard turtle movement and rotation functions with its own wrappers. Using it is as simple as importing the Navigator API and substituting all calls to turtle.left/right/forward/back/up and down with the equivalent Navigator functions.

To begin with your program you need to initialize Navigator first. This is done by calling NAVIGATOR.InitializeNavigator(). This function loads the current position data of your turtle from the file and should thus be called first in any Navigator program. The suggested code block for this function is as follows:

-- Initialize Navigator
if (NAVIGATOR.InitializeNavigator()) then
   print("Navigator file found, reading position.")
else
   print("Program running first time. Setting initial position.")
   NAVIGATOR.SetPosition(x, y, z, direction)
end

Following this the process really is as simple as replacing turtle.something function with their navigator equivalent. E.g:

Navigator.Forward()

Detailed Documentation:
function InitializeNavigator()
Initializes the navigator program by reading the stored location and facing data from the LOCATION file located next to the main program.

function SetPosition(X, Y, Z, FACING)
Sets the position and facing of the turtle.
Parameters:
X, Y and Z - Coordinates of the turtle in the game world where you've placed it.
Direction - The direction the turtle is facing initially. Direction uses the minecraft standard direction axis as per F3.

function PrintDirections()
Prints the numerical values of the 4 cardinal directions for easy reference by developers.

function GetDirectionINT(direction)
Converts the string names of the cardinal directions (e.g. "North", "E", "S", "South"…) into numbers for ease of use by developers.
Parameters:
direction - Non case sensitive string representation of the direction either as a full name (e.g. "North, "SOUTH", "eAsT") or just the first character (e.g. "N", "s", "E", "w").
Example of use:

NAVIGATOR.TurnRight(NAVIGATOR.GetDirectionINT("west"))

function GetPosition()
Retrieves the position and facing of the turtle from Navigator.
Example of use:

x, y, z, direction = NAVIGATOR.GetPosition()

function TurnLeft( goal )
Attempts to turn the turtle left.
Parameters:
Goal - (optional) The final direction the turtle should face once turning is complete. Failing to provide this parameter makes the turtle do only a single turn as per turtle.left().
Example of use:

NAVIGATOR.TurnLeft(NAVIGATOR.GetDirectionINT("west"))
NAVIGATOR.TurnLeft()

function TurnRight( goal )
Attempts to turn the turtle right.
Parameters:
Goal - (optional) The final direction the turtle should face once turning is complete. Failing to provide this parameter makes the turtle do only a single turn as per turtle.right().
Example of use:

NAVIGATOR.TurnRight(NAVIGATOR.GetDirectionINT("west"))
NAVIGATOR.TurnRight()

function Up(goal)
Attempts to move the turtle upwards.
Parameters:
Goal - (optional)The number of steps to take.
[indent=1]Failing to provide this parameter makes the turtle do only a single step as per turtle.up().[/indent]
[indent=1]If the parameter is provided and the turtle fails to make all the required steps the turtle will reset to its starting position.[/indent]
Example of use:

NAVIGATOR.Up(3)
Return:
boolean whether the turtle succeeded in moving upwards.

function Down(goal)
Attempts to move the turtle downward.
Parameters:
Goal - (optional)The number of steps to take.
[indent=1]Failing to provide this parameter makes the turtle do only a single step as per turtle.down().[/indent]
[indent=1]If the parameter is provided and the turtle fails to make all the required steps the turtle will reset to its starting position.[/indent]
Example of use:

NAVIGATOR.Down(13)
Return:
boolean whether the turtle succeeded in moving downwards.

function Forward(goal)
Attempts to move the turtle forward.
Parameters:
Goal - (optional)The number of steps to take.
[indent=1]Failing to provide this parameter makes the turtle do only a single step as per turtle.forward().[/indent]
[indent=1]If the parameter is provided and the turtle fails to make all the required steps the turtle will reset to its starting position.[/indent]
Example of use:

NAVIGATOR.Forward()
Return:
boolean whether the turtle succeeded in moving forward.

function Back(goal)
Attempts to move the turtle backward.
Parameters:
Goal - (optional)The number of steps to take.
[indent=1]Failing to provide this parameter makes the turtle do only a single step as per turtle.back().[/indent]
[indent=1]If the parameter is provided and the turtle fails to make all the required steps the turtle will reset to its starting position.[/indent]
Example of use:

NAVIGATOR.Back(47)
Return:
boolean whether the turtle succeeded in moving backward.

Example Program:
This program services 3 stacks of furnaces in my current house. Each stack is 3 tall and they are spaced 3 tiles apart. The turtle goes through them stack by stack and drops anything it finds inside into a common chest.
Spoiler


os.loadAPI("rom/apis/NAVIGATOR")

local function Pickup()
	NAVIGATOR.TurnRight(NAVIGATOR.GetDirectionINT("west"))	--facing +z
	turtle.suck(64)
end

local function Drop()	  
	NAVIGATOR.TurnRight(NAVIGATOR.GetDirectionINT("west"))	--facing +z
	turtle.drop(64)
end

local function MoveToSlot(slot)
	x, y, z, direction = NAVIGATOR.GetPosition()
  
	dist = math.abs(slot - z)
  
	--print("Moveto " .. slot .. " from " .. z .. " dist: " .. dist)
	if(slot > z) then
		--print("N")
		NAVIGATOR.TurnRight(0)
		NAVIGATOR.Forward(dist)
	elseif(slot < z) then
		--print("S")
		NAVIGATOR.TurnRight(2)
		NAVIGATOR.Forward(dist)
	end
end

local function Dump()
	detail = turtle.getItemDetail(1)
  
	if(detail) then
		MoveToSlot(-8)
		Drop()
	end
end

local function DoStack(stack)
	Pickup()
	Dump()
	MoveToSlot(stack)
  
	NAVIGATOR.Up(1)
	Pickup()
	NAVIGATOR.Down(1)
	Dump()
	MoveToSlot(stack)
  
	NAVIGATOR.Down(1)
	Pickup()
	NAVIGATOR.Up(1)
	Dump()
	MoveToSlot(stack)
end

-- Reset Position
-- Initialize Navigator
if (NAVIGATOR.InitializeNavigator()) then
	--print("Navigator file found, resetting position.")
  
	x, y, z, direction = NAVIGATOR.GetPosition()
	-- First Y:
	if (y > 0) then
		--print("Moving down.")
		NAVIGATOR.Down(y)
	elseif (y < 0) then
		--print("Moving up.")
		NAVIGATOR.Up(-y)
	else
		--print("Vertical is fine.")
	end
  
	-- Now Z:
	if(z > 0) then
		--print("Moving north.")
		-- Turn to positive z and move.
		NAVIGATOR.TurnRight(2)	--facing -z  
	elseif(z < 0) then
		--print("Moving south.")
		-- Turn to negative z and move.  
		NAVIGATOR.TurnRight(0)	--facing +z  
	end
		NAVIGATOR.Forward(math.abs(z))
  
	-- X is not needed as the unit only moves in one line.
else
	--print("Resetting navigator")
	NAVIGATOR.SetPosition(0, 0, 0, 0)
end

while true do
	DoStack(0)
	os.sleep(1)
  
	DoStack(-3)
	os.sleep(1)
  
	DoStack(-6)
	os.sleep(1)
end



License and terms of use:
Things you can do:
- Use, modify and distribute the program in any way you see fit as long as you don't make money off it and you give me credit.
Things you can't do:
- Claim my code as your own.
- Make money off my code in any way, shape or form (donations included).


Please comment, rate and subscribe. :)/>
And make sure to tell me about the cool and inventive things you do with this.
Edited on 20 March 2019 - 09:14 PM
Purple #2
Posted 20 March 2019 - 10:16 PM
UPDATE: Apparently attachments don't work for some reason. And I just figured that out now. I just did not think attaching the file would not actually attach a file. Go figure.
Anyway, here is the full code for you to save as NAVIGATOR. Sorry for the inconvenience.

Just copy paste this into a file using notepad or something.
Spoiler

local direction = 0

local function GetXFromDirection()
	if		(direction == 1)		 then	 return -1
	elseif	 (direction == 3)		then	 return 1
	else									 return 0
	end
end

local function GetZFromDirection()
	if		(direction == 0)		 then	 return 1
	elseif	 (direction == 2)		then	 return -1
	else									 return 0
	end
end

local x = 0
local y = 0
local z = 0

-- Save direction and coordinates to file.
local function Store()
	local h = fs.open("location", "w")
	h.writeLine(x)
	h.writeLine(y)
	h.writeLine(z)
	h.writeLine(direction)
	h.close()
end

-- Reads direction and coordinates from file.
local function Read()	
	if fs.exists("location") then
		local h = fs.open("location", "r")
		x			= tonumber(h.readLine())
		y			= tonumber(h.readLine())
		z			= tonumber(h.readLine())
		direction	= tonumber(h.readLine())
		h.close()
		
		return true
	end
	
	return false
end

function moveBackOnce()
	-- Try and move.
	moved = turtle.back()
	-- If move success increment coordinates.
	if(moved) then
		x = x - GetXFromDirection()
		z = z - GetZFromDirection()
		
		Store()
	end
	
	return moved
end

function moveForwardOnce()
	-- Try and move.
	moved = turtle.forward()
	-- If move success increment coordinates.
	if(moved) then
		x = x + GetXFromDirection()
		z = z + GetZFromDirection()
		
		Store()
	end
		return moved
end

-- Turn Right.
local function TurnLeftOnce()
	if(direction > 0) then
		direction = direction - 1
	else
		direction = 3
	end
	
	turtle.turnLeft()
		
	Store()
end

-- Turn Left.
local function TurnRightOnce()
	if(direction < 3) then
		direction = direction + 1
	else
		direction = 0
	end
	
	turtle.turnRight()

	Store()
end

-- Move up.
local function moveUpOnce()
	moved = turtle.up()
	
	if(moved) then
		y = y + 1
		
		Store()
	end
	
	return moved
end

-- Move down.
local function moveDownOnce()
	moved = turtle.down()
	
	if(moved) then
		y = y - 1
		
		Store()
	end
	
	return moved
end

-- =======================================================================================================================================================
-- Public API
-- =======================================================================================================================================================
-- Initalization:
-- =======================================================================================================================================================
-- Sets the position and heading of the turtle. Use for initialization.
-- FACING:
--[[
	0 = South
	1 = West
	2 = North
	3 = East
--]]
function SetPosition(X, Y, Z, FACING)
	x			= X
	y			= Y
	z			= Z
	direction	= FACING
	Store()
end

-- Initializes the navigator from file backed storage.
function InitializeNavigator()
	return Read()
end

-- Prints the direction enumeration in a user readable format.
function PrintDirections()
	print("0 = South")
	print("1 = West")
	print("2 = North")
	print("3 = East")	
end

-- Takes N, S, E, W and converts it to numbers.
function GetDirectionINT(dir)
	dir = string.lower(dir)

	if(dir == "s") or (dir == "south")	 then return 0 end
	if(dir == "n") or (dir == "north")	then return 2 end
	if(dir == "e") or (dir == "east")	  then return 3 end
	if(dir == "w") or (dir == "west")	 then return 1 end
end

-- Returns the current position of the turtle.
function GetPosition()
	Read()
	return x, y, z, direction
end
-- =======================================================================================================================================================
-- Rotation:
-- =======================================================================================================================================================

-- Turns left.
-- Goal (optinal): The target heading to turn toward.
-- Returns if movement succesfull.
function TurnLeft( goal )
	goal = goal or "T"
		
	if(goal == "T") then
		TurnLeftOnce()
	else
		while (direction ~= goal) do
			TurnLeftOnce()
		end
	end
end

-- Turns right.
-- Goal (optinal): The target heading to turn toward.
-- Returns if movement succesfull.
function TurnRight( goal )
	goal = goal or "T"
	--print(goal .. " : " .. direction)
	
	if(goal == "T") then
		TurnRightOnce()
	else
		while (direction ~= goal) do
			--print(goal .. " : " .. direction)
			TurnRightOnce()
		end
	end
end
-- =======================================================================================================================================================
-- Moving:
-- =======================================================================================================================================================
local function Navigate(mainMovementFunction, resetMovementFunction, goal)
	goal = goal or "T"
	steps = 0
	
	-- If no steps required return true.
	if(goal == 0) then return true end
	
	-- If only a single step is required than do it.
	if(goal == "T") then return mainMovementFunction() end
	
	-- Try and move to the target position
	moved = false
	while goal > steps do
		moved = mainMovementFunction()
		
		if(moved == true) then
			steps = steps + 1
		else
			-- If cant move reset
			while steps > 0 do
				resetMovementFunction()
				steps = steps - 1
			end
			-- And break out of the movement loop
			return false
		end
	end
	
	return true
end

-- Moves Up and returns if succesfull.
-- Goal (optinal): Number of steps taken. If movement is obstructed turtle will reset to its starting position.
-- Returns if movement succesfull.
function Up(goal)
	return Navigate(moveUpOnce, moveDownOnce, goal)
end

-- Moves Down and returns if succesfull.
-- Goal (optinal): Number of steps taken. If movement is obstructed turtle will reset to its starting position.
-- Returns if movement succesfull.
function Down(goal)
	return Navigate(moveDownOnce, moveUpOnce, goal)
end

-- Moves back and returns if succesfull.
-- Goal (optinal): Number of steps taken. If movement is obstructed turtle will reset to its starting position.
-- Returns if movement succesfull.
function Back(goal)
	return Navigate(moveBackOnce, moveForwardOnce, goal)
end

-- Moves forward and returns if succesfull.
-- Goal (optinal): Number of steps taken. If movement is obstructed turtle will reset to its starting position.
-- Returns if movement succesfull.
function Forward(goal)
	return Navigate(moveForwardOnce, moveBackOnce, goal)
end
Edited on 20 March 2019 - 09:22 PM