From 1ff2fed62f27bf19d2150d8a5881a0ba0f6d27dd Mon Sep 17 00:00:00 2001 From: Felix Agner <felix.agner@control.lth.se> Date: Tue, 1 Nov 2022 11:21:32 +0100 Subject: [PATCH] initial commit. Added code along with a build so that it can hopefully be easily installed --- LICENSE | 21 +++ __pycache__/Omnibot.cpython-310.pyc | Bin 0 -> 3785 bytes dist/omnibot_client-0.0.1-py3-none-any.whl | Bin 0 -> 8245 bytes dist/omnibot_client-0.0.1.tar.gz | Bin 0 -> 6745 bytes pyproject.toml | 22 +++ src/omnibot_client/ControllerTest.py | 120 ++++++++++++++++ src/omnibot_client/Dummybot.py | 40 ++++++ src/omnibot_client/Omnibot.py | 159 +++++++++++++++++++++ src/omnibot_client/__init__.py | 0 test/gitkeep | 0 10 files changed, 362 insertions(+) create mode 100644 LICENSE create mode 100644 __pycache__/Omnibot.cpython-310.pyc create mode 100644 dist/omnibot_client-0.0.1-py3-none-any.whl create mode 100644 dist/omnibot_client-0.0.1.tar.gz create mode 100644 pyproject.toml create mode 100644 src/omnibot_client/ControllerTest.py create mode 100644 src/omnibot_client/Dummybot.py create mode 100644 src/omnibot_client/Omnibot.py create mode 100644 src/omnibot_client/__init__.py create mode 100644 test/gitkeep diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b0471c3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Department of Automatic Control, Lund University + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/__pycache__/Omnibot.cpython-310.pyc b/__pycache__/Omnibot.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07df5f4bb8d582d36a2ff6892b6165d2280c37f7 GIT binary patch literal 3785 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 Vw>WGdPPYTqOvRwc;b0J90s!Uvn8N@7 literal 0 HcmV?d00001 diff --git a/dist/omnibot_client-0.0.1-py3-none-any.whl b/dist/omnibot_client-0.0.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..af5823726089324bb369e8e7479102449be92426 GIT binary patch literal 8245 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 literal 0 HcmV?d00001 diff --git a/dist/omnibot_client-0.0.1.tar.gz b/dist/omnibot_client-0.0.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..41e7273a4e6402e3f1ea24c2309233b41c8b84db GIT binary patch literal 6745 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 literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ccba303 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "omnibot_client" +version = "0.0.1" +authors = [ + { name="Felix Agner", email="felix.agner@control.lth.se" }, +] +description = "A python package for connecting to omnibots via TCP." +readme = "README.md" +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + +[project.urls] +"Homepage" = "https://gitlab.control.lth.se/processes/omnibot/omnibotclient.py" +"Bug Tracker" = "https://gitlab.control.lth.se/processes/omnibot/omnibotclient.py/issues" diff --git a/src/omnibot_client/ControllerTest.py b/src/omnibot_client/ControllerTest.py new file mode 100644 index 0000000..3e81f50 --- /dev/null +++ b/src/omnibot_client/ControllerTest.py @@ -0,0 +1,120 @@ +# general +import numpy as np +import sys +from time import time, sleep + +# threads +import threading + +# Controller +from inputs import get_gamepad + +from Omnibot import OmniBot + +class ControllerState: + """ + Class that logs the controller state + """ + def __init__(self): + """ Initialize the controller """ + self.x = 128 + self.y = 128 + self.r = 128 + self.on = True + + def set_x(self,x): + self.x = x + def set_y(self,y): + self.y = y + def set_r(self,r): + self.r = r + def state_data(self): + return 'x'+str(self.x)+'y'+str(self.y)+'r'+str(self.r) + def set_state(self,data): + s = str(data) + ind = [s.find(i) for i in ['x','y','r']] + self.set_x(int(s[ind[0]+1:ind[1]])) + self.set_y(int(s[ind[1]+1:ind[2]])) + self.set_r(int(s[ind[2]+1:-1])) + +def log_controller(state): + """ + Function that continuously listens to any event from the controller, and logs the change in + the controller state "state" + """ + while 1: + events = get_gamepad() + for event in events: + if event.code == "ABS_X": + state.set_x(event.state) + elif event.code == "ABS_Y": + state.set_y(event.state) + elif event.code == "ABS_Z": + state.set_r(event.state) + elif event.code == "BTN_PINKIE" and event.state == 1: + # The "BTN_PINKIE" (right index trigger) + # is currently used as an off-button. + state.on = False + + + +def run_omnibot_with_controller(HOST,verbose=False): + """ + Connect to an omnibot at ip HOST and proceed to steer it with a controller and print current position of omnibot. + """ + + # Robot parameters + R = 0.16 # Distance between center of robot and wheels + a1 = 2*np.pi/3 # Angle between x axis and first wheel + a2 = 4*np.pi/3 # Angle between x axis and second wheel + r = 0.028*0.45/18 # Wheel radius. Has been fudge-factored because the actual velocity of the wheels did not align with the set-points. + + + vel_factor = 6 # Fudge factor that changes the relative velocity of the bot + + def phidot(xdot,ang): + """Returns reference velocities for the wheels, given current system state and reference velocities""" + M = -1/r*np.array([[-np.sin(ang), np.cos(ang), R ],[-np.sin(ang+a1), np.cos(ang+a1), R],[-np.sin(ang+a2), np.cos(ang+a2), R]]) + return M.dot(xdot) + + # Start loggin controller + state = ControllerState() + t = threading.Thread(target=log_controller, args=(state,), daemon=True) + t.start() + + ts = 0.1 # sampling time + + with OmniBot(HOST,verbose=verbose) as bot: + no_error = True + while state.on and no_error: + t0 = time() + + # Get gamepad updates. + # Use some scaling to turn the controller state values into + # reasonable velocities. + w = (128-state.r)*np.pi/1000 + vy = (128-state.x)/1500 + vx = (128-state.y)/1500 + dphi = vel_factor*phidot([vx,vy,w],0.0) + + for i,v in enumerate(dphi): + bot.set_speed(i,round(v)) + + print('x:'+bot.get_x()) + print('y:'+bot.get_y()) + + sleep(max(0,t0+ts-time())) + + +if __name__ == '__main__': + + # Server settings + HOST = "localhost" + + if len(sys.argv) > 1: + # If an input is given to the script, it will be interpreted as the intended + # IP-address. Baseline is localhost. + HOST = sys.argv[1] + + print(f"HOST: \t {HOST}") + run_omnibot_with_controller(HOST) \ No newline at end of file diff --git a/src/omnibot_client/Dummybot.py b/src/omnibot_client/Dummybot.py new file mode 100644 index 0000000..d710439 --- /dev/null +++ b/src/omnibot_client/Dummybot.py @@ -0,0 +1,40 @@ +# general +import sys +from time import time, sleep + +from Omnibot import OmniBot + + +def run_dummybot(HOST,verbose=True): + """ + Runs a dummybot at host HOST that rotates a bit while printing x and y coordinates. + """ + + ts = 0.1 # sampling time + tstart = time() + + with OmniBot(HOST,verbose=verbose) as bot: + print("Connected") + + while time() < tstart + 5: + t0 = time() + + bot.set_speed([]) + + + print('x:'+bot.get_x()) + print('y:'+bot.get_y()) + + sleep(max(0,t0+ts-time())) + +if __name__ == '__main__': + # Server settings + HOST = "localhost" + + if len(sys.argv) > 1: + # If an input is given to the script, it will be interpreted as the intended + # IP-address. Baseline is localhost. + HOST = sys.argv[1] + + print(f"HOST: \t {HOST}") + run_dummybot(HOST) \ No newline at end of file diff --git a/src/omnibot_client/Omnibot.py b/src/omnibot_client/Omnibot.py new file mode 100644 index 0000000..4482668 --- /dev/null +++ b/src/omnibot_client/Omnibot.py @@ -0,0 +1,159 @@ +import socket + +class OmniBot(object): + """ + A class that implements a servo-client which is capable of sending servo speed-settings + to an omnibot. + + HOST: Host name of server (string) + PORT: Port to connect to (int) (default 9998) + verbose: choose level of chit-chat (boolean) + """ + def __init__(self,HOST,PORT=9998,verbose=False): + self.HOST = HOST + self.PORT = PORT + self.verbose=verbose + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + def __enter__(self): + """ + Boot up the socket and connect to the omnibot server + """ + if self.verbose: + print("Connecting to HOST: " + str(self.HOST) + ", PORT: " + str(self.PORT)) + + self.sock.connect((self.HOST, self.PORT)) # Connect to the robot + if self.verbose: + print("Connection successful.") + + + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + """ + Close down the socket after closing the client + """ + if self.verbose: + print("Closing down socket") + self.sock.close() + + def send_and_receive(self,message): + """Send a message to the omnibot and return the answer that the omnibot gave.""" + # Send the package + self.sock.sendall(bytes(message,'utf-8')) + # Receive confirmation + ret=self.sock.recv(1024).decode('utf-8') + + return ret + + def set_speed(self,i,v): + """ + Set the speed of servo i to v + + + Communicates with messages on the form "wivvvv" + where w stands for "write"self. + i stands for index of desired servo + vvvv stands for requested target speed for the servo + """ + + package = 'w'+str(i)+str(v) + if self.verbose: + print("Sending " + package) + + + ret = self.send_and_receive(package) + + if ret[0] == "e": + raise Exception(ret) + elif self.verbose: + print(ret) + + def set_speeds(self,v): + """ + Set the speed of the servos to the values in vector v + """ + for (i,vi) in enumerate(v): + self.set_speed(i,vi) + + def get_x(self): + """Get x coordinate + + Sends a message "rx" + where "r" stands for "read" and "x" stands for "x" + """ + if self.verbose: + print("Requesting x") + + ret = self.send_and_receive("rx") + if ret[0] == "e": + raise Exception(ret) + + if self.verbose: + print("x: "+ret) + + return float(ret) + + def get_y(self): + """Get y coordinate + + Sends a message "ry" + where "r" stands for "read" and "y" stands for "y" + """ + if self.verbose: + print("Requesting y") + + ret = self.send_and_receive("ry") + if ret[0] == "e": + raise Exception(ret) + + if self.verbose: + print("y: "+ret) + + return float(ret) + + def get_z(self): + """Get z coordinate + + Sends a message "rz" + where "r" stands for "read" and "z" stands for "z" + """ + if self.verbose: + print("Requesting z") + + ret = self.send_and_receive("rz") + if ret[0] == "e": + raise Exception(ret) + + if self.verbose: + print("z: "+ret) + + return float(ret) + + def get_theta(self): + """Get x coordinate + + Sends a message "rtheta" + where "r" stands for "read" and "theta" stands for "theta" + """ + if self.verbose: + print("Requesting theta") + + ret = self.send_and_receive("rtheta") + if ret[0] == "e": + raise Exception(ret) + + if self.verbose: + print("theta: "+ret) + + return float(ret) + + + def get_state(self): + """"Get state vector of [x y theta]^T.""" + + x = self.get_x() + y = self.get_y() + theta = self.get_theta() + + return [x,y,theta] diff --git a/src/omnibot_client/__init__.py b/src/omnibot_client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/gitkeep b/test/gitkeep new file mode 100644 index 0000000..e69de29 -- GitLab