diff --git a/code/requirements.txt b/code/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..2c11c33284bbded00c5c153b9e6f62ece604eaa1 --- /dev/null +++ b/code/requirements.txt @@ -0,0 +1,23 @@ +Package Version +---------------------- --------- +cflib 0.1.17.1 +dynamixel-sdk 3.7.31 +libusb-package 1.0.24.2 +numpy 1.22.1 +opencv-python-headless 4.5.5.62 +packaging 21.3 +pandas 1.4.0 +pip 20.3.4 +pkg-resources 0.0.0 +pyparsing 3.0.7 +PyQt5-sip 12.9.0 +pyserial 3.5 +python-dateutil 2.8.2 +python-dynamixel -UNKNOWN- +pytz 2021.3 +pyusb 1.2.1 +QtPy 2.0.0 +scipy 1.7.3 +setuptools 44.1.1 +six 1.16.0 + diff --git a/code/run-trajectory b/code/run-trajectory new file mode 100644 index 0000000000000000000000000000000000000000..670073e165c47a4f1cdf9e9e560dcf6fc5b7fe0d --- /dev/null +++ b/code/run-trajectory @@ -0,0 +1,23 @@ +#!/bin/bash + +USERNAME=pi +HOST=130.235.83.216 +echo "ssh-password for $USERNAME@$HOST" +read PASSWORD + +echo 'name of trajectory-file?' +read FILENAME + +FILELOCATION="~/PlanningLab" +echo "Copying $FILENAME to $USERNAME@$HOST:$FILELOCATION" +sshpass -p $PASSWORD scp $FILENAME "$USERNAME@$HOST:$FILELOCATION" + +echo "Executing trajectory" +COMMAND="cd $FILELOCATION ; source planning_env/bin/activate ; python track_path.py $FILENAME ; rm $FILENAME ; exit" +sshpass -p $PASSWORD ssh -o StrictHostKeyChecking=no $USERNAME@$HOST "$COMMAND" + +#scp "-i $PASSWORD" $FILENAME "$USERNAME@$HOST:$FILELOCATION" +#"$PASSWORD" +#SCRIPT="pwd; cd PlanningLab ; source planning-env/bin/activate ; python track_path.py $FILENAME" +#scp -l $FILENAME $USERNAME $HOST +#ssh -o StrictHostKeyChecking=no -l username hostname "pwd; ls" diff --git a/code/track_path.py b/code/track_path.py new file mode 100644 index 0000000000000000000000000000000000000000..3524404be0effeed27fc9ea90773e01241ec283d --- /dev/null +++ b/code/track_path.py @@ -0,0 +1,306 @@ +import numpy as np +import pandas as pd +import sys + +# Dynamixel packages +from dynamixel.model.xm430_w210_t_r import XM430_W210_T_R +import dynamixel.channel +import time + +#Crazy packages +import logging +from threading import Timer + +import cflib.crtp # noqa +from cflib.crazyflie import Crazyflie +from cflib.crazyflie.log import LogConfig +from cflib.utils import uri_helper + +logging.basicConfig(level=logging.ERROR) + + +######################################################################## +# SUPPORTING CLASSES AND FUNCTIONS +######################################################################## + +def load_servos(): +""" +Servo load function, based on the dynamixel implementation of Anders Blomdell. +""" + +# The speed and channel numbers can be found and checked via the dynamixel software "dynamixel wizard". +# The device can be found by e.g. lsusb, and is currently adapted for the three-color robots. + + channel = dynamixel.channel.Channel(speed=57600,device='/dev/ttyACM0') + servos = [ XM430_W210_T_R(channel, 1), + XM430_W210_T_R(channel, 2), + XM430_W210_T_R(channel, 3) ] + for s in servos: + s.torque_enable.write(0) + print(s.model_number.read(), s.id.read()) + s.operating_mode.write(1) + s.bus_watchdog.write(0) # Clear old watchdog error + s.bus_watchdog.write(100) # 2 second timeout + s.torque_enable.write(1) + + return servos + +class SimplePI: + """ + Simple PI implementation with basic anti-windup. The _saturation - variable sets the saturation of the anti-windup. + """ + + _saturation = 0.1 + + def __init__(self,kp,ki,dt): + """ Initialize the controller """ + + self.e = np.zeros(3) + self.dt = dt # Sample rate + self.kp = kp # P-part gain + self.ki = ki # I-part gain + + def update_control(self,e): + self.e += e*self.dt # Update cumulative error + + # Saturate internal error for anti-windup + self.e = np.sign(self.e)*np.array([min(ei,self._saturation) for ei in abs(self.e)]) + + # Return control signal + return self.kp*e + self.ki*self.e + + +class CrazyLogger: + + """ + Simple logging class that logs the Crazyflie Stabilizer from a supplied + link uri and disconnects after 150s. + """ + # Define which states to log: + namelink = {'stateEstimate.x': 0, 'stateEstimate.y': 1,'stateEstimate.z': 2, 'stabilizer.yaw':3} + #_Offset for crazyflie position on robot to center + position_offset = 0.11 + + def __init__(self, link_uri): + """ Initialize and run the example with the specified link_uri """ + + self._cf = Crazyflie(rw_cache='./cache') + self._state = np.array([0.0,0.0,0.0,0.0]) + + # Connect some callbacks from the Crazyflie API + self._cf.connected.add_callback(self._connected) + self._cf.disconnected.add_callback(self._disconnected) + self._cf.connection_failed.add_callback(self._connection_failed) + self._cf.connection_lost.add_callback(self._connection_lost) + + print('Connecting to %s' % link_uri) + + # Try to connect to the Crazyflie + self._cf.open_link(link_uri) + + # Variable used to keep main loop occupied until disconnect + self.is_connected = True + + def _connected(self, link_uri): + """ This callback is called form the Crazyflie API when a Crazyflie + has been connected and the TOCs have been downloaded.""" + print('Connected to %s' % link_uri) + + # The definition of the logconfig can be made before connecting + self._lg_stab = LogConfig(name='Stabilizer', period_in_ms=100) + self._lg_stab.add_variable('stateEstimate.x', 'float') + self._lg_stab.add_variable('stateEstimate.y', 'float') + self._lg_stab.add_variable('stateEstimate.z', 'float') + self._lg_stab.add_variable('stabilizer.yaw', 'float') + + # The fetch-as argument can be set to FP16 to save space in the log packet + #self._lg_stab.add_variable('pm.vbat', 'FP16') + + # Adding the configuration cannot be done until a Crazyflie is + # connected, since we need to check that the variables we + # would like to log are in the TOC. + try: + self._cf.log.add_config(self._lg_stab) + # This callback will receive the data + self._lg_stab.data_received_cb.add_callback(self._stab_log_data) + # This callback will be called on errors + self._lg_stab.error_cb.add_callback(self._stab_log_error) + # Start the logging + self._lg_stab.start() + except KeyError as e: + print('Could not start log configuration,' + '{} not found in TOC'.format(str(e))) + except AttributeError: + print('Could not add Stabilizer log config, bad configuration.') + + # Start a timer to disconnect in 150s + t = Timer(150, self._cf.close_link) + t.start() + + def _stab_log_error(self, logconf, msg): + """Callback from the log API when an error occurs""" + print('Error when logging %s: %s' % (logconf.name, msg)) + + def _stab_log_data(self, timestamp, data, logconf): + """Callback from a the log API when data arrives""" + #print(f'[{timestamp}][{logconf.name}]: ', end='') + for name, value in data.items(): + self._state[self._namelink[name]] = value + #print(f'{name}: {value:3.3f} ', end='\n') + #print() + + def _connection_failed(self, link_uri, msg): + """Callback when connection initial connection fails (i.e no Crazyflie + at the specified address)""" + print('Connection to %s failed: %s' % (link_uri, msg)) + self.is_connected = False + + def _connection_lost(self, link_uri, msg): + """Callback when disconnected after a connection has been made (i.e + Crazyflie moves out of range)""" + print('Connection to %s lost: %s' % (link_uri, msg)) + + def _disconnected(self, link_uri): + """Callback when the Crazyflie is disconnected (called in all cases)""" + print('Disconnected from %s' % link_uri) + self.is_connected = False + + def x(self): + """ Return x-position of robot center""" + # Note that the offset of the crazyflie mount position is included + return self._state[0] - np.sin(self._state[3])*self._position_offset + + def y(self): + """ Return y-position of robot center""" + # Note that the offset of the crazyflie mount position is included + return self._state[1] + np.cos(self._state[3])*self._position_offset + + def z(self): + """ Return z-position of robot center""" + return self._state[2] + + def theta(self): + """ Return direction of robot center, measured in radians between -pi and pi.""" + # Note that the offset of the crazyflie mount position is included + return np.mod(self._state[3]*np.pi/180 + 11*np.pi/6,2*np.pi)-np.pi + + def states(self): + return self._state + + def close(self): + self._cf.close_link() + +######################################################################## +# MAIN SCRIPT +######################################################################## +if __name__ == '__main__': + + # Read input trajectory filename + if len(sys.argv) > 1: + print("Input file name:",sys.argv[1]) + filename = sys.argv[1] + else: + print("No file name inupt. Using default file name: trajectory.csv") + filename = "trajectory.csv" + + # Load trajectory + print("Attempting to load trajectory file",filename) + trajectory = pd.read_csv(filename) + print("\nTrajectory loaded.") + x = trajectory.x.to_numpy() + y = trajectory.y.to_numpy() + # Update theta to be in correct range (-pi,pi) + theta = ((trajectory.theta + np.pi).mod(2*np.pi)-np.pi).to_numpy() # Put theta in range -pi -> pi instead of 0 -> 2pi + + #Find derivatives + trajectory[["xdot","ydot","thetadot"]] = trajectory[["x","y","theta"]].diff(axis = 0).fillna(0)/0.1 + + #FUDGE FACTOR TO DECREASE SPEED FOR TESTING!!!! + # Should probably be removed in future iterations. Was included because + # the robot was exceeding servo speed settings. + fudgefactor = 3.0 + velref = trajectory[['xdot','ydot','thetadot']].to_numpy()/fudgefactor + + # Create time vector + dt = trajectory.t.diff().mean()*fudgefactor + t = trajectory.t.to_numpy()*fudgefactor + tend = t[-1] + n = t.size + print("Sample time:",dt) + print("Experiment time:",tend) + print("Time steps:",n) + + # Read controller parameters + if len(sys.argv) > 3: + print("\nController parameters:") + kp = float(sys.argv[2]) + ki = float(sys.argv[3]) + else: + print("\nNo controller paramater input. Using default values:") + kp = 0.1 + ki = 0.01 + + print("kp =",kp) + print("ki =",ki) + + + # 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. + + print("Bot dimensions:") + print("R =",R) + print("R =",r) + + 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) + + + # Initiate experiment + print('Initiating experiment') + servos = load_servos() # Use predefined funtion to initiate servo connection + cflib.crtp.init_drivers() # Initiate drivers for crazyflie + uri = uri_helper.uri_from_env(default='usb://0') # Connection-uri for crazyflie via USB + cl = CrazyLogger(uri) # Create a crazyflie-based logger + pi = SimplePI(kp,ki,dt) # Create a PI-controller + + + time.sleep(1) # Wait for connection to work + t0 = time.time() + + # Main control loop + for i in range(n-1): + + # Read current position error: + e = np.array([trajectory.x.iloc[i]-cl.x(),trajectory.y.iloc[i]-cl.y(),trajectory.theta.iloc[i]-cl.theta()]) + + # Create reference speed through feed-forward (velref) and feedback (pi): + xdot = velref[i,:] + pi.update_control(e) + + # Transform from x,y,theta-speeds to wheel rotational speeds. + ph = phidot(xdot,cl.theta()) + + # Set all servospeeds (enact control signal) + for (j,s) in enumerate(servos): + s.goal_velocity.write(round(ph[j])) + + # Printing position and tracking error + print("x:",cl.x(),"\t y:",cl.y(),"\t theta:",cl.theta()) + print("Position error:",e,"\n") + + # Wait until next loop-iteration + time.sleep(max(t[i+1]+t0-time.time(),0)) + + + + # Shutdown + print("Experiment done, shutting down servos and logger") + for s in servos: + s.goal_velocity.write(0) + print("Finishing position: x =",cl.x(),"\t y= ",cl.y(),"\t theta =",cl.theta()) + cl.close() + diff --git a/doc/MAX4_project_report.pdf b/doc/MAX4_project_report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6785eef1470515640d96c999ac103034c5085206 Binary files /dev/null and b/doc/MAX4_project_report.pdf differ diff --git a/doc/figs/beacon.jpg b/doc/figs/beacon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..da0d5e370b92f569934b7aa2741c6441497a03d2 Binary files /dev/null and b/doc/figs/beacon.jpg differ diff --git a/doc/figs/bot.jpg b/doc/figs/bot.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8b054f3f59ceaacc1f753c079ef76685a56ab22a Binary files /dev/null and b/doc/figs/bot.jpg differ diff --git a/doc/figs/charger.jpg b/doc/figs/charger.jpg new file mode 100644 index 0000000000000000000000000000000000000000..53c912e8a85b6b5777aef79b3889472e81cb0c36 Binary files /dev/null and b/doc/figs/charger.jpg differ diff --git a/doc/figs/dynamics.png b/doc/figs/dynamics.png new file mode 100644 index 0000000000000000000000000000000000000000..1e90a3133aa0a56858e825091001efa46e0775fa Binary files /dev/null and b/doc/figs/dynamics.png differ diff --git a/doc/ombibot_description.aux b/doc/ombibot_description.aux new file mode 100644 index 0000000000000000000000000000000000000000..ed7749768c530f0cf12faaaa6c8cb44078ce3226 --- /dev/null +++ b/doc/ombibot_description.aux @@ -0,0 +1,24 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {1}Background}{1}{}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {1.1}MAX 4 project}{1}{}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {1.2}FRTN75 lab}{1}{}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {2}General Description}{1}{}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {3}Robot Dynamics}{1}{}\protected@file@percent } +\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Robot dynamics. Image stolen from \textit {MAX4\_project\_report.pdf}\relax }}{1}{}\protected@file@percent } +\providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}} +\newlabel{fig:dynamics}{{1}{1}} +\@writefile{toc}{\contentsline {section}{\numberline {4}Crazyflie Positioning}{2}{}\protected@file@percent } +\@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces HTC vive positioning system.\relax }}{2}{}\protected@file@percent } +\newlabel{fig:beacon}{{2}{2}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}Setting up the crazyflie}{2}{}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Setting up lighthouse system}{3}{}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {4.3}Connecting to raspberry pi}{3}{}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {4.4}Reconfigure coordinate system}{3}{}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {5}Dynamixel Servos}{3}{}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {6}Raspberry Pi}{3}{}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {7}Frame and Power Supply}{4}{}\protected@file@percent } +\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Battery charging system.\relax }}{4}{}\protected@file@percent } +\newlabel{fig:charger}{{3}{4}} +\@writefile{toc}{\contentsline {section}{\numberline {8}Communication}{5}{}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {9}Code and Scripts}{5}{}\protected@file@percent } +\gdef \@abspage@last{6} diff --git a/doc/ombibot_description.log b/doc/ombibot_description.log new file mode 100644 index 0000000000000000000000000000000000000000..21f13eda0f535e3dbda655d1d6c005eb986c6bf0 --- /dev/null +++ b/doc/ombibot_description.log @@ -0,0 +1,372 @@ +This is pdfTeX, Version 3.141592653-2.6-1.40.23 (TeX Live 2021) (preloaded format=pdflatex 2022.2.17) 8 MAR 2022 14:39 +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**ombibot_description.tex +(./ombibot_description.tex +LaTeX2e <2021-11-15> patch level 1 +L3 programming layer <2022-02-05> +(/opt/texlive/2021/texmf-dist/tex/latex/base/article.cls +Document Class: article 2021/10/04 v1.4n Standard LaTeX document class +(/opt/texlive/2021/texmf-dist/tex/latex/base/size10.clo +File: size10.clo 2021/10/04 v1.4n Standard LaTeX file (size option) +) +\c@part=\count185 +\c@section=\count186 +\c@subsection=\count187 +\c@subsubsection=\count188 +\c@paragraph=\count189 +\c@subparagraph=\count190 +\c@figure=\count191 +\c@table=\count192 +\abovecaptionskip=\skip47 +\belowcaptionskip=\skip48 +\bibindent=\dimen138 +) +(/opt/texlive/2021/texmf-dist/tex/latex/base/inputenc.sty +Package: inputenc 2021/02/14 v1.3d Input encoding file +\inpenc@prehook=\toks16 +\inpenc@posthook=\toks17 +) +(/opt/texlive/2021/texmf-dist/tex/latex/base/fontenc.sty +Package: fontenc 2021/04/29 v2.0v Standard LaTeX package +) +(/opt/texlive/2021/texmf-dist/tex/latex/url/url.sty +\Urlmuskip=\muskip16 +Package: url 2013/09/16 ver 3.4 Verb mode for urls, etc. +) +(/opt/texlive/2021/texmf-dist/tex/latex/amsmath/amsmath.sty +Package: amsmath 2021/10/15 v2.17l AMS math features +\@mathmargin=\skip49 + +For additional information on amsmath, use the `?' option. +(/opt/texlive/2021/texmf-dist/tex/latex/amsmath/amstext.sty +Package: amstext 2021/08/26 v2.01 AMS text + +(/opt/texlive/2021/texmf-dist/tex/latex/amsmath/amsgen.sty +File: amsgen.sty 1999/11/30 v2.0 generic functions +\@emptytoks=\toks18 +\ex@=\dimen139 +)) +(/opt/texlive/2021/texmf-dist/tex/latex/amsmath/amsbsy.sty +Package: amsbsy 1999/11/29 v1.2d Bold Symbols +\pmbraise@=\dimen140 +) +(/opt/texlive/2021/texmf-dist/tex/latex/amsmath/amsopn.sty +Package: amsopn 2021/08/26 v2.02 operator names +) +\inf@bad=\count193 +LaTeX Info: Redefining \frac on input line 234. +\uproot@=\count194 +\leftroot@=\count195 +LaTeX Info: Redefining \overline on input line 399. +\classnum@=\count196 +\DOTSCASE@=\count197 +LaTeX Info: Redefining \ldots on input line 496. +LaTeX Info: Redefining \dots on input line 499. +LaTeX Info: Redefining \cdots on input line 620. +\Mathstrutbox@=\box50 +\strutbox@=\box51 +\big@size=\dimen141 +LaTeX Font Info: Redeclaring font encoding OML on input line 743. +LaTeX Font Info: Redeclaring font encoding OMS on input line 744. +\macc@depth=\count198 +\c@MaxMatrixCols=\count199 +\dotsspace@=\muskip17 +\c@parentequation=\count266 +\dspbrk@lvl=\count267 +\tag@help=\toks19 +\row@=\count268 +\column@=\count269 +\maxfields@=\count270 +\andhelp@=\toks20 +\eqnshift@=\dimen142 +\alignsep@=\dimen143 +\tagshift@=\dimen144 +\tagwidth@=\dimen145 +\totwidth@=\dimen146 +\lineht@=\dimen147 +\@envbody=\toks21 +\multlinegap=\skip50 +\multlinetaggap=\skip51 +\mathdisplay@stack=\toks22 +LaTeX Info: Redefining \[ on input line 2938. +LaTeX Info: Redefining \] on input line 2939. +) +(/opt/texlive/2021/texmf-dist/tex/latex/amsfonts/amsfonts.sty +Package: amsfonts 2013/01/14 v3.01 Basic AMSFonts support +\symAMSa=\mathgroup4 +\symAMSb=\mathgroup5 +LaTeX Font Info: Redeclaring math symbol \hbar on input line 98. +LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold' +(Font) U/euf/m/n --> U/euf/b/n on input line 106. +) +(/opt/texlive/2021/texmf-dist/tex/latex/amsfonts/amssymb.sty +Package: amssymb 2013/01/14 v3.01 AMS font symbols +) +(/opt/texlive/2021/texmf-dist/tex/latex/graphics/graphicx.sty +Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR) + +(/opt/texlive/2021/texmf-dist/tex/latex/graphics/keyval.sty +Package: keyval 2014/10/28 v1.15 key=value parser (DPC) +\KV@toks@=\toks23 +) +(/opt/texlive/2021/texmf-dist/tex/latex/graphics/graphics.sty +Package: graphics 2021/03/04 v1.4d Standard LaTeX Graphics (DPC,SPQR) + +(/opt/texlive/2021/texmf-dist/tex/latex/graphics/trig.sty +Package: trig 2021/08/11 v1.11 sin cos tan (DPC) +) +(/opt/texlive/2021/texmf-dist/tex/latex/graphics-cfg/graphics.cfg +File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration +) +Package graphics Info: Driver file: pdftex.def on input line 107. + +(/opt/texlive/2021/texmf-dist/tex/latex/graphics-def/pdftex.def +File: pdftex.def 2020/10/05 v1.2a Graphics/color driver for pdftex +)) +\Gin@req@height=\dimen148 +\Gin@req@width=\dimen149 +) +(/opt/texlive/2021/texmf-dist/tex/latex/caption/caption.sty +Package: caption 2020/10/26 v3.5g Customizing captions (AR) + +(/opt/texlive/2021/texmf-dist/tex/latex/caption/caption3.sty +Package: caption3 2020/10/21 v2.2e caption3 kernel (AR) +\captionmargin=\dimen150 +\captionmargin@=\dimen151 +\captionwidth=\dimen152 +\caption@tempdima=\dimen153 +\caption@indent=\dimen154 +\caption@parindent=\dimen155 +\caption@hangindent=\dimen156 +Package caption Info: Standard document class detected. +) +\c@caption@flags=\count271 +\c@continuedfloat=\count272 +) +(/opt/texlive/2021/texmf-dist/tex/latex/caption/subcaption.sty +Package: subcaption 2020/10/07 v1.3j Sub-captions (AR) +\c@subfigure=\count273 +\c@subtable=\count274 +) +(/opt/texlive/2021/texmf-dist/tex/latex/xcolor/xcolor.sty +Package: xcolor 2021/10/31 v2.13 LaTeX color extensions (UK) + +(/opt/texlive/2021/texmf-dist/tex/latex/graphics-cfg/color.cfg +File: color.cfg 2016/01/02 v1.6 sample color configuration +) +Package xcolor Info: Driver file: pdftex.def on input line 227. +Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1352. +Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1356. +Package xcolor Info: Model `RGB' extended on input line 1368. +Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1370. +Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1371. +Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1372. +Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1373. +Package xcolor Info: Model `Gray' substituted by `gray' on input line 1374. +Package xcolor Info: Model `wave' substituted by `hsb' on input line 1375. +) +(/opt/texlive/2021/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def +File: l3backend-pdftex.def 2022-02-07 L3 backend support: PDF output (pdfTeX) +\l__color_backend_stack_int=\count275 +\l__pdf_internal_box=\box52 +) +(./ombibot_description.aux) +\openout1 = `ombibot_description.aux'. + +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 14. +LaTeX Font Info: ... okay on input line 14. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 14. +LaTeX Font Info: ... okay on input line 14. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 14. +LaTeX Font Info: ... okay on input line 14. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 14. +LaTeX Font Info: ... okay on input line 14. +LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 14. +LaTeX Font Info: ... okay on input line 14. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 14. +LaTeX Font Info: ... okay on input line 14. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 14. +LaTeX Font Info: ... okay on input line 14. + +(/opt/texlive/2021/texmf-dist/tex/context/base/mkii/supp-pdf.mkii +[Loading MPS to PDF converter (version 2006.09.02).] +\scratchcounter=\count276 +\scratchdimen=\dimen157 +\scratchbox=\box53 +\nofMPsegments=\count277 +\nofMParguments=\count278 +\everyMPshowfont=\toks24 +\MPscratchCnt=\count279 +\MPscratchDim=\dimen158 +\MPnumerator=\count280 +\makeMPintoPDFobject=\count281 +\everyMPtoPDFconversion=\toks25 +) (/opt/texlive/2021/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty +Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf +Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4 +85. + +(/opt/texlive/2021/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv +e +)) +Package caption Info: Begin \AtBeginDocument code. +Package caption Info: End \AtBeginDocument code. +<figs/bot.jpg, id=1, 4047.12pt x 3035.34pt> +File: figs/bot.jpg Graphic file (type jpg) +<use figs/bot.jpg> +Package pdftex.def Info: figs/bot.jpg used on input line 29. +(pdftex.def) Requested size: 345.0pt x 258.71838pt. + [1 + +{/var/lib/texlive/2021/fonts/map/pdftex/updmap/pdftex.map} <./figs/bot.jpg>] +<figs/dynamics.png, id=9, 819.06pt x 852.18375pt> +File: figs/dynamics.png Graphic file (type png) +<use figs/dynamics.png> +Package pdftex.def Info: figs/dynamics.png used on input line 51. +(pdftex.def) Requested size: 137.9979pt x 143.56898pt. +LaTeX Font Info: Trying to load font information for U+msa on input line 56. + + +(/opt/texlive/2021/texmf-dist/tex/latex/amsfonts/umsa.fd +File: umsa.fd 2013/01/14 v3.01 AMS symbols A +) +LaTeX Font Info: Trying to load font information for U+msb on input line 56. + + +(/opt/texlive/2021/texmf-dist/tex/latex/amsfonts/umsb.fd +File: umsb.fd 2013/01/14 v3.01 AMS symbols B +) [1 <./figs/dynamics.png>] +LaTeX Font Info: Trying to load font information for T1+cmtt on input line 7 +3. + (/opt/texlive/2021/texmf-dist/tex/latex/base/t1cmtt.fd +File: t1cmtt.fd 2019/12/16 v2.5j Standard LaTeX font definitions +) +Overfull \hbox (127.96808pt too wide) in paragraph at lines 73--74 +\T1/cmr/m/n/10 This is done via a light-house deck ($\T1/cmtt/m/n/10 https : / +/ www . bitcraze . io / products / lighthouse-[]positioning-[]deck/$\T1/cmr/m/n +/10 ) + [] + +<figs/beacon.jpg, id=22, 4047.12pt x 3035.34pt> +File: figs/beacon.jpg Graphic file (type jpg) +<use figs/beacon.jpg> +Package pdftex.def Info: figs/beacon.jpg used on input line 77. +(pdftex.def) Requested size: 241.49895pt x 181.0936pt. + +Underfull \hbox (badness 10000) in paragraph at lines 83--85 + + [] + + +Overfull \hbox (90.19258pt too wide) in paragraph at lines 86--89 +\T1/cmr/bx/n/10 Cfclient\T1/cmr/m/n/10 : $\T1/cmtt/m/n/10 https : / / www . bit +craze . io / documentation / repository / crazyflie-[]clients-[]python / + [] + + +Underfull \hbox (badness 10000) in paragraph at lines 86--89 + + [] + + +Overfull \hbox (51.29236pt too wide) in paragraph at lines 90--92 +\T1/cmr/m/n/10 sions: $\T1/cmtt/m/n/10 https : / / www . bitcraze . io / docume +ntation / repository / crazyflie-[]lib-[]python / + [] + + +Underfull \hbox (badness 10000) in paragraph at lines 90--92 + + [] + + +Overfull \hbox (18.16156pt too wide) in paragraph at lines 93--95 +\T1/cmtt/m/n/10 bitcraze . io / documentation / repository / crazyflie-[]client +s-[]python / master / + [] + +[2 <./figs/beacon.jpg>] +Overfull \hbox (101.41913pt too wide) in paragraph at lines 99--101 +\T1/cmr/m/n/10 here: $\T1/cmtt/m/n/10 https : / / www . bitcraze . io / documen +tation / tutorials / getting-[]started-[]with-[]lighthouse/$ + [] + + +Underfull \hbox (badness 10000) in paragraph at lines 107--110 + + [] + + +Overfull \hbox (8.13599pt too wide) in paragraph at lines 115--118 +\T1/cmr/m/n/10 be found here: $\T1/cmtt/m/n/10 https : / / gitlab . control . l +th . se / anders _ blomdell / dynamixel$ + [] + + +Underfull \hbox (badness 10000) in paragraph at lines 115--118 + + [] + + +Underfull \hbox (badness 10000) in paragraph at lines 123--127 + + [] + +[3] +Underfull \hbox (badness 10000) in paragraph at lines 128--130 + + [] + + +Underfull \hbox (badness 10000) in paragraph at lines 131--133 + + [] + + +Underfull \hbox (badness 10000) in paragraph at lines 139--141 + + [] + + +Underfull \hbox (badness 10000) in paragraph at lines 142--144 + + [] + +<figs/charger.jpg, id=33, 4047.12pt x 3035.34pt> +File: figs/charger.jpg Graphic file (type jpg) +<use figs/charger.jpg> +Package pdftex.def Info: figs/charger.jpg used on input line 149. +(pdftex.def) Requested size: 241.49895pt x 181.0936pt. +[4 <./figs/charger.jpg>] [5] (./ombibot_description.aux) ) +Here is how much of TeX's memory you used: + 4721 strings out of 479077 + 73781 string characters out of 5869644 + 363665 words of memory out of 5000000 + 22699 multiletter control sequences out of 15000+600000 + 478125 words of font info for 46 fonts, out of 8000000 for 9000 + 788 hyphenation exceptions out of 8191 + 55i,8n,63p,454b,320s stack positions out of 5000i,500n,10000p,200000b,80000s +{/opt/texlive/2021/te +xmf-dist/fonts/enc/dvips/cm-super/cm-super-t1.enc}</opt/texlive/2021/texmf-dist +/fonts/type1/public/amsfonts/cm/cmex10.pfb></opt/texlive/2021/texmf-dist/fonts/ +type1/public/amsfonts/cm/cmmi10.pfb></opt/texlive/2021/texmf-dist/fonts/type1/p +ublic/amsfonts/cm/cmr10.pfb></opt/texlive/2021/texmf-dist/fonts/type1/public/am +sfonts/cm/cmr7.pfb></opt/texlive/2021/texmf-dist/fonts/type1/public/amsfonts/cm +/cmsy10.pfb></opt/texlive/2021/texmf-dist/fonts/type1/public/cm-super/sfbx1000. +pfb></opt/texlive/2021/texmf-dist/fonts/type1/public/cm-super/sfbx1200.pfb></op +t/texlive/2021/texmf-dist/fonts/type1/public/cm-super/sfbx1440.pfb></opt/texliv +e/2021/texmf-dist/fonts/type1/public/cm-super/sfbx2488.pfb></opt/texlive/2021/t +exmf-dist/fonts/type1/public/cm-super/sfrm1000.pfb></opt/texlive/2021/texmf-dis +t/fonts/type1/public/cm-super/sfrm2488.pfb></opt/texlive/2021/texmf-dist/fonts/ +type1/public/cm-super/sfti1000.pfb></opt/texlive/2021/texmf-dist/fonts/type1/pu +blic/cm-super/sftt1000.pfb> +Output written on ombibot_description.pdf (6 pages, 7029374 bytes). +PDF statistics: + 95 PDF objects out of 1000 (max. 8388607) + 55 compressed objects within 1 object stream + 0 named destinations out of 1000 (max. 500000) + 21 words of extra memory for PDF output out of 10000 (max. 10000000) + diff --git a/doc/ombibot_description.pdf b/doc/ombibot_description.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c24307922403a7e06b6105a4d908ac68fc235a36 Binary files /dev/null and b/doc/ombibot_description.pdf differ diff --git a/doc/ombibot_description.synctex.gz b/doc/ombibot_description.synctex.gz new file mode 100644 index 0000000000000000000000000000000000000000..e7750687ad4ff55466a51139df660b130bc55afe Binary files /dev/null and b/doc/ombibot_description.synctex.gz differ diff --git a/doc/ombibot_description.tex b/doc/ombibot_description.tex new file mode 100644 index 0000000000000000000000000000000000000000..a1090bd1a43597749f62f2f70f986ac8c7fbab6d --- /dev/null +++ b/doc/ombibot_description.tex @@ -0,0 +1,162 @@ +\documentclass[10pt,a4paper]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{url} +\usepackage{amsmath} +\usepackage{amsfonts} +\usepackage{amssymb} +\usepackage{graphicx} +\usepackage{caption} +\usepackage{subcaption} +\usepackage{xcolor} +\setlength{\parindent}{0cm} + +\begin{document} + \begin{titlepage} + \begin{center} + \vspace*{1cm} + + \Huge{\textbf{Omnibot}} + + \vspace{0.5cm} + Process Documentation + + + \vfill + + + + \includegraphics[width=\textwidth]{figs/bot.jpg} + \vspace{0.8cm} + + \end{center} + \end{titlepage} + + \section{Background} + The omnibot is a robot mounted with omni wheels, allowing it to traverse in any given direction. There have been a few projects run on the omnibot. The base was 3d-printed by Anders Blomdell, who also attached the dynamixel servos and wheels. Mounts and power supply was managed by Alexander Pisarevskiy. + + \subsection{MAX 4 project} + A masters project has been done in using the omnibots, along with a mounted delta robot, to draw floor markings. An overview of this project can be found in the file \textit{MAX4\_project\_report.pdf}. + + \subsection{FRTN75 lab} + In the course FRTN75, a motion planning lab was held with the omnibot. Here we pretended that the robot could only move in accordance to car dynamics. The lab can be found by logging into any of the lab computers \textit{philon-xx.control.lth.se} where xx is e.g. 04, a number between 1 and 12, through a browser. Then going into the frtn75-planning lab on the jupyterhub server. + + \section{General Description} + + \section{Robot Dynamics} + In the MAX4 project, the connection between wheel rotational speed and robot translational and rotational speed. See the file \textit{MAX4\_project\_report.pdf} for more details. The results are summarized here, and illustrated in Figure \ref{fig:dynamics}. + + \begin{figure}[h] + \centering + \includegraphics[width=0.4\textwidth]{figs/dynamics} + \caption{Robot dynamics. Image stolen from \textit{MAX4\_project\_report.pdf}} + \label{fig:dynamics} + \end{figure} + + Given coordinates $x$, $y$ and rotation $\theta$ in a global coordinate system and wheels labelled 1, 2 and 3. The connection between $\dot{x}$, $\dot{y}$ and $\dot{\theta}$ and the rotational speed of the wheels, $\dot{\phi}_1$, $\dot{\phi}_2$ and $\dot{\phi}_3$ is + + \begin{equation} + \begin{pmatrix} + \dot{\phi}_1 \\ \dot{\phi}_2 \\ \dot{\phi}_3 + \end{pmatrix} = \frac{1}{r}\begin{pmatrix} + -\sin(\theta) & \cos(\theta) & R \\ + -\sin(\theta +2\pi/3) & \cos(\theta +2\pi/3) & R \\ + -\sin(\theta +4\pi/3) & \cos(\theta +4\pi/3) & R \\ + \end{pmatrix} \begin{pmatrix} + \dot{x} \\ \dot{y} \\ \dot{\theta} + \end{pmatrix} + \end{equation} + Here $R$ is the distance from the center of the robot to the wheels, and $r$ is the radius of the omniwheels. + + \section{Crazyflie Positioning} + + A crazyflie (version $\geq$ 2.0) has been used to provide positioning for the robot. This is done via a lighthouse deck (\url{https://www.bitcraze.io/products/lighthouse-positioning-deck/}) mounted on the crazyflie, along with HTC vive beacons mounted in the ceiling above the robot (\ref{fig:beacon}). + + \begin{figure}[h] + \centering + \includegraphics[width=0.7\textwidth]{figs/beacon} + \caption{HTC vive positioning system.} + \label{fig:beacon} + \end{figure} + + \subsection{Setting up the crazyflie} + When setting up the robot, make sure the crazyflie firmware is up to date, along with the firmware on the lighthouse deck. To update the version, use an external laptop together with a crazyradio and the crazyflie client (pip install cfclient). + \\ + + \textbf{Crazyradio}: \url{https://www.bitcraze.io/products/crazyradio-pa/}\\ + \textbf{Cfclient}: \url{https://www.bitcraze.io/documentation/repository/crazyflie-clients-python/master/userguides/userguide_client} + \\ + + To be able to connect to the crazyflie, make sure you have the right USB permissions: \url{https://www.bitcraze.io/documentation/repository/crazyflie-lib-python/0.1.9/usb_permissions/} + \\ + + Once you have the crazyflie client up and running, update to the latest firmware. To do this, you must use a crazyradio to connect to the crazyflie, and the crazyflie must be connected to a battery (sadly it doesn't work to just connect to it via usb). You will find firmware update instructions here: + \url{https://www.bitcraze.io/documentation/repository/crazyflie-clients-python/master/userguides/userguide_client/#firmware-upgrade} + + Once the firmware has been updated, you will not need the battery again. + + \subsection{Setting up lighthouse system} + The beacons are mounted via 3d-printed mounts. See the 3Dprint-folder for models. Instructions on how to configure the lighthouse system can be found here: + \url{https://www.bitcraze.io/documentation/tutorials/getting-started-with-lighthouse/} + + + \subsection{Connecting to raspberry pi} + The raspberry pi is connected to the crazyflie via USB, which provides both power and communication. If you notice that the crazyflie turns on, but you cannot establish connection via the USB, try switching USB cables. Some USB cables don't transfer data. + + \subsection{Reconfigure coordinate system} + Before you start running the robot, you will have to configure the robot's coordinate system. This is most easily done by connecting to the crazyflie via an external laptop using the crazyradio dongel and the crafyflie client. Instructions can be found here: + \url{https://www.bitcraze.io/documentation/tutorials/getting-started-with-lighthouse/} + \\ + + Note that the coordinate system doesn't need to be reconfigured everytime you boot up the crazyflie. However, if you make any changes like moving your beacons or testing area you will have to do this step. + + \section{Dynamixel Servos} + + The interface between the raspberry pi and the dynamixel servos driving the wheels is developed by Anders Blomdell in the form of a python package that can be found here: + \url{https://gitlab.control.lth.se/anders_blomdell/dynamixel} + \\ + + The servos have several important settings. When writing a script to control the robot, it is important to know the channel number and communication speed of each servo. The easiest ways to check or change the current settings on the servos is by downloading the dynamixel wizard to a laptop, and connecting the servos to the laptop. The wizard also allows changing the maximum set-speed of the servos. + \url{https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_wizard2/} + + \section{Raspberry Pi} + The Raspberry Pi needs very little preparation. In the FRTN75-setup, it was booted with a simple raspian distribution. Instructions for setting up a raspberry pi with raspian: + \url{https://www.raspberrypi.com/documentation/computers/getting-started.html} + \textbf{Make sure to change the default password of the raspberry pi before connecting it to the internet!} + \\ + + In the FRTN75-course, a few python packages were needed, listed in \textit{requirements.txt} in the \textit{code} folder. Note that the python-dynamixel package is not available from pip, but needs to be pip-installed from the gitlab-repository you find under the \textit{dynamixel servos} section. + \\ + + In order to allow the raspberry pi to communicate with the crazyflie, you need to ensure the right USB settings. Instructions for this are available under the \textit{Crazyflie positioning} section. + \\ + + If the communication method of choice is ssh, as in the FRTN75-course, make sure SSH is enabled on the raspberry pi. + + + \section{Frame and Power Supply} + + The frame was designed and 3d-printed by Anders Blomdell. Alexander Pisarevskiy designed and printed the mounts for the power supply, raspberry pi and crazyflie. 3D-print designs can be found in the \textit{3Dprint}-folder. + \\ + + The robot can be fed either through battery or cable. When the cable is connected it overrides the battery supply. During the FRTN75-labs, only the battery supply was used. One battery would last around two lab sessions à 4 hours, but in each session the robot was only on and running for around 1 hour in total. + \\ + + The batteries are charged with the charging system in Figure \ref{fig:charger}. + + \begin{figure}[h] + \centering + \includegraphics[width=0.7\textwidth]{figs/charger} + \caption{Battery charging system.} + \label{fig:charger} + \end{figure} + + \section{Communication} + In the FRTN75 course, there was no online communication between the raspberry pi and external computers. A local router was set up in the lab, where the raspberry pi was given a static IP address. This allowed students to ssh into the device to leave a reference trajectory file (csv format), and then execute the script \texttt{track\_path.py}, which read the file and executed it. + + \section{Code and Scripts} + + + + \url{https://gitlab.control.lth.se/anders_blomdell/dynamixel} +\end{document}