Sedrowow

Aeronautics Autopilot

Apr 21st, 2026 (edited)
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.93 KB | Gaming | 0 0
  1. -- ==========================================
  2. -- MULTI-MODE AERONAUTICS FLIGHT CONTROLLER
  3. -- ==========================================
  4.  
  5. -- TRANSPORT MODE: "quad", "steer", or "prop"
  6. local transport_mode = "prop"
  7.  
  8. -- Configuration
  9. local config = {
  10.     invert_analog = true,
  11.    
  12.     -- Base Outputs
  13.     lift_side = "bottom",    
  14.     forward_side = "front",    
  15.     reverse_side = "back",  
  16.     steer_left = "left",    
  17.     steer_right = "right",  
  18.    
  19.     -- Quadcopter Outputs (X-Configuration)
  20.     quad_fl = "left",  
  21.     quad_fr = "front",  
  22.     quad_bl = "back",  
  23.     quad_br = "right",  
  24.    
  25.     -- Logical Lift States (0 = Off, 15 = Max Power)
  26.     lift_takeoff = 15,
  27.     lift_hover = 12,    
  28.     lift_land = 8,    
  29.     lift_off = 0,
  30.    
  31.     -- Flight Parameters
  32.     min_altitude = 150,      
  33.     max_descent_rate = -1.5,
  34.     loop_delay = 0.5,
  35.    
  36.     -- Propeller / Steering Tuning
  37.     p_gain = 0.3,        
  38.     d_gain = 0.1,         -- Braking strength against spins
  39.     deadzone = 3,        
  40.     max_steer_power = 4,  -- Max turn speed (1-15)
  41.    
  42.     -- Quadcopter Tuning
  43.     max_tilt = 15,        
  44.     gimbal_p = 0.4        
  45. }
  46.  
  47. -- Flight States
  48. local STATE_STANDBY = 0
  49. local STATE_TAKEOFF = 1
  50. local STATE_TRANSIT = 2
  51. local STATE_ARRIVED = 3
  52. local STATE_LANDING = 4
  53. local STATE_HOVER = 5
  54.  
  55. local current_state = STATE_STANDBY
  56. local last_altitude = 0
  57. local target_hover_alt = config.min_altitude
  58.  
  59. -- ==========================================
  60. -- PERIPHERAL INITIALIZATION
  61. -- ==========================================
  62. local nav_name, alt_name, gimbal_name = nil, nil, nil
  63.  
  64. for _, name in ipairs(peripheral.getNames()) do
  65.     if string.find(name, "navigation_table") then nav_name = name
  66.     elseif string.find(name, "altitude_sensor") then alt_name = name
  67.     elseif string.find(name, "gimbal_sensor") then gimbal_name = name end
  68. end
  69.  
  70. if not nav_name or not alt_name then
  71.     error("CRITICAL: Navigation Table or Altitude Sensor missing!")
  72. end
  73. if transport_mode == "quad" and not gimbal_name then
  74.     error("CRITICAL: Quad mode requires a Gimbal Sensor on the network!")
  75. end
  76.  
  77. local nav = peripheral.wrap(nav_name)
  78. local alt = peripheral.wrap(alt_name)
  79. local gimbal = gimbal_name and peripheral.wrap(gimbal_name) or nil
  80.  
  81. -- ==========================================
  82. -- HELPER FUNCTIONS
  83. -- ==========================================
  84. local function setOutput(side, power)
  85.     power = math.max(0, math.min(15, math.floor(power)))
  86.     if config.invert_analog then
  87.         redstone.setAnalogOutput(side, 15 - power)
  88.     else
  89.         redstone.setAnalogOutput(side, power)
  90.     end
  91. end
  92.  
  93. local function hardStopThrust()
  94.     if transport_mode == "quad" then
  95.         setOutput(config.quad_fl, 0)
  96.         setOutput(config.quad_fr, 0)
  97.         setOutput(config.quad_bl, 0)
  98.         setOutput(config.quad_br, 0)
  99.     else
  100.         setOutput(config.lift_side, 0)
  101.         setOutput(config.forward_side, 0)
  102.         setOutput(config.reverse_side, 0)
  103.         setOutput(config.steer_left, 0)
  104.         setOutput(config.steer_right, 0)
  105.     end
  106. end
  107.  
  108. local function applyStandardThrust(current_angle, last_angle, distance_flag)
  109.     if distance_flag == "ARRIVED" then
  110.         setOutput(config.forward_side, 0)
  111.         setOutput(config.reverse_side, 5)
  112.         setOutput(config.steer_left, 0)
  113.         setOutput(config.steer_right, 0)
  114.         return
  115.     end
  116.  
  117.     setOutput(config.forward_side, 15)
  118.     setOutput(config.reverse_side, 0)
  119.    
  120.     local turn_rate = 0
  121.     if last_angle ~= nil then
  122.         local delta_angle = (current_angle - last_angle + 180) % 360 - 180
  123.         turn_rate = delta_angle / config.loop_delay
  124.     end
  125.  
  126.     -- PD MATH: Steer towards the target (P) but fight against spinning too fast (D)
  127.     local steer_effort = (current_angle * config.p_gain) - (turn_rate * config.d_gain)
  128.     local power = math.min(config.max_steer_power, math.abs(steer_effort))
  129.    
  130.     if steer_effort > config.deadzone then
  131.         setOutput(config.steer_right, power)
  132.         setOutput(config.steer_left, 0)
  133.     elseif steer_effort < -config.deadzone then
  134.         setOutput(config.steer_left, power)
  135.         setOutput(config.steer_right, 0)
  136.     else
  137.         setOutput(config.steer_left, 0)
  138.         setOutput(config.steer_right, 0)
  139.     end
  140. end
  141.  
  142. local function applyQuadThrust(target_angle, current_pitch, current_roll, base_lift, is_transit)
  143.     local target_pitch = 0
  144.     local target_roll = 0
  145.    
  146.     if is_transit and target_angle then
  147.         local rad_angle = math.rad(target_angle)
  148.         target_pitch = math.cos(rad_angle) * config.max_tilt
  149.         target_roll = math.sin(rad_angle) * config.max_tilt
  150.     end
  151.  
  152.     local pitch_error = current_pitch - target_pitch
  153.     local roll_error = current_roll - target_roll
  154.    
  155.     local pitch_adj = pitch_error * config.gimbal_p
  156.     local roll_adj = roll_error * config.gimbal_p
  157.  
  158.     local out_fl = base_lift + pitch_adj + roll_adj
  159.     local out_fr = base_lift + pitch_adj - roll_adj
  160.     local out_bl = base_lift - pitch_adj + roll_adj
  161.     local out_br = base_lift - pitch_adj - roll_adj
  162.  
  163.     setOutput(config.quad_fl, out_fl)
  164.     setOutput(config.quad_fr, out_fr)
  165.     setOutput(config.quad_bl, out_bl)
  166.     setOutput(config.quad_br, out_br)
  167. end
  168.  
  169. -- ==========================================
  170. -- THREAD 1: KEYBOARD LISTENER
  171. -- ==========================================
  172. local function keyboardThread()
  173.     local last_s_time = 0
  174.     local last_x_time = 0
  175.     local x_press_count = 0
  176.    
  177.     while true do
  178.         local event, key = os.pullEvent("char")
  179.         local now = os.clock()
  180.        
  181.         if key == "s" then
  182.             if current_state == STATE_STANDBY then
  183.                 print(">>> MANUAL START: TAKEOFF INITIATED <<<")
  184.                 current_state = STATE_TAKEOFF
  185.                 last_s_time = 0
  186.             elseif current_state > STATE_STANDBY and current_state ~= STATE_HOVER then
  187.                 if now - last_s_time < 0.5 then
  188.                     print(">>> PAUSE: HOVER ENGAGED <<<")
  189.                     local ok, current_alt = pcall(alt.getHeight)
  190.                     target_hover_alt = ok and math.max(current_alt, 10) or config.min_altitude
  191.                     current_state = STATE_HOVER
  192.                 end
  193.             end
  194.             last_s_time = now
  195.            
  196.         elseif key == "r" and current_state == STATE_HOVER then
  197.             print(">>> RESUMING FLIGHT <<<")
  198.             current_state = STATE_TAKEOFF
  199.            
  200.         elseif key == "l" and current_state > STATE_STANDBY and current_state ~= STATE_LANDING then
  201.             print(">>> MANUAL SAFE LANDING INITIATED <<<")
  202.             current_state = STATE_LANDING
  203.            
  204.         elseif key == "x" then
  205.             if now - last_x_time < 0.6 then
  206.                 x_press_count = x_press_count + 1
  207.             else
  208.                 x_press_count = 1
  209.             end
  210.             last_x_time = now
  211.            
  212.             if x_press_count >= 3 then
  213.                 print(">>> EMERGENCY KILL SWITCH TRIGGERED! ENGINES CUT <<<")
  214.                 current_state = STATE_STANDBY
  215.                 x_press_count = 0
  216.             end
  217.         end
  218.     end
  219. end
  220.  
  221. -- ==========================================
  222. -- THREAD 2: MAIN FLIGHT LOGIC
  223. -- ==========================================
  224. local function flightLogicThread()
  225.     local last_angle = nil
  226.    
  227.     while true do
  228.         local alt_ok, current_alt = pcall(alt.getHeight)
  229.         local nav_ok, current_angle = pcall(nav.getRelativeAngle)
  230.        
  231.         current_angle = current_angle or 0
  232.        
  233.         local gim_ok, angles = true, {0, 0}
  234.         if transport_mode == "quad" then
  235.             gim_ok, angles = pcall(gimbal.getAngles)
  236.         end
  237.        
  238.         if not alt_ok or not nav_ok or not gim_ok then
  239.             sleep(config.loop_delay)
  240.         else
  241.             if current_state == STATE_STANDBY then
  242.                 hardStopThrust()
  243.             else
  244.                 local current_pitch = angles[1] or 0
  245.                 local current_roll = angles[2] or 0
  246.                 local base_lift = config.lift_off
  247.  
  248.                 if current_state == STATE_TAKEOFF then
  249.                     base_lift = config.lift_takeoff
  250.                     if current_alt >= config.min_altitude then current_state = STATE_TRANSIT end
  251.                    
  252.                 elseif current_state == STATE_TRANSIT then
  253.                     base_lift = config.lift_hover
  254.                    
  255.                     -- NEW BULLETPROOF ARRIVAL DETECTION
  256.                     if last_angle ~= nil then
  257.                         local was_facing_target = math.abs(last_angle) < 60
  258.                         local is_target_behind = math.abs(current_angle) > 120
  259.                         if was_facing_target and is_target_behind then
  260.                             current_state = STATE_ARRIVED
  261.                         end
  262.                     end
  263.  
  264.                 elseif current_state == STATE_ARRIVED then
  265.                     base_lift = config.lift_hover
  266.                    
  267.                 elseif current_state == STATE_HOVER then
  268.                     local descent_rate = (current_alt - last_altitude) / config.loop_delay
  269.                     if current_alt < target_hover_alt - 2 or descent_rate < -0.5 then
  270.                         base_lift = config.lift_takeoff
  271.                     elseif current_alt > target_hover_alt + 2 then
  272.                         base_lift = config.lift_land
  273.                     else
  274.                         base_lift = config.lift_hover
  275.                     end
  276.  
  277.                 elseif current_state == STATE_LANDING then
  278.                     local descent_rate = (current_alt - last_altitude) / config.loop_delay
  279.                     if descent_rate < config.max_descent_rate then
  280.                         base_lift = config.lift_hover -- Fixed Trampoline Bug
  281.                     else
  282.                         base_lift = config.lift_land
  283.                     end
  284.                     if current_alt <= 1 or (descent_rate == 0 and current_alt < config.min_altitude - 10) then
  285.                         print("Touchdown. Systems standing by.")
  286.                         current_state = STATE_STANDBY
  287.                         hardStopThrust()
  288.                     end
  289.                 end
  290.  
  291.                 if transport_mode == "quad" then
  292.                     local is_transit = (current_state == STATE_TRANSIT)
  293.                     applyQuadThrust(current_angle, current_pitch, current_roll, base_lift, is_transit)
  294.                 else
  295.                     setOutput(config.lift_side, base_lift)
  296.                     -- HAS_TARGET BUG REMOVED HERE
  297.                     if current_state == STATE_TRANSIT then
  298.                         applyStandardThrust(current_angle, last_angle, "TRANSIT")
  299.                     elseif current_state == STATE_ARRIVED then
  300.                         applyStandardThrust(current_angle, last_angle, "ARRIVED")
  301.                         sleep(2)
  302.                         current_state = STATE_LANDING
  303.                     elseif current_state == STATE_HOVER or current_state == STATE_LANDING then
  304.                         setOutput(config.forward_side, 0)
  305.                         setOutput(config.reverse_side, 0)
  306.                         setOutput(config.steer_left, 0)
  307.                         setOutput(config.steer_right, 0)
  308.                     end
  309.                 end
  310.             end
  311.            
  312.             last_altitude = current_alt
  313.             last_angle = current_angle
  314.             sleep(config.loop_delay)
  315.         end
  316.     end
  317. end
  318.  
  319. -- ==========================================
  320. -- BOOT SEQUENCE
  321. -- ==========================================
  322. term.clear()
  323. term.setCursorPos(1,1)
  324. print("Aeronautics OS v6.2")
  325. print("-------------------------")
  326. print("[S]    Start Navigation")
  327. print("[S x2] Pause / Hover in place")
  328. print("[R]    Resume Navigation")
  329. print("[L]    Safe Land Here")
  330. print("[X x3] KILL SWITCH")
  331. print("-------------------------")
  332. parallel.waitForAny(keyboardThread, flightLogicThread)
Advertisement
Add Comment
Please, Sign In to add comment