Compare commits
63 Commits
d57bcf0ad4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| eb8faa9c10 | |||
| 165c0cefa9 | |||
| 131bbd9f0a | |||
| dd70e5ddb2 | |||
| a65299be26 | |||
| 7174c16628 | |||
| fa96b2f59f | |||
| f816761e0f | |||
| 056c10c346 | |||
| 908a2153fa | |||
| 7bcabcbbe6 | |||
| 1aa391f56c | |||
| e7831845a8 | |||
| d228666c87 | |||
| b8b22f29b1 | |||
| 46c04bf646 | |||
| 61c8f88d40 | |||
| cb7b9490bf | |||
| 600a683a12 | |||
| 373089d374 | |||
| 23644f7072 | |||
| e11c6483f9 | |||
| 7c0cd566b1 | |||
| aab62f51cc | |||
| 12cad05ed6 | |||
| 92988ac4f5 | |||
| 68a3f4589b | |||
| ed25934cb7 | |||
| d2bacd30bb | |||
| f38c765c40 | |||
| 91c1723a9e | |||
| 406cc966ec | |||
| d07b952d66 | |||
| 85fd1d0d85 | |||
| 0766e4ce5a | |||
| a408dd9f34 | |||
| c80855dc2f | |||
| 1ab0510826 | |||
| 485c2eac37 | |||
| 996c49f5a3 | |||
| 1de102e132 | |||
| 06a10ff0f9 | |||
| 9b397337da | |||
| 39a8b0ba88 | |||
| 3ab9bc411f | |||
| a508fbad58 | |||
| b431e1a0a6 | |||
| 13f970af76 | |||
| 1f27dc0cda | |||
| b712a38006 | |||
| ce11e77d7c | |||
| 19c423ecfa | |||
| 2d6e142eb6 | |||
| aff1a45a58 | |||
| ab8d65f477 | |||
| 539fdd92fe | |||
| 4976c5b227 | |||
| e27c899c17 | |||
| e69c370c97 | |||
| 9c22f72d4e | |||
| dd4bc11c43 | |||
| e13ae84aae | |||
| a9f483a05d |
4
docs.md
4
docs.md
@@ -307,6 +307,7 @@ Thrusters = {
|
||||
primary_altitude_thruster = true,
|
||||
primary_fore_thruster = false,
|
||||
primary_aft_thruster = false,
|
||||
x_config_equivalent = "1", -- if you're running a standard x configuration, what prop is this equivalent to?
|
||||
type = "rotator",
|
||||
name = "gyroscopic_propeller_bearing_0",
|
||||
thruster = nil,
|
||||
@@ -334,6 +335,7 @@ Thrusters = {
|
||||
primary_altitude_thruster = true,
|
||||
primary_fore_thruster = false,
|
||||
primary_aft_thruster = false,
|
||||
x_config_equivalent = "2", -- if you're running a standard x configuration, what prop is this equivalent to?
|
||||
type = "rotator",
|
||||
name = "gyroscopic_propeller_bearing_1",
|
||||
thruster = nil,
|
||||
@@ -361,6 +363,7 @@ Thrusters = {
|
||||
primary_altitude_thruster = true,
|
||||
primary_fore_thruster = false,
|
||||
primary_aft_thruster = false,
|
||||
x_config_equivalent = "4", -- if you're running a standard x configuration, what prop is this equivalent to?
|
||||
type = "rotator",
|
||||
name = "gyroscopic_propeller_bearing_2",
|
||||
thruster = nil,
|
||||
@@ -388,6 +391,7 @@ Thrusters = {
|
||||
primary_altitude_thruster = true,
|
||||
primary_fore_thruster = false,
|
||||
primary_aft_thruster = false,
|
||||
x_config_equivalent = "3", -- if you're running a standard x configuration, what prop is this equivalent to?
|
||||
type = "rotator",
|
||||
name = "gyroscopic_propeller_bearing_3",
|
||||
thruster = nil,
|
||||
|
||||
464
main.lua
464
main.lua
@@ -76,7 +76,7 @@ function serializeTable(val, name, skipnewlines, depth)
|
||||
|
||||
local tmp = string.rep(" ", depth)
|
||||
if name then
|
||||
if not string.match(name, '^[a-zA-z_][a-zA-Z0-9_]*$') then
|
||||
if not string.match(name, '^[a-zA-Z_][a-zA-Z0-9_]*$') then
|
||||
name = string.gsub(name, "'", "\\'")
|
||||
name = "['".. name .. "']"
|
||||
end
|
||||
@@ -241,61 +241,60 @@ function PropellerInit()
|
||||
local propellers = {}
|
||||
|
||||
for _, v in ipairs(TransmissionTypes) do
|
||||
table.insert(transmissions, peripheral.find(v))
|
||||
local vlist = { peripheral.find(v) }
|
||||
if vlist ~= nil and vlist[1] ~= nil then
|
||||
table.insert(transmissions, vlist)
|
||||
end
|
||||
end
|
||||
|
||||
for _, v in ipairs(PropellerTypes) do
|
||||
table.insert(propellers, peripheral.find(v))
|
||||
local vlist = peripheral.find(v)
|
||||
if vlist ~= nil and vlist[1] ~= nil then
|
||||
table.insert(propellers, vlist)
|
||||
end
|
||||
end
|
||||
|
||||
for pi, pv in ipairs(propellers) do
|
||||
for ti, tv in ipairs(transmissions) do
|
||||
if pv.getSubnetworkAnchorId() == tv.getSelfId() then
|
||||
if Thrusters[peripheral.getName(pv)] == nil then
|
||||
Thrusters[peripheral.getName(pv)] = {
|
||||
type = "rotator",
|
||||
thruster = pv,
|
||||
transmission = tv,
|
||||
--[[
|
||||
NOTE:
|
||||
affectVectors will have values depending on how the thruster's
|
||||
thrust vector interacts with the vessel's attitude
|
||||
angular:
|
||||
possible directions for angular.yaw: "port"|"star"
|
||||
possible directions for angular.roll: "port"|"star"
|
||||
possible directions for angular.pitch: "up"|"down"
|
||||
lateral:
|
||||
possible directions for lateral.x: "port"|"star"
|
||||
possible directions for lateral.y: "up"|"down"
|
||||
possible directions for lateral.z: "fore"|"aft"
|
||||
]]
|
||||
affectVectors = {
|
||||
angular = {
|
||||
yaw = nil,
|
||||
pitch = nil,
|
||||
roll = nil
|
||||
if Thrusters[peripheral.getName(pv)] == nil then
|
||||
for _, tv in pairs(transmissions) do
|
||||
if pv.getSourceId() == tv.getSelfId() then
|
||||
if Thrusters[peripheral.getName(pv)] == nil then
|
||||
Thrusters[peripheral.getName(pv)] = {
|
||||
type = "rotator",
|
||||
thruster = pv,
|
||||
transmission = peripheral.wrap(tv),
|
||||
--[[
|
||||
NOTE:
|
||||
affectVectors will have values depending on how the thruster's
|
||||
thrust vector interacts with the vessel's attitude
|
||||
angular:
|
||||
possible directions for angular.yaw: "port"|"star"
|
||||
possible directions for angular.roll: "port"|"star"
|
||||
possible directions for angular.pitch: "up"|"down"
|
||||
lateral:
|
||||
possible directions for lateral.x: "port"|"star"
|
||||
possible directions for lateral.y: "up"|"down"
|
||||
possible directions for lateral.z: "fore"|"aft"
|
||||
]]
|
||||
affectVectors = {
|
||||
angular = {
|
||||
yaw = nil,
|
||||
pitch = nil,
|
||||
roll = nil
|
||||
},
|
||||
lateral = {
|
||||
x = nil,
|
||||
y = nil,
|
||||
z = nil
|
||||
}
|
||||
},
|
||||
lateral = {
|
||||
x = nil,
|
||||
y = nil,
|
||||
z = nil
|
||||
}
|
||||
},
|
||||
power = (tv.getSignal())/15
|
||||
}
|
||||
end
|
||||
if Thrusters[peripheral.getName(pv)] ~= nil then
|
||||
if Thrusters[peripheral.getName(pv)].type == nil then
|
||||
Thrusters[peripheral.getName(pv)].type = "rotator"
|
||||
power = (tv.getSignal())/15
|
||||
}
|
||||
end
|
||||
Thrusters[peripheral.getName(pv)].thruster = pv
|
||||
Thrusters[peripheral.getName(pv)].transmission = tv
|
||||
if Thrusters[peripheral.getName(pv)].affectVectors == nil then
|
||||
Thrusters[peripheral.getName(pv)].affectVectors = {}
|
||||
end
|
||||
Thrusters[peripheral.getName(pv)].power = (tv.getSignal())/15
|
||||
end
|
||||
end
|
||||
elseif Config.Debug then
|
||||
print("DEBUG: thruster of name "..peripheral.getName(pv).." already indexed, skipping (called by PropellerInit)")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -304,7 +303,10 @@ function ThrusterInit()
|
||||
local thrusters = {}
|
||||
|
||||
for _, v in ipairs(ThrusterTypes) do
|
||||
table.insert(thrusters, peripheral.find(v))
|
||||
local vlist = peripheral.find(v)
|
||||
if vlist ~= nil and vlist[1] ~= nil then
|
||||
table.insert(thrusters, vlist)
|
||||
end
|
||||
end
|
||||
|
||||
for ti, tv in ipairs(thrusters) do
|
||||
@@ -340,22 +342,90 @@ function ThrusterInit()
|
||||
},
|
||||
power = tv.getPower()
|
||||
}
|
||||
elseif Config.Debug then
|
||||
print("DEBUG: thruster of name "..peripheral.getName(tv).." already indexed, skipping (called by ThrusterInit)")
|
||||
end
|
||||
if Thrusters[peripheral.getName(tv)] ~= nil then
|
||||
if Thrusters[peripheral.getName(tv)].type == nil then
|
||||
Thrusters[peripheral.getName(tv)].type = "thruster"
|
||||
end
|
||||
end
|
||||
|
||||
-- this is for populating nil values from thruster config files
|
||||
local function populateThrusterValues()
|
||||
for _, thruster in pairs(Thrusters) do
|
||||
if thruster.name == nil and thruster.thruster == nil then -- as long as we have one of thruster or name, we can fetch other data
|
||||
print("ERROR: Thruster ".._.." has no name or thruster object. Removing entry in thruster list")
|
||||
Thrusters[_] = nil
|
||||
return
|
||||
end
|
||||
if thruster.name == nil and thruster.thruster ~= nil then
|
||||
thruster.name = peripheral.getName(thruster.thruster)
|
||||
end
|
||||
|
||||
if thruster.thruster == nil and thruster.name ~= nil then
|
||||
thruster.thruster = peripheral.wrap(thruster.name)
|
||||
end
|
||||
if thruster.type == nil then
|
||||
if Config.Debug then print("DEBUG: Thruster "..thruster.name.." does not have a type.") end
|
||||
for _, tt in ipairs(ThrusterTypes) do
|
||||
if peripheral.getType(thruster.thruster) == tt then
|
||||
if Config.Debug then print("DEBUG: Detected thruster "..thruster.name.." is of type 'thruster'.") end
|
||||
thruster.type = "thruster"
|
||||
break
|
||||
end
|
||||
end
|
||||
Thrusters[peripheral.getName(tv)].thruster = tv
|
||||
Thrusters[peripheral.getName(tv)].transmission = nil
|
||||
if Thrusters[peripheral.getName(tv)].affectVectors == nil then
|
||||
Thrusters[peripheral.getName(tv)].affectVectors = {}
|
||||
|
||||
for _, pt in ipairs(PropellerTypes) do
|
||||
if peripheral.getType(thruster.thruster) == pt then
|
||||
if Config.Debug then print("DEBUG: Detected thruster "..thruster.name.." is of type 'rotator'.") end
|
||||
thruster.type = "rotator"
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if thruster.type == "rotator" and thruster.transmission == nil then
|
||||
if Config.Debug then print("DEBUG: Thruster "..thruster.name.." does not have an associated transmission.") end
|
||||
local subnetworkId = peripheral.wrap(thruster.name).getSourceId()
|
||||
for _, transmissionType in ipairs(TransmissionTypes) do
|
||||
local transmissions = { peripheral.find(transmissionType) }
|
||||
if transmissions ~= nil and transmissions[1] ~= nil then
|
||||
if Config.Debug then print("DEBUG: transmission of type "..transmissionType.." found.") end
|
||||
for _, transmission in ipairs(transmissions) do
|
||||
local thisTransmissionId = transmission.getSelfId()
|
||||
if Config.Debug then print("DEBUG: Comparing subnetwork IDs values: "..thisTransmissionId.." == "..subnetworkId) end
|
||||
if transmission.getSelfId() == subnetworkId then
|
||||
if Config.Debug then print("DEBUG: Detected that thruster "..thruster.name.." is connected to transmission "..peripheral.getName(transmission)) end
|
||||
thruster.transmission = peripheral.getName(transmission)
|
||||
break
|
||||
elseif Config.Debug then
|
||||
print("DEBUG: Comparison inequal")
|
||||
end
|
||||
end
|
||||
elseif Config.Debug then
|
||||
print("DEBUG: No transmissions of type "..transmissionType.." (print from populateThrusterValues)")
|
||||
end
|
||||
end
|
||||
end
|
||||
if thruster.power == nil then
|
||||
if Config.Debug then print("DEBUG: Thruster "..thruster.name..": thruster power is not populated") end
|
||||
if thruster.type == "thruster" then
|
||||
thruster.power = thruster.thruster.getPower()
|
||||
elseif thruster.type == "rotator" then
|
||||
if thruster.transmission ~= nil then
|
||||
local transmissionType = peripheral.getType(peripheral.wrap(thruster.transmission))
|
||||
if transmissionType == "analog_transmission" then
|
||||
thruster.power = (peripheral.wrap(thruster.transmission).getSignal())/15
|
||||
elseif transmissionType == "Create_RotationSpeedController" then
|
||||
thruster.power = (peripheral.wrap(thruster.transmission).getTargetSpeed())/256
|
||||
end
|
||||
else
|
||||
print("ERROR: Thruster "..peripheral.getName(thruster.thruster).." has no transmission.")
|
||||
end
|
||||
end
|
||||
Thrusters[peripheral.getName(tv)].power = tv.getPower()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Update()
|
||||
populateThrusterValues()
|
||||
PropellerInit()
|
||||
ThrusterInit()
|
||||
end
|
||||
@@ -454,8 +524,8 @@ local function partiallyUpdateThrusters(thrusterList)
|
||||
for _, tt in ipairs(TransmissionTypes) do
|
||||
local transmissions = peripheral.find(tt)
|
||||
for _, t in ipairs(transmissions) do
|
||||
if tv.getSubnetworkAnchorId() == t.getSelfId() then
|
||||
thisThruster.transmission = t
|
||||
if tv.getSourceId() == t.getSelfId() then
|
||||
thisThruster.transmission = peripheral.getName(t)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -487,7 +557,7 @@ function PollVelocity()
|
||||
-- Velocity Sensors
|
||||
VelocityVectors = {}
|
||||
|
||||
if velSensors[0] == nil then -- verify array is not empty
|
||||
if velSensors[1] ~= nil then -- verify array is not empty
|
||||
|
||||
SensorData.Velocity = {}
|
||||
|
||||
@@ -495,15 +565,28 @@ function PollVelocity()
|
||||
|
||||
local vsAxis = velsensor.getAxis()
|
||||
local vsVelocity = velsensor.getVelocity()
|
||||
|
||||
-- correct for heading correction value
|
||||
if Config.SensorCorrection.Heading == -90 or Config.SensorCorrection.Heading == 270 then
|
||||
if vsAxis == "x" then vsAxis = "z" end
|
||||
if vsAxis == "z" then vsAxis = "x" end
|
||||
elseif Config.SensorCorrection.Heading == 180 or Config.SensorCorrection.Heading == -180 then
|
||||
vsVelocity = -vsVelocity
|
||||
elseif Config.SensorCorrection.Heading == 90 or Config.SensorCorrection.Heading == -270 then
|
||||
if vsAxis == "x" then vsAxis = "z" end
|
||||
if vsAxis == "z" then vsAxis = "x" end
|
||||
vsVelocity = -vsVelocity
|
||||
end
|
||||
|
||||
VelocityVectors[vsAxis] = vsVelocity
|
||||
|
||||
SensorData.Velocity.Raw = VelocityVectors
|
||||
|
||||
if Config.Debug then
|
||||
print("DEBUG: PollVelocity fetched sensor data: "..tableToString(SensorData.Velocity))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if Config.Debug then
|
||||
print("DEBUG: PollVelocity fetched sensor data: "..tableToString(SensorData.Velocity))
|
||||
end
|
||||
else
|
||||
if Config.Debug then
|
||||
print("DEBUG: No velocity sensors found.")
|
||||
@@ -594,13 +677,27 @@ function PollGimbal()
|
||||
zAngle = 0 -- roll angle
|
||||
}
|
||||
for i, v in ipairs(gimbalSensor.getAngles()) do
|
||||
if i == 0 then
|
||||
if i == 1 then
|
||||
Angles.xAngle = v
|
||||
elseif i == 1 then
|
||||
elseif i == 2 then
|
||||
Angles.zAngle = v
|
||||
end
|
||||
end
|
||||
|
||||
-- correct for heading correction value
|
||||
if Config.SensorCorrection.Heading == -90 or Config.SensorCorrection.Heading == 270 then
|
||||
local temp = Angles.xAngle
|
||||
Angles.xAngle = Angles.zAngle
|
||||
Angles.zAngle = temp
|
||||
elseif Config.SensorCorrection.Heading == 180 or Config.SensorCorrection.Heading == -180 then
|
||||
Angles.xAngle = -Angles.xAngle
|
||||
Angles.zAngle = -Angles.zAngle
|
||||
elseif Config.SensorCorrection.Heading == 90 or Config.SensorCorrection.Heading == -270 then
|
||||
local temp = Angles.xAngle
|
||||
Angles.xAngle = -Angles.zAngle
|
||||
Angles.zAngle = -temp
|
||||
end
|
||||
|
||||
AngularRates = {
|
||||
wx = 0, -- pitch rate
|
||||
wy = 0, -- yaw rate
|
||||
@@ -615,6 +712,21 @@ function PollGimbal()
|
||||
AngularRates.wz = v
|
||||
end
|
||||
end
|
||||
|
||||
-- correct for heading correction value
|
||||
if Config.SensorCorrection.Heading == -90 or Config.SensorCorrection.Heading == 270 then
|
||||
local temp = AngularRates.wx
|
||||
AngularRates.wx = AngularRates.wz
|
||||
AngularRates.wz = temp
|
||||
elseif Config.SensorCorrection.Heading == 180 or Config.SensorCorrection.Heading == -180 then
|
||||
AngularRates.wx = -AngularRates.wx
|
||||
AngularRates.wz = -AngularRates.wz
|
||||
elseif Config.SensorCorrection.Heading == 90 or Config.SensorCorrection.Heading == -270 then
|
||||
local temp = AngularRates.wx
|
||||
AngularRates.wx = -AngularRates.wz
|
||||
AngularRates.wz = -temp
|
||||
end
|
||||
|
||||
LinearAcceleration = {
|
||||
ax = 0, -- port-starboard axis
|
||||
ay = 0, -- up-down axis
|
||||
@@ -630,12 +742,26 @@ function PollGimbal()
|
||||
end
|
||||
end
|
||||
|
||||
-- correct for heading correction value
|
||||
if Config.SensorCorrection.Heading == -90 or Config.SensorCorrection.Heading == 270 then
|
||||
local temp = LinearAcceleration.ax
|
||||
LinearAcceleration.ax = LinearAcceleration.az
|
||||
LinearAcceleration.az = temp
|
||||
elseif Config.SensorCorrection.Heading == 180 or Config.SensorCorrection.Heading == -180 then
|
||||
LinearAcceleration.ax = -LinearAcceleration.ax
|
||||
LinearAcceleration.az = -LinearAcceleration.az
|
||||
elseif Config.SensorCorrection.Heading == 90 or Config.SensorCorrection.Heading == -270 then
|
||||
local temp = LinearAcceleration.ax
|
||||
LinearAcceleration.ax = -LinearAcceleration.az
|
||||
LinearAcceleration.az = -temp
|
||||
end
|
||||
|
||||
SensorData.Gimbal.Angles = Angles
|
||||
SensorData.Gimbal.AngularRates = AngularRates
|
||||
SensorData.Gimbal.LinearAcceleration = LinearAcceleration
|
||||
|
||||
if Config.Debug then
|
||||
print("DEBUG: PollGimbaal fetched sensor data: "..tableToString(SensorData.Gimbal))
|
||||
print("DEBUG: PollGimbal fetched sensor data: "..tableToString(SensorData.Gimbal))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -656,10 +782,24 @@ end
|
||||
|
||||
-- thruster is a thruster type, power is a vector from 0.0 to 1.0
|
||||
function SetThrusterPower(thruster, power)
|
||||
if thruster == nil then
|
||||
print("ERROR: SetThrusterPower called with nil thruster.")
|
||||
return
|
||||
end
|
||||
|
||||
if power > 1.0 then power = 1.0 elseif power < 0.0 then power = 0.0 end
|
||||
|
||||
--if Config.Debug then print("DEBUG: thruster "..thruster.name..": power "..power) end
|
||||
|
||||
if thruster.type == "rotator" then
|
||||
thruster.power = power
|
||||
local actualPower = 0
|
||||
if peripheral.getType(thruster.transmission) == "Create_RotationSpeedController" then
|
||||
if thruster.transmission == nil then
|
||||
print("ERROR: SetThrusterPower called with nil transmission for rotator thruster.")
|
||||
print("Thruster: "..thruster.name)
|
||||
return
|
||||
end
|
||||
if peripheral.getType(peripheral.wrap(thruster.transmission)) == "Create_RotationSpeedController" then
|
||||
if (math.ceil(power*256) - power*256 <= 0.5) then
|
||||
actualPower = math.ceil(power*256)
|
||||
end
|
||||
@@ -669,7 +809,7 @@ function SetThrusterPower(thruster, power)
|
||||
|
||||
thruster.transmission.setTargetSpeed(actualPower)
|
||||
end
|
||||
if peripheral.getType(thruster.transmission) == "analog_transmission" then
|
||||
if peripheral.getType(peripheral.wrap(thruster.transmission)) == "analog_transmission" then
|
||||
if (math.ceil(power*15) - power*15) <= 0.5 then
|
||||
actualPower = math.ceil(power*15)
|
||||
end
|
||||
@@ -679,16 +819,16 @@ function SetThrusterPower(thruster, power)
|
||||
|
||||
if actualPower < 1 then
|
||||
actualPower = 15 -- signal 15 will decouple the speed and stop the motion
|
||||
end
|
||||
if actualPower <= 15 then
|
||||
elseif actualPower <= 15 then
|
||||
actualPower = actualPower - 1
|
||||
end
|
||||
|
||||
thruster.transmission.setSignal(actualPower)
|
||||
peripheral.wrap(thruster.transmission).setSignal(actualPower)
|
||||
|
||||
-- if Config.Debug then print("DEBUG: actualPower: "..actualPower) end
|
||||
end
|
||||
|
||||
end
|
||||
if thruster.type == "thruster" then
|
||||
elseif thruster.type == "thruster" then
|
||||
thruster.power = power
|
||||
thruster.thruster.setPowerNormalized(power)
|
||||
end
|
||||
@@ -720,12 +860,36 @@ function CreatePID(kp, ki, kd)
|
||||
ki = ki,
|
||||
kd = kd,
|
||||
integral = 0, lastError = 0,
|
||||
|
||||
minOutput = 0.0,
|
||||
maxOutput = 32.0,
|
||||
update = function(self, setpoint, pv, dt)
|
||||
if dt <= 0 then return 0.0 end
|
||||
|
||||
local error = setpoint - pv
|
||||
self.integral = self.integral + (error * dt)
|
||||
local derivative = (error - self.lastError) / dt
|
||||
|
||||
local P = self.kp * error
|
||||
|
||||
local D = self.kd * ((error - self.lastError) / dt)
|
||||
|
||||
local potential_i = self.integral + (self.ki * error * dt)
|
||||
|
||||
local raw_output = P + potential_i + D
|
||||
|
||||
if Config.Debug then print("DEBUG: PID raw output: "..raw_output) end
|
||||
|
||||
local clampedOutput = 0
|
||||
|
||||
if raw_output >= self.minOutput and raw_output <= self.maxOutput then
|
||||
self.integral = potential_i
|
||||
clampedOutput = raw_output
|
||||
else
|
||||
clampedOutput = math.max(self.minOutput, math.min(raw_output, self.maxOutput))
|
||||
end
|
||||
|
||||
self.lastError = error
|
||||
return (self.kp * error) + (self.ki * self.integral) + (self.kd * derivative)
|
||||
|
||||
return clampedOutput
|
||||
end
|
||||
}
|
||||
end
|
||||
@@ -734,64 +898,117 @@ end
|
||||
PIDs = {
|
||||
-- angular
|
||||
-- attitude PIDs
|
||||
RollAttitudePID = CreatePID(0.1, 0.01, 0.05),
|
||||
PitchAttitudePID = CreatePID(0.1, 0.01, 0.05),
|
||||
RollAttitudePID = CreatePID(3.0, 0.01, 0.05),
|
||||
PitchAttitudePID = CreatePID(3.0, 0.01, 0.05),
|
||||
YawAttitudePID = CreatePID(0.1, 0.01, 0.05),
|
||||
|
||||
-- rate PIDs
|
||||
RollRatePID = CreatePID(0.1, 0.01, 0.05),
|
||||
PitchRatePID = CreatePID(0.1, 0.01, 0.05),
|
||||
YawRatePID = CreatePID(0.1, 0.01, 0.05),
|
||||
RollRatePID = CreatePID(0.8, 0.01, 0.05),
|
||||
PitchRatePID = CreatePID(0.8, 0.01, 0.05),
|
||||
YawRatePID = CreatePID(0.5, 0.01, 0.05),
|
||||
|
||||
-- lateral
|
||||
-- value PIDs
|
||||
AltitudePID = CreatePID(0.1, 0.01, 0.05),
|
||||
AltitudePID = CreatePID(0.6, 0.01, 0.05),
|
||||
PortStarPID = CreatePID(0.1, 0.01, 0.05),
|
||||
ForeAftPID = CreatePID(0.1, 0.01, 0.05),
|
||||
|
||||
-- rate PIDs
|
||||
VerticalRatePID = CreatePID(0.1, 0.01, 0.05),
|
||||
VerticalRatePID = CreatePID(0.8, 0.01, 0.05),
|
||||
PortStarRatePID = CreatePID(0.1, 0.01, 0.05),
|
||||
ForeAftRatePID = CreatePID(0.1, 0.01, 0.05)
|
||||
}
|
||||
|
||||
-- min and max thrust for thrust normalization
|
||||
local minThrust = math.huge
|
||||
local maxThrust = -math.huge
|
||||
|
||||
local function normalize(val, min, max)
|
||||
if min == max then return 0.5 end
|
||||
return (val - min) / (max - min)
|
||||
end
|
||||
|
||||
function UpdateGlobalThrust()
|
||||
-- for each thruster, there is a table of affectVectors that determine in what directions the thruster can apply thrust
|
||||
-- each thruster has a power value from 0 to 1, which is the amount of thrust the thruster is currently applying
|
||||
-- depending on the desired thrust vectors, some counteractive desired thrust vectors will be ignored
|
||||
|
||||
local dt = GetDeltaTime()
|
||||
local dt = GetDeltaTime() / 1000
|
||||
|
||||
if SensorData.Gimbal == nil or SensorData.Altitude == nil or SensorData.Velocity == nil then
|
||||
print("ERROR: One or more of SensorData is nil. Cannot update thrust.")
|
||||
return
|
||||
end
|
||||
|
||||
local throttleOutput = PIDs.AltitudePID:update(Config.Autopilot.AutopilotDesiredAltitude or 0, SensorData.Altitude.Altitude or 0, dt)
|
||||
|
||||
local pitchOutput = PIDs.PitchRatePID:update(Config.Autopilot.AutopilotDesiredPitchRate or 0, SensorData.Gimbal.AngularRates.wx or 0, dt)
|
||||
local rollOutput = PIDs.RollRatePID:update(Config.Autopilot.AutopilotDesiredRollRate or 0, SensorData.Gimbal.AngularRates.wz or 0, dt)
|
||||
local yawOutput = PIDs.YawRatePID:update(Config.Autopilot.AutopilotDesiredYawRate or 0, SensorData.Gimbal.AngularRates.wy or 0, dt)
|
||||
local AltitudeRateOutput = PIDs.VerticalRatePID:update(Config.Autopilot.AutopilotDesiredAltitude, SensorData.Altitude.Altitude, dt)
|
||||
local throttleOutput = PIDs.AltitudePID:update(AltitudeRateOutput, SensorData.Altitude.VerticalSpeed, dt)
|
||||
if Config.Debug then print("DEBUG: Throttle Output: "..throttleOutput) end
|
||||
|
||||
local pitchOutput = PIDs.PitchAttitudePID:update(0, SensorData.Gimbal.Angles.xAngle, dt)
|
||||
local pitchRateOutput = PIDs.PitchRatePID:update(pitchOutput, SensorData.Gimbal.AngularRates.wx or 0, dt)
|
||||
if Config.Debug then print("DEBUG: Pitch Output: "..pitchRateOutput) end
|
||||
|
||||
local rollOutput = PIDs.RollAttitudePID:update(0, SensorData.Gimbal.Angles.zAngle, dt)
|
||||
local rollRateOutput = PIDs.RollRatePID:update(rollOutput, SensorData.Gimbal.AngularRates.wz or 0, dt)
|
||||
if Config.Debug then print("DEBUG: Roll Output: "..rollRateOutput) end
|
||||
|
||||
local yawOutput = 5
|
||||
if Config.Autopilot.AutopilotDesiredHeading ~= nil then yawOutput = PIDs.YawAttitudePID:update(Config.Autopilot.AutopilotDesiredHeading, SensorData.NavTable.Heading, dt) end
|
||||
local yawRateOutput = PIDs.YawRatePID:update(Config.Autopilot.AutopilotDesiredYawRate or 0, SensorData.Gimbal.AngularRates.wy or 0, dt)
|
||||
|
||||
for _, thruster in pairs(Thrusters) do
|
||||
local desiredThrust = 0
|
||||
|
||||
-- Calculate desired thrust based on affectVectors and PID outputs
|
||||
if thruster.affectVectors.angular.pitch ~= nil and thruster.primary_pitch_thruster then
|
||||
desiredThrust = desiredThrust + pitchOutput
|
||||
end
|
||||
if thruster.affectVectors.angular.roll ~= nil and thruster.primary_roll_thruster then
|
||||
desiredThrust = desiredThrust + rollOutput
|
||||
end
|
||||
if thruster.affectVectors.angular.yaw ~= nil and thruster.primary_yaw_thruster then
|
||||
desiredThrust = desiredThrust + yawOutput
|
||||
end
|
||||
if thruster.x_config_equivalent ~= nil then
|
||||
|
||||
-- Calculate desired thrust based on affectVectors and PID outputs
|
||||
if thruster.affectVectors.angular.pitch ~= nil and thruster.primary_pitch_thruster then
|
||||
if thruster.affectVectors.angular.pitch == "up" then
|
||||
desiredThrust = desiredThrust + pitchRateOutput
|
||||
else
|
||||
desiredThrust = desiredThrust - pitchRateOutput
|
||||
end
|
||||
end
|
||||
|
||||
if thruster.affectVectors.angular.roll ~= nil and thruster.primary_roll_thruster then
|
||||
if thruster.affectVectors.angular.roll == "port" then
|
||||
desiredThrust = desiredThrust + rollRateOutput
|
||||
else
|
||||
desiredThrust = desiredThrust - rollRateOutput
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if thruster.affectVectors.angular.yaw ~= nil and thruster.primary_yaw_thruster then
|
||||
if thruster.affectVectors.angular.yaw == "port" then
|
||||
desiredThrust = desiredThrust + yawRateOutput
|
||||
else
|
||||
desiredThrust = desiredThrust - yawRateOutput
|
||||
end
|
||||
end
|
||||
|
||||
if thruster.affectVectors.lateral.y == "up" then
|
||||
desiredThrust = desiredThrust + throttleOutput
|
||||
if thruster.affectVectors.lateral.y == "up" then
|
||||
desiredThrust = desiredThrust + throttleOutput
|
||||
end
|
||||
|
||||
--if Config.Debug then print("DEBUG: "..thruster.name..": desiredThrust "..desiredThrust.." (from UpdateGlobalThrust)") end
|
||||
|
||||
SetThrusterPower(thruster, desiredThrust)
|
||||
else
|
||||
if Config.Debug then print("DEBUG: Thruster has x_config_equivalent set") end
|
||||
|
||||
local prop1 = throttleOutput + pitchRateOutput + rollRateOutput -- front-left
|
||||
local prop2 = throttleOutput + pitchRateOutput - rollRateOutput -- front-right
|
||||
local prop3 = throttleOutput - pitchRateOutput - rollRateOutput -- back-right
|
||||
local prop4 = throttleOutput - pitchRateOutput + rollRateOutput -- back-left
|
||||
|
||||
if thruster.x_config_equivalent == "1" then SetThrusterPower(thruster, prop1)
|
||||
elseif thruster.x_config_equivalent == "2" then SetThrusterPower(thruster, prop2)
|
||||
elseif thruster.x_config_equivalent == "3" then SetThrusterPower(thruster, prop3)
|
||||
elseif thruster.x_config_equivalent == "4" then SetThrusterPower(thruster, prop4)
|
||||
else print("ERROR: Thruster "..thruster.name.." has invalid x_config_equivalent value")
|
||||
end
|
||||
end
|
||||
|
||||
-- Normalize the desired thrust to be between 0 and 1
|
||||
local normalizedThrust = math.max(0, math.min(1, (desiredThrust + 1) / 2)) -- Convert from [-1, 1] to [0, 1]
|
||||
|
||||
SetThrusterPower(thruster, normalizedThrust)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -842,7 +1059,10 @@ function Init()
|
||||
end
|
||||
end
|
||||
|
||||
-- If we're here, the config loaded successfully and the initialization is done.
|
||||
-- If we're here, the config loaded successfully.
|
||||
|
||||
populateThrusterValues()
|
||||
Update()
|
||||
end
|
||||
|
||||
-- Display functions for monitors
|
||||
@@ -862,9 +1082,9 @@ function displayInstrumentPanel(monitor, sensorData)
|
||||
if next(sensorData) ~= nil then
|
||||
if sensorData then
|
||||
m.setCursorPos(1, 3)
|
||||
m.write("Pitch: " .. string.format("%.2f", SensorData.Gimbal.Angles[0] or 0))
|
||||
m.write("Pitch: " .. string.format("%.2f", SensorData.Gimbal.Angles.xAngle or 0))
|
||||
m.setCursorPos(1, 4)
|
||||
m.write("Roll: " .. string.format("%.2f", SensorData.Gimbal.Angles[1] or 0))
|
||||
m.write("Roll: " .. string.format("%.2f", SensorData.Gimbal.Angles.zAngle or 0))
|
||||
m.setCursorPos(1, 5)
|
||||
m.write("Yaw: " .. string.format("%.2f", SensorData.NavTable.Heading or 0))
|
||||
m.setCursorPos(1, 6)
|
||||
@@ -943,6 +1163,12 @@ function collectSensorData()
|
||||
end
|
||||
|
||||
function WriteConfigFiles()
|
||||
-- since the thruster object will change every time, set each thruster's thruster object to nil before writing to the config file, so that the config file only contains the thruster names and not the thruster objects
|
||||
for _, thruster in pairs(Thrusters) do
|
||||
thruster.thruster = nil
|
||||
thruster.transmission = nil
|
||||
end
|
||||
|
||||
print("Writing to thruster config file.")
|
||||
local thrusterConfigFile = fs.open(Config.thrusterConfigPath, "w+")
|
||||
thrusterConfigFile.write(tableToString(Thrusters))
|
||||
@@ -957,13 +1183,13 @@ end
|
||||
function Main()
|
||||
Init()
|
||||
|
||||
os.sleep(5) -- pause for any initialization errors to display
|
||||
|
||||
local sentinel = true
|
||||
print("Mainloop starting. Press 'q' to stop the loop and save configuration changes.")
|
||||
while sentinel do
|
||||
|
||||
PollSensors()
|
||||
|
||||
UpdateStabilization()
|
||||
UpdateGlobalThrust()
|
||||
|
||||
-- Update monitor displays
|
||||
@@ -975,17 +1201,10 @@ function Main()
|
||||
displayAutopilotControls(Config.Monitors.AutopilotControlMonitor, SensorData)
|
||||
end
|
||||
|
||||
local unindexedThrusters = checkIfThrusterIsIndexed()
|
||||
if unindexedThrusters ~= nil then
|
||||
partiallyUpdateThrusters(unindexedThrusters)
|
||||
end
|
||||
|
||||
local function getTerminateEvent()
|
||||
local event, key, is_held = os.pullEventRaw("key")
|
||||
if keys.getName(key) == "q" then
|
||||
print("Quit Input Received")
|
||||
|
||||
WriteConfigFiles()
|
||||
|
||||
sentinel = false
|
||||
else
|
||||
@@ -997,6 +1216,17 @@ function Main()
|
||||
|
||||
parallel.waitForAny(do_sleep, getTerminateEvent)
|
||||
end
|
||||
|
||||
for _, t in pairs(Thrusters) do
|
||||
if t.type == "rotator" then
|
||||
SetThrusterPower(t, 0.0)
|
||||
peripheral.wrap(t.transmission).releaseSignal() -- release control of transmissions on quit
|
||||
else
|
||||
SetThrusterPower(t, 0.0)
|
||||
end
|
||||
end
|
||||
|
||||
WriteConfigFiles()
|
||||
end
|
||||
|
||||
-- Run the main function when the script starts
|
||||
|
||||
@@ -6,6 +6,7 @@ return {
|
||||
primary_altitude_thruster = true,
|
||||
primary_fore_thruster = false,
|
||||
primary_aft_thruster = false,
|
||||
x_config_equivalent = "1",
|
||||
type = "rotator",
|
||||
name = "gyroscopic_propeller_bearing_0",
|
||||
thruster = nil,
|
||||
@@ -31,6 +32,7 @@ return {
|
||||
primary_altitude_thruster = true,
|
||||
primary_fore_thruster = false,
|
||||
primary_aft_thruster = false,
|
||||
x_config_equivalent = "2",
|
||||
type = "rotator",
|
||||
name = "gyroscopic_propeller_bearing_1",
|
||||
thruster = nil,
|
||||
@@ -56,6 +58,7 @@ return {
|
||||
primary_altitude_thruster = true,
|
||||
primary_fore_thruster = false,
|
||||
primary_aft_thruster = false,
|
||||
x_config_equivalent = "4",
|
||||
type = "rotator",
|
||||
name = "gyroscopic_propeller_bearing_2",
|
||||
thruster = nil,
|
||||
@@ -81,6 +84,7 @@ return {
|
||||
primary_altitude_thruster = true,
|
||||
primary_fore_thruster = false,
|
||||
primary_aft_thruster = false,
|
||||
x_config_equivalent = "3",
|
||||
type = "rotator",
|
||||
name = "gyroscopic_propeller_bearing_3",
|
||||
thruster = nil,
|
||||
|
||||
Reference in New Issue
Block a user