├── examples
└── ksp-pynet
│ ├── ksppynet
│ ├── __init__.py
│ ├── ksppynet.py
│ ├── flight_plan_node.py
│ └── flight_plan.py
│ ├── kpconsole.kv
│ └── main.py
├── README.md
├── LICENSE
└── Art_Whaleys_KRPC_Demos
├── Art_Whaleys_KRPC_Demos.pyproj
├── docking_autopilot.py
├── pid.py
├── node_executor.py
├── rover.py
├── rendezvous.py
├── Simple_Launch_Script.py
└── landing.py
/examples/ksp-pynet/ksppynet/__init__.py:
--------------------------------------------------------------------------------
1 | """The ksppynet package"""
2 | from .ksppynet import *
3 | from .flight_plan import *
4 |
5 | __all__ = (ksppynet.__all__,
6 | flight_plan.__all__)
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Community Code Library for kRPC
2 |
3 | This is a library of code examples for use with the [kRPC
4 | mod](https://github.com/krpc/krpc) for Kerbal Space Program, developed by the
5 | kRPC community.
6 |
7 | ## Contributing
8 |
9 | If you would like to contribute to the library, please feel free to open a pull request!
10 |
11 | ## Licensing
12 |
13 | Code contributed to this repository is under the MIT license. The act of
14 | submitting content constitutes implied consent to have the content placed under the
15 | MIT license.
16 |
17 | The full license terms can be found in the LICENSE file alongside this readme,
18 | but in summary, you are free to share, re-use and modify any of the code as long
19 | as you (1) give attribution that links back to here; and (2) don't sue. No
20 | warranty is given, or liability assumed.
21 |
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 kRPC Community
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Art_Whaleys_KRPC_Demos/Art_Whaleys_KRPC_Demos.pyproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | 2.0
6 | {6afe5bee-de58-43d5-9762-8a90b3f8a0db}
7 |
8 |
9 |
10 | .
11 | .
12 | {888888a0-9f3d-457c-b088-3a5042f75d52}
13 | Standard Python launcher
14 | {2af0f10d-7135-4994-9156-5d01c9c11b7e}
15 | 2.7
16 |
17 |
18 |
19 |
20 | 10.0
21 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Code
30 |
31 |
32 | Code
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Art_Whaleys_KRPC_Demos/docking_autopilot.py:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | ### Docking Autopilot Library and Example
3 | ######################################################################
4 | ### Like all of the scripts in my folder here, this file contains
5 | ### functions you might want to include into your own scripts for
6 | ### actual use and a demo in the 'main' function that you can just
7 | ### run to see how it works.
8 | ###
9 | ### This file demonstrates an automatic docking. It assumes that
10 | ### your vessel is already on the correct side of the vessel to dock
11 | ### to. In the near future I'll work on code to allow it to clear
12 | ### the target vessel safely and put itself on the correct side, but
13 | ### for now it's a great and fairly simple example of using some
14 | ### basic PID methods to control vessels with precision.
15 | ######################################################################
16 |
17 | import time
18 | import krpc
19 | import collections
20 | import math
21 |
22 | from pid import PID
23 |
24 | v3 = collections.namedtuple('v3', 'right forward up')
25 |
26 | ##############################################################################
27 | ## Main - only run when this file is explicitly executed
28 | ##############################################################################
29 | def main():
30 | conn = krpc.connect()
31 | dock(conn)
32 |
33 | ##############################################################################
34 | ## dock - The actually interesting function in this file.
35 | ## works by lining vessel up parallel, with 10m of separation between
36 | ## docking ports. When this is confirmed, it moves forward slowly to dock.
37 | ##############################################################################
38 | def dock(conn, speed_limit = 1.0):
39 |
40 | #Setup KRPC
41 | sc = conn.space_center
42 | v = sc.active_vessel
43 | t = sc.target_docking_port
44 | ap = v.auto_pilot
45 | rf = v.orbit.body.reference_frame
46 |
47 | #Setup Auto Pilot
48 | ap.reference_frame = rf
49 | ap.target_direction = tuple(x * -1 for x in t.direction(rf))
50 | ap.engage()
51 |
52 | #create PIDs
53 | upPID = PID(.75,.25,1)
54 | rightPID = PID(.75,.25,1)
55 | forwardPID = PID(.75,.2,.5)
56 |
57 | proceed=False
58 | #'proceed' is a flag that signals that we're lined up and ready to dock.
59 | # Otherwise the program will try to line up 10m from the docking port.
60 |
61 | #LineUp and then dock - in the same loop with 'proceed' controlling whether
62 | #we're headed for the 10m safe point, or headed forward to dock.
63 | while True:
64 | offset = getOffsets(v, t) #grab data and compute setpoints
65 | velocity = getVelocities(v, t)
66 | if proceedCheck(offset): #Check whether we're lined up and ready to dock
67 | proceed = True
68 | setpoints = getSetpoints(offset, proceed, speed_limit)
69 |
70 | upPID.setpoint(setpoints.up) #set PID setpoints
71 | rightPID.setpoint(setpoints.right)
72 | forwardPID.setpoint(setpoints.forward)
73 |
74 | v.control.up = -upPID.update(velocity.up) #steer vessel
75 | v.control.right = -rightPID.update(velocity.right)
76 | v.control.forward = -forwardPID.update(velocity.forward)
77 |
78 | time.sleep(.1)
79 |
80 | ##############################################################################
81 | ## Helper Functions
82 | ##############################################################################
83 | def getOffsets(v, t):
84 | '''
85 | returns the distance (right, forward, up) between docking ports.
86 | '''
87 | return v3._make(t.part.position(v.parts.controlling.reference_frame))
88 |
89 | def getVelocities(v, t):
90 | '''
91 | returns the relative velocities (right, forward, up)
92 | '''
93 | return v3._make(v.velocity(t.reference_frame))
94 |
95 | def getSetpoints(offset, proceed, speed_limit):
96 | '''
97 | returns the computed set points -
98 | set points are actually just the offset distances clamped to the
99 | speed_limit variable! This way we slow down as we get closer to the right
100 | heading.
101 | '''
102 | tvup = max(min(offset.up, speed_limit), -speed_limit)
103 | tvright = -1 * (max(min(offset.right, speed_limit), -speed_limit))
104 | if proceed:
105 | tvforward = -.2
106 | else:
107 | tvforward = max(min((10 - offset.forward), speed_limit), -speed_limit)
108 | return v3(tvright, tvforward, tvup)
109 |
110 | def proceedCheck(offset):
111 | '''
112 | returns true if we're lined up and ready to move forward to dock.
113 | '''
114 | return (offset.up < .1 and
115 | offset.right < .1 and
116 | math.fabs(10 - offset.forward)<.1)
117 |
118 |
119 |
120 | ##This calls the main function which is at the top of the file for readability's sake!
121 | if __name__ == '__main__':
122 | main()
123 | print('--')
124 |
--------------------------------------------------------------------------------
/Art_Whaleys_KRPC_Demos/pid.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | ### PID Controller Library and Example
3 | ##############################################################################
4 | ### Like all of the scripts in my folder here, this file contains
5 | ### functions you might want to include into your own scripts for
6 | ### actual use and a demo in the 'main' function that you can just
7 | ### run to see how it works.
8 | ###
9 | ### This file includes a simple and generic PID controller that we use in
10 | ### many of the other examples when we want to smoothly control one value
11 | ### based on our measurement of another. The PID class docstring explains
12 | ### the basics of using it in your project. The demo code below that
13 | ### shows how to use the PID controller to hold a vertical velocity with
14 | ### variation of engine thrust.
15 | ##############################################################################
16 |
17 | import time
18 | import krpc
19 |
20 | class PID(object):
21 | '''
22 | Generic PID Controller Class
23 | Based on the PID recipe at :
24 |
25 | http://code.activestate.com/recipes/577231-discrete-pid-controller/
26 |
27 | and the code and discussions in the blog at:
28 |
29 | http://brettbeauregard.com/blog/2011/04/
30 | improving-the-beginners-pid-introduction/
31 |
32 | An instance is created with the format
33 | your_pid=PID(P=.0001, I=0.00001, D=0.000001)
34 |
35 | Finding the right values for those three gain numbers is called 'tuning' and
36 | that's beyond the scope of this doc string!
37 |
38 | Use your_pid.setpoint(X) to set the target output value of the controller.
39 |
40 | Regularly call your_pid.update(Y), passing it the input data that the
41 | controller should respond to.
42 | output_data = your_pid.update(input_data)
43 |
44 | '''
45 |
46 | def __init__(self, P=1.0, I=0.1, D=0.01):
47 | self.Kp = P #P controls reaction to the instantaneous error
48 | self.Ki = I #I controls reaction to the history of error
49 | self.Kd = D #D prevents overshoot by considering rate of change
50 | self.P = 0.0
51 | self.I = 0.0
52 | self.D = 0.0
53 | self.SetPoint = 0.0 #Target value for controller
54 | self.ClampI = 1.0 #clamps i_term to prevent 'windup.'
55 | self.LastTime = time.time()
56 | self.LastMeasure = 0.0
57 |
58 | def update(self,measure):
59 | now = time.time()
60 | change_in_time = now - self.LastTime
61 | if not change_in_time:
62 | change_in_time = 1.0 #avoid potential divide by zero if PID just created.
63 |
64 | error = self.SetPoint - measure
65 | self.P = error
66 | self.I += error
67 | self.I = self.clamp_i(self.I) # clamp to prevent windup lag
68 | self.D = (measure - self.LastMeasure) / (change_in_time)
69 |
70 | self.LastMeasure = measure # store data for next update
71 | self.lastTime = now
72 |
73 | return (self.Kp * self.P) + (self.Ki * self.I) - (self.Kd * self.D)
74 |
75 | def clamp_i(self, i):
76 | if i > self.ClampI:
77 | return self.ClampI
78 | elif i < -self.ClampI:
79 | return -self.ClampI
80 | else:
81 | return i
82 |
83 | def setpoint(self, value):
84 | self.SetPoint = value
85 | self.I = 0.0
86 |
87 | ##############################################################################
88 | ## Demo Code Below This Line!
89 | ##############################################################################
90 |
91 | Target_Velocity = 5 # The value we're trying to limit ourselves to
92 |
93 | ##############################################################################
94 | ## Main -- only run when we execute this file directly.
95 | ## ignored when we import the PID into other files!
96 | ##############################################################################
97 | def main():
98 | #Setup KRPC
99 | conn = krpc.connect()
100 | sc = conn.space_center
101 | v = sc.active_vessel
102 | telem = v.flight(v.orbit.body.reference_frame)
103 |
104 | # Create PID controller.
105 | p = PID(P=.25, I=0.025, D=0.0025)
106 | p.ClampI = 20
107 | p.setpoint(Target_Velocity)
108 |
109 | # starting with locked SAS and throttle at full
110 | v.control.sas = True
111 | v.control.throttle = 1.0
112 | while not v.thrust: # stage if we just launched a new rocket
113 | v.control.activate_next_stage()
114 |
115 | # Loop Forever, or until you get the point of this example and stop it.
116 | while True:
117 | the_pids_output=p.update(telem.vertical_speed)
118 | v.control.throttle=the_pids_output
119 | print('Vertical V:{:03.2f} PID returns:{:03.2f} Throttle:{:03.2f}'
120 | .format(telem.vertical_speed,
121 | the_pids_output,
122 | v.control.throttle))
123 | time.sleep(.1)
124 |
125 |
126 |
127 | if __name__ == '__main__':
128 | main()
129 | print('--')
130 |
--------------------------------------------------------------------------------
/Art_Whaleys_KRPC_Demos/node_executor.py:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | ### Node Execution Library and Example
3 | ######################################################################
4 | ### Like all of the scripts in my folder here, this file contains
5 | ### functions you might want to include into your own scripts for
6 | ### actual use and a demo in the 'main' function that you can just
7 | ### run to see how it works.
8 | ###
9 | ### This file shows how to execute maneuver nodes. The docstring
10 | ### for the execute_next_node function explains how to make this work!
11 | ### And you can see an example of doing so in the launch script.
12 | ######################################################################
13 |
14 | import krpc
15 | import math
16 | import time
17 |
18 | def main():
19 | conn = krpc.connect()
20 | #Demo of all three major functions in this file - uncomment the one you want!
21 | execute_btn(conn) #Creates an on screen button to execute the next node
22 | # execute_next_node(conn) #Executes the next node!
23 | # execute_all_nodes(conn) #executes ALL nodes instead of just the next one!
24 |
25 | def execute_next_node(conn):
26 | '''
27 | This is the actually interesting function in this script!
28 |
29 | Executes the Next Maneuver Node for the vessel provided.
30 | If you just open and run this file, it will execute a node and exit.
31 | You can also include this file into your own script with the line
32 |
33 | from node_executor import execute_next_node
34 |
35 | at the top of your script, and then anytime you want to execute a node
36 | you just have to call (execute_next_node(conn) passing it the active
37 | KRPC connection as a parameter.
38 |
39 | I'm also demonstrating two different ways to point the vessel with the
40 | autopilot. One relies on the vessel having SAS Node holding capabilty,
41 | the other uses the KRPC built-in auto-pilot. The one built into
42 | KRPC can require some tuning depending on your vessel... but works on
43 | any vessel regardless of pilot skill/probe core choice!
44 | '''
45 | space_center = conn.space_center
46 | vessel = space_center.active_vessel
47 | ap=vessel.auto_pilot
48 |
49 | # Grab the next node if it exists
50 | try:
51 | node = vessel.control.nodes[0]
52 | except Exception:
53 | return #Fail silently but gracefully if there was no node to execute
54 |
55 |
56 | # Orient vessel to the node
57 | ################## One Way To Orient Vessel!##############
58 | rf = vessel.orbit.body.reference_frame
59 | ap.reference_frame=rf
60 | ap.engage()
61 | ap.target_direction = node.remaining_burn_vector(rf)
62 | ap.wait()
63 |
64 | ################## Another Way To Orient Vessel!########
65 | #ap.sas = True
66 | #time.sleep(.1)
67 | #ap.sas_mode = vessel.auto_pilot.sas_mode.maneuver
68 | #ap.wait()
69 |
70 | # Calculate the length and start of burn
71 | m = vessel.mass
72 | isp = vessel.specific_impulse
73 | dv = node.delta_v
74 | F = vessel.available_thrust
75 | G = 9.81
76 | burn_time = (m - (m / math.exp(dv / (isp * G)))) / (F / (isp * G))
77 |
78 | # Warp until burn
79 | space_center.warp_to(node.ut - (burn_time / 2.0) - 5.0)
80 | while node.time_to > (burn_time / 2.0):
81 | pass
82 | ap.wait()
83 |
84 | # Actually Burn
85 | vessel.control.throttle = thrust_controller(vessel, node.remaining_delta_v)
86 | while node.remaining_delta_v > .1:
87 | ap.target_direction=node.remaining_burn_vector(rf)#comment out this line
88 | #if using the vessel sas method to orient vessel
89 | vessel.control.throttle = thrust_controller(vessel, node.remaining_delta_v)
90 |
91 | # Finish Up
92 | ap.disengage()
93 | vessel.control.throttle = 0.0
94 | node.remove()
95 |
96 | def execute_all_nodes(conn):
97 |
98 | '''
99 | as the name implies - this function executes ALL maneuver nodes currently
100 | planned for the vessel in series.
101 | '''
102 | space_center = conn.space_center
103 | vessel = space_center.active_vessel
104 | while vessel.control.nodes:
105 | execute_next_node(conn)
106 |
107 | def thrust_controller(vessel, deltaV):
108 | '''
109 | This function is somewhat arbitrary in it's working - there's not a 'rule'
110 | that I've found on how to feather out throttle towards the end of a burn
111 | but given that the chances of overshooting go up with TWR (since we fly
112 | in a world with discrete physics frames!) it makes sense to relate the
113 | throttle to the TWR for this purpose.
114 | '''
115 | TWR= vessel.max_thrust/vessel.mass
116 | if deltaV < TWR / 3:
117 | return .05
118 | elif deltaV < TWR / 2:
119 | return .1
120 | elif deltaV < TWR:
121 | return .25
122 | else:
123 | return 1.0
124 |
125 | def execute_btn(conn):
126 | '''
127 | Demo of how to use the UI Service to turn this node execution function into
128 | a handy little utility for doing something useful. Just puts a button on
129 | the screen. When you click it - it executes the next maneuver node.
130 | '''
131 | space_center = conn.space_center
132 | vessel = space_center.active_vessel
133 | canvas = conn.ui.stock_canvas # draw on the main screen
134 | panel = canvas.add_panel() #container for our button
135 | rect = panel.rect_transform #rect to define panel
136 | rect.size = (100, 30) #panel size
137 | rect.position = (110-(canvas.rect_transform.size[0]/2), 0) #left middle
138 | button = panel.add_button("Execute Node") #add the button
139 | button.rect_transform.position = (0, 20) #locate the button
140 | button_clicked = conn.add_stream(getattr, button, 'clicked') #watch button
141 | while True: #if button clicked, execute the next node
142 | if button_clicked():
143 | execute_next_node(conn)
144 | button.clicked = False
145 |
146 | # ----------------------------------------------------------------------------
147 | # Activate main loop, if we are executing THIS file explicitly.
148 | # ----------------------------------------------------------------------------
149 | if __name__ == "__main__" :
150 | main()
151 |
--------------------------------------------------------------------------------
/examples/ksp-pynet/kpconsole.kv:
--------------------------------------------------------------------------------
1 | #:kivy 1.0
2 | #
3 | # kpconsole.kv: Layout of the console for ksp-pynet.
4 | #
5 |
6 | :
7 | size: (160, 50)
8 | text_size: self.size
9 | halign: "center"
10 | valign: "middle"
11 | size_hint: (None, None)
12 |
13 | :
14 | size: (300, 30)
15 | text_size: self.size
16 | halign: "right"
17 | valign: "middle"
18 | size_hint: (None, None)
19 | font_name: 'DejaVuSans'
20 |
21 | :
22 | size: (200, 30)
23 | multiline: False
24 | size_hint: (None, None)
25 |
26 | :
27 | size: (200, 30)
28 | multiline: False
29 | size_hint: (None, None)
30 |
31 | #
32 | # MenuActive: Displayed when the application is connected to KSP via KRPC.
33 | #
34 | :
35 | ScrollView:
36 | GridLayout:
37 | cols: 1
38 | size_hint_y: None
39 | height: self.minimum_height
40 | spacing: 10
41 |
42 | MenuButtonTemplate:
43 | text: 'Diagnostics'
44 | on_press: app.screen.current = 'diags'
45 |
46 | MenuButtonTemplate:
47 | text: 'Flight Planner'
48 | on_press: app.screen.current = 'flight_plan'
49 |
50 | MenuButtonTemplate:
51 | text: 'Browser'
52 | on_press: app.screen.current = 'browser'
53 |
54 | MenuButtonTemplate:
55 | text: 'Viewer'
56 | on_press:
57 | app.screen.current = 'viewer'
58 | app.console.display.viewer.register(app, True)
59 |
60 | MenuButtonTemplate:
61 | text: 'Maneuver'
62 | on_press: app.screen.current = 'maneuver'
63 |
64 | #
65 | # Menu: Displayed when the application is not connected to KSP.
66 | #
67 |