From 8eae7a30202d3b43dea4713ed622ec3b39018d86 Mon Sep 17 00:00:00 2001 From: Edwardius Date: Sat, 14 Sep 2024 22:15:24 +0000 Subject: [PATCH] deploy: 8e464c9facc3ce0249f1972fa298d8320c1a3b2b --- 404.html | 2 +- _next/static/HPtZE7tLPS-ha8V86Ub9q/_buildManifest.js | 1 + .../_ssgManifest.js | 0 _next/static/YlLFyGdU_SrsODunXMTi2/_buildManifest.js | 1 - _next/static/chunks/nextra-data-en-US.json | 2 +- ...t-cd811edd351d3f54.js => about-17076f551e06b3ec.js} | 2 +- ...c32dfc54f9edfed8.js => finance-a66e3088f38dc285.js} | 2 +- ...=> creating_personal_purchases-f42681233d53aea5.js} | 2 +- ... => creating_purchase_requests-852257e2f5958217.js} | 2 +- ...x-893f3f5baf8889d0.js => index-c049793a8afb547a.js} | 2 +- ...76f81e71d7ab9.js => onboarding-764be76f2dc054dd.js} | 2 +- ...2.js => asd_general_onboarding-f0660809c8e051b7.js} | 2 +- .../onboarding/asd_watcloud_dev-7f8279904ea7777e.js | 1 + .../onboarding/asd_watcloud_dev-9e5669c2bda3b049.js | 1 - ...115d9.js => vp_general_onboard-30ddbb1624efcb3b.js} | 2 +- about.html | 4 ++-- finance.html | 4 ++-- finance/creating_personal_purchases.html | 4 ++-- finance/creating_purchase_requests.html | 4 ++-- index.html | 4 ++-- onboarding.html | 4 ++-- onboarding/asd_general_onboarding.html | 4 ++-- onboarding/asd_watcloud_dev.html | 10 +++++++--- onboarding/vp_general_onboard.html | 4 ++-- 24 files changed, 35 insertions(+), 31 deletions(-) create mode 100644 _next/static/HPtZE7tLPS-ha8V86Ub9q/_buildManifest.js rename _next/static/{YlLFyGdU_SrsODunXMTi2 => HPtZE7tLPS-ha8V86Ub9q}/_ssgManifest.js (100%) delete mode 100644 _next/static/YlLFyGdU_SrsODunXMTi2/_buildManifest.js rename _next/static/chunks/pages/{about-cd811edd351d3f54.js => about-17076f551e06b3ec.js} (98%) rename _next/static/chunks/pages/{finance-c32dfc54f9edfed8.js => finance-a66e3088f38dc285.js} (98%) rename _next/static/chunks/pages/finance/{creating_personal_purchases-4b80d218a17c4c27.js => creating_personal_purchases-f42681233d53aea5.js} (97%) rename _next/static/chunks/pages/finance/{creating_purchase_requests-318142eb027c495a.js => creating_purchase_requests-852257e2f5958217.js} (96%) rename _next/static/chunks/pages/{index-893f3f5baf8889d0.js => index-c049793a8afb547a.js} (98%) rename _next/static/chunks/pages/{onboarding-a6a76f81e71d7ab9.js => onboarding-764be76f2dc054dd.js} (98%) rename _next/static/chunks/pages/onboarding/{asd_general_onboarding-3fb2e6af6a7ef012.js => asd_general_onboarding-f0660809c8e051b7.js} (99%) create mode 100644 _next/static/chunks/pages/onboarding/asd_watcloud_dev-7f8279904ea7777e.js delete mode 100644 _next/static/chunks/pages/onboarding/asd_watcloud_dev-9e5669c2bda3b049.js rename _next/static/chunks/pages/onboarding/{vp_general_onboard-6b966bc9a65115d9.js => vp_general_onboard-30ddbb1624efcb3b.js} (99%) diff --git a/404.html b/404.html index 5a346bb..6d6bd6f 100644 --- a/404.html +++ b/404.html @@ -1 +1 @@ -404: This page could not be found

404

This page could not be found.

\ No newline at end of file +404: This page could not be found

404

This page could not be found.

\ No newline at end of file diff --git a/_next/static/HPtZE7tLPS-ha8V86Ub9q/_buildManifest.js b/_next/static/HPtZE7tLPS-ha8V86Ub9q/_buildManifest.js new file mode 100644 index 0000000..64b247f --- /dev/null +++ b/_next/static/HPtZE7tLPS-ha8V86Ub9q/_buildManifest.js @@ -0,0 +1 @@ +self.__BUILD_MANIFEST=function(a,e,s){return{__rewrites:{afterFiles:[{has:void 0,source:"/:path*/_meta",destination:"/404"}],beforeFiles:[],fallback:[]},"/":[a,e,s,"static/chunks/pages/index-c049793a8afb547a.js"],"/_error":["static/chunks/pages/_error-ee5b5fb91d29d86f.js"],"/about":[a,e,s,"static/chunks/pages/about-17076f551e06b3ec.js"],"/finance":[a,e,s,"static/chunks/pages/finance-a66e3088f38dc285.js"],"/finance/creating_personal_purchases":[a,e,s,"static/chunks/pages/finance/creating_personal_purchases-f42681233d53aea5.js"],"/finance/creating_purchase_requests":[a,e,s,"static/chunks/pages/finance/creating_purchase_requests-852257e2f5958217.js"],"/onboarding":[a,e,s,"static/chunks/pages/onboarding-764be76f2dc054dd.js"],"/onboarding/asd_general_onboarding":[a,e,s,"static/chunks/pages/onboarding/asd_general_onboarding-f0660809c8e051b7.js"],"/onboarding/asd_watcloud_dev":[a,e,s,"static/chunks/pages/onboarding/asd_watcloud_dev-7f8279904ea7777e.js"],"/onboarding/vp_general_onboard":[a,e,s,"static/chunks/pages/onboarding/vp_general_onboard-30ddbb1624efcb3b.js"],sortedPages:["/","/_app","/_error","/about","/finance","/finance/creating_personal_purchases","/finance/creating_purchase_requests","/onboarding","/onboarding/asd_general_onboarding","/onboarding/asd_watcloud_dev","/onboarding/vp_general_onboard"]}}("static/css/54fde772ad89017e.css","static/chunks/318-c9ba87fb15cc0206.js","static/chunks/630-659f445206df61de.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/_next/static/YlLFyGdU_SrsODunXMTi2/_ssgManifest.js b/_next/static/HPtZE7tLPS-ha8V86Ub9q/_ssgManifest.js similarity index 100% rename from _next/static/YlLFyGdU_SrsODunXMTi2/_ssgManifest.js rename to _next/static/HPtZE7tLPS-ha8V86Ub9q/_ssgManifest.js diff --git a/_next/static/YlLFyGdU_SrsODunXMTi2/_buildManifest.js b/_next/static/YlLFyGdU_SrsODunXMTi2/_buildManifest.js deleted file mode 100644 index 6e9bfd5..0000000 --- a/_next/static/YlLFyGdU_SrsODunXMTi2/_buildManifest.js +++ /dev/null @@ -1 +0,0 @@ -self.__BUILD_MANIFEST=function(a,e,s){return{__rewrites:{afterFiles:[{has:void 0,source:"/:path*/_meta",destination:"/404"}],beforeFiles:[],fallback:[]},"/":[a,e,s,"static/chunks/pages/index-893f3f5baf8889d0.js"],"/_error":["static/chunks/pages/_error-ee5b5fb91d29d86f.js"],"/about":[a,e,s,"static/chunks/pages/about-cd811edd351d3f54.js"],"/finance":[a,e,s,"static/chunks/pages/finance-c32dfc54f9edfed8.js"],"/finance/creating_personal_purchases":[a,e,s,"static/chunks/pages/finance/creating_personal_purchases-4b80d218a17c4c27.js"],"/finance/creating_purchase_requests":[a,e,s,"static/chunks/pages/finance/creating_purchase_requests-318142eb027c495a.js"],"/onboarding":[a,e,s,"static/chunks/pages/onboarding-a6a76f81e71d7ab9.js"],"/onboarding/asd_general_onboarding":[a,e,s,"static/chunks/pages/onboarding/asd_general_onboarding-3fb2e6af6a7ef012.js"],"/onboarding/asd_watcloud_dev":[a,e,s,"static/chunks/pages/onboarding/asd_watcloud_dev-9e5669c2bda3b049.js"],"/onboarding/vp_general_onboard":[a,e,s,"static/chunks/pages/onboarding/vp_general_onboard-6b966bc9a65115d9.js"],sortedPages:["/","/_app","/_error","/about","/finance","/finance/creating_personal_purchases","/finance/creating_purchase_requests","/onboarding","/onboarding/asd_general_onboarding","/onboarding/asd_watcloud_dev","/onboarding/vp_general_onboard"]}}("static/css/54fde772ad89017e.css","static/chunks/318-c9ba87fb15cc0206.js","static/chunks/630-659f445206df61de.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/_next/static/chunks/nextra-data-en-US.json b/_next/static/chunks/nextra-data-en-US.json index 79c6de4..46ed93b 100644 --- a/_next/static/chunks/nextra-data-en-US.json +++ b/_next/static/chunks/nextra-data-en-US.json @@ -1 +1 @@ -{"/about":{"title":"About WATonomous","data":{"":"WATonomous is the autonomous vehicle design team at the University of Waterloo. We are an agile group of developers, engineers, businessmen, and marketers looking to lead the next generation of robotic systems and their applications to society.","our-big-audacious-goal#Our Big Audacious Goal":"Members of WATonomous center around a singular goal:\nTo show the world a bunch of students can build a self-driving car!\n... and in doing so, drive ourselves to become better people :)."}},"/finance":{"title":"Finance System","data":{"":"Instructions on how to use the WATonomous Finance System, which is used to track funding, our account balances, and manage and reimburse purhcases.Creating and Submitting a Personal Purchase Request Contains details for members on how to purchase items using their own money and get reimbursed for it. (go-to)Creating and Submitting a Purchase Request Contains details for members on how to purchase items using WATonomous cash or directly from one of our funds. (go-to)"}},"/finance/creating_personal_purchases":{"title":"Creating Personal Purchases","data":{"":"Go to https://finance-frontend.watonomous.ca/\nCreate New Ticket > Ticket Type: Personal Purchase\nEnter all purchase details. For funding item link, you would either be using FI-1 (WATO Cash) or one of the Funding Items that match the category. For example, if you're purchasing an RTX 4090 it would probably fit under GPU funding.\nAwait team approval. Please reach out to the finance team and the faculty advisor.\nOnce this has been completed, the status will transition to READY_FOR_BUY. You should purchase the item at this step.\nOnce the item has been purchased, please upload the University of Waterloo Finance Expense Claim Form. Instructions found below.\nClick Confirm Item(s) Purchased and Submit Reimbursement","creating-expense-claim-forms#Creating Expense Claim Forms":"Navigate here\nClick the link in step 1 titled \"reimbursement form (excel)\"\nEnter the following fields\nPayee is Student\nClaimant Name, Student Number, Department, Phone Number, E-mail Address\nMailing Address, City, Province/State, Postal Code. For these, use the address your items were shipped to, or your personal address.\nDestination/Reason for Request: Please put the corresponding reference number. For WEEF, this can be found here. Reach out to @smileycow on Discord if you do not know what this is\nIgnore the Travel Advance Request section, and fill in your item details on the next section. For the description, please put the name of your item, and if available, the reason it was purchased.\nHas an advance been issued: No\nEnter your signature of Claimant\nFollow the rest of the instructions on the original page (steps 2 to 4)\nUpload the receipt to the corresponding personal purchase at https://finance-frontend.watonomous.ca/"}},"/finance/creating_purchase_requests":{"title":"Creating Purchase Requests","data":{"":"Go to https://finance-frontend.watonomous.ca/\nCreate New Ticket > Ticket Type: UW Finance Purchase\nEnter all purchase details. For funding item link, you would either be using FI-1 (WATO Cash) or one of the Funding Items that match the category. For example, if you're purchasing an RTX 4090 it would probably fit under GPU funding.\nAwait team approval. Please reach out to the finance team and the faculty advisor.\nOnce this has been completed, the item will be sent to the finance coordinator. They will purchase the item, in which case the status will be ORDERED\nOnce the item has arrived, the item will be transitioned to READY_FOR_PICKUP, and the pick up instructions will give you next steps\nOnce completed, the item should be in the PICKED_UP state, completing the flow."}},"/":{"title":"Welcome to the Wiki","data":{"":"Welcome to the WATonomous Wiki! This is an open-source wiki for all things WATonomous.","new-here#New Here?":"","how-to-edit#How to Edit":"Editing the WATonomous wiki is easy. Simply click Edit this page on the far right of each page. Note, you must be part of the WATonomous Organization to be able to edit.You can use regular react components, or choose from a plethora of pre-built components and tools here."}},"/onboarding":{"title":"Onboarding","data":{"":"WATonomous onboarding guides. The following are quick jists of what each onboarding guide is for:ASD - Developing with WATcloud Contains setup steps for getting access to WATcloud as well as how to use WATcloud in the Autonomous Software Division. (go-to)ASD - General Onboarding Contains a walk-through that teaches you the basics of ROS2, Docker, Docker Compose, and the rest of our robotics software stack. (go-to)"}},"/onboarding/asd_watcloud_dev":{"title":"Developing with WATcloud","data":{"":"You must complete your Cluster Access Form before proceeding with this guide.\nHere, we discuss setting up WATcloud to be used for software development in the Autonomous Software Division.","why-watcloud-what-is-watcloud#Why WATcloud? What is WATcloud?":"Due to the high computational requirements of many aspects of the ASD stack, WATO has a large server infrastructure for remote development WATcloud. In this section, you will learn to connect to WATcloud on VS code. Connecting to a server to do remote development is not only a crucial aspect of software development at WATonomous, but is also a very common practice in the industry.\nFun Fact: WATcloud closely mimics server infrastructures used by OpenAI, NASA, Nvidia, and more!","a-look-from-afar#A Look from Afar":"","how-does-watcloud-share-compute-resources-fairly#How does WATcloud share compute resources fairly?":"WATcloud relies heavily on a resource management tool known as SLURM. SLURM ensures that all resources in WATcloud are shared in a fair and well-managed manner.For the everyday developer, you can imagine SLURM as a \"build your own computer\" tool. You specify to SLURM what compute resources you want (CPU, RAM, GPU, memory, time limit, etc.) and SLURM will build a compute node with the resources it has on hand.","so-how-does-remote-development-actually-work#So how does remote development actually work?":"Remote development for a WATonomous member typically consists of a local machine, host machine, SLURM node, and a docker container. They are defined below:\nLocal Machine Your personal computer.\nHost Machine The computer you connect to. In the case of WATcloud, this is the SLURM login node.\nSLURM Node Used to manage compute resources. It creates SLURM Jobs according to your needs.\nSLURM Job An \"imaginary computer\" that is created by WATcloud. You specify to WATcloud what compute you need by running commands in the SLURM login node.\nDocker Container An isolated coding environment.\nTo do remote development in the Autonomous Software Division, the process can be summed up by the image below:\nAs shown in the image, there are two ways to use a SLURM node.","job-scheduling-vs-interactive-development#Job Scheduling vs Interactive Development":"Use job scheduling when you want to run a command for a very long time (>1 day long). Use interactive development when you are actively making changes to your code and testing it.For most WATonomous members, you would use job scheduling for tasks like training neural networks, large data processing, numerical optimization, etc. On the other hand, you would use interactive development when you are coding/testing ROS2 nodes, interacting with / visualizing live data, making code changes in general, etc.","setting-up-watcloud-for-asd#Setting up WATcloud for ASD":"This section is experimental. Please let us know of any issues on our Discord\nDealing with SSH can be quite foreign to alot of new developers. Thankfully, we provide a series of helper scripts that will make setup for WATcloud easier on you.","general-setup#General Setup":"This section is required so that you have proper access to our server cluster.","local-machine-clone-the-wato_asd_tooling-repository#[Local Machine] Clone the wato_asd_tooling repository":"git clone git@github.com:WATonomous/wato_asd_tooling.git","local-machine-generate-an-ssh-config#[Local Machine] Generate an SSH config":"If you have never created an ~/.ssh/config file before, do that now. Note, we assume that all your SSH files are stored under ~/.ssh\ntouch ~/.ssh/config\nGenerate a WATcloud SSH config. Follow the prompts whenever you get them.\ncd wato_asd_tooling\nbash ssh_helpers/generate_ssh_config.sh\nYou should now be able to connect our cluster using these commands:\nssh tr-ubuntu3\nssh derek3-ubuntu2\nssh delta-ubuntu2","local-machine-setup-vscode-for-ssh#[Local Machine] Setup VScode for SSH":"To do this, download the Remote - SSH VScode Extension. After that, you should be able to attach VScode to any of the machines.","local-machine-setup-agent-forwarding#[Local Machine] Setup Agent Forwarding":"Agent forwarding lets us carry our identity onto other machines that we connect to. What this means is, you can use git commands on other machines without having to create an SSH key on each machine you connect to.Setup agent forwarding with our helper script. Follow the prompts whenever you get them.\ncd wato_asd_tooling\nbash ssh_helpers/configure_agent_forwarding.sh","host-machine-confirm-agent-forwarding-works#[Host Machine] Confirm Agent Forwarding Works":"You should now be able to use git on all the WATcloud machines you connect to. Confirm by running the following inside a WATcloud machine you connected to.\nssh -T git@github.com\nDeliverable Get SSH and SSH Agent Forwarding working.","setup-for-interactive-development#Setup for Interactive Development":"Unlike job scheduling, SLURM was not built to handle interactive development. Luckily we have a team of very talented individuals, and we managed to make interactive development work nonetheless :).Creating an interactive development environment entails starting an SSH server inside the SLURM node, some wacky SSH key sharing, a netcast proxycommand, as well as pointing docker to a persistent filesystem. You don't have to do that though. You just need to do the following.","one-time-setup#One-time Setup":"Follow these steps if you are setting up the SLURM dev sessions for the first time, or you were using past solutions for SLURM that WATO provided.","host-machine-add-your-public-key-into-the-slurm-nodes-authorized-keys#[Host Machine] Add your public key into the SLURM node's authorized keys":"Copy your public key on your local computer and paste it into the authorized_keys file on the SLURM node.\nyour_local_machine$ cat ~/.ssh/your_key.pub (copy the output of this)\nyour_local_machine$ ssh [tr-ubuntu3 or derek3-ubuntu2]\nwatcloud_slurm_node$ touch ~/.ssh/authorized_keys\nwatcloud_slurm_node$ nano ~/.ssh/authorized_keys (paste you rpublic key into here)","local-machine-build-the-computer-you-desire#[Local Machine] Build the Computer you desire!":"Use your favorite text editor to edit wato_asd_tooling/session_config.sh.\ncd wato_asd_tooling\nnano session_config.sh\nThe file itself contains descriptions of all of the parameters and how to set them.","local-machine-start-a-slurm-dev-session#[Local Machine] Start a SLURM Dev Session":"Run the helper script to startup a SLURM Dev Node. Follow all the prompts carefully.\ncd wato_asd_tooling\nbash start_interactive_session.sh\nDO NOT START MORE THAN ONE DEV NODE. You have a chance of corrupting your docker filesystem. Starting more than one dev node is like building multiple computers. It is NOT the same as creating multiple terminals.","local-machine-setup-ssh-for-slurm#[Local Machine] Setup SSH for SLURM":"Run this last helper script LOCALLY. Follow the prompts carefully.\ncd wato_asd_tooling\nbash ssh_helpers/setup_slurm_ssh.sh","local-machine-stop-the-slurm-dev-session#[Local Machine] Stop the SLURM Dev Session":"Stop the interactive session by hitting Ctrl+C.","starting-a-slurm-dev-session-regularly#Starting a SLURM Dev Session Regularly":"Once you have done the one-time setup, connecting to a SLURM Dev Session is easy.","local-machineoptional-build-the-computer-you-desire#[Local Machine][Optional] Build the Computer you desire!":"Use your favorite text editor to edit wato_asd_tooling/session_config.sh.\ncd wato_asd_tooling\nnano session_config.sh","local-machine-start-a-slurm-dev-session-1#[Local Machine] Start a SLURM Dev Session":"Run the helper script to startup a SLURM Dev Node. Follow all the prompts carefully.\ncd wato_asd_tooling\nbash start_interactive_session.sh","local-machine-connect-to-the-slurm-dev-session-with-vscode#[Local Machine] Connect to the SLURM Dev Session with VScode":"You can connect to the SLURM Dev Session using the VScode ssh extension. The remote host is called asd-dev-session by default.\nAnd you're good to go! Whenever you want to startup a SLURM Dev Node, start one up by running start_interactive_session.sh, and then SSH into the SLURM node through VScode.","setup-for-job-scheduling#Setup for Job Scheduling":"There is no setup. Creating an SLURM job is really easy. It was what SLURM was designed for. You can view docs on SLURM in the WATcloud documentation.\nDeliverable Run a SLURM batch job with 2 CPUs that counts to 60."}},"/onboarding/vp_general_onboard":{"title":"WATO-VP Onboarding Document","data":{"":"Last modified: September 11th, 2024","what-is-watonomous-short-form-wato#What is WATonomous? (Short form: WATO)":"Started in summer 2017, WATonomous is the University of Waterloo's first student-run autonomous car team. We are an agile group of developers, engineers, and businessmen looking to lead the next generation of robotic systems and their applications. Ultimately, we just want to prove that you don't need a room full of current FAANG employees to build a self-driving car! All you need for a self-driving car is a self-driven individual ;)","past-achievements#Past Achievements":"Up till 2023, we had a Chevrolet, and we are proud to have finished 2nd place in the SAE AutoDrive Challenge, an international competition to build a Level 4 autonomous vehicle in four years!Following the competition, we continued our path towards perfect autonomy, completing research papers on various autonomous vehicle technologies including action classification, control, and environment modeling. We have a track record of submitting to prestigious research conferences like ICRA, ICVES, and ITS.","current-plan#Current Plan":"We have a brand new car, a Kia Soul EV! We are planning on reaching level 5 autonomy all from scratch! Progress has been made over the past year, and we are aiming to be able to test track it by the end of summer 2025!","impact-goals#Impact, Goals":"We want to prove that a group of dedicated students, and NOT industry professionals, can work together and create a self-driving automotive system that can compete with the likes of the WAYMO taxi and Google Self-Driving Car.For the end of the Fall 2024 term, we hope to have the majority of our vehicle platforming work completed and to have the car (along with its peripheral systems) operate under its own power. We are also expecting a substantial computing power upgrade to the onboard systems that we hope to have ready by the Winter 2024 term.","a-bit-about-vehicle-platform#A Bit About Vehicle Platform...":"Directors: Xander Hayhoe, Mahir Mahota","interfacing#Interfacing":"Leads: Mariam ElSahhar\nCurrent Projects:\nMost components on the interfacing PCBs are soldered. We do need to add connectors and completely resolder a whole new board.\nBoards and all assembled hardware logic need to be thoroughly tested (based on predetermined test parameters) before implementation into the car.\nInstall DBW system with the joysticking tele-drive module for user overriding.\nMigrate EXISTING Joystick controller code from ROS1 to ROS2.\nImplement arbiter and MUX logic for the vehicle architecture, signals control.\nRun final tests of the ass","power-systems#Power systems":"Leads: Benjamin Liu\nCurrent Projects:\nFor the end of October, we hope to have the car’s sensor rack and compute module running off of the internal 12V battery. In the car’s current state, these systems along with other peripherals run off of external power sources such as a wall outlet. To reach a minimal viable product, we need this crucial step to be completed.","onboarding-challenges#Onboarding Challenges:":"Interfacing Challenge: Do the ASD Assignment, as it uses the ROS2 Stack, which VP will do a lot of work with! WATO ASD Assignment\nNote: Instead of sending proof of completion to Eddy, please send the proof to Xander; Discord: @Xander1452.\nElectrical Challenge: Take the Electrical Safety and Awareness course offered on LEARN to all Waterloo students (self-enrollment)."}},"/onboarding/asd_general_onboarding":{"title":"General Autonomous Software Onboarding - ASD Assignment","data":{"":"You must complete ASD - Developing with WATcloud before proceeding with this guide.","introduction#Introduction":"Congratulations! WATonomous has recognized your talent, and we would like to get you ready to contribute to the team.The Autonomous Software Division (ASD) assignment is designed to train a newcomer robotics programing at WATonomous from the ground up.The onboarding assignment consists of the following:\nAccessing the WATonomous Server Cluster (WATcloud)\nGetting used to ROS2 - Docker robotics infrastructure\nWriting your own control program to direct a robot in simulation\nShould you finish this assignment, you will have all the skills necessary to contribute towards building a real autonomous vehicle. Note, this assignment will not cover ML concepts. However, ML is used throughout robot autonomy, so we highly recommend you learn ML on your own time. Here’s some ML resources for you (DO NOT SHARE).\nAfter completing this assignment, you will become an official member of WATonomous with a dedicated watonomous email.\nPlease contact the Director of ASD (Eddy Zhou) on discord, showing proof of completion and two subteams you are interested in joining. Link to subteams here.The ASD leads are here to help so feel free to ask any questions about the onboarding assignment in the asd-questions channel on discord. This document is pretty long, but rest assured that a large majority of the assignment is a gigantic walk through.Good Luck!","server-access#Server Access":"Server access steps have been moved to here. Please let the WATcloud leads know if you have any issues.Once you've familiarized yourself\nDeliverable 1.0 Create a custom SLURM interactive development job. The SLURM job must have VRAM. You can specify the job to run for 2 hours so that you don't get kicked out while you are developing.\nDeliverable 2.0 Connect VS Code to a WATcloud SLURM job. Clone the ASD Training Repository into your WATcloud user’s home directory (~/). Open your cloned repository in VS Code. If you aren’t familiar with git, here’s a cheatsheet.\nDeliverable 2.0.1 STAR THE WATO_MONOREPO. It’s voluntary, but it will help us legitimize the repository overtime :).\nDeliverable 2.1 In the opened ASD Training Repo, create a new branch titled _training.","asd-training-repository#ASD Training Repository":"In the previous section, you would have cloned the ASD Training Repository into your user’s home directory on the server. Please keep this folder open as you read.The repository you just cloned contains a barebones setup of WATonomous’ current ASD infrastructure. It closely mirrors the infrastructure of the wato_monorepo, which is the central repository for all of the code that goes into the car. You can read up on the reasoning behind having a monorepo here.","our-tech-stack#Our Tech stack":"Our autonomy stack contains a multitude of coding tools and libraries (PyTorch, TensorFlow, OpenCV, Numpy, Scikit, Foxglove, Gymnasium, etc.), but the three MOST-PROMINENT, REOCCURING TOOLS you must know are ROS2, Docker, and Docker Compose.These software tools enable cool AI tools and algorithms to communicate properly in a robotic system. Permutations of these open source tools are used throughout the cutting-edge robotics industry. It is especially prevalent in R&D, where new tools need to be integrated and tested at an alarming rate.","ros2#ROS2":"The Robotics Operating System 2 (ROS2) is the second iteration of open source tools and libraries used for quickly building robot applications. It helps us intuitively do interprocess communication without the need to dig extremely deep into low-level programming. ROS2 is the monorepo’s main communication interface.\nDeliverable 3.0 Identify locations in the code where ROS2 is being used.","docker#Docker":"For our ASD stack, we use Docker to containerize all of our code. Docker can be thought of as a lightweight virtual machine, allowing us to create separate environments (containers) for running code. This makes our codebase portable and modular.Docker uses docker files (.Dockerfile extension) to configure and set up each container. Dockerfiles generally start with a machine base, and commands are specified to setup the machine, install dependencies, and setup the code workspace. Here is an example dockerfile from the wato_monorepo.Generally the Dockerfiles for your modules will be set up already by your team leads. However, Docker is still a very useful tool to learn, and you can reference the getting starteddocumentation for more details if interested. Those who can understand our monorepo down to the docker-level are much valued :).\nDeliverable 3.1.0 Identify locations in the code where Docker is being used.\nDeliverable 3.1.1 What is the difference between a Dockerfile, a Docker Image, and a Docker Container? Refer to online references.","docker-compose-and-wato#Docker Compose and wato":"Docker Compose is a utility for managing a codebase with multiple docker components. It provides an intuitive yaml interface to configure, build, and run docker containers. With a single Docker Compose command, you can startup multiple containers at once.In our ASD training repo, and our monorepo, you will make great use of a software tool known as ‘watod’. Under the hood, watod is a wrapper around Docker Compose. You can use watod just like how you would use Docker Compose.\nDeliverable 3.2.0 Identify locations where Docker Compose is being used. What does WATO call these Docker Compose files? Hint: directory name\nDeliverable 3.2.1 Identify environment variables in a docker-compose file (Hint: they start with a $). How are these environment variables set during runtime? The next question can clear up this question.\nDeliverable 3.2.2 Identify any files prefixed with ‘watod’. Take a closer look at ‘watod-setup-env.sh’, what is it doing? What script do you run to run ‘watod-setup-env.sh’? How does this relate to the environment variables found in the docker-compose file?\nDeliverable 3.2.3 In your own words, explain what watod does. How does it interface with our docker-compose files?","whats-up-with-the-long-image-names#What's up with the long image names?":"You may have noticed docker image names along the lines of “git.uwaterloo.ca:5050/watonomous/wato_monorepo/…”. This long image name refers to a docker image stored in a Docker Registry.Building docker images from scratch can take an extremely long time. So our team takes advantage of Docker Registries to quickly pull pre-built images. This saves us a lot of time.In the ASD Training Assignment, docker registries are disabled. This is why you see the manifest error when building. No fret, we disabled registries on purpose. In the actual wato_monorepo, you’ll have to learn how to use registries.","a-visual-understanding#A Visual Understanding":"Below is a possible way to visualize how our ASD infrastructure works:The entire system communicates within itself using ROS2 messages. All the cool algorithms for perception, planning, control, etc. are wrapped in their own ROS2 nodes to function concurrently. Various nodes share docker containers if they require the same libraries and tools to function (eg. two nodes may require the same version of PyTorch, so they share the same docker container).\nDeliverable 3.2.3 In your own words, describe an analogy to real life that closely resembles the ASD infrastructure.","on-startup#On startup":"The visualization of the tech stack above represents our code in a running state. On startup, watod (docker-compose under the hood) is used to orchestrate the startup of each and every docker container, ROS2 node, and core algorithms. It carefully and automatically builds out the entire software architecture piece by piece until you end up with the visualization above.","training-overview#Training overview":"Now that you have a decent understanding of our tech stack. We can now move on to the final part of the ASD training. This part will be the most time consuming, and it will also contain an extremely limited amount of hand holding. Our team is here to help, so if you have any questions, feel free to ask on Discord!The provided training repository contains the following:\nA Gazebo server, which is a robotics simulator allowing us to interact with and write code for a virtual robot\nA Foxglove websocket, which streams ROS data over a websocket to be viewed using the Foxglove ROS visualization dashboard.\nROS2, Docker, and WATO backend infrastructure.\nSample ROS2 nodes in C++ and Python that demonstrate custom message passing.\nA barebones ROS2 node which you will write your code in.\nThe end result of the training will be some custom-written ROS2 nodes which will interact with various sensors on the robot, perform some computation/processing, and output commands to control the robot. All of this will be done using ROS2 as a framework.","set-up#Set up":"In previous sections, we made you analyze the ASD Training code. Now is the time to learn how to use it.","watod-orchestration#Watod Orchestration":"As you may or may not know, watod is used to orchestrate our entire tech stack together. It sets any necessary environment variables that our docker compose profiles would need on startup, and then calls the docker compose command to begin building all the docker containers.Oftentimes, you don’t need to run the entire software pipeline to begin development, so you may only need to run a minimum slice of the software pipeline to begin. The powerful thing about watod is that you can configure what profiles (docker-compose files) to start up. This equates to only starting only a portion of the entire stack as opposed to starting up the whole thing.","watod-configsh#watod-config.sh":"To configure what profiles to startup, we use watod-config.sh Specifically the ACTIVE_PROFILES field.\n## ----------------------- Watod2 Configuration File Override ----------------------------\n## ACTIVE PROFILES CONFIGURATION\n## List of active profiles to run, defined in docker-compose.yaml.\n##\n## Possible values:\n## - vis_tools : starts tools for data visualization (foxglove)\n## - gazebo : starts robot simulator (gazebo)\n## - samples : starts up sample nodes for reference (optional)\n## - robot : starts up robot nodes\nACTIVE_PROFILES=\"vis_tools gazebo samples\"\n## Name to append to docker containers. DEFAULT = \n# COMPOSE_PROJECT_NAME=\"\"\n## Tag to use. Images are formatted as : with forward slashes replaced with dashes.\n## DEFAULT = \n# TAG=\"\"\nYour watod-config.sh would have ACTIVE_PROFILES commented out, so please populate this variable just like above. vis_tools, gazebo, and samples are all profiles.\nNOTE: Once you are a full ASD member, please leave the watod-config.sh unchanged when you make a Pull Request.","watod-up#./watod up":"After you’ve changed your watod-config.sh, simply startup the training stack by entering./watod upin your command line while inside the ./wato_asd_training/ directory. You should then see docker compose building all the Docker images present in each of the profiles you specified. You could startup each of the three profiles alone if you wanted to. Use Ctrl + C to stop watod.\nDeliverable 4.0 Startup all three profiles in the training repo (vis_tools, gazebo, and samples). After you’ve done that, stop watod and startup each profile alone. Do this again with two of the three profiles.","dev-containers#Dev containers":"Dev containers are a very important aspect of WATonomous’ development cycle. When you run ./watod up, you spin up multiple containers that you can develop in. Inside these containers, you can change code, debug, and develop to your heart’s content, and all changes made inside the dev container will propagate out to your host machine.The typical WATonomous development cycle looks like this:","up-a-container#Up a container":"Up a container using ./watod up","enter-the-container#Enter the container":"Enter the container using VScode Docker extension","make-changes-inside-the-container#Make changes inside the container":"Make changes inside the container and debug with ros2 tools\nChanges in the container automatically propagate out (and vice versa)","git-add-commit-and-push#Git add, commit, and push!":"Outside the container, Git add, commit, and push\nThe main benefit to dev containers is that you can use linters and tools installed inside the container. For example, you cannot install numpy in the WATcloud host, but you can install numpy in a container and play around with it inside that container.\nDeliverable 4.0.1 Enter the transformer container and change its BUFFER_CAPACITY.\nDeliverable 4.0.2 Rebuild the transformer inside the container using colcon build under the ~/ament_ws directory. Source your changes with source install/setup.bash.\nDeliverable 4.0.3 Launch the transformer node with ros2 launch transformer transformer.launch.py. You should notice your transformer buffer fill up faster or slower based on the new BUFFER_CAPACITY you specified.\nDeliverable 4.0.4 Outside the container, check that your changes to the transformer have propagated out using git status. You should see something like the following…\nYou can also to ./watod up outside your container, and the changes you made inside the container should reflect in the robot logs.\nDeliverable 4.0.5 BONUS when doing ./watod up, the transformer node spins up automatically. Can you think of a way to not make the node spin up? Hint: you need to stop the node from launching, but you have to keep the container running for an indefinite period.","foxglove#Foxglove":"Foxglove is an open source data visualization tool for robotics. Founded by a couple of folks at Cruise, Foxglove’s main advantage is its ability to visualize server-side data.You may have heard of RViz, which is ROS’s native visualization software. As powerful as this tool can be, RViz is not great for server-side development. In order to use RViz on our server, you’d have to spin up a VNC docker container. This is what our team has done in the past, but it was terrible. RViz would run at no faster than 3fps.","port-forwarding#Port Forwarding":"In a nutshell, Port Forwarding establishes a connection between a communication endpoint on one device in a private network to another device on another network. We use Port Forwarding to view Graphic User Interfaces (GUIs) and stream data to your local machine. The world of networking is scary, so we will try to make it as straightforward as possible.A recurring pattern for Port Forwarding is: “software A is streaming data out of some port ####, I want to read this data myself, so I will forward port #### to my machine to read it”. If software A is in a Docker container on a server host machine, then you have to forward the port from the Docker container to the host machine, and then from the host machine to your local machine.You don’t have to do much port forwarding yourself, but it’s good to know the gist of it as we move forward.","using-foxglove#Using Foxglove":"Make sure you have the gazebo and vis_tools profiles running\nHave some sort of web browser on your local machine\nHave the asd_training_config downloaded on your local machine (this is in the training repo)\nOnce you have the two profiles running, you should notice an auto-forwarded port in your VS Code.\nThis port is where foxglove is streaming its data from. You don’t have to do any forwarding yourself, as we’ve done it for you ;P.To view this data, go to the Foxglove website, create an account (with your personal email, we need to hash out sponsorship from Foxglove before you can use your watonomous email), and either visualize the data through your web browser or a local download of Foxglove.","viewing-ros-data-through-foxglove#Viewing ROS data through foxglove":"To view the data coming from the forwarded port, in the Foxglove dashboard click Visualize Data -> Open Connection -> Foxglove Websocket and set it as the port that was forwarded. In the example above, that would be ws://localhost:33401. It will be different for everyone.\nYou can now proceed to open your Foxglove Studio to begin viewing the robot simulation.\nDeliverable 4.1 Up the ASD Training Repo and view your robot simulation in Foxglove on your local machine.\nYour Foxglove Studio should look something like this. Don’t worry if it looks slightly different. We will fix that now. In the ASD Training Repo, we’ve provided a configuration file that sets up your Foxglove Studio to look exactly like how we want it to look. If you haven’t yet, download the configuration file from the repo.You then need to load this file into your local Foxglove Studio.Reload your Foxglove, and you should see something like the following.\nDeliverable 4.2 Upload the ASD Foxglove config to your local foxglove.\nDeliverable 4.3 Interact with Foxglove Studio. You can move around the robot with the Teleop Panel. Here are a couple of subtasks to get you acquainted.\nDeliverable 4.3.1\nDeliverable 4.3.2 BONUS: View the list of Topics. What is a Topic? This can be a great opportunity to get an initial idea of ROS Topics.\nDeliverable 4.3.2 BONUS: The visualized data is, in-reality, a bunch of numbers. Foxglove (and RViz) do some post processing to make it look intuitive. View the /lidar topic’s raw data through a new Raw Message Panel.","hands-on-training#Hands-on Training":"Now that your development environment is set up, we can begin hands-on training. You can always refer back to the previous steps for reference. Kind reminder that our team is here to help, so if you ever have any questions, please feel free to ask in the ASD-Questions channel :).Originally, the intent of this training was to code up a semi-complete navigation system to make the robot rather-intelligently navigate to a destination of your choice. However, for the sake of not blowing up your mind, we have trimmed the assignment down to at least touch all the fundamental concepts we would like you to learn. If you would like to do the whole assignment, feel free to reach out, and we can give you more info on how the robot should navigate the map.","the-basis-of-robot-navigation#The Basis of Robot navigation":"Robot navigation is a very large field of study, so let’s be more specific. The navigation problem we are most concerned with here at WATonomous is: given an end position, and no prior knowledge of the environment, navigate to that end position.In Foxglove Studio, you should see an environment that closely relates to the problem above.\nHere, the end position can be specified by the user to be anywhere on the map.In an ideal environment, where obstacles don’t move, robot navigation can be quite simple. A naive approach to this problem could be: Occupancy, Immediate Trajectory, and Execution. We provide a brief description of them below:Occupancy: A map of squares indicating where the robot can and cannot go.Immediate Trajectory: The planned trajectory to take to get to the end position.Execution: Following the planned trajectory as best as we can.In this system, the occupancy around the robot is found continuously. Given this geometric representation, we can produce a trajectory using the Immediate Trajectory planner. This trajectory is then Executed for a period of time. The whole process repeats continuously until an endpoint is reached.This is not much different to the real autonomy stack of the car. A higher fidelity Occupancy is found using Perception. Immediate trajectories need to factor in the complexity of the road with Navigation. And we mist execute on this trajectory with Control.To formalize, Perception would receive sensor messages and do processing. Navigation would receive the processed sensor data and figure out a set of actions to take. Control would receive the actions and perform them, finally sending commands to the motor. In fact, WATonomous’s software stack follows a very similar architecture, but with more node subdivision within each group to perform more complex computations.","goal#Goal":"The Foxglove simulation consists of a robot within an environment. The robot has three wheels, two motors, a camera, and a 1-dimensional laser scanner.You can freely control the robot’s movement within the environment using the Foxglove Teleop panel.As you may have noticed, the Teleop control of the robot is pretty terrible. It oversteers, stops late, and has a ton of inertia. What if we could simply instruct the robot to move to a specific point on the map instead?The goal of your hands-on training is to do just that! By specifying a goal on the foxglove map, your robot should naively navigate to that point on the map.\nNote: It is important to know that the node you are about to create does not allow the robot to avoid obstacles. It will simply follow the straight-line trajectory to the point you specified.\nAs naive as this sounds, this is actually the basis of robot control. You will see later how this simple control algorithm you’ll make is used with the rest of the navigation stack to achieve obstacle avoidance!","ros#ROS":"Robot Operating System (ROS) is a popular framework for robotics codebases. At its core, it is a message broker – a system which manages and passes data between programs. This allows for modularity, management of complex data types, and interoperability. What makes ROS so powerful is the ability to easily interface libraries and tools that were built on top of ROS to bring advanced capabilities and interactive visualizations to your software stack. Some examples that we will be using are:\nFoxglove, a web-based ROS dashboard for visualizing data and interacting with the robot\nGeometry messages, a ROS package which includes a lot of standard geometry data types like Pose, Point, Twist, Quaternion, etc.\nTransforms 2 (Tf2), a ROS package which allows easy transformations between coordinate frames\nThe basic building block of the ROS framework are nodes. Nodes are responsible for performing a specific set of tasks, and interact with other nodes by receiving (subscribing) or sending out (publishing) messages. You can think of ROS like a network, with many nodes interacting with each other through ROS to achieve the overall function of the software stack.On the technical side, ROS nodes are just c++ or python programs with the ROS library. The ROS framework then manages the building, running, and message passing of each node behind the scenes. The ROS library provides the syntax for creating a node, subscribing to messages, sending out messages, and native objects for ROS messages.\nNote: This document will give a quick overview of ROS2 code, however we highly recommend going through the ROS2 Getting Started documentation which is much more detailed and explains ROS2 from start to finish. There may be a steep learning curve, so if you are confused throughout these training modules, feel free to ask the team leads for an explanation on discord!\nHere is an example ROS node in C++:\n#include \"transformer_node.hpp\"\nExampleNode::ExampleNode() : Node(\"example_node\"){\n // Subscribe to a topic\n subscription_ = this->create_subscription(\n \"/example_topic0\", 20, \nstd::bind(&ExampleNode::subscription_callback, this, \nstd::placeholders::_1));\n // Publish to a topic\n publisher_ =\n this->create_publisher(\"/example_topic1\", 20);\n}\n// Subscription callback: gets called when a message from the subscription comes in\nvoid ExampleNode::subscription_callback(const geometry_msgs::msg::Pose::SharedPtr msg)\n{\n\t// Print to the console\nRCLCPP_INFO(this->get_logger(), \"Received message…\");\n\t…\n\t// Publish a message\n \tpublisher_->publish(msg);\n}\n// Boilerplate code for starting up the node (gets called by ROS framework)\nint main(int argc, char ** argv)\n{\n rclcpp::init(argc, argv);\n rclcpp::spin(std::make_shared());\n rclcpp::shutdown();\n return 0;\n}\nLet’s break it down.First, we have the constructor of our node, which inherits the Node class from the ROS library (rclcpp). There we pass the name of our node, “example_node”.\nExampleNode::ExampleNode() : Node(\"example_node\"){\n …\n}","publisher#Publisher":"Inside the constructor, we initialize our publishers and subscribers. Here is the syntax to create a publisher with the rclcpp library:\n// In header file:\nrclcpp::Publisher::SharedPtr publisher_;\n// In node constructor:\npublisher_ = this->create_publisher(\"/TOPIC\", 20);\nWhere MESSAGE_TYPE is the message you are publishing (i.e. geometry_msgs::msg::Pose for a Pose message) and “/TOPIC” is the topic you are publishing to. The 20 at the end is the buffer size, 20 is a good number for that.\nYou can then publish a message to the publisher using:\npublisher_->publish(msg);\nWhere msg is an object of the type you specified (i.e. geometry_msgs::msg::Pose()).","subscriber#Subscriber":"Next, here is the syntax to create a subscriber:\n// In header file:\nrclcpp::Subscription::SharedPtr subscriber_;\n// In node constructor:\nsubscriber_= this->create_subscription(\n \"/TOPIC\", 20, \nstd::bind(&CALLBACK, this, \nstd::placeholders::_1));\nWhere MESSAGE_TYPE is the message in the topic you want to subscribe to (i.e. geometry_msgs::msg::Pose), “/TOPIC” is the topic to subscribe to, and CALLBACK is a reference to a member function that will get called when a new message comes in (i.e. ExampleNode::subscription_callback). Again, the 20 is the buffer size, and std::placeholders::_1 is boilerplate code for calling the callback with an argument.When a message is received by the subscriber, it calls the callback function with a pointer to the message. Here is an example callback function you would define in your node class:\n// In header file:\nvoid ExampleNode::subscription_callback(const geometry_msgs::msg::Pose::SharedPtr msg);\n//Implementation:\nvoid ExampleNode::subscription_callback(const geometry_msgs::msg::Pose::SharedPtr msg)\n{\n …\n}\nThe ::SharedPtr is a special type of smart pointer, but it is functionally the same as a normal pointer and can be dereferenced and accessed using ->. Values of the message can be accessed just like a normal C++ object, such as “msg->position.x, msg->position.y, msg->position.z” to access the coordinates of the Pose message.You can reference the property names of any message by searching up the message followed by “ros2” on google. For example, here are the docs for the Pose message: docs for message, and you can see all the variables you can access in it.","timers#Timers":"Timers are tasks that execute in a repeated loop. Example use cases for timers are control loops which consistently update motor commands based on observations. The syntax to create a timer is the following:\n//In header file:\nrclcpp::TimerBase::SharedPtr timer_;\nvoid timer_callback();\n//In node constructor:\ntimer_ = this->create_wall_timer(std::chrono::milliseconds(delay_ms), std::bind(&YourNode::timer_callback, this));\n//Callback implementation:\nvoid YourNode::timer_callback(){\n}\nWhere delay_ms is the time between timer calls in milliseconds, and YourNode::timer_callback is the callback function that is called when the timer executes.","printing#Printing":"To print to the console, use RCLCPP_INFO followed by a string. You can pass additional parameters to the RCLCPP_INFO and they get added to the string using c++ sprintf format. For example, adding %f to the string will add in a floating point number to the print, which is passed in as a parameter to RCLCPP_INFO. The following code segment shows printing position.x to the console.\nRCLCPP_INFO(this->get_logger(), \"Received message… x=%f\", msg->position.x);\n// Will output “Received message… x=10.5” or whatever position.x is\nThese are all of the core functionalities to writing a node in ROS! As an exercise, try implementing a publisher and subscriber in C++ using one of the empty nodes available in the training repo, such as src/robot/control/src/control_node.cpp\nDeliverable 5.1 Write a timer that executes every second that will call a callback function and print out a message of your choice to the console. Verify that it works by running ./watod up and viewing the console printouts.\nDeliverable 5.2 Create a publisher that publishes a std_msgs::msg::String message to the “/example_string” topic. Modify the timer callback function you wrote in Deliverable 5.1 to include publishing of a string message to your publisher with a message of your choice. Verify the messages are being sent by adding a “raw message” widget to foxglove and set it to your new topic name. Congratulations, you published your first message!\nDeliverable 5.3 Write a subscriber that receives nav_msgs::msg::Odometry messages from the “/model/robot/odometry” topic (should be automatically available via Gazebo when you run the training repo) and prints its x,y,z coordinates to the console. Reference the ros2 documentation for the Odometry message properties. Congratulations, you subscribed to your first message!\nYou have now successfully written a node to publish and subscribe to messages, which is the core functionality of ROS! You can now write nodes to interact with robots via subscribing and publishing ROS messages.","control#Control":"Now it’s time to implement your first node to interact with the robot! This node will be responsible for receiving a goal position to drive towards and send motor commands to the robot to drive towards it.To set a position for the robot to drive to, we will be using Foxglove’s 3D panel publish feature, which allows you to click on the 3D map and publish a message. In Foxglove under panel settings for the 3D panel, you should see the following listed under the Publish tab:This is where you select the message type and topic to publish to when you click on the 3D map. If you use our configuration file, these will be automatically selected to geometry_msgs::msg::Point and “/goal_point” topic.In order to publish that message, click on the circle icon at the top right of the 3D panel (shown below) and click on a point on the map.You can verify that your point is published by adding a Raw Message widget and point it to the “/goal_pose” topic and watch as a message appears when you click on the circle icon and click on the map.The purpose of the control node is to subscribe to this topic and drive the robot towards the point you clicked on the map.\nYou will be writing code in the control node, so go ahead open up src/robot/control/src/control_node.cpp in the wato_asd_training repo. Now, to receive these messages, create a new subscriber in the Control node (a blank node called Control has been setup already in the repo, you may edit that node) and make it subscribe to geometry_msgs::msg::PointStamped messages in the “/goal_point” message. Then, create a callback and print out the x,y,z values of that PointStamped message (reference link) using RCLCPP_INFO.\nDeliverable 6.1 Create a subscriber that receives geometry_msgs::msg::PointStamped messages from the “/goal_point” topic. Verify you receive the messages by printing out the x,y,z coordinates to the console.\nNow that you can receive the goal pose, the next step is to control the robot. Firstly, we need a timer callback that will run the control loop on repeat. To do this, create a timer that runs every 100ms and calls a blank function for now.\nDeliverable 6.2 Create a timer that calls a blank callback function every 100ms for control. You can verify it works by printing a random message using RCLCPP_INFO.\nInside this timer, we will be using a simple proportional controller. A proportional controller is the first term in PID control, which simply multiplies a constant scalar to the error, where error is the distance between your current point and desired point.Command = Kp * errorIn this case, we have two error terms: x and y. The x error will define forward movement, and the y error will define angular movement.However, there is one problem. Currently, the point message is defined in the world coordinate frame, while we want the error in the robot’s relative coordinate frame to do relative movements. In order to do this, we will take advantage of ROS2’s transform system: TF2","transforms#Transforms":"Transforms define coordinate frames for geometry objects. One example could be a coordinate relative to the world versus relative to the robot, or a coordinate relative to an arm of the robot versus the base of the robot. Here is an illustration of a complex robot with multiple transforms (shown by red,blue,green axis markers):\nLuckily, the ROS2 library has an easy way for dealing with transforms. The gazebo environment we provided automatically publishes the transform of the robot, which allows us to easily converta point in world space to robot space. Once the point is in robot space, we can get the relative x and y offset from the robot’s position to the point.To access ROS2’s transform system, you must create a tf_buffer and tf_listener using the following syntax:\n// In header file \nstd::unique_ptr tf_buffer_;\nstd::shared_ptr tf_listener_;\n \n// In node constructor\ntf_buffer_ = std::make_unique(this->get_clock());\ntf_listener_ = std::make_shared(*tf_buffer_);\nThen, to access the transform between two frames, use the following syntax:\ngeometry_msgs::msg::TransformStamped transform;\ntry {\n transform = tf_buffer_->lookupTransform(\"TO_FRAME\", \"FROM_FRAME\", tf2::TimePointZero);\n} catch (const tf2::TransformException & ex) {\n RCLCPP_INFO(this->get_logger(), \"Could not transform %s\", ex.what());\n}\nWhere “TO_FRAME” is the frame you want to transform the point into, and “FROM_FRAME” is the frame the point originated in.Then to transform a point using that frame:\nauto transformed_point = geometry_msgs::msg::PointStamped();\n tf2::doTransform(original_point, transformed_point, transform);\nWhere original_point is the point in the FROM_FRAME and transformed_point is that same point in the TO_FRAME.We will be leveraging this code to transform our global point published to /goal_point into the robot’s frame. The goal_point will be in the frame “sim_world” (which is our global frame in this case) and the robot’s frame is “robot”.\nDeliverable 6.2 Create a tf_buffer and tf_listener in your control node. Then, in the control callback, get the transform from “sim_world” to “robot” and transform the goal_point to the robot frame. You may need to store goal_point in an instance variable in the Node class to access it in the timer callback after it has been received from the subscriber.\nNow that the point is transformed, the x coordinate relates to the robot’s forward motion while the y coordinate relates to the robot’s side to side motion (angular). Therefore, an easy proportional control loop may be derived where the linear command is proportional to the X component and the angular command is proportional to the Y component.\nDeliverable 6.3 Create two variables called Kp_linear and Kp_angular which will be the scalars for the proportional loop, initialize them to 0.5 as a good starting guess (these can be tuned later). Multiply Kp_linear by the X component of the goal_point transformed in the robot frame and Kp_angular by the Y component of the goal_point transformed in the robot frame. These will be your control signals to send to the robot.\nNow that you have the control signals, you must publish them to the robot. By default, the robot takes in control signals in the form of a geometry_msgs::msg::Twist message, which has a linear and angular velocity associated with it. The simulated robot is setup to receive Twist messages in the “/cmd_vel” topic, so you can directly send commands to the robot by creating a publisher in the control node that publishes geometry_msgs::msg::Twist messages to the “/cmd_vel” topic. Once the subscriber is made, you can then package up your linear and angular velocity commands inside of a Twist message (linear velocity will use the x component of the linear Vector3 of Twist and angular velocity will use the z component of the angular Vector3 of Twist. The rest will remain 0) and publish to the robot to control it!\nDeliverable 6.4 Create a publisher that publishes geometry_msgs::msg::Twist messages to the “/cmd_vel” topic. In your control timer, create a new Twist message, set the linear x component to the result of the linear proportional loop and the angular z component to the result of the angular proportional loop. Then, your robot should drive towards the goal_point message when you publish in Foxglove!","conclusion#Conclusion":"Congratulations! You have now successfully controlled a robot using ROS2! The fundamentals of publishers, subscribers, and a few tricks you learned like Foxglove and transforms will be extremely valuable in your development in the WATO ASD software stack.Please contact the Director of ASD (Eddy Zhou) on discord, showing proof of completion and 2 subteams you are interested in joining. Link to subteams here.\nWelcome to WATonomous! :))"}}} \ No newline at end of file +{"/about":{"title":"About WATonomous","data":{"":"WATonomous is the autonomous vehicle design team at the University of Waterloo. We are an agile group of developers, engineers, businessmen, and marketers looking to lead the next generation of robotic systems and their applications to society.","our-big-audacious-goal#Our Big Audacious Goal":"Members of WATonomous center around a singular goal:\nTo show the world a bunch of students can build a self-driving car!\n... and in doing so, drive ourselves to become better people :)."}},"/finance":{"title":"Finance System","data":{"":"Instructions on how to use the WATonomous Finance System, which is used to track funding, our account balances, and manage and reimburse purhcases.Creating and Submitting a Personal Purchase Request Contains details for members on how to purchase items using their own money and get reimbursed for it. (go-to)Creating and Submitting a Purchase Request Contains details for members on how to purchase items using WATonomous cash or directly from one of our funds. (go-to)"}},"/finance/creating_personal_purchases":{"title":"Creating Personal Purchases","data":{"":"Go to https://finance-frontend.watonomous.ca/\nCreate New Ticket > Ticket Type: Personal Purchase\nEnter all purchase details. For funding item link, you would either be using FI-1 (WATO Cash) or one of the Funding Items that match the category. For example, if you're purchasing an RTX 4090 it would probably fit under GPU funding.\nAwait team approval. Please reach out to the finance team and the faculty advisor.\nOnce this has been completed, the status will transition to READY_FOR_BUY. You should purchase the item at this step.\nOnce the item has been purchased, please upload the University of Waterloo Finance Expense Claim Form. Instructions found below.\nClick Confirm Item(s) Purchased and Submit Reimbursement","creating-expense-claim-forms#Creating Expense Claim Forms":"Navigate here\nClick the link in step 1 titled \"reimbursement form (excel)\"\nEnter the following fields\nPayee is Student\nClaimant Name, Student Number, Department, Phone Number, E-mail Address\nMailing Address, City, Province/State, Postal Code. For these, use the address your items were shipped to, or your personal address.\nDestination/Reason for Request: Please put the corresponding reference number. For WEEF, this can be found here. Reach out to @smileycow on Discord if you do not know what this is\nIgnore the Travel Advance Request section, and fill in your item details on the next section. For the description, please put the name of your item, and if available, the reason it was purchased.\nHas an advance been issued: No\nEnter your signature of Claimant\nFollow the rest of the instructions on the original page (steps 2 to 4)\nUpload the receipt to the corresponding personal purchase at https://finance-frontend.watonomous.ca/"}},"/finance/creating_purchase_requests":{"title":"Creating Purchase Requests","data":{"":"Go to https://finance-frontend.watonomous.ca/\nCreate New Ticket > Ticket Type: UW Finance Purchase\nEnter all purchase details. For funding item link, you would either be using FI-1 (WATO Cash) or one of the Funding Items that match the category. For example, if you're purchasing an RTX 4090 it would probably fit under GPU funding.\nAwait team approval. Please reach out to the finance team and the faculty advisor.\nOnce this has been completed, the item will be sent to the finance coordinator. They will purchase the item, in which case the status will be ORDERED\nOnce the item has arrived, the item will be transitioned to READY_FOR_PICKUP, and the pick up instructions will give you next steps\nOnce completed, the item should be in the PICKED_UP state, completing the flow."}},"/":{"title":"Welcome to the Wiki","data":{"":"Welcome to the WATonomous Wiki! This is an open-source wiki for all things WATonomous.","new-here#New Here?":"","how-to-edit#How to Edit":"Editing the WATonomous wiki is easy. Simply click Edit this page on the far right of each page. Note, you must be part of the WATonomous Organization to be able to edit.You can use regular react components, or choose from a plethora of pre-built components and tools here."}},"/onboarding":{"title":"Onboarding","data":{"":"WATonomous onboarding guides. The following are quick jists of what each onboarding guide is for:ASD - Developing with WATcloud Contains setup steps for getting access to WATcloud as well as how to use WATcloud in the Autonomous Software Division. (go-to)ASD - General Onboarding Contains a walk-through that teaches you the basics of ROS2, Docker, Docker Compose, and the rest of our robotics software stack. (go-to)"}},"/onboarding/asd_watcloud_dev":{"title":"Developing with WATcloud","data":{"":"You must complete your Cluster Access Form before proceeding with this guide.\nHere, we discuss setting up WATcloud to be used for software development in the Autonomous Software Division.","why-watcloud-what-is-watcloud#Why WATcloud? What is WATcloud?":"Due to the high computational requirements of many aspects of the ASD stack, WATO has a large server infrastructure for remote development WATcloud. In this section, you will learn to connect to WATcloud on VS code. Connecting to a server to do remote development is not only a crucial aspect of software development at WATonomous, but is also a very common practice in the industry.\nFun Fact: WATcloud closely mimics server infrastructures used by OpenAI, NASA, Nvidia, and more!","a-look-from-afar#A Look from Afar":"","how-does-watcloud-share-compute-resources-fairly#How does WATcloud share compute resources fairly?":"WATcloud relies heavily on a resource management tool known as SLURM. SLURM ensures that all resources in WATcloud are shared in a fair and well-managed manner.For the everyday developer, you can imagine SLURM as a \"build your own computer\" tool. You specify to SLURM what compute resources you want (CPU, RAM, GPU, memory, time limit, etc.) and SLURM will build a compute node with the resources it has on hand.","so-how-does-remote-development-actually-work#So how does remote development actually work?":"Remote development for a WATonomous member typically consists of a local machine, host machine, SLURM node, and a docker container. They are defined below:\nLocal Machine Your personal computer.\nHost Machine The computer you connect to. In the case of WATcloud, this is the SLURM login node.\nSLURM Node Used to manage compute resources. It creates SLURM Jobs according to your needs.\nSLURM Job An \"imaginary computer\" that is created by WATcloud. You specify to WATcloud what compute you need by running commands in the SLURM login node.\nDocker Container An isolated coding environment.\nTo do remote development in the Autonomous Software Division, the process can be summed up by the image below:\nAs shown in the image, there are two ways to use a SLURM node.","job-scheduling-vs-interactive-development#Job Scheduling vs Interactive Development":"Use job scheduling when you want to run a command for a very long time (>1 day long). Use interactive development when you are actively making changes to your code and testing it.For most WATonomous members, you would use job scheduling for tasks like training neural networks, large data processing, numerical optimization, etc. On the other hand, you would use interactive development when you are coding/testing ROS2 nodes, interacting with / visualizing live data, making code changes in general, etc.","setting-up-watcloud-for-asd#Setting up WATcloud for ASD":"This section is experimental. Please let us know of any issues on our Discord\nDealing with SSH can be quite foreign to alot of new developers. Thankfully, we provide a series of helper scripts that will make setup for WATcloud easier on you.","general-setup#General Setup":"This section is required so that you have proper access to our server cluster.","local-machine-clone-the-wato_asd_tooling-repository#[Local Machine] Clone the wato_asd_tooling repository":"git clone git@github.com:WATonomous/wato_asd_tooling.git","local-machine-generate-an-ssh-config#[Local Machine] Generate an SSH config":"If you have never created an ~/.ssh/config file before, do that now. Note, we assume that all your SSH files are stored under ~/.ssh\ntouch ~/.ssh/config\nGenerate a WATcloud SSH config. Follow the prompts whenever you get them.\ncd wato_asd_tooling\nbash ssh_helpers/generate_ssh_config.sh\nYou should now be able to connect our cluster using these commands:\nssh tr-ubuntu3\nssh derek3-ubuntu2\nssh delta-ubuntu2","local-machine-setup-vscode-for-ssh#[Local Machine] Setup VScode for SSH":"To do this, download the Remote - SSH VScode Extension. After that, you should be able to attach VScode to any of the machines.","local-machine-setup-agent-forwarding#[Local Machine] Setup Agent Forwarding":"Agent forwarding lets us carry our identity onto other machines that we connect to. What this means is, you can use git commands on other machines without having to create an SSH key on each machine you connect to.Setup agent forwarding with our helper script. Follow the prompts whenever you get them.\ncd wato_asd_tooling\nbash ssh_helpers/configure_agent_forwarding.sh","host-machine-confirm-agent-forwarding-works#[Host Machine] Confirm Agent Forwarding Works":"You should now be able to use git on all the WATcloud machines you connect to. Confirm by running the following inside a WATcloud machine you connected to.\nssh -T git@github.com\nDeliverable Get SSH and SSH Agent Forwarding working.","setup-for-interactive-development#Setup for Interactive Development":"Unlike job scheduling, SLURM was not built to handle interactive development. Luckily we have a team of very talented individuals, and we managed to make interactive development work nonetheless :).Creating an interactive development environment entails starting an SSH server inside the SLURM node, some wacky SSH key sharing, a netcast proxycommand, as well as pointing docker to a persistent filesystem. You don't have to do that though. You just need to do the following.","one-time-setup#One-time Setup":"Follow these steps if you are setting up the SLURM dev sessions for the first time, or you were using past solutions for SLURM that WATO provided.","host-machine-add-your-public-key-into-the-slurm-nodes-authorized-keys#[Host Machine] Add your public key into the SLURM node's authorized keys":"Copy your public key on your local computer and paste it into the authorized_keys file on the SLURM node.\nyour_local_machine$ cat ~/.ssh/your_key.pub (copy the output of this)\nyour_local_machine$ ssh [tr-ubuntu3 or derek3-ubuntu2]\nwatcloud_slurm_node$ touch ~/.ssh/authorized_keys\nwatcloud_slurm_node$ nano ~/.ssh/authorized_keys (paste you rpublic key into here)","host-machine-add-the-following-to-your-bashrc-in-your-slurm-node#[Host Machine] Add the following to your bashrc in your SLURM node":"watcloud_slurm_node$ nano ~/.bashrc\nAdd the following to the end of your ~/.bashrc file.\nif [[ \"$(hostname)\" == *\"slurm\"* ]]; then \n export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/tmp/run}\n export XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-/tmp/config}\n export DOCKER_HOST=\"unix://${XDG_RUNTIME_DIR}/docker.sock\"\nfi","local-machine-build-the-computer-you-desire#[Local Machine] Build the Computer you desire!":"Use your favorite text editor to edit wato_asd_tooling/session_config.sh.\ncd wato_asd_tooling\nnano session_config.sh\nThe file itself contains descriptions of all of the parameters and how to set them.","local-machine-start-a-slurm-dev-session#[Local Machine] Start a SLURM Dev Session":"Run the helper script to startup a SLURM Dev Node. Follow all the prompts carefully.\ncd wato_asd_tooling\nbash start_interactive_session.sh\nDO NOT START MORE THAN ONE DEV NODE. You have a chance of corrupting your docker filesystem. Starting more than one dev node is like building multiple computers. It is NOT the same as creating multiple terminals.","local-machine-setup-ssh-for-slurm#[Local Machine] Setup SSH for SLURM":"Run this last helper script LOCALLY. Follow the prompts carefully.\ncd wato_asd_tooling\nbash ssh_helpers/setup_slurm_ssh.sh","local-machine-stop-the-slurm-dev-session#[Local Machine] Stop the SLURM Dev Session":"Stop the interactive session by hitting Ctrl+C.","starting-a-slurm-dev-session-regularly#Starting a SLURM Dev Session Regularly":"Once you have done the one-time setup, connecting to a SLURM Dev Session is easy.","local-machineoptional-build-the-computer-you-desire#[Local Machine][Optional] Build the Computer you desire!":"Use your favorite text editor to edit wato_asd_tooling/session_config.sh.\ncd wato_asd_tooling\nnano session_config.sh","local-machine-start-a-slurm-dev-session-1#[Local Machine] Start a SLURM Dev Session":"Run the helper script to startup a SLURM Dev Node. Follow all the prompts carefully.\ncd wato_asd_tooling\nbash start_interactive_session.sh","local-machine-connect-to-the-slurm-dev-session-with-vscode#[Local Machine] Connect to the SLURM Dev Session with VScode":"You can connect to the SLURM Dev Session using the VScode ssh extension. The remote host is called asd-dev-session by default.\nAnd you're good to go! Whenever you want to startup a SLURM Dev Node, start one up by running start_interactive_session.sh, and then SSH into the SLURM node through VScode.","setup-for-job-scheduling#Setup for Job Scheduling":"There is no setup. Creating an SLURM job is really easy. It was what SLURM was designed for. You can view docs on SLURM in the WATcloud documentation.\nDeliverable Run a SLURM batch job with 2 CPUs that counts to 60."}},"/onboarding/vp_general_onboard":{"title":"WATO-VP Onboarding Document","data":{"":"Last modified: September 11th, 2024","what-is-watonomous-short-form-wato#What is WATonomous? (Short form: WATO)":"Started in summer 2017, WATonomous is the University of Waterloo's first student-run autonomous car team. We are an agile group of developers, engineers, and businessmen looking to lead the next generation of robotic systems and their applications. Ultimately, we just want to prove that you don't need a room full of current FAANG employees to build a self-driving car! All you need for a self-driving car is a self-driven individual ;)","past-achievements#Past Achievements":"Up till 2023, we had a Chevrolet, and we are proud to have finished 2nd place in the SAE AutoDrive Challenge, an international competition to build a Level 4 autonomous vehicle in four years!Following the competition, we continued our path towards perfect autonomy, completing research papers on various autonomous vehicle technologies including action classification, control, and environment modeling. We have a track record of submitting to prestigious research conferences like ICRA, ICVES, and ITS.","current-plan#Current Plan":"We have a brand new car, a Kia Soul EV! We are planning on reaching level 5 autonomy all from scratch! Progress has been made over the past year, and we are aiming to be able to test track it by the end of summer 2025!","impact-goals#Impact, Goals":"We want to prove that a group of dedicated students, and NOT industry professionals, can work together and create a self-driving automotive system that can compete with the likes of the WAYMO taxi and Google Self-Driving Car.For the end of the Fall 2024 term, we hope to have the majority of our vehicle platforming work completed and to have the car (along with its peripheral systems) operate under its own power. We are also expecting a substantial computing power upgrade to the onboard systems that we hope to have ready by the Winter 2024 term.","a-bit-about-vehicle-platform#A Bit About Vehicle Platform...":"Directors: Xander Hayhoe, Mahir Mahota","interfacing#Interfacing":"Leads: Mariam ElSahhar\nCurrent Projects:\nMost components on the interfacing PCBs are soldered. We do need to add connectors and completely resolder a whole new board.\nBoards and all assembled hardware logic need to be thoroughly tested (based on predetermined test parameters) before implementation into the car.\nInstall DBW system with the joysticking tele-drive module for user overriding.\nMigrate EXISTING Joystick controller code from ROS1 to ROS2.\nImplement arbiter and MUX logic for the vehicle architecture, signals control.\nRun final tests of the ass","power-systems#Power systems":"Leads: Benjamin Liu\nCurrent Projects:\nFor the end of October, we hope to have the car’s sensor rack and compute module running off of the internal 12V battery. In the car’s current state, these systems along with other peripherals run off of external power sources such as a wall outlet. To reach a minimal viable product, we need this crucial step to be completed.","onboarding-challenges#Onboarding Challenges:":"Interfacing Challenge: Do the ASD Assignment, as it uses the ROS2 Stack, which VP will do a lot of work with! WATO ASD Assignment\nNote: Instead of sending proof of completion to Eddy, please send the proof to Xander; Discord: @Xander1452.\nElectrical Challenge: Take the Electrical Safety and Awareness course offered on LEARN to all Waterloo students (self-enrollment)."}},"/onboarding/asd_general_onboarding":{"title":"General Autonomous Software Onboarding - ASD Assignment","data":{"":"You must complete ASD - Developing with WATcloud before proceeding with this guide.","introduction#Introduction":"Congratulations! WATonomous has recognized your talent, and we would like to get you ready to contribute to the team.The Autonomous Software Division (ASD) assignment is designed to train a newcomer robotics programing at WATonomous from the ground up.The onboarding assignment consists of the following:\nAccessing the WATonomous Server Cluster (WATcloud)\nGetting used to ROS2 - Docker robotics infrastructure\nWriting your own control program to direct a robot in simulation\nShould you finish this assignment, you will have all the skills necessary to contribute towards building a real autonomous vehicle. Note, this assignment will not cover ML concepts. However, ML is used throughout robot autonomy, so we highly recommend you learn ML on your own time. Here’s some ML resources for you (DO NOT SHARE).\nAfter completing this assignment, you will become an official member of WATonomous with a dedicated watonomous email.\nPlease contact the Director of ASD (Eddy Zhou) on discord, showing proof of completion and two subteams you are interested in joining. Link to subteams here.The ASD leads are here to help so feel free to ask any questions about the onboarding assignment in the asd-questions channel on discord. This document is pretty long, but rest assured that a large majority of the assignment is a gigantic walk through.Good Luck!","server-access#Server Access":"Server access steps have been moved to here. Please let the WATcloud leads know if you have any issues.Once you've familiarized yourself\nDeliverable 1.0 Create a custom SLURM interactive development job. The SLURM job must have VRAM. You can specify the job to run for 2 hours so that you don't get kicked out while you are developing.\nDeliverable 2.0 Connect VS Code to a WATcloud SLURM job. Clone the ASD Training Repository into your WATcloud user’s home directory (~/). Open your cloned repository in VS Code. If you aren’t familiar with git, here’s a cheatsheet.\nDeliverable 2.0.1 STAR THE WATO_MONOREPO. It’s voluntary, but it will help us legitimize the repository overtime :).\nDeliverable 2.1 In the opened ASD Training Repo, create a new branch titled _training.","asd-training-repository#ASD Training Repository":"In the previous section, you would have cloned the ASD Training Repository into your user’s home directory on the server. Please keep this folder open as you read.The repository you just cloned contains a barebones setup of WATonomous’ current ASD infrastructure. It closely mirrors the infrastructure of the wato_monorepo, which is the central repository for all of the code that goes into the car. You can read up on the reasoning behind having a monorepo here.","our-tech-stack#Our Tech stack":"Our autonomy stack contains a multitude of coding tools and libraries (PyTorch, TensorFlow, OpenCV, Numpy, Scikit, Foxglove, Gymnasium, etc.), but the three MOST-PROMINENT, REOCCURING TOOLS you must know are ROS2, Docker, and Docker Compose.These software tools enable cool AI tools and algorithms to communicate properly in a robotic system. Permutations of these open source tools are used throughout the cutting-edge robotics industry. It is especially prevalent in R&D, where new tools need to be integrated and tested at an alarming rate.","ros2#ROS2":"The Robotics Operating System 2 (ROS2) is the second iteration of open source tools and libraries used for quickly building robot applications. It helps us intuitively do interprocess communication without the need to dig extremely deep into low-level programming. ROS2 is the monorepo’s main communication interface.\nDeliverable 3.0 Identify locations in the code where ROS2 is being used.","docker#Docker":"For our ASD stack, we use Docker to containerize all of our code. Docker can be thought of as a lightweight virtual machine, allowing us to create separate environments (containers) for running code. This makes our codebase portable and modular.Docker uses docker files (.Dockerfile extension) to configure and set up each container. Dockerfiles generally start with a machine base, and commands are specified to setup the machine, install dependencies, and setup the code workspace. Here is an example dockerfile from the wato_monorepo.Generally the Dockerfiles for your modules will be set up already by your team leads. However, Docker is still a very useful tool to learn, and you can reference the getting starteddocumentation for more details if interested. Those who can understand our monorepo down to the docker-level are much valued :).\nDeliverable 3.1.0 Identify locations in the code where Docker is being used.\nDeliverable 3.1.1 What is the difference between a Dockerfile, a Docker Image, and a Docker Container? Refer to online references.","docker-compose-and-wato#Docker Compose and wato":"Docker Compose is a utility for managing a codebase with multiple docker components. It provides an intuitive yaml interface to configure, build, and run docker containers. With a single Docker Compose command, you can startup multiple containers at once.In our ASD training repo, and our monorepo, you will make great use of a software tool known as ‘watod’. Under the hood, watod is a wrapper around Docker Compose. You can use watod just like how you would use Docker Compose.\nDeliverable 3.2.0 Identify locations where Docker Compose is being used. What does WATO call these Docker Compose files? Hint: directory name\nDeliverable 3.2.1 Identify environment variables in a docker-compose file (Hint: they start with a $). How are these environment variables set during runtime? The next question can clear up this question.\nDeliverable 3.2.2 Identify any files prefixed with ‘watod’. Take a closer look at ‘watod-setup-env.sh’, what is it doing? What script do you run to run ‘watod-setup-env.sh’? How does this relate to the environment variables found in the docker-compose file?\nDeliverable 3.2.3 In your own words, explain what watod does. How does it interface with our docker-compose files?","whats-up-with-the-long-image-names#What's up with the long image names?":"You may have noticed docker image names along the lines of “git.uwaterloo.ca:5050/watonomous/wato_monorepo/…”. This long image name refers to a docker image stored in a Docker Registry.Building docker images from scratch can take an extremely long time. So our team takes advantage of Docker Registries to quickly pull pre-built images. This saves us a lot of time.In the ASD Training Assignment, docker registries are disabled. This is why you see the manifest error when building. No fret, we disabled registries on purpose. In the actual wato_monorepo, you’ll have to learn how to use registries.","a-visual-understanding#A Visual Understanding":"Below is a possible way to visualize how our ASD infrastructure works:The entire system communicates within itself using ROS2 messages. All the cool algorithms for perception, planning, control, etc. are wrapped in their own ROS2 nodes to function concurrently. Various nodes share docker containers if they require the same libraries and tools to function (eg. two nodes may require the same version of PyTorch, so they share the same docker container).\nDeliverable 3.2.3 In your own words, describe an analogy to real life that closely resembles the ASD infrastructure.","on-startup#On startup":"The visualization of the tech stack above represents our code in a running state. On startup, watod (docker-compose under the hood) is used to orchestrate the startup of each and every docker container, ROS2 node, and core algorithms. It carefully and automatically builds out the entire software architecture piece by piece until you end up with the visualization above.","training-overview#Training overview":"Now that you have a decent understanding of our tech stack. We can now move on to the final part of the ASD training. This part will be the most time consuming, and it will also contain an extremely limited amount of hand holding. Our team is here to help, so if you have any questions, feel free to ask on Discord!The provided training repository contains the following:\nA Gazebo server, which is a robotics simulator allowing us to interact with and write code for a virtual robot\nA Foxglove websocket, which streams ROS data over a websocket to be viewed using the Foxglove ROS visualization dashboard.\nROS2, Docker, and WATO backend infrastructure.\nSample ROS2 nodes in C++ and Python that demonstrate custom message passing.\nA barebones ROS2 node which you will write your code in.\nThe end result of the training will be some custom-written ROS2 nodes which will interact with various sensors on the robot, perform some computation/processing, and output commands to control the robot. All of this will be done using ROS2 as a framework.","set-up#Set up":"In previous sections, we made you analyze the ASD Training code. Now is the time to learn how to use it.","watod-orchestration#Watod Orchestration":"As you may or may not know, watod is used to orchestrate our entire tech stack together. It sets any necessary environment variables that our docker compose profiles would need on startup, and then calls the docker compose command to begin building all the docker containers.Oftentimes, you don’t need to run the entire software pipeline to begin development, so you may only need to run a minimum slice of the software pipeline to begin. The powerful thing about watod is that you can configure what profiles (docker-compose files) to start up. This equates to only starting only a portion of the entire stack as opposed to starting up the whole thing.","watod-configsh#watod-config.sh":"To configure what profiles to startup, we use watod-config.sh Specifically the ACTIVE_PROFILES field.\n## ----------------------- Watod2 Configuration File Override ----------------------------\n## ACTIVE PROFILES CONFIGURATION\n## List of active profiles to run, defined in docker-compose.yaml.\n##\n## Possible values:\n## - vis_tools : starts tools for data visualization (foxglove)\n## - gazebo : starts robot simulator (gazebo)\n## - samples : starts up sample nodes for reference (optional)\n## - robot : starts up robot nodes\nACTIVE_PROFILES=\"vis_tools gazebo samples\"\n## Name to append to docker containers. DEFAULT = \n# COMPOSE_PROJECT_NAME=\"\"\n## Tag to use. Images are formatted as : with forward slashes replaced with dashes.\n## DEFAULT = \n# TAG=\"\"\nYour watod-config.sh would have ACTIVE_PROFILES commented out, so please populate this variable just like above. vis_tools, gazebo, and samples are all profiles.\nNOTE: Once you are a full ASD member, please leave the watod-config.sh unchanged when you make a Pull Request.","watod-up#./watod up":"After you’ve changed your watod-config.sh, simply startup the training stack by entering./watod upin your command line while inside the ./wato_asd_training/ directory. You should then see docker compose building all the Docker images present in each of the profiles you specified. You could startup each of the three profiles alone if you wanted to. Use Ctrl + C to stop watod.\nDeliverable 4.0 Startup all three profiles in the training repo (vis_tools, gazebo, and samples). After you’ve done that, stop watod and startup each profile alone. Do this again with two of the three profiles.","dev-containers#Dev containers":"Dev containers are a very important aspect of WATonomous’ development cycle. When you run ./watod up, you spin up multiple containers that you can develop in. Inside these containers, you can change code, debug, and develop to your heart’s content, and all changes made inside the dev container will propagate out to your host machine.The typical WATonomous development cycle looks like this:","up-a-container#Up a container":"Up a container using ./watod up","enter-the-container#Enter the container":"Enter the container using VScode Docker extension","make-changes-inside-the-container#Make changes inside the container":"Make changes inside the container and debug with ros2 tools\nChanges in the container automatically propagate out (and vice versa)","git-add-commit-and-push#Git add, commit, and push!":"Outside the container, Git add, commit, and push\nThe main benefit to dev containers is that you can use linters and tools installed inside the container. For example, you cannot install numpy in the WATcloud host, but you can install numpy in a container and play around with it inside that container.\nDeliverable 4.0.1 Enter the transformer container and change its BUFFER_CAPACITY.\nDeliverable 4.0.2 Rebuild the transformer inside the container using colcon build under the ~/ament_ws directory. Source your changes with source install/setup.bash.\nDeliverable 4.0.3 Launch the transformer node with ros2 launch transformer transformer.launch.py. You should notice your transformer buffer fill up faster or slower based on the new BUFFER_CAPACITY you specified.\nDeliverable 4.0.4 Outside the container, check that your changes to the transformer have propagated out using git status. You should see something like the following…\nYou can also to ./watod up outside your container, and the changes you made inside the container should reflect in the robot logs.\nDeliverable 4.0.5 BONUS when doing ./watod up, the transformer node spins up automatically. Can you think of a way to not make the node spin up? Hint: you need to stop the node from launching, but you have to keep the container running for an indefinite period.","foxglove#Foxglove":"Foxglove is an open source data visualization tool for robotics. Founded by a couple of folks at Cruise, Foxglove’s main advantage is its ability to visualize server-side data.You may have heard of RViz, which is ROS’s native visualization software. As powerful as this tool can be, RViz is not great for server-side development. In order to use RViz on our server, you’d have to spin up a VNC docker container. This is what our team has done in the past, but it was terrible. RViz would run at no faster than 3fps.","port-forwarding#Port Forwarding":"In a nutshell, Port Forwarding establishes a connection between a communication endpoint on one device in a private network to another device on another network. We use Port Forwarding to view Graphic User Interfaces (GUIs) and stream data to your local machine. The world of networking is scary, so we will try to make it as straightforward as possible.A recurring pattern for Port Forwarding is: “software A is streaming data out of some port ####, I want to read this data myself, so I will forward port #### to my machine to read it”. If software A is in a Docker container on a server host machine, then you have to forward the port from the Docker container to the host machine, and then from the host machine to your local machine.You don’t have to do much port forwarding yourself, but it’s good to know the gist of it as we move forward.","using-foxglove#Using Foxglove":"Make sure you have the gazebo and vis_tools profiles running\nHave some sort of web browser on your local machine\nHave the asd_training_config downloaded on your local machine (this is in the training repo)\nOnce you have the two profiles running, you should notice an auto-forwarded port in your VS Code.\nThis port is where foxglove is streaming its data from. You don’t have to do any forwarding yourself, as we’ve done it for you ;P.To view this data, go to the Foxglove website, create an account (with your personal email, we need to hash out sponsorship from Foxglove before you can use your watonomous email), and either visualize the data through your web browser or a local download of Foxglove.","viewing-ros-data-through-foxglove#Viewing ROS data through foxglove":"To view the data coming from the forwarded port, in the Foxglove dashboard click Visualize Data -> Open Connection -> Foxglove Websocket and set it as the port that was forwarded. In the example above, that would be ws://localhost:33401. It will be different for everyone.\nYou can now proceed to open your Foxglove Studio to begin viewing the robot simulation.\nDeliverable 4.1 Up the ASD Training Repo and view your robot simulation in Foxglove on your local machine.\nYour Foxglove Studio should look something like this. Don’t worry if it looks slightly different. We will fix that now. In the ASD Training Repo, we’ve provided a configuration file that sets up your Foxglove Studio to look exactly like how we want it to look. If you haven’t yet, download the configuration file from the repo.You then need to load this file into your local Foxglove Studio.Reload your Foxglove, and you should see something like the following.\nDeliverable 4.2 Upload the ASD Foxglove config to your local foxglove.\nDeliverable 4.3 Interact with Foxglove Studio. You can move around the robot with the Teleop Panel. Here are a couple of subtasks to get you acquainted.\nDeliverable 4.3.1\nDeliverable 4.3.2 BONUS: View the list of Topics. What is a Topic? This can be a great opportunity to get an initial idea of ROS Topics.\nDeliverable 4.3.2 BONUS: The visualized data is, in-reality, a bunch of numbers. Foxglove (and RViz) do some post processing to make it look intuitive. View the /lidar topic’s raw data through a new Raw Message Panel.","hands-on-training#Hands-on Training":"Now that your development environment is set up, we can begin hands-on training. You can always refer back to the previous steps for reference. Kind reminder that our team is here to help, so if you ever have any questions, please feel free to ask in the ASD-Questions channel :).Originally, the intent of this training was to code up a semi-complete navigation system to make the robot rather-intelligently navigate to a destination of your choice. However, for the sake of not blowing up your mind, we have trimmed the assignment down to at least touch all the fundamental concepts we would like you to learn. If you would like to do the whole assignment, feel free to reach out, and we can give you more info on how the robot should navigate the map.","the-basis-of-robot-navigation#The Basis of Robot navigation":"Robot navigation is a very large field of study, so let’s be more specific. The navigation problem we are most concerned with here at WATonomous is: given an end position, and no prior knowledge of the environment, navigate to that end position.In Foxglove Studio, you should see an environment that closely relates to the problem above.\nHere, the end position can be specified by the user to be anywhere on the map.In an ideal environment, where obstacles don’t move, robot navigation can be quite simple. A naive approach to this problem could be: Occupancy, Immediate Trajectory, and Execution. We provide a brief description of them below:Occupancy: A map of squares indicating where the robot can and cannot go.Immediate Trajectory: The planned trajectory to take to get to the end position.Execution: Following the planned trajectory as best as we can.In this system, the occupancy around the robot is found continuously. Given this geometric representation, we can produce a trajectory using the Immediate Trajectory planner. This trajectory is then Executed for a period of time. The whole process repeats continuously until an endpoint is reached.This is not much different to the real autonomy stack of the car. A higher fidelity Occupancy is found using Perception. Immediate trajectories need to factor in the complexity of the road with Navigation. And we mist execute on this trajectory with Control.To formalize, Perception would receive sensor messages and do processing. Navigation would receive the processed sensor data and figure out a set of actions to take. Control would receive the actions and perform them, finally sending commands to the motor. In fact, WATonomous’s software stack follows a very similar architecture, but with more node subdivision within each group to perform more complex computations.","goal#Goal":"The Foxglove simulation consists of a robot within an environment. The robot has three wheels, two motors, a camera, and a 1-dimensional laser scanner.You can freely control the robot’s movement within the environment using the Foxglove Teleop panel.As you may have noticed, the Teleop control of the robot is pretty terrible. It oversteers, stops late, and has a ton of inertia. What if we could simply instruct the robot to move to a specific point on the map instead?The goal of your hands-on training is to do just that! By specifying a goal on the foxglove map, your robot should naively navigate to that point on the map.\nNote: It is important to know that the node you are about to create does not allow the robot to avoid obstacles. It will simply follow the straight-line trajectory to the point you specified.\nAs naive as this sounds, this is actually the basis of robot control. You will see later how this simple control algorithm you’ll make is used with the rest of the navigation stack to achieve obstacle avoidance!","ros#ROS":"Robot Operating System (ROS) is a popular framework for robotics codebases. At its core, it is a message broker – a system which manages and passes data between programs. This allows for modularity, management of complex data types, and interoperability. What makes ROS so powerful is the ability to easily interface libraries and tools that were built on top of ROS to bring advanced capabilities and interactive visualizations to your software stack. Some examples that we will be using are:\nFoxglove, a web-based ROS dashboard for visualizing data and interacting with the robot\nGeometry messages, a ROS package which includes a lot of standard geometry data types like Pose, Point, Twist, Quaternion, etc.\nTransforms 2 (Tf2), a ROS package which allows easy transformations between coordinate frames\nThe basic building block of the ROS framework are nodes. Nodes are responsible for performing a specific set of tasks, and interact with other nodes by receiving (subscribing) or sending out (publishing) messages. You can think of ROS like a network, with many nodes interacting with each other through ROS to achieve the overall function of the software stack.On the technical side, ROS nodes are just c++ or python programs with the ROS library. The ROS framework then manages the building, running, and message passing of each node behind the scenes. The ROS library provides the syntax for creating a node, subscribing to messages, sending out messages, and native objects for ROS messages.\nNote: This document will give a quick overview of ROS2 code, however we highly recommend going through the ROS2 Getting Started documentation which is much more detailed and explains ROS2 from start to finish. There may be a steep learning curve, so if you are confused throughout these training modules, feel free to ask the team leads for an explanation on discord!\nHere is an example ROS node in C++:\n#include \"transformer_node.hpp\"\nExampleNode::ExampleNode() : Node(\"example_node\"){\n // Subscribe to a topic\n subscription_ = this->create_subscription(\n \"/example_topic0\", 20, \nstd::bind(&ExampleNode::subscription_callback, this, \nstd::placeholders::_1));\n // Publish to a topic\n publisher_ =\n this->create_publisher(\"/example_topic1\", 20);\n}\n// Subscription callback: gets called when a message from the subscription comes in\nvoid ExampleNode::subscription_callback(const geometry_msgs::msg::Pose::SharedPtr msg)\n{\n\t// Print to the console\nRCLCPP_INFO(this->get_logger(), \"Received message…\");\n\t…\n\t// Publish a message\n \tpublisher_->publish(msg);\n}\n// Boilerplate code for starting up the node (gets called by ROS framework)\nint main(int argc, char ** argv)\n{\n rclcpp::init(argc, argv);\n rclcpp::spin(std::make_shared());\n rclcpp::shutdown();\n return 0;\n}\nLet’s break it down.First, we have the constructor of our node, which inherits the Node class from the ROS library (rclcpp). There we pass the name of our node, “example_node”.\nExampleNode::ExampleNode() : Node(\"example_node\"){\n …\n}","publisher#Publisher":"Inside the constructor, we initialize our publishers and subscribers. Here is the syntax to create a publisher with the rclcpp library:\n// In header file:\nrclcpp::Publisher::SharedPtr publisher_;\n// In node constructor:\npublisher_ = this->create_publisher(\"/TOPIC\", 20);\nWhere MESSAGE_TYPE is the message you are publishing (i.e. geometry_msgs::msg::Pose for a Pose message) and “/TOPIC” is the topic you are publishing to. The 20 at the end is the buffer size, 20 is a good number for that.\nYou can then publish a message to the publisher using:\npublisher_->publish(msg);\nWhere msg is an object of the type you specified (i.e. geometry_msgs::msg::Pose()).","subscriber#Subscriber":"Next, here is the syntax to create a subscriber:\n// In header file:\nrclcpp::Subscription::SharedPtr subscriber_;\n// In node constructor:\nsubscriber_= this->create_subscription(\n \"/TOPIC\", 20, \nstd::bind(&CALLBACK, this, \nstd::placeholders::_1));\nWhere MESSAGE_TYPE is the message in the topic you want to subscribe to (i.e. geometry_msgs::msg::Pose), “/TOPIC” is the topic to subscribe to, and CALLBACK is a reference to a member function that will get called when a new message comes in (i.e. ExampleNode::subscription_callback). Again, the 20 is the buffer size, and std::placeholders::_1 is boilerplate code for calling the callback with an argument.When a message is received by the subscriber, it calls the callback function with a pointer to the message. Here is an example callback function you would define in your node class:\n// In header file:\nvoid ExampleNode::subscription_callback(const geometry_msgs::msg::Pose::SharedPtr msg);\n//Implementation:\nvoid ExampleNode::subscription_callback(const geometry_msgs::msg::Pose::SharedPtr msg)\n{\n …\n}\nThe ::SharedPtr is a special type of smart pointer, but it is functionally the same as a normal pointer and can be dereferenced and accessed using ->. Values of the message can be accessed just like a normal C++ object, such as “msg->position.x, msg->position.y, msg->position.z” to access the coordinates of the Pose message.You can reference the property names of any message by searching up the message followed by “ros2” on google. For example, here are the docs for the Pose message: docs for message, and you can see all the variables you can access in it.","timers#Timers":"Timers are tasks that execute in a repeated loop. Example use cases for timers are control loops which consistently update motor commands based on observations. The syntax to create a timer is the following:\n//In header file:\nrclcpp::TimerBase::SharedPtr timer_;\nvoid timer_callback();\n//In node constructor:\ntimer_ = this->create_wall_timer(std::chrono::milliseconds(delay_ms), std::bind(&YourNode::timer_callback, this));\n//Callback implementation:\nvoid YourNode::timer_callback(){\n}\nWhere delay_ms is the time between timer calls in milliseconds, and YourNode::timer_callback is the callback function that is called when the timer executes.","printing#Printing":"To print to the console, use RCLCPP_INFO followed by a string. You can pass additional parameters to the RCLCPP_INFO and they get added to the string using c++ sprintf format. For example, adding %f to the string will add in a floating point number to the print, which is passed in as a parameter to RCLCPP_INFO. The following code segment shows printing position.x to the console.\nRCLCPP_INFO(this->get_logger(), \"Received message… x=%f\", msg->position.x);\n// Will output “Received message… x=10.5” or whatever position.x is\nThese are all of the core functionalities to writing a node in ROS! As an exercise, try implementing a publisher and subscriber in C++ using one of the empty nodes available in the training repo, such as src/robot/control/src/control_node.cpp\nDeliverable 5.1 Write a timer that executes every second that will call a callback function and print out a message of your choice to the console. Verify that it works by running ./watod up and viewing the console printouts.\nDeliverable 5.2 Create a publisher that publishes a std_msgs::msg::String message to the “/example_string” topic. Modify the timer callback function you wrote in Deliverable 5.1 to include publishing of a string message to your publisher with a message of your choice. Verify the messages are being sent by adding a “raw message” widget to foxglove and set it to your new topic name. Congratulations, you published your first message!\nDeliverable 5.3 Write a subscriber that receives nav_msgs::msg::Odometry messages from the “/model/robot/odometry” topic (should be automatically available via Gazebo when you run the training repo) and prints its x,y,z coordinates to the console. Reference the ros2 documentation for the Odometry message properties. Congratulations, you subscribed to your first message!\nYou have now successfully written a node to publish and subscribe to messages, which is the core functionality of ROS! You can now write nodes to interact with robots via subscribing and publishing ROS messages.","control#Control":"Now it’s time to implement your first node to interact with the robot! This node will be responsible for receiving a goal position to drive towards and send motor commands to the robot to drive towards it.To set a position for the robot to drive to, we will be using Foxglove’s 3D panel publish feature, which allows you to click on the 3D map and publish a message. In Foxglove under panel settings for the 3D panel, you should see the following listed under the Publish tab:This is where you select the message type and topic to publish to when you click on the 3D map. If you use our configuration file, these will be automatically selected to geometry_msgs::msg::Point and “/goal_point” topic.In order to publish that message, click on the circle icon at the top right of the 3D panel (shown below) and click on a point on the map.You can verify that your point is published by adding a Raw Message widget and point it to the “/goal_pose” topic and watch as a message appears when you click on the circle icon and click on the map.The purpose of the control node is to subscribe to this topic and drive the robot towards the point you clicked on the map.\nYou will be writing code in the control node, so go ahead open up src/robot/control/src/control_node.cpp in the wato_asd_training repo. Now, to receive these messages, create a new subscriber in the Control node (a blank node called Control has been setup already in the repo, you may edit that node) and make it subscribe to geometry_msgs::msg::PointStamped messages in the “/goal_point” message. Then, create a callback and print out the x,y,z values of that PointStamped message (reference link) using RCLCPP_INFO.\nDeliverable 6.1 Create a subscriber that receives geometry_msgs::msg::PointStamped messages from the “/goal_point” topic. Verify you receive the messages by printing out the x,y,z coordinates to the console.\nNow that you can receive the goal pose, the next step is to control the robot. Firstly, we need a timer callback that will run the control loop on repeat. To do this, create a timer that runs every 100ms and calls a blank function for now.\nDeliverable 6.2 Create a timer that calls a blank callback function every 100ms for control. You can verify it works by printing a random message using RCLCPP_INFO.\nInside this timer, we will be using a simple proportional controller. A proportional controller is the first term in PID control, which simply multiplies a constant scalar to the error, where error is the distance between your current point and desired point.Command = Kp * errorIn this case, we have two error terms: x and y. The x error will define forward movement, and the y error will define angular movement.However, there is one problem. Currently, the point message is defined in the world coordinate frame, while we want the error in the robot’s relative coordinate frame to do relative movements. In order to do this, we will take advantage of ROS2’s transform system: TF2","transforms#Transforms":"Transforms define coordinate frames for geometry objects. One example could be a coordinate relative to the world versus relative to the robot, or a coordinate relative to an arm of the robot versus the base of the robot. Here is an illustration of a complex robot with multiple transforms (shown by red,blue,green axis markers):\nLuckily, the ROS2 library has an easy way for dealing with transforms. The gazebo environment we provided automatically publishes the transform of the robot, which allows us to easily converta point in world space to robot space. Once the point is in robot space, we can get the relative x and y offset from the robot’s position to the point.To access ROS2’s transform system, you must create a tf_buffer and tf_listener using the following syntax:\n// In header file \nstd::unique_ptr tf_buffer_;\nstd::shared_ptr tf_listener_;\n \n// In node constructor\ntf_buffer_ = std::make_unique(this->get_clock());\ntf_listener_ = std::make_shared(*tf_buffer_);\nThen, to access the transform between two frames, use the following syntax:\ngeometry_msgs::msg::TransformStamped transform;\ntry {\n transform = tf_buffer_->lookupTransform(\"TO_FRAME\", \"FROM_FRAME\", tf2::TimePointZero);\n} catch (const tf2::TransformException & ex) {\n RCLCPP_INFO(this->get_logger(), \"Could not transform %s\", ex.what());\n}\nWhere “TO_FRAME” is the frame you want to transform the point into, and “FROM_FRAME” is the frame the point originated in.Then to transform a point using that frame:\nauto transformed_point = geometry_msgs::msg::PointStamped();\n tf2::doTransform(original_point, transformed_point, transform);\nWhere original_point is the point in the FROM_FRAME and transformed_point is that same point in the TO_FRAME.We will be leveraging this code to transform our global point published to /goal_point into the robot’s frame. The goal_point will be in the frame “sim_world” (which is our global frame in this case) and the robot’s frame is “robot”.\nDeliverable 6.2 Create a tf_buffer and tf_listener in your control node. Then, in the control callback, get the transform from “sim_world” to “robot” and transform the goal_point to the robot frame. You may need to store goal_point in an instance variable in the Node class to access it in the timer callback after it has been received from the subscriber.\nNow that the point is transformed, the x coordinate relates to the robot’s forward motion while the y coordinate relates to the robot’s side to side motion (angular). Therefore, an easy proportional control loop may be derived where the linear command is proportional to the X component and the angular command is proportional to the Y component.\nDeliverable 6.3 Create two variables called Kp_linear and Kp_angular which will be the scalars for the proportional loop, initialize them to 0.5 as a good starting guess (these can be tuned later). Multiply Kp_linear by the X component of the goal_point transformed in the robot frame and Kp_angular by the Y component of the goal_point transformed in the robot frame. These will be your control signals to send to the robot.\nNow that you have the control signals, you must publish them to the robot. By default, the robot takes in control signals in the form of a geometry_msgs::msg::Twist message, which has a linear and angular velocity associated with it. The simulated robot is setup to receive Twist messages in the “/cmd_vel” topic, so you can directly send commands to the robot by creating a publisher in the control node that publishes geometry_msgs::msg::Twist messages to the “/cmd_vel” topic. Once the subscriber is made, you can then package up your linear and angular velocity commands inside of a Twist message (linear velocity will use the x component of the linear Vector3 of Twist and angular velocity will use the z component of the angular Vector3 of Twist. The rest will remain 0) and publish to the robot to control it!\nDeliverable 6.4 Create a publisher that publishes geometry_msgs::msg::Twist messages to the “/cmd_vel” topic. In your control timer, create a new Twist message, set the linear x component to the result of the linear proportional loop and the angular z component to the result of the angular proportional loop. Then, your robot should drive towards the goal_point message when you publish in Foxglove!","conclusion#Conclusion":"Congratulations! You have now successfully controlled a robot using ROS2! The fundamentals of publishers, subscribers, and a few tricks you learned like Foxglove and transforms will be extremely valuable in your development in the WATO ASD software stack.Please contact the Director of ASD (Eddy Zhou) on discord, showing proof of completion and 2 subteams you are interested in joining. Link to subteams here.\nWelcome to WATonomous! :))"}}} \ No newline at end of file diff --git a/_next/static/chunks/pages/about-cd811edd351d3f54.js b/_next/static/chunks/pages/about-17076f551e06b3ec.js similarity index 98% rename from _next/static/chunks/pages/about-cd811edd351d3f54.js rename to _next/static/chunks/pages/about-17076f551e06b3ec.js index 104628f..16ab13b 100644 --- a/_next/static/chunks/pages/about-cd811edd351d3f54.js +++ b/_next/static/chunks/pages/about-17076f551e06b3ec.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[521],{5199:function(e,n,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/about",function(){return a(9857)}])},9857:function(e,n,a){"use strict";a.r(n),a.d(n,{__toc:function(){return u},default:function(){return c}});var A=a(5893),o=a(2673),t=a(7913),i=a(4102);a(9128);var r=a(2643),d={src:"/_next/static/media/onboarding_pic.ae2c7794.jpg",height:1024,width:1792,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAJMM/wD/xAAaEAADAAMBAAAAAAAAAAAAAAABAgMABRIh/9oACAEBAAE/AF2thMVZEakZCgJA95Of/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAIRIf/aAAgBAgEBPwBtlP/EABcRAAMBAAAAAAAAAAAAAAAAAAACESH/2gAIAQMBAT8ATLD/2Q==",blurWidth:8,blurHeight:5},s=a(9013);let u=[{depth:2,value:"Our Big Audacious Goal",id:"our-big-audacious-goal"}];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",img:"img",h2:"h2"},(0,r.a)(),e.components);return(0,A.jsxs)(A.Fragment,{children:[(0,A.jsx)(n.h1,{children:"About WATonomous"}),"\n",(0,A.jsx)(n.p,{children:"WATonomous is the autonomous vehicle design team at the University of Waterloo. We are an agile group of developers, engineers, businessmen, and marketers looking to lead the next generation of robotic systems and their applications to society."}),"\n",(0,A.jsx)(n.p,{children:(0,A.jsx)(n.img,{placeholder:"blur",src:d})}),"\n",(0,A.jsx)(n.h2,{id:"our-big-audacious-goal",children:"Our Big Audacious Goal"}),"\n",(0,A.jsx)(n.p,{children:"Members of WATonomous center around a singular goal:"}),"\n","\n",(0,A.jsx)(s.UW,{children:(0,A.jsx)(n.p,{children:"To show the world a bunch of students can build a self-driving car!"})}),"\n",(0,A.jsx)(n.p,{children:"... and in doing so, drive ourselves to become better people :)."})]})}let g={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,r.a)(),e.components);return n?(0,A.jsx)(n,{...e,children:(0,A.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/about.mdx",route:"/about",timestamp:1726189146e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"About WATonomous",headings:u},pageNextRoute:"/about",nextraLayout:t.ZP,themeConfig:i.Z};var c=(0,o.j)(g)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=5199)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[521],{5199:function(e,n,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/about",function(){return a(9857)}])},9857:function(e,n,a){"use strict";a.r(n),a.d(n,{__toc:function(){return u},default:function(){return c}});var A=a(5893),o=a(2673),t=a(7913),i=a(4102);a(9128);var r=a(2643),d={src:"/_next/static/media/onboarding_pic.ae2c7794.jpg",height:1024,width:1792,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAJMM/wD/xAAaEAADAAMBAAAAAAAAAAAAAAABAgMABRIh/9oACAEBAAE/AF2thMVZEakZCgJA95Of/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAIRIf/aAAgBAgEBPwBtlP/EABcRAAMBAAAAAAAAAAAAAAAAAAACESH/2gAIAQMBAT8ATLD/2Q==",blurWidth:8,blurHeight:5},s=a(9013);let u=[{depth:2,value:"Our Big Audacious Goal",id:"our-big-audacious-goal"}];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",img:"img",h2:"h2"},(0,r.a)(),e.components);return(0,A.jsxs)(A.Fragment,{children:[(0,A.jsx)(n.h1,{children:"About WATonomous"}),"\n",(0,A.jsx)(n.p,{children:"WATonomous is the autonomous vehicle design team at the University of Waterloo. We are an agile group of developers, engineers, businessmen, and marketers looking to lead the next generation of robotic systems and their applications to society."}),"\n",(0,A.jsx)(n.p,{children:(0,A.jsx)(n.img,{placeholder:"blur",src:d})}),"\n",(0,A.jsx)(n.h2,{id:"our-big-audacious-goal",children:"Our Big Audacious Goal"}),"\n",(0,A.jsx)(n.p,{children:"Members of WATonomous center around a singular goal:"}),"\n","\n",(0,A.jsx)(s.UW,{children:(0,A.jsx)(n.p,{children:"To show the world a bunch of students can build a self-driving car!"})}),"\n",(0,A.jsx)(n.p,{children:"... and in doing so, drive ourselves to become better people :)."})]})}let g={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,r.a)(),e.components);return n?(0,A.jsx)(n,{...e,children:(0,A.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/about.mdx",route:"/about",timestamp:1726352057e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"About WATonomous",headings:u},pageNextRoute:"/about",nextraLayout:t.ZP,themeConfig:i.Z};var c=(0,o.j)(g)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=5199)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/finance-c32dfc54f9edfed8.js b/_next/static/chunks/pages/finance-a66e3088f38dc285.js similarity index 98% rename from _next/static/chunks/pages/finance-c32dfc54f9edfed8.js rename to _next/static/chunks/pages/finance-a66e3088f38dc285.js index 622ff7e..7e67edb 100644 --- a/_next/static/chunks/pages/finance-c32dfc54f9edfed8.js +++ b/_next/static/chunks/pages/finance-a66e3088f38dc285.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[504],{3250:function(e,n,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/finance",function(){return a(9346)}])},9346:function(e,n,a){"use strict";a.r(n),a.d(n,{__toc:function(){return d}});var o=a(5893),t=a(2673),r=a(7913),i=a(4102);a(9128);var s=a(2643);let d=[];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",a:"a",code:"code"},(0,s.a)(),e.components);return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{children:"Finance System"}),"\n",(0,o.jsxs)(n.p,{children:["Instructions on how to use the ",(0,o.jsx)(n.a,{href:"finance-frontend.watonomous.ca",children:"WATonomous Finance System"}),", which is used to track funding, our account balances, and manage and reimburse purhcases."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"Creating and Submitting a Personal Purchase Request"})," Contains details for members on how to purchase items using their own money and get reimbursed for it. (",(0,o.jsx)(n.a,{href:"/finance/creating_personal_purchases",children:"go-to"}),")"]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"Creating and Submitting a Purchase Request"})," Contains details for members on how to purchase items using WATonomous cash or directly from one of our funds. (",(0,o.jsx)(n.a,{href:"/finance/creating_purchase_requests",children:"go-to"}),")"]})]})}let c={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,s.a)(),e.components);return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/finance.mdx",route:"/finance",timestamp:1726189146e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Finance System",headings:d},pageNextRoute:"/finance",nextraLayout:r.ZP,themeConfig:i.Z};n.default=(0,t.j)(c)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=3250)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[504],{3250:function(e,n,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/finance",function(){return a(9346)}])},9346:function(e,n,a){"use strict";a.r(n),a.d(n,{__toc:function(){return d}});var o=a(5893),t=a(2673),r=a(7913),i=a(4102);a(9128);var s=a(2643);let d=[];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",a:"a",code:"code"},(0,s.a)(),e.components);return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{children:"Finance System"}),"\n",(0,o.jsxs)(n.p,{children:["Instructions on how to use the ",(0,o.jsx)(n.a,{href:"finance-frontend.watonomous.ca",children:"WATonomous Finance System"}),", which is used to track funding, our account balances, and manage and reimburse purhcases."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"Creating and Submitting a Personal Purchase Request"})," Contains details for members on how to purchase items using their own money and get reimbursed for it. (",(0,o.jsx)(n.a,{href:"/finance/creating_personal_purchases",children:"go-to"}),")"]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"Creating and Submitting a Purchase Request"})," Contains details for members on how to purchase items using WATonomous cash or directly from one of our funds. (",(0,o.jsx)(n.a,{href:"/finance/creating_purchase_requests",children:"go-to"}),")"]})]})}let c={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,s.a)(),e.components);return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/finance.mdx",route:"/finance",timestamp:1726352057e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Finance System",headings:d},pageNextRoute:"/finance",nextraLayout:r.ZP,themeConfig:i.Z};n.default=(0,t.j)(c)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=3250)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/finance/creating_personal_purchases-4b80d218a17c4c27.js b/_next/static/chunks/pages/finance/creating_personal_purchases-f42681233d53aea5.js similarity index 97% rename from _next/static/chunks/pages/finance/creating_personal_purchases-4b80d218a17c4c27.js rename to _next/static/chunks/pages/finance/creating_personal_purchases-f42681233d53aea5.js index 9409a88..3f60aaa 100644 --- a/_next/static/chunks/pages/finance/creating_personal_purchases-4b80d218a17c4c27.js +++ b/_next/static/chunks/pages/finance/creating_personal_purchases-f42681233d53aea5.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[60],{2682:function(e,n,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/finance/creating_personal_purchases",function(){return t(699)}])},699:function(e,n,t){"use strict";t.r(n),t.d(n,{__toc:function(){return d}});var a=t(5893),i=t(2673),r=t(7913),o=t(4102);t(9128);var s=t(2643);let d=[{depth:2,value:"Creating Expense Claim Forms",id:"creating-expense-claim-forms"}];function _createMdxContent(e){let n=Object.assign({h1:"h1",ol:"ol",li:"li",a:"a",code:"code",h2:"h2"},(0,s.a)(),e.components);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.h1,{children:"Creating Personal Purchases"}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsxs)(n.li,{children:["Go to ",(0,a.jsx)(n.a,{href:"https://finance-frontend.watonomous.ca/",children:"https://finance-frontend.watonomous.ca/"})]}),"\n",(0,a.jsx)(n.li,{children:"Create New Ticket > Ticket Type: Personal Purchase"}),"\n",(0,a.jsx)(n.li,{children:"Enter all purchase details. For funding item link, you would either be using FI-1 (WATO Cash) or one of the Funding Items that match the category. For example, if you're purchasing an RTX 4090 it would probably fit under GPU funding."}),"\n",(0,a.jsx)(n.li,{children:"Await team approval. Please reach out to the finance team and the faculty advisor."}),"\n",(0,a.jsxs)(n.li,{children:["Once this has been completed, the status will transition to ",(0,a.jsx)(n.code,{children:"READY_FOR_BUY"}),". You should purchase the item at this step."]}),"\n",(0,a.jsx)(n.li,{children:"Once the item has been purchased, please upload the University of Waterloo Finance Expense Claim Form. Instructions found below."}),"\n",(0,a.jsx)(n.li,{children:"Click Confirm Item(s) Purchased and Submit Reimbursement"}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"creating-expense-claim-forms",children:"Creating Expense Claim Forms"}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsxs)(n.li,{children:["Navigate ",(0,a.jsx)(n.a,{href:"https://uwaterloo.ca/engineering-endowment-foundation/getting-funding/spending-funding/submitting-reimbursement-request",children:"here"})]}),"\n",(0,a.jsx)(n.li,{children:'Click the link in step 1 titled "reimbursement form (excel)"'}),"\n",(0,a.jsx)(n.li,{children:"Enter the following fields"}),"\n",(0,a.jsx)(n.li,{children:"Payee is Student"}),"\n",(0,a.jsx)(n.li,{children:"Claimant Name, Student Number, Department, Phone Number, E-mail Address"}),"\n",(0,a.jsx)(n.li,{children:"Mailing Address, City, Province/State, Postal Code. For these, use the address your items were shipped to, or your personal address."}),"\n",(0,a.jsxs)(n.li,{children:["Destination/Reason for Request: Please put the corresponding reference number. For WEEF, this can be found ",(0,a.jsx)(n.a,{href:"https://docs.google.com/spreadsheets/d/1V4yUz8EjZCB78KKeBZ5JefzUg3Ql7SdXDFFWxUmVhuE/edit#gid=227889332",children:"here."})," Reach out to @smileycow on Discord if you do not know what this is"]}),"\n",(0,a.jsx)(n.li,{children:"Ignore the Travel Advance Request section, and fill in your item details on the next section. For the description, please put the name of your item, and if available, the reason it was purchased."}),"\n",(0,a.jsx)(n.li,{children:"Has an advance been issued: No"}),"\n",(0,a.jsx)(n.li,{children:"Enter your signature of Claimant"}),"\n",(0,a.jsxs)(n.li,{children:["Follow the rest of the instructions on the ",(0,a.jsx)(n.a,{href:"https://uwaterloo.ca/engineering-endowment-foundation/getting-funding/spending-funding/submitting-reimbursement-request",children:"original page"})," (steps 2 to 4)"]}),"\n",(0,a.jsxs)(n.li,{children:["Upload the receipt to the corresponding personal purchase at ",(0,a.jsx)(n.a,{href:"https://finance-frontend.watonomous.ca/",children:"https://finance-frontend.watonomous.ca/"})]}),"\n"]})]})}let c={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,s.a)(),e.components);return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/finance/creating_personal_purchases.mdx",route:"/finance/creating_personal_purchases",timestamp:1726189146e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Creating Personal Purchases",headings:d},pageNextRoute:"/finance/creating_personal_purchases",nextraLayout:r.ZP,themeConfig:o.Z};n.default=(0,i.j)(c)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=2682)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[60],{2682:function(e,n,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/finance/creating_personal_purchases",function(){return t(699)}])},699:function(e,n,t){"use strict";t.r(n),t.d(n,{__toc:function(){return d}});var a=t(5893),i=t(2673),r=t(7913),o=t(4102);t(9128);var s=t(2643);let d=[{depth:2,value:"Creating Expense Claim Forms",id:"creating-expense-claim-forms"}];function _createMdxContent(e){let n=Object.assign({h1:"h1",ol:"ol",li:"li",a:"a",code:"code",h2:"h2"},(0,s.a)(),e.components);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.h1,{children:"Creating Personal Purchases"}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsxs)(n.li,{children:["Go to ",(0,a.jsx)(n.a,{href:"https://finance-frontend.watonomous.ca/",children:"https://finance-frontend.watonomous.ca/"})]}),"\n",(0,a.jsx)(n.li,{children:"Create New Ticket > Ticket Type: Personal Purchase"}),"\n",(0,a.jsx)(n.li,{children:"Enter all purchase details. For funding item link, you would either be using FI-1 (WATO Cash) or one of the Funding Items that match the category. For example, if you're purchasing an RTX 4090 it would probably fit under GPU funding."}),"\n",(0,a.jsx)(n.li,{children:"Await team approval. Please reach out to the finance team and the faculty advisor."}),"\n",(0,a.jsxs)(n.li,{children:["Once this has been completed, the status will transition to ",(0,a.jsx)(n.code,{children:"READY_FOR_BUY"}),". You should purchase the item at this step."]}),"\n",(0,a.jsx)(n.li,{children:"Once the item has been purchased, please upload the University of Waterloo Finance Expense Claim Form. Instructions found below."}),"\n",(0,a.jsx)(n.li,{children:"Click Confirm Item(s) Purchased and Submit Reimbursement"}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"creating-expense-claim-forms",children:"Creating Expense Claim Forms"}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsxs)(n.li,{children:["Navigate ",(0,a.jsx)(n.a,{href:"https://uwaterloo.ca/engineering-endowment-foundation/getting-funding/spending-funding/submitting-reimbursement-request",children:"here"})]}),"\n",(0,a.jsx)(n.li,{children:'Click the link in step 1 titled "reimbursement form (excel)"'}),"\n",(0,a.jsx)(n.li,{children:"Enter the following fields"}),"\n",(0,a.jsx)(n.li,{children:"Payee is Student"}),"\n",(0,a.jsx)(n.li,{children:"Claimant Name, Student Number, Department, Phone Number, E-mail Address"}),"\n",(0,a.jsx)(n.li,{children:"Mailing Address, City, Province/State, Postal Code. For these, use the address your items were shipped to, or your personal address."}),"\n",(0,a.jsxs)(n.li,{children:["Destination/Reason for Request: Please put the corresponding reference number. For WEEF, this can be found ",(0,a.jsx)(n.a,{href:"https://docs.google.com/spreadsheets/d/1V4yUz8EjZCB78KKeBZ5JefzUg3Ql7SdXDFFWxUmVhuE/edit#gid=227889332",children:"here."})," Reach out to @smileycow on Discord if you do not know what this is"]}),"\n",(0,a.jsx)(n.li,{children:"Ignore the Travel Advance Request section, and fill in your item details on the next section. For the description, please put the name of your item, and if available, the reason it was purchased."}),"\n",(0,a.jsx)(n.li,{children:"Has an advance been issued: No"}),"\n",(0,a.jsx)(n.li,{children:"Enter your signature of Claimant"}),"\n",(0,a.jsxs)(n.li,{children:["Follow the rest of the instructions on the ",(0,a.jsx)(n.a,{href:"https://uwaterloo.ca/engineering-endowment-foundation/getting-funding/spending-funding/submitting-reimbursement-request",children:"original page"})," (steps 2 to 4)"]}),"\n",(0,a.jsxs)(n.li,{children:["Upload the receipt to the corresponding personal purchase at ",(0,a.jsx)(n.a,{href:"https://finance-frontend.watonomous.ca/",children:"https://finance-frontend.watonomous.ca/"})]}),"\n"]})]})}let c={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,s.a)(),e.components);return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/finance/creating_personal_purchases.mdx",route:"/finance/creating_personal_purchases",timestamp:1726352057e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Creating Personal Purchases",headings:d},pageNextRoute:"/finance/creating_personal_purchases",nextraLayout:r.ZP,themeConfig:o.Z};n.default=(0,i.j)(c)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=2682)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/finance/creating_purchase_requests-318142eb027c495a.js b/_next/static/chunks/pages/finance/creating_purchase_requests-852257e2f5958217.js similarity index 96% rename from _next/static/chunks/pages/finance/creating_purchase_requests-318142eb027c495a.js rename to _next/static/chunks/pages/finance/creating_purchase_requests-852257e2f5958217.js index 2016999..ba32ca9 100644 --- a/_next/static/chunks/pages/finance/creating_purchase_requests-318142eb027c495a.js +++ b/_next/static/chunks/pages/finance/creating_purchase_requests-852257e2f5958217.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[205],{395:function(e,n,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/finance/creating_purchase_requests",function(){return a(8088)}])},8088:function(e,n,a){"use strict";a.r(n),a.d(n,{__toc:function(){return c}});var t=a(5893),i=a(2673),o=a(7913),r=a(4102);a(9128);var s=a(2643);let c=[];function _createMdxContent(e){let n=Object.assign({h1:"h1",ol:"ol",li:"li",a:"a",code:"code"},(0,s.a)(),e.components);return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{children:"Creating Purchase Requests"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Go to ",(0,t.jsx)(n.a,{href:"https://finance-frontend.watonomous.ca/",children:"https://finance-frontend.watonomous.ca/"})]}),"\n",(0,t.jsx)(n.li,{children:"Create New Ticket > Ticket Type: UW Finance Purchase"}),"\n",(0,t.jsx)(n.li,{children:"Enter all purchase details. For funding item link, you would either be using FI-1 (WATO Cash) or one of the Funding Items that match the category. For example, if you're purchasing an RTX 4090 it would probably fit under GPU funding."}),"\n",(0,t.jsx)(n.li,{children:"Await team approval. Please reach out to the finance team and the faculty advisor."}),"\n",(0,t.jsxs)(n.li,{children:["Once this has been completed, the item will be sent to the finance coordinator. They will purchase the item, in which case the status will be ",(0,t.jsx)(n.code,{children:"ORDERED"})]}),"\n",(0,t.jsxs)(n.li,{children:["Once the item has arrived, the item will be transitioned to ",(0,t.jsx)(n.code,{children:"READY_FOR_PICKUP"}),", and the pick up instructions will give you next steps"]}),"\n",(0,t.jsxs)(n.li,{children:["Once completed, the item should be in the ",(0,t.jsx)(n.code,{children:"PICKED_UP"})," state, completing the flow."]}),"\n"]})]})}let d={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,s.a)(),e.components);return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/finance/creating_purchase_requests.mdx",route:"/finance/creating_purchase_requests",timestamp:1726189146e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Creating Purchase Requests",headings:c},pageNextRoute:"/finance/creating_purchase_requests",nextraLayout:o.ZP,themeConfig:r.Z};n.default=(0,i.j)(d)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=395)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[205],{395:function(e,n,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/finance/creating_purchase_requests",function(){return a(8088)}])},8088:function(e,n,a){"use strict";a.r(n),a.d(n,{__toc:function(){return c}});var t=a(5893),i=a(2673),o=a(7913),r=a(4102);a(9128);var s=a(2643);let c=[];function _createMdxContent(e){let n=Object.assign({h1:"h1",ol:"ol",li:"li",a:"a",code:"code"},(0,s.a)(),e.components);return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{children:"Creating Purchase Requests"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Go to ",(0,t.jsx)(n.a,{href:"https://finance-frontend.watonomous.ca/",children:"https://finance-frontend.watonomous.ca/"})]}),"\n",(0,t.jsx)(n.li,{children:"Create New Ticket > Ticket Type: UW Finance Purchase"}),"\n",(0,t.jsx)(n.li,{children:"Enter all purchase details. For funding item link, you would either be using FI-1 (WATO Cash) or one of the Funding Items that match the category. For example, if you're purchasing an RTX 4090 it would probably fit under GPU funding."}),"\n",(0,t.jsx)(n.li,{children:"Await team approval. Please reach out to the finance team and the faculty advisor."}),"\n",(0,t.jsxs)(n.li,{children:["Once this has been completed, the item will be sent to the finance coordinator. They will purchase the item, in which case the status will be ",(0,t.jsx)(n.code,{children:"ORDERED"})]}),"\n",(0,t.jsxs)(n.li,{children:["Once the item has arrived, the item will be transitioned to ",(0,t.jsx)(n.code,{children:"READY_FOR_PICKUP"}),", and the pick up instructions will give you next steps"]}),"\n",(0,t.jsxs)(n.li,{children:["Once completed, the item should be in the ",(0,t.jsx)(n.code,{children:"PICKED_UP"})," state, completing the flow."]}),"\n"]})]})}let d={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,s.a)(),e.components);return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/finance/creating_purchase_requests.mdx",route:"/finance/creating_purchase_requests",timestamp:1726352057e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Creating Purchase Requests",headings:c},pageNextRoute:"/finance/creating_purchase_requests",nextraLayout:o.ZP,themeConfig:r.Z};n.default=(0,i.j)(d)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=395)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/index-893f3f5baf8889d0.js b/_next/static/chunks/pages/index-c049793a8afb547a.js similarity index 98% rename from _next/static/chunks/pages/index-893f3f5baf8889d0.js rename to _next/static/chunks/pages/index-c049793a8afb547a.js index c3ff046..6d4c147 100644 --- a/_next/static/chunks/pages/index-893f3f5baf8889d0.js +++ b/_next/static/chunks/pages/index-c049793a8afb547a.js @@ -3,4 +3,4 @@ * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. - */let c=(0,h.Z)("CarTaxiFront",[["path",{d:"M10 2h4",key:"n1abiw"}],["path",{d:"m21 8-2 2-1.5-3.7A2 2 0 0 0 15.646 5H8.4a2 2 0 0 0-1.903 1.257L5 10 3 8",key:"1imjwt"}],["path",{d:"M7 14h.01",key:"1qa3f1"}],["path",{d:"M17 14h.01",key:"7oqj8z"}],["rect",{width:"18",height:"8",x:"3",y:"10",rx:"2",key:"a7itu8"}],["path",{d:"M5 18v2",key:"ppbyun"}],["path",{d:"M19 18v2",key:"gy7782"}]]),g=(0,h.Z)("NotebookPen",[["path",{d:"M13.4 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7.4",key:"re6nr2"}],["path",{d:"M2 6h4",key:"aawbzj"}],["path",{d:"M2 10h4",key:"l0bgd4"}],["path",{d:"M2 14h4",key:"1gsvsf"}],["path",{d:"M2 18h4",key:"1bu2t1"}],["path",{d:"M18.4 2.6a2.17 2.17 0 0 1 3 3L16 11l-4 1 1-4Z",key:"1dba1m"}]]),u=[{depth:2,value:"New Here?",id:"new-here"},{depth:2,value:"How to Edit",id:"how-to-edit"}];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",img:"img",h2:"h2",code:"code",a:"a"},(0,r.a)(),e.components);return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{children:"Welcome to the Wiki"}),"\n",(0,t.jsx)(n.p,{children:"Welcome to the WATonomous Wiki! This is an open-source wiki for all things WATonomous."}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.img,{placeholder:"blur",src:d})}),"\n",(0,t.jsx)(n.h2,{id:"new-here",children:"New Here?"}),"\n","\n",(0,t.jsxs)(s.oy,{num:2,children:[(0,t.jsx)(s.Zb,{icon:(0,t.jsx)(c,{}),title:"Read up on who we are!",href:"/about"}),(0,t.jsx)(s.Zb,{icon:(0,t.jsx)(g,{}),title:"Take a look at our Onboarding Guides",href:"/onboarding"})]}),"\n",(0,t.jsx)(n.h2,{id:"how-to-edit",children:"How to Edit"}),"\n",(0,t.jsxs)(n.p,{children:["Editing the WATonomous wiki is easy. Simply click ",(0,t.jsx)(n.code,{children:"Edit this page"})," on the far right of each page. Note, you must be part of the WATonomous Organization to be able to edit."]}),"\n",(0,t.jsxs)(n.p,{children:["You can use regular react components, or choose from a plethora of pre-built components and tools ",(0,t.jsx)(n.a,{href:"https://nextra.site/docs",children:"here"}),"."]})]})}let l={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,r.a)(),e.components);return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/index.mdx",route:"/",timestamp:1726189146e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Welcome to the Wiki",headings:u},pageNextRoute:"/",nextraLayout:A.ZP,themeConfig:i.Z};var p=(0,o.j)(l)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=1464)}),_N_E=e.O()}]); \ No newline at end of file + */let c=(0,h.Z)("CarTaxiFront",[["path",{d:"M10 2h4",key:"n1abiw"}],["path",{d:"m21 8-2 2-1.5-3.7A2 2 0 0 0 15.646 5H8.4a2 2 0 0 0-1.903 1.257L5 10 3 8",key:"1imjwt"}],["path",{d:"M7 14h.01",key:"1qa3f1"}],["path",{d:"M17 14h.01",key:"7oqj8z"}],["rect",{width:"18",height:"8",x:"3",y:"10",rx:"2",key:"a7itu8"}],["path",{d:"M5 18v2",key:"ppbyun"}],["path",{d:"M19 18v2",key:"gy7782"}]]),g=(0,h.Z)("NotebookPen",[["path",{d:"M13.4 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7.4",key:"re6nr2"}],["path",{d:"M2 6h4",key:"aawbzj"}],["path",{d:"M2 10h4",key:"l0bgd4"}],["path",{d:"M2 14h4",key:"1gsvsf"}],["path",{d:"M2 18h4",key:"1bu2t1"}],["path",{d:"M18.4 2.6a2.17 2.17 0 0 1 3 3L16 11l-4 1 1-4Z",key:"1dba1m"}]]),u=[{depth:2,value:"New Here?",id:"new-here"},{depth:2,value:"How to Edit",id:"how-to-edit"}];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",img:"img",h2:"h2",code:"code",a:"a"},(0,r.a)(),e.components);return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{children:"Welcome to the Wiki"}),"\n",(0,t.jsx)(n.p,{children:"Welcome to the WATonomous Wiki! This is an open-source wiki for all things WATonomous."}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.img,{placeholder:"blur",src:d})}),"\n",(0,t.jsx)(n.h2,{id:"new-here",children:"New Here?"}),"\n","\n",(0,t.jsxs)(s.oy,{num:2,children:[(0,t.jsx)(s.Zb,{icon:(0,t.jsx)(c,{}),title:"Read up on who we are!",href:"/about"}),(0,t.jsx)(s.Zb,{icon:(0,t.jsx)(g,{}),title:"Take a look at our Onboarding Guides",href:"/onboarding"})]}),"\n",(0,t.jsx)(n.h2,{id:"how-to-edit",children:"How to Edit"}),"\n",(0,t.jsxs)(n.p,{children:["Editing the WATonomous wiki is easy. Simply click ",(0,t.jsx)(n.code,{children:"Edit this page"})," on the far right of each page. Note, you must be part of the WATonomous Organization to be able to edit."]}),"\n",(0,t.jsxs)(n.p,{children:["You can use regular react components, or choose from a plethora of pre-built components and tools ",(0,t.jsx)(n.a,{href:"https://nextra.site/docs",children:"here"}),"."]})]})}let l={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,r.a)(),e.components);return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/index.mdx",route:"/",timestamp:1726352057e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Welcome to the Wiki",headings:u},pageNextRoute:"/",nextraLayout:A.ZP,themeConfig:i.Z};var p=(0,o.j)(l)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=1464)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/onboarding-a6a76f81e71d7ab9.js b/_next/static/chunks/pages/onboarding-764be76f2dc054dd.js similarity index 98% rename from _next/static/chunks/pages/onboarding-a6a76f81e71d7ab9.js rename to _next/static/chunks/pages/onboarding-764be76f2dc054dd.js index 1c6f547..0e5d149 100644 --- a/_next/static/chunks/pages/onboarding-a6a76f81e71d7ab9.js +++ b/_next/static/chunks/pages/onboarding-764be76f2dc054dd.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[411],{8228:function(e,n,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/onboarding",function(){return a(9687)}])},9687:function(e,n,a){"use strict";a.r(n),a.d(n,{__toc:function(){return s}});var o=a(5893),t=a(2673),r=a(7913),i=a(4102);a(9128);var d=a(2643);let s=[];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",code:"code",a:"a"},(0,d.a)(),e.components);return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{children:"Onboarding"}),"\n",(0,o.jsx)(n.p,{children:"WATonomous onboarding guides. The following are quick jists of what each onboarding guide is for:"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"ASD - Developing with WATcloud"})," Contains setup steps for getting access to WATcloud as well as how to use WATcloud in the Autonomous Software Division. (",(0,o.jsx)(n.a,{href:"/onboarding/asd_watcloud_dev",children:"go-to"}),")"]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"ASD - General Onboarding"})," Contains a walk-through that teaches you the basics of ROS2, Docker, Docker Compose, and the rest of our robotics software stack. (",(0,o.jsx)(n.a,{href:"/onboarding/asd_general_onboarding",children:"go-to"}),")"]})]})}let c={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,d.a)(),e.components);return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/onboarding.mdx",route:"/onboarding",timestamp:1726189146e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Onboarding",headings:s},pageNextRoute:"/onboarding",nextraLayout:r.ZP,themeConfig:i.Z};n.default=(0,t.j)(c)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=8228)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[411],{8228:function(e,n,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/onboarding",function(){return a(9687)}])},9687:function(e,n,a){"use strict";a.r(n),a.d(n,{__toc:function(){return s}});var o=a(5893),t=a(2673),r=a(7913),i=a(4102);a(9128);var d=a(2643);let s=[];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",code:"code",a:"a"},(0,d.a)(),e.components);return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{children:"Onboarding"}),"\n",(0,o.jsx)(n.p,{children:"WATonomous onboarding guides. The following are quick jists of what each onboarding guide is for:"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"ASD - Developing with WATcloud"})," Contains setup steps for getting access to WATcloud as well as how to use WATcloud in the Autonomous Software Division. (",(0,o.jsx)(n.a,{href:"/onboarding/asd_watcloud_dev",children:"go-to"}),")"]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"ASD - General Onboarding"})," Contains a walk-through that teaches you the basics of ROS2, Docker, Docker Compose, and the rest of our robotics software stack. (",(0,o.jsx)(n.a,{href:"/onboarding/asd_general_onboarding",children:"go-to"}),")"]})]})}let c={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,d.a)(),e.components);return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/onboarding.mdx",route:"/onboarding",timestamp:1726352057e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Onboarding",headings:s},pageNextRoute:"/onboarding",nextraLayout:r.ZP,themeConfig:i.Z};n.default=(0,t.j)(c)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=8228)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/onboarding/asd_general_onboarding-3fb2e6af6a7ef012.js b/_next/static/chunks/pages/onboarding/asd_general_onboarding-f0660809c8e051b7.js similarity index 99% rename from _next/static/chunks/pages/onboarding/asd_general_onboarding-3fb2e6af6a7ef012.js rename to _next/static/chunks/pages/onboarding/asd_general_onboarding-f0660809c8e051b7.js index 31f15d5..51da808 100644 --- a/_next/static/chunks/pages/onboarding/asd_general_onboarding-3fb2e6af6a7ef012.js +++ b/_next/static/chunks/pages/onboarding/asd_general_onboarding-f0660809c8e051b7.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[785],{871:function(e,s,o){(window.__NEXT_P=window.__NEXT_P||[]).push(["/onboarding/asd_general_onboarding",function(){return o(679)}])},679:function(e,s,o){"use strict";o.r(s),o.d(s,{__toc:function(){return B},default:function(){return T}});var n=o(5893),t=o(2673),i=o(7913),r=o(4102);o(9128);var a=o(2643),l={src:"/_next/static/media/infrastructure.9106ed3d.jpg",height:303,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAAqoT/xAAaEAACAwEBAAAAAAAAAAAAAAACAwEEEQAS/9oACAEBAAE/ADrMNiIO03Ak9zI9d//EABcRAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQIBAT8AuI//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==",blurWidth:8,blurHeight:5},c={src:"/_next/static/media/container_up.599b81e2.jpg",height:74,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAEACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABgEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAjgf/xAAXEAADAQAAAAAAAAAAAAAAAAAAAgUx/9oACAEBAAE/AKWqf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:1},A={src:"/_next/static/media/vscode_docker_extension.c0d85d3e.jpg",height:349,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAngP/xAAbEAACAQUAAAAAAAAAAAAAAAABAgMAERITIf/aAAgBAQABPwDcVikuiMyyYKxHQAK//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:5},h={src:"/_next/static/media/changes.99ce1ab3.jpg",height:394,width:623,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAIEV/8QAGBABAAMBAAAAAAAAAAAAAAAAAQACAxT/2gAIAQEAAT8Ac7c910VNGf/EABYRAQEBAAAAAAAAAAAAAAAAAAEAMf/aAAgBAgEBPwB1v//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:5},d={src:"/_next/static/media/changes_outside.8bf0243b.jpg",height:395,width:625,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAn4P/xAAaEAACAgMAAAAAAAAAAAAAAAABAgADEiEj/9oACAEBAAE/AHrbGzodMZ//xAAWEQEBAQAAAAAAAAAAAAAAAAABACH/2gAIAQIBAT8AcW//xAAVEQEBAAAAAAAAAAAAAAAAAAAAAf/aAAgBAwEBPwCP/9k=",blurWidth:8,blurHeight:5},p={src:"/_next/static/media/git_status.c1c8ac78.jpg",height:145,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAIACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABgEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAjgf/xAAZEAABBQAAAAAAAAAAAAAAAAADAAQRIVL/2gAIAQEAAT8AcWIk5X//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/AH//xAAVEQEBAAAAAAAAAAAAAAAAAAAAAf/aAAgBAwEBPwCP/9k=",blurWidth:8,blurHeight:2},u={src:"/_next/static/media/foxglove.d856c80a.jpg",height:290,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAIgV/8QAHBAAAgICAwAAAAAAAAAAAAAAAQIDBAUSABEh/9oACAEBAAE/AMXDFdzENWXcK25JRuj4Of/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:5},x={src:"/_next/static/media/profiles.9298bbba.jpg",height:185,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAMACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAngP/xAAaEAABBQEAAAAAAAAAAAAAAAABAAIDETNB/9oACAEBAAE/AJcw7tlf/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:3},g={src:"/_next/static/media/foxglove_1.0e31fef7.jpg",height:117,width:368,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAMACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABgEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAjwf/xAAbEAACAQUAAAAAAAAAAAAAAAACAwABEiGS0f/aAAgBAQABPwBiAvpk9y7P/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:3},m={src:"/_next/static/media/foxglove_2.5d945360.jpg",height:252,width:379,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAI4V/8QAGxAAAQQDAAAAAAAAAAAAAAAAAQACERIUM8L/2gAIAQEAAT8Ah+UTc7Ol/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:5},j={src:"/_next/static/media/foxglove_3.87436e4b.jpg",height:277,width:479,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAT/2gAMAwEAAhADEAAAAIwUP//EABkQAAMAAwAAAAAAAAAAAAAAAAECAwATIf/aAAgBAQABPwBBellG9uvn/8QAGBEAAgMAAAAAAAAAAAAAAAAAAQIAAxH/2gAIAQIBAT8ArRWB0T//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==",blurWidth:8,blurHeight:5},k={src:"/_next/static/media/foxglove_studio.34cdd874.jpg",height:354,width:623,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABwEBAQAAAAAAAAAAAAAAAAAAAAL/2gAMAwEAAhADEAAAAJwKf//EABoQAAIDAQEAAAAAAAAAAAAAAAIDAQQRAKH/2gAIAQEAAT8Am09FmGKYQsCdEt3ye//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:5},y={src:"/_next/static/media/layout.8060e8f5.jpg",height:756,width:940,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAYACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAJAUf//EABwQAAICAgMAAAAAAAAAAAAAAAECAxIAIQQFEf/aAAgBAQABPwCLsYuCTNIjNFQgpVWFiNGp0fM//8QAFhEAAwAAAAAAAAAAAAAAAAAAAAES/9oACAECAQE/AKZ//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:6},v={src:"/_next/static/media/grid.ea05b56b.jpg",height:903,width:1600,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABwEBAQAAAAAAAAAAAAAAAAAAAgX/2gAMAwEAAhADEAAAAJwKI//EABoQAAICAwAAAAAAAAAAAAAAAAECAxEABDL/2gAIAQEAAT8AbZSJ3LRFqo9EZ//EABoRAAICAwAAAAAAAAAAAAAAAAEDAAIEERL/2gAIAQIBAT8AxUrYsm9dnqf/xAAXEQEAAwAAAAAAAAAAAAAAAAABAAIR/9oACAEDAQE/ALOM/9k=",blurWidth:8,blurHeight:5},w={src:"/_next/static/media/grid_2.2ef23278.jpg",height:507,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAgACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAwT/2gAMAwEAAhADEAAAAIQUF//EABkQAAIDAQAAAAAAAAAAAAAAABESAAEhMf/aAAgBAQABPwAUpOtyf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:8},f={src:"/_next/static/media/goal.f8fee143.jpg",height:352,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAYACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABgEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAAArwj/AP/EABoQAAEFAQAAAAAAAAAAAAAAAAMAAQISFBH/2gAIAQEAAT8A1D0sTkrXX//EABURAQEAAAAAAAAAAAAAAAAAAAAB/9oACAECAQE/AK//xAAVEQEBAAAAAAAAAAAAAAAAAAAAAf/aAAgBAwEBPwCP/9k=",blurWidth:8,blurHeight:6},b={src:"/_next/static/media/publish.a2a1af8d.jpg",height:133,width:482,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAIACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAIgV/8QAGxAAAgEFAAAAAAAAAAAAAAAAAQIEABIhIjH/2gAIAQEAAT8Aj7RmY5N3a//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:2},E={src:"/_next/static/media/goal_pose.d4fa3091.jpg",height:259,width:109,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAgAAwMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAgP/2gAMAwEAAhADEAAAAIYVP//EABgQAQEAAwAAAAAAAAAAAAAAAAIBACJB/9oACAEBAAE/AChTN+Z//8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8Aj//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:3,blurHeight:8},C={src:"/_next/static/media/data.4e5d0659.jpg",height:288,width:491,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAAnAT/xAAbEAABBAMAAAAAAAAAAAAAAAACAAERIQMEE//aAAgBAQABPwDayk/OKkbX/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFhEAAwAAAAAAAAAAAAAAAAAAAAEx/9oACAEDAQE/AFD/2Q==",blurWidth:8,blurHeight:5},Q={src:"/_next/static/media/transforms.e5968bb5.jpg",height:471,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAcACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABwEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAKcJP//EAB0QAAMAAAcAAAAAAAAAAAAAAAECBAADBRESQWH/2gAIAQEAAT8AnaldSqnOceCAFSy7+nvH/8QAGREAAQUAAAAAAAAAAAAAAAAAAAECERJx/9oACAECAQE/AGradP/EABURAQEAAAAAAAAAAAAAAAAAAAAB/9oACAEDAQE/AI//2Q==",blurWidth:8,blurHeight:7},I=o(9013);let B=[{depth:2,value:"Introduction",id:"introduction"},{depth:2,value:"Server Access",id:"server-access"},{depth:2,value:"ASD Training Repository",id:"asd-training-repository"},{depth:3,value:"Our Tech stack",id:"our-tech-stack"},{depth:3,value:"ROS2",id:"ros2"},{depth:3,value:"Docker",id:"docker"},{depth:3,value:"Docker Compose and wato",id:"docker-compose-and-wato"},{depth:3,value:"What's up with the long image names?",id:"whats-up-with-the-long-image-names"},{depth:3,value:"A Visual Understanding",id:"a-visual-understanding"},{depth:3,value:"On startup",id:"on-startup"},{depth:2,value:"Training overview",id:"training-overview"},{depth:3,value:"Set up",id:"set-up"},{depth:4,value:"Watod Orchestration",id:"watod-orchestration"},{depth:4,value:"watod-config.sh",id:"watod-configsh"},{depth:4,value:"./watod up",id:"watod-up"},{depth:3,value:"Dev containers",id:"dev-containers"},{depth:3,value:"Up a container",id:"up-a-container"},{depth:3,value:"Enter the container",id:"enter-the-container"},{depth:3,value:"Make changes inside the container",id:"make-changes-inside-the-container"},{depth:3,value:"Git add, commit, and push!",id:"git-add-commit-and-push"},{depth:2,value:"Foxglove",id:"foxglove"},{depth:3,value:"Port Forwarding",id:"port-forwarding"},{depth:3,value:"Using Foxglove",id:"using-foxglove"},{depth:3,value:"Viewing ROS data through foxglove",id:"viewing-ros-data-through-foxglove"},{depth:2,value:"Hands-on Training",id:"hands-on-training"},{depth:3,value:"The Basis of Robot navigation",id:"the-basis-of-robot-navigation"},{depth:3,value:"Goal",id:"goal"},{depth:3,value:"ROS",id:"ros"},{depth:3,value:"Publisher",id:"publisher"},{depth:3,value:"Subscriber",id:"subscriber"},{depth:3,value:"Timers",id:"timers"},{depth:3,value:"Printing",id:"printing"},{depth:3,value:"Control",id:"control"},{depth:3,value:"Transforms",id:"transforms"},{depth:2,value:"Conclusion",id:"conclusion"}];function _createMdxContent(e){let s=Object.assign({h1:"h1",p:"p",a:"a",h2:"h2",strong:"strong",ol:"ol",li:"li",code:"code",h3:"h3",img:"img",ul:"ul",h4:"h4",pre:"pre",span:"span"},(0,a.a)(),e.components);return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(s.h1,{children:"General Autonomous Software Onboarding - ASD Assignment"}),"\n","\n",(0,n.jsx)(I.UW,{type:"error",emoji:"️❗",children:(0,n.jsxs)(s.p,{children:["You must complete ",(0,n.jsx)(s.a,{href:"/onboarding/asd_watcloud_dev",children:"ASD - Developing with WATcloud"})," before proceeding with this guide."]})}),"\n",(0,n.jsx)(s.h2,{id:"introduction",children:"Introduction"}),"\n",(0,n.jsx)(s.p,{children:"Congratulations! WATonomous has recognized your talent, and we would like to get you ready to contribute to the team."}),"\n",(0,n.jsx)(s.p,{children:"The Autonomous Software Division (ASD) assignment is designed to train a newcomer robotics programing at WATonomous from the ground up."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"The onboarding assignment consists of the following:"})}),"\n",(0,n.jsxs)(s.ol,{children:["\n",(0,n.jsx)(s.li,{children:(0,n.jsx)(s.strong,{children:"Accessing the WATonomous Server Cluster (WATcloud)"})}),"\n",(0,n.jsx)(s.li,{children:(0,n.jsx)(s.strong,{children:"Getting used to ROS2 - Docker robotics infrastructure"})}),"\n",(0,n.jsx)(s.li,{children:(0,n.jsx)(s.strong,{children:"Writing your own control program to direct a robot in simulation"})}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["Should you finish this assignment, you will have all the skills necessary to contribute towards building a real autonomous vehicle. Note, this assignment will not cover ML concepts. However, ML is used throughout robot autonomy, so we highly recommend you learn ML on your own time. ",(0,n.jsx)(s.a,{href:"https://mega.nz/folder/gXdVhJID#XCXqwUR-9Y43slf_LJaUDw",children:"Here’s some ML resources for you"})," (DO NOT SHARE)."]}),"\n",(0,n.jsx)(I.UW,{type:"info",emoji:"ℹ️",children:(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"After completing this assignment, you will become an official member of WATonomous with a dedicated watonomous email."})})}),"\n",(0,n.jsxs)(s.p,{children:["Please contact the Director of ASD (",(0,n.jsx)(s.a,{href:"e23zhou@watonomous.ca",children:"Eddy Zhou"}),") on discord, showing proof of completion and ",(0,n.jsx)(s.strong,{children:"two"})," subteams you are interested in joining. ",(0,n.jsx)(s.a,{href:"https://www.watonomous.ca/roles",children:"Link to subteams here"}),"."]}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"The ASD leads are here to help"})," so feel free to ask any questions about the onboarding assignment in the asd-questions channel on discord. This document is pretty long, but rest assured that a large majority of the assignment is a gigantic walk through."]}),"\n",(0,n.jsx)(s.p,{children:"Good Luck!"}),"\n",(0,n.jsx)(s.h2,{id:"server-access",children:"Server Access"}),"\n",(0,n.jsxs)(s.p,{children:["Server access steps have been moved to ",(0,n.jsx)(s.a,{href:"https://wiki.watonomous.ca/onboarding/asd_watcloud_dev",children:"here"}),". Please let the WATcloud leads know if you have any issues."]}),"\n",(0,n.jsx)(s.p,{children:"Once you've familiarized yourself"}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 1.0"})," Create a custom SLURM interactive development job. The SLURM job must have VRAM. You can specify the job to run for 2 hours so that you don't get kicked out while you are developing."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 2.0"})," Connect VS Code to a WATcloud SLURM job. Clone the ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_asd_training",children:"ASD Training Repository"})," into your WATcloud user’s home directory (~/). Open your cloned repository in VS Code. If you aren’t familiar with git, here’s a ",(0,n.jsx)(s.a,{href:"https://www.google.com/url?q=https://education.github.com/git-cheat-sheet-education.pdf&sa=D&source=docs&ust=1725499216448502&usg=AOvVaw1rF47Ac-OyVH_LDB6B4S3t",children:"cheatsheet"}),"."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 2.0.1"})," ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_monorepo",children:(0,n.jsx)(s.strong,{children:"STAR THE WATO_MONOREPO"})}),". It’s voluntary, but it will help us legitimize the repository overtime :)."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 2.1"})," In the opened ASD Training Repo, create a new branch titled ",(0,n.jsx)(s.code,{children:"_training"}),"."]})}),"\n",(0,n.jsx)(s.h2,{id:"asd-training-repository",children:"ASD Training Repository"}),"\n",(0,n.jsxs)(s.p,{children:["In the previous section, you would have cloned the ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_asd_training",children:"ASD Training Repository"})," into your user’s home directory on the server. ",(0,n.jsx)(s.strong,{children:"Please keep this folder open as you read."})]}),"\n",(0,n.jsxs)(s.p,{children:["The repository you just cloned contains a barebones setup of WATonomous’ current ASD infrastructure. It closely mirrors the infrastructure of the ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_monorepo",children:"wato_monorepo"}),", which is the central repository for all of the code that goes into the car. You can read up on the reasoning behind having a monorepo ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_monorepo/blob/main/docs/monorepo.md",children:"here"}),"."]}),"\n",(0,n.jsx)(s.h3,{id:"our-tech-stack",children:"Our Tech stack"}),"\n",(0,n.jsxs)(s.p,{children:["Our autonomy stack contains a multitude of coding tools and libraries (PyTorch, TensorFlow, OpenCV, Numpy, Scikit, Foxglove, Gymnasium, etc.), but the three ",(0,n.jsx)(s.strong,{children:"MOST-PROMINENT, REOCCURING TOOLS"})," you must know are ",(0,n.jsx)(s.a,{href:"https://docs.ros.org/en/humble/index.html",children:"ROS2"}),", ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/",children:"Docker"}),", and ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/",children:"Docker Compose"}),"."]}),"\n",(0,n.jsx)(s.p,{children:"These software tools enable cool AI tools and algorithms to communicate properly in a robotic system. Permutations of these open source tools are used throughout the cutting-edge robotics industry. It is especially prevalent in R&D, where new tools need to be integrated and tested at an alarming rate."}),"\n",(0,n.jsx)(s.h3,{id:"ros2",children:"ROS2"}),"\n",(0,n.jsxs)(s.p,{children:["The ",(0,n.jsx)(s.a,{href:"https://docs.ros.org/en/humble/index.html",children:"Robotics Operating System 2"})," (ROS2) is the second iteration of open source tools and libraries used for quickly building robot applications. It helps us intuitively do interprocess communication without the need to dig extremely deep into low-level programming. ROS2 is the monorepo’s main communication interface."]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.0"})," Identify locations in the code where ROS2 is being used."]})}),"\n",(0,n.jsx)(s.h3,{id:"docker",children:"Docker"}),"\n",(0,n.jsxs)(s.p,{children:["For our ASD stack, we use ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/",children:"Docker"})," to containerize all of our code. Docker can be thought of as a lightweight virtual machine, allowing us to create separate environments (containers) for running code. This makes our codebase portable and modular."]}),"\n",(0,n.jsxs)(s.p,{children:["Docker uses docker files (.Dockerfile extension) to configure and set up each container. Dockerfiles generally start with a machine base, and commands are specified to setup the machine, install dependencies, and setup the code workspace. Here is an ",(0,n.jsxs)(s.a,{href:"https://github.com/WATonomous/wato_monorepo/blob/main/docker/samples/cpp/aggregator.Dockerfile",children:["example dockerfile from the ",(0,n.jsx)(s.code,{children:"wato_monorepo"})]}),"."]}),"\n",(0,n.jsxs)(s.p,{children:["Generally the Dockerfiles for your modules will be set up already by your team leads. However, Docker is still a very useful tool to learn, and you can reference the ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/get-started/",children:"getting started"})]}),"\n",(0,n.jsx)(s.p,{children:"documentation for more details if interested. Those who can understand our monorepo down to the docker-level are much valued :)."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.1.0"})," Identify locations in the code where Docker is being used."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.1.1"})," What is the difference between a Dockerfile, a Docker Image, and a Docker Container? Refer to online references."]})}),"\n",(0,n.jsx)(s.h3,{id:"docker-compose-and-wato",children:"Docker Compose and wato"}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.a,{href:"https://docs.docker.com/compose/",children:"Docker Compose"})," is a utility for managing a codebase with multiple docker components. It provides an intuitive yaml interface to configure, build, and run docker containers. With a single Docker Compose command, you can startup multiple containers at once."]}),"\n",(0,n.jsx)(s.p,{children:"In our ASD training repo, and our monorepo, you will make great use of a software tool known as ‘watod’. Under the hood, watod is a wrapper around Docker Compose. You can use watod just like how you would use Docker Compose."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.0"})," Identify locations where Docker Compose is being used. What does WATO call these Docker Compose files? Hint: directory name"]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.1"})," Identify environment variables in a docker-compose file (Hint: they start with a $). How are these environment variables set during runtime? The next question can clear up this question."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.2"})," Identify any files prefixed with ‘watod’. Take a closer look at ‘watod-setup-env.sh’, what is it doing? What script do you run to run ‘watod-setup-env.sh’? How does this relate to the environment variables found in the docker-compose file?"]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.3"})," In your own words, explain what watod does. How does it interface with our docker-compose files?"]})}),"\n",(0,n.jsx)(s.h3,{id:"whats-up-with-the-long-image-names",children:"What's up with the long image names?"}),"\n",(0,n.jsxs)(s.p,{children:["You may have noticed docker image names along the lines of “git.uwaterloo.ca:5050/watonomous/wato_monorepo/…”. This long image name refers to a docker image stored in a ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/registry/",children:"Docker Registry"}),"."]}),"\n",(0,n.jsx)(s.p,{children:"Building docker images from scratch can take an extremely long time. So our team takes advantage of Docker Registries to quickly pull pre-built images. This saves us a lot of time."}),"\n",(0,n.jsx)(s.p,{children:"In the ASD Training Assignment, docker registries are disabled. This is why you see the manifest error when building. No fret, we disabled registries on purpose. In the actual wato_monorepo, you’ll have to learn how to use registries."}),"\n",(0,n.jsx)(s.h3,{id:"a-visual-understanding",children:"A Visual Understanding"}),"\n",(0,n.jsx)(s.p,{children:"Below is a possible way to visualize how our ASD infrastructure works:"}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:l})}),"\n",(0,n.jsxs)(s.p,{children:["The entire system communicates within itself using ROS2 messages. All the cool algorithms for perception, planning, control, etc. are wrapped in their own ROS2 nodes to function concurrently. Various nodes share ",(0,n.jsx)(s.code,{children:"docker containers"})," if they require the same libraries and tools to function (eg. two nodes may require the same version of PyTorch, so they share the same docker container)."]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.3"})," In your own words, describe an analogy to real life that closely resembles the ASD infrastructure."]})}),"\n",(0,n.jsx)(s.h3,{id:"on-startup",children:"On startup"}),"\n",(0,n.jsx)(s.p,{children:"The visualization of the tech stack above represents our code in a running state. On startup, watod (docker-compose under the hood) is used to orchestrate the startup of each and every docker container, ROS2 node, and core algorithms. It carefully and automatically builds out the entire software architecture piece by piece until you end up with the visualization above."}),"\n",(0,n.jsx)(s.h2,{id:"training-overview",children:"Training overview"}),"\n",(0,n.jsxs)(s.p,{children:["Now that you have a decent understanding of our tech stack. We can now move on to the final part of the ASD training. This part will be the most time consuming, and it will also contain an extremely limited amount of hand holding. ",(0,n.jsx)(s.strong,{children:"Our team is here to help, so if you have any questions, feel free to ask on Discord!"})]}),"\n",(0,n.jsx)(s.p,{children:"The provided training repository contains the following:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:["A ",(0,n.jsx)(s.a,{href:"https://gazebosim.org/home",children:"Gazebo server"}),", which is a robotics simulator allowing us to interact with and write code for a virtual robot"]}),"\n",(0,n.jsxs)(s.li,{children:["A ",(0,n.jsx)(s.a,{href:"https://app.foxglove.dev/signin",children:"Foxglove"})," websocket, which streams ROS data over a websocket to be viewed using the Foxglove ROS visualization dashboard."]}),"\n",(0,n.jsx)(s.li,{children:"ROS2, Docker, and WATO backend infrastructure."}),"\n",(0,n.jsx)(s.li,{children:"Sample ROS2 nodes in C++ and Python that demonstrate custom message passing."}),"\n",(0,n.jsx)(s.li,{children:"A barebones ROS2 node which you will write your code in."}),"\n"]}),"\n",(0,n.jsx)(s.p,{children:"The end result of the training will be some custom-written ROS2 nodes which will interact with various sensors on the robot, perform some computation/processing, and output commands to control the robot. All of this will be done using ROS2 as a framework."}),"\n",(0,n.jsx)(s.h3,{id:"set-up",children:"Set up"}),"\n",(0,n.jsx)(s.p,{children:"In previous sections, we made you analyze the ASD Training code. Now is the time to learn how to use it."}),"\n",(0,n.jsx)(s.h4,{id:"watod-orchestration",children:"Watod Orchestration"}),"\n",(0,n.jsxs)(s.p,{children:["As you may or may not know, ",(0,n.jsx)(s.code,{children:"watod"})," is used to orchestrate our entire tech stack together. It sets any necessary environment variables that our ",(0,n.jsx)(s.code,{children:"docker compose"})," profiles would need on startup, and then calls the docker compose command to begin building all the docker containers."]}),"\n",(0,n.jsxs)(s.p,{children:["Oftentimes, you don’t need to run the entire software pipeline to begin development, so you may only need to run a minimum slice of the software pipeline to begin. The powerful thing about ",(0,n.jsx)(s.code,{children:"watod"})," is that you can configure what profiles (docker-compose files) to start up. This equates to only starting only a portion of the entire stack as opposed to starting up the whole thing."]}),"\n",(0,n.jsx)(s.h4,{id:"watod-configsh",children:"watod-config.sh"}),"\n",(0,n.jsxs)(s.p,{children:["To configure what profiles to startup, we use ",(0,n.jsx)(s.code,{children:"watod-config.sh"})," Specifically the ",(0,n.jsx)(s.code,{children:"ACTIVE_PROFILES"})," field."]}),"\n",(0,n.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## ----------------------- Watod2 Configuration File Override ----------------------------"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## ACTIVE PROFILES CONFIGURATION"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## List of active profiles to run, defined in docker-compose.yaml."})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"##"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## Possible values:"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## - vis_tools : starts tools for data visualization (foxglove)"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## - gazebo : starts robot simulator (gazebo)"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## - samples : starts up sample nodes for reference (optional)"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## - robot : starts up robot nodes"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"ACTIVE_PROFILES"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"vis_tools gazebo samples"'})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## Name to append to docker containers. DEFAULT = "})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:'# COMPOSE_PROJECT_NAME=""'})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## Tag to use. Images are formatted as : with forward slashes replaced with dashes."})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## DEFAULT = "})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:'# TAG=""'})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsxs)(s.p,{children:["Your watod-config.sh would have ACTIVE_PROFILES commented out, so please populate this variable just like above. ",(0,n.jsx)(s.code,{children:"vis_tools"}),", ",(0,n.jsx)(s.code,{children:"gazebo"}),", and ",(0,n.jsx)(s.code,{children:"samples"})," are all profiles."]}),"\n",(0,n.jsx)(I.UW,{type:"error",emoji:"️❗",children:(0,n.jsx)(s.p,{children:"NOTE: Once you are a full ASD member, please leave the watod-config.sh unchanged when you make a Pull Request."})}),"\n",(0,n.jsx)(s.h4,{id:"watod-up",children:"./watod up"}),"\n",(0,n.jsxs)(s.p,{children:["After you’ve changed your ",(0,n.jsx)(s.code,{children:"watod-config.sh"}),", simply startup the training stack by entering"]}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.code,{children:"./watod up"})}),"\n",(0,n.jsxs)(s.p,{children:["in your command line while inside the ",(0,n.jsx)(s.code,{children:"./wato_asd_training/"})," directory. You should then see docker compose building all the Docker images present in each of the profiles you specified. You could startup each of the three profiles alone if you wanted to. Use ",(0,n.jsx)(s.code,{children:"Ctrl + C"})," to stop watod."]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0"})," Startup all three profiles in the training repo (vis_tools, gazebo, and samples). After you’ve done that, stop ",(0,n.jsx)(s.code,{children:"watod"})," and startup each profile alone. Do this again with two of the three profiles."]})}),"\n",(0,n.jsx)(s.h3,{id:"dev-containers",children:"Dev containers"}),"\n",(0,n.jsx)(s.p,{children:"Dev containers are a very important aspect of WATonomous’ development cycle. When you run ./watod up, you spin up multiple containers that you can develop in. Inside these containers, you can change code, debug, and develop to your heart’s content, and all changes made inside the dev container will propagate out to your host machine."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"The typical WATonomous development cycle looks like this:"})}),"\n",(0,n.jsxs)(I.Rg,{children:[(0,n.jsx)(s.h3,{id:"up-a-container",children:"Up a container"}),(0,n.jsxs)(s.p,{children:["Up a container using ./watod up\n",(0,n.jsx)(s.img,{placeholder:"blur",src:c})]}),(0,n.jsx)(s.h3,{id:"enter-the-container",children:"Enter the container"}),(0,n.jsxs)(s.p,{children:["Enter the container using VScode Docker extension\n",(0,n.jsx)(s.img,{placeholder:"blur",src:A})]}),(0,n.jsx)(s.h3,{id:"make-changes-inside-the-container",children:"Make changes inside the container"}),(0,n.jsxs)(s.p,{children:["Make changes inside the container and debug with ros2 tools\n",(0,n.jsx)(s.img,{placeholder:"blur",src:h})]}),(0,n.jsxs)(s.p,{children:["Changes in the container automatically propagate out (and vice versa)\n",(0,n.jsx)(s.img,{placeholder:"blur",src:d})]}),(0,n.jsx)(s.h3,{id:"git-add-commit-and-push",children:"Git add, commit, and push!"}),(0,n.jsx)(s.p,{children:"Outside the container, Git add, commit, and push"})]}),"\n",(0,n.jsx)(s.p,{children:"The main benefit to dev containers is that you can use linters and tools installed inside the container. For example, you cannot install numpy in the WATcloud host, but you can install numpy in a container and play around with it inside that container."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.1"})," Enter the transformer container and change its ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_asd_training/blob/074cb7400fcfdc12130b520c606044ec16083e48/src/samples/cpp/transformer/include/transformer_core.hpp#L20",children:"BUFFER_CAPACITY"}),"."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.2"})," Rebuild the transformer inside the container using colcon build ",(0,n.jsx)(s.strong,{children:"under the ~/ament_ws directory"}),". Source your changes with source install/setup.bash."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.3"})," Launch the transformer node with ros2 launch transformer transformer.launch.py. You should notice your transformer buffer fill up faster or slower based on the new BUFFER_CAPACITY you specified."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.4"})," Outside the container, check that your changes to the transformer have propagated out using git status. You should see something like the following…"]})}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:p})}),"\n",(0,n.jsx)(s.p,{children:"You can also to ./watod up outside your container, and the changes you made inside the container should reflect in the robot logs."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.5"})," ",(0,n.jsx)(s.strong,{children:"BONUS"})," when doing ./watod up, the transformer node spins up automatically. Can you think of a way to not make the node spin up? Hint: you need to stop the node from launching, but you have to keep the container running for an indefinite period."]})}),"\n",(0,n.jsx)(s.h2,{id:"foxglove",children:"Foxglove"}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:u})}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.a,{href:"https://foxglove.dev/",children:"Foxglove"})," is an open source data visualization tool for robotics. Founded by a couple of folks at ",(0,n.jsx)(s.a,{href:"https://webviz.io/",children:"Cruise"}),", Foxglove’s main advantage is its ability to visualize server-side data."]}),"\n",(0,n.jsx)(s.p,{children:"You may have heard of RViz, which is ROS’s native visualization software. As powerful as this tool can be, RViz is not great for server-side development. In order to use RViz on our server, you’d have to spin up a VNC docker container. This is what our team has done in the past, but it was terrible. RViz would run at no faster than 3fps."}),"\n",(0,n.jsx)(s.h3,{id:"port-forwarding",children:"Port Forwarding"}),"\n",(0,n.jsxs)(s.p,{children:["In a nutshell, ",(0,n.jsx)(s.a,{href:"https://www.coeosolutions.com/news/what-is-port-forwarding",children:"Port Forwarding"})," establishes a connection between a communication endpoint on one device in a private network to another device on another network. We use Port Forwarding to view Graphic User Interfaces (GUIs) and stream data to your local machine. The world of networking is scary, so we will try to make it as straightforward as possible."]}),"\n",(0,n.jsxs)(s.p,{children:["A recurring pattern for Port Forwarding is: ",(0,n.jsx)(s.code,{children:"“software A is streaming data out of some port ####, I want to read this data myself, so I will forward port #### to my machine to read it”"}),". If software A is in a Docker container on a server host machine, then you have to forward the port from the Docker container to the host machine, and then from the host machine to your local machine."]}),"\n",(0,n.jsx)(s.p,{children:"You don’t have to do much port forwarding yourself, but it’s good to know the gist of it as we move forward."}),"\n",(0,n.jsx)(s.h3,{id:"using-foxglove",children:"Using Foxglove"}),"\n",(0,n.jsxs)(s.ol,{children:["\n",(0,n.jsxs)(s.li,{children:["Make sure you have the ",(0,n.jsx)(s.strong,{children:"gazebo"})," and ",(0,n.jsx)(s.strong,{children:"vis_tools"})," profiles running"]}),"\n",(0,n.jsx)(s.li,{children:"Have some sort of web browser on your local machine"}),"\n",(0,n.jsx)(s.li,{children:"Have the asd_training_config downloaded on your local machine (this is in the training repo)"}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["Once you have the two profiles running, you should notice an auto-forwarded port in your VS Code.\n",(0,n.jsx)(s.img,{placeholder:"blur",src:x})]}),"\n",(0,n.jsx)(s.p,{children:"This port is where foxglove is streaming its data from. You don’t have to do any forwarding yourself, as we’ve done it for you ;P."}),"\n",(0,n.jsxs)(s.p,{children:["To view this data, go to the Foxglove website, create an account ",(0,n.jsx)(s.code,{children:"(with your personal email, we need to hash out sponsorship from Foxglove before you can use your watonomous email)"}),", and either visualize the data through your web browser or a local download of Foxglove."]}),"\n",(0,n.jsx)(s.h3,{id:"viewing-ros-data-through-foxglove",children:"Viewing ROS data through foxglove"}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"To view the data coming from the forwarded port"}),", in the Foxglove dashboard click Visualize Data -> Open Connection -> Foxglove Websocket and set it as the port that was forwarded. In the example above, that would be ",(0,n.jsx)(s.code,{children:"ws://localhost:33401"}),". It will be different for everyone."]}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.img,{placeholder:"blur",src:g}),"\n",(0,n.jsx)(s.img,{placeholder:"blur",src:m}),"\n",(0,n.jsx)(s.img,{placeholder:"blur",src:j})]}),"\n",(0,n.jsx)(s.p,{children:"You can now proceed to open your Foxglove Studio to begin viewing the robot simulation."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.1"})," Up the ASD Training Repo and view your robot simulation in Foxglove on your local machine."]})}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:k})}),"\n",(0,n.jsx)(s.p,{children:"Your Foxglove Studio should look something like this. Don’t worry if it looks slightly different. We will fix that now. In the ASD Training Repo, we’ve provided a configuration file that sets up your Foxglove Studio to look exactly like how we want it to look. If you haven’t yet, download the configuration file from the repo."}),"\n",(0,n.jsx)(s.p,{children:"You then need to load this file into your local Foxglove Studio."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:y})}),"\n",(0,n.jsx)(s.p,{children:"Reload your Foxglove, and you should see something like the following."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:v})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.2"})," Upload the ASD Foxglove config to your local foxglove."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.3"})," Interact with Foxglove Studio. You can move around the robot with the Teleop Panel. Here are a couple of subtasks to get you acquainted."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"Deliverable 4.3.1"})})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.3.2"})," ",(0,n.jsx)(s.strong,{children:"BONUS:"})," View the list of Topics. What is a Topic? This can be a great opportunity to get an initial idea of ROS Topics."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.3.2"})," ",(0,n.jsx)(s.strong,{children:"BONUS:"})," The visualized data is, in-reality, a bunch of numbers. Foxglove (and RViz) do some post processing to make it look intuitive. View the ",(0,n.jsx)(s.code,{children:"/lidar"})," topic’s raw data through a new Raw Message Panel."]})}),"\n",(0,n.jsx)(s.h2,{id:"hands-on-training",children:"Hands-on Training"}),"\n",(0,n.jsxs)(s.p,{children:["Now that your development environment is set up, we can begin hands-on training. You can always refer back to the previous steps for reference. ",(0,n.jsx)(s.strong,{children:"Kind reminder that our team is here to help, so if you ever have any questions, please feel free to ask in the ASD-Questions channel :)."})]}),"\n",(0,n.jsx)(s.p,{children:"Originally, the intent of this training was to code up a semi-complete navigation system to make the robot rather-intelligently navigate to a destination of your choice. However, for the sake of not blowing up your mind, we have trimmed the assignment down to at least touch all the fundamental concepts we would like you to learn. If you would like to do the whole assignment, feel free to reach out, and we can give you more info on how the robot should navigate the map."}),"\n",(0,n.jsx)(s.h3,{id:"the-basis-of-robot-navigation",children:"The Basis of Robot navigation"}),"\n",(0,n.jsxs)(s.p,{children:["Robot navigation is a very large field of study, so let’s be more specific. The navigation problem we are most concerned with here at WATonomous is: ",(0,n.jsx)(s.strong,{children:"given an end position, and no prior knowledge of the environment, navigate to that end position."})]}),"\n",(0,n.jsxs)(s.p,{children:["In Foxglove Studio, you should see an environment that closely relates to the problem above.\n",(0,n.jsx)(s.img,{placeholder:"blur",src:w})]}),"\n",(0,n.jsx)(s.p,{children:"Here, the end position can be specified by the user to be anywhere on the map."}),"\n",(0,n.jsx)(s.p,{children:"In an ideal environment, where obstacles don’t move, robot navigation can be quite simple. A naive approach to this problem could be: Occupancy, Immediate Trajectory, and Execution. We provide a brief description of them below:"}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Occupancy:"})," A map of squares indicating where the robot can and cannot go."]}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Immediate Trajectory:"})," The planned trajectory to take to get to the end position."]}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Execution:"})," Following the planned trajectory as best as we can."]}),"\n",(0,n.jsx)(s.p,{children:"In this system, the occupancy around the robot is found continuously. Given this geometric representation, we can produce a trajectory using the Immediate Trajectory planner. This trajectory is then Executed for a period of time. The whole process repeats continuously until an endpoint is reached."}),"\n",(0,n.jsx)(s.p,{children:"This is not much different to the real autonomy stack of the car. A higher fidelity Occupancy is found using Perception. Immediate trajectories need to factor in the complexity of the road with Navigation. And we mist execute on this trajectory with Control."}),"\n",(0,n.jsx)(s.p,{children:"To formalize, Perception would receive sensor messages and do processing. Navigation would receive the processed sensor data and figure out a set of actions to take. Control would receive the actions and perform them, finally sending commands to the motor. In fact, WATonomous’s software stack follows a very similar architecture, but with more node subdivision within each group to perform more complex computations."}),"\n",(0,n.jsx)(s.h3,{id:"goal",children:"Goal"}),"\n",(0,n.jsx)(s.p,{children:"The Foxglove simulation consists of a robot within an environment. The robot has three wheels, two motors, a camera, and a 1-dimensional laser scanner."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:f})}),"\n",(0,n.jsx)(s.p,{children:"You can freely control the robot’s movement within the environment using the Foxglove Teleop panel."}),"\n",(0,n.jsxs)(s.p,{children:["As you may have noticed, the Teleop control of the robot is pretty terrible. It oversteers, stops late, and has a ton of inertia. ",(0,n.jsx)(s.strong,{children:"What if we could simply instruct the robot to move to a specific point on the map instead?"})]}),"\n",(0,n.jsx)(s.p,{children:"The goal of your hands-on training is to do just that! By specifying a goal on the foxglove map, your robot should naively navigate to that point on the map."}),"\n",(0,n.jsx)(I.UW,{type:"error",emoji:"️❗",children:(0,n.jsx)(s.p,{children:"Note: It is important to know that the node you are about to create does not allow the robot to avoid obstacles. It will simply follow the straight-line trajectory to the point you specified.\nAs naive as this sounds, this is actually the basis of robot control. You will see later how this simple control algorithm you’ll make is used with the rest of the navigation stack to achieve obstacle avoidance!"})}),"\n",(0,n.jsx)(s.h3,{id:"ros",children:"ROS"}),"\n",(0,n.jsx)(s.p,{children:"Robot Operating System (ROS) is a popular framework for robotics codebases. At its core, it is a message broker – a system which manages and passes data between programs. This allows for modularity, management of complex data types, and interoperability. What makes ROS so powerful is the ability to easily interface libraries and tools that were built on top of ROS to bring advanced capabilities and interactive visualizations to your software stack. Some examples that we will be using are:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"Foxglove, a web-based ROS dashboard for visualizing data and interacting with the robot"}),"\n",(0,n.jsx)(s.li,{children:"Geometry messages, a ROS package which includes a lot of standard geometry data types like Pose, Point, Twist, Quaternion, etc."}),"\n",(0,n.jsx)(s.li,{children:"Transforms 2 (Tf2), a ROS package which allows easy transformations between coordinate frames"}),"\n"]}),"\n",(0,n.jsx)(s.p,{children:"The basic building block of the ROS framework are nodes. Nodes are responsible for performing a specific set of tasks, and interact with other nodes by receiving (subscribing) or sending out (publishing) messages. You can think of ROS like a network, with many nodes interacting with each other through ROS to achieve the overall function of the software stack."}),"\n",(0,n.jsx)(s.p,{children:"On the technical side, ROS nodes are just c++ or python programs with the ROS library. The ROS framework then manages the building, running, and message passing of each node behind the scenes. The ROS library provides the syntax for creating a node, subscribing to messages, sending out messages, and native objects for ROS messages."}),"\n",(0,n.jsx)(I.UW,{type:"error",emoji:"️❗",children:(0,n.jsx)(s.p,{children:"Note: This document will give a quick overview of ROS2 code, however we highly recommend going through the ROS2 Getting Started documentation which is much more detailed and explains ROS2 from start to finish. There may be a steep learning curve, so if you are confused throughout these training modules, feel free to ask the team leads for an explanation on discord!"})}),"\n",(0,n.jsx)(s.p,{children:"Here is an example ROS node in C++:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"#include"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"transformer_node.hpp"'})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"() : "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"Node"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"example_node"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"){"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Subscribe to a topic"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" subscription_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"create_subscription"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"/example_topic0"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"20"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bind"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"subscription_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"placeholders"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"_1));"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Publish to a topic"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" publisher_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"create_publisher"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"/example_topic1"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"20"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:");"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Subscription callback: gets called when a message from the subscription comes in"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"subscription_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"SharedPtr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" msg)"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Print to the console"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"RCLCPP_INFO"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"get_logger"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"()"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"Received message…"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:");"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" …"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Publish a message"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"publisher_"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"publish"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(msg);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Boilerplate code for starting up the node (gets called by ROS framework)"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"int"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"main"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"int"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" argc"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"char"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"**"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" argv)"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"init"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(argc"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" argv);"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"spin"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"make_shared"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:">());"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"shutdown"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"();"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:";"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"Let’s break it down."})}),"\n",(0,n.jsx)(s.p,{children:"First, we have the constructor of our node, which inherits the Node class from the ROS library (rclcpp). There we pass the name of our node, “example_node”."}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"() : "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"Node"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"example_node"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"){"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" …"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.h3,{id:"publisher",children:"Publisher"}),"\n",(0,n.jsx)(s.p,{children:"Inside the constructor, we initialize our publishers and subscribers. Here is the syntax to create a publisher with the rclcpp library:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In header file:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Publisher<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"MESSAGE_TYPE"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"SharedPtr publisher_;"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In node constructor:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"publisher_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"create_publisher"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"MESSAGE_TYPE"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"/TOPIC"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"20"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:");"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where MESSAGE_TYPE is the message you are publishing (i.e. geometry_msgs::msg::Pose for a Pose message) and “/TOPIC” is the topic you are publishing to. The 20 at the end is the buffer size, 20 is a good number for that.\nYou can then publish a message to the publisher using:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"publisher_"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"publish"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(msg);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where msg is an object of the type you specified (i.e. geometry_msgs::msg::Pose())."}),"\n",(0,n.jsx)(s.h3,{id:"subscriber",children:"Subscriber"}),"\n",(0,n.jsx)(s.p,{children:"Next, here is the syntax to create a subscriber:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In header file:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Subscription<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"MESSAGE_TYPE"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"SharedPtr subscriber_;"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In node constructor:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"subscriber_"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"create_subscription"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"MESSAGE_TYPE"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"/TOPIC"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"20"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bind"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"CALLBACK"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"placeholders"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"_1));"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where MESSAGE_TYPE is the message in the topic you want to subscribe to (i.e. geometry_msgs::msg::Pose), “/TOPIC” is the topic to subscribe to, and CALLBACK is a reference to a member function that will get called when a new message comes in (i.e. ExampleNode::subscription_callback). Again, the 20 is the buffer size, and std::placeholders::_1 is boilerplate code for calling the callback with an argument."}),"\n",(0,n.jsx)(s.p,{children:"When a message is received by the subscriber, it calls the callback function with a pointer to the message. Here is an example callback function you would define in your node class:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In header file:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"subscription_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"SharedPtr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" msg);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//Implementation:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"subscription_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"SharedPtr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" msg)"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" …"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"The ::SharedPtr is a special type of smart pointer, but it is functionally the same as a normal pointer and can be dereferenced and accessed using ->. Values of the message can be accessed just like a normal C++ object, such as “msg->position.x, msg->position.y, msg->position.z” to access the coordinates of the Pose message."}),"\n",(0,n.jsxs)(s.p,{children:["You can reference the property names of any message by searching up the message followed by “ros2” on google. For example, here are the docs for the Pose message: ",(0,n.jsx)(s.a,{href:"http://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/Pose.html",children:"docs for message"}),", and you can see all the variables you can access in it."]}),"\n",(0,n.jsx)(s.h3,{id:"timers",children:"Timers"}),"\n",(0,n.jsx)(s.p,{children:"Timers are tasks that execute in a repeated loop. Example use cases for timers are control loops which consistently update motor commands based on observations. The syntax to create a timer is the following:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//In header file:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TimerBase"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"SharedPtr timer_;"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"timer_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"();"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//In node constructor:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"timer_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"create_wall_timer"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"chrono"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"milliseconds"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(delay_ms)"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bind"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"YourNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"timer_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"));"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//Callback implementation:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" YourNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"timer_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(){"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where delay_ms is the time between timer calls in milliseconds, and YourNode::timer_callback is the callback function that is called when the timer executes."}),"\n",(0,n.jsx)(s.h3,{id:"printing",children:"Printing"}),"\n",(0,n.jsxs)(s.p,{children:["To print to the console, use RCLCPP_INFO followed by a string. You can pass additional parameters to the RCLCPP_INFO and they get added to the string using c++ sprintf ",(0,n.jsx)(s.a,{href:"https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm",children:"format"}),". For example, adding %f to the string will add in a floating point number to the print, which is passed in as a parameter to RCLCPP_INFO. The following code segment shows printing position.x to the console."]}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"RCLCPP_INFO"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"get_logger"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"()"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"Received message… x='}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"%f"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"position"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"x);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Will output “Received message… x=10.5” or whatever position.x is"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsxs)(s.p,{children:["These are all of the core functionalities to writing a node in ROS! As an exercise, try implementing a publisher and subscriber in C++ using one of the empty nodes available in the training repo, such as ",(0,n.jsx)(s.code,{children:"src/robot/control/src/control_node.cpp"})]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 5.1"})," Write a timer that executes every second that will call a callback function and print out a message of your choice to the console. Verify that it works by running ./watod up and viewing the console printouts."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 5.2"})," Create a publisher that publishes a std_msgs::msg::String message to the “/example_string” topic. Modify the timer callback function you wrote in Deliverable 5.1 to include publishing of a string message to your publisher with a message of your choice. Verify the messages are being sent by adding a “raw message” widget to foxglove and set it to your new topic name. Congratulations, you published your first message!"]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 5.3"})," Write a subscriber that receives nav_msgs::msg::Odometry messages from the “/model/robot/odometry” topic (should be automatically available via Gazebo when you run the training repo) and prints its x,y,z coordinates to the console. Reference the ros2 documentation for the Odometry message properties. Congratulations, you subscribed to your first message!"]})}),"\n",(0,n.jsx)(s.p,{children:"You have now successfully written a node to publish and subscribe to messages, which is the core functionality of ROS! You can now write nodes to interact with robots via subscribing and publishing ROS messages."}),"\n",(0,n.jsx)(s.h3,{id:"control",children:"Control"}),"\n",(0,n.jsx)(s.p,{children:"Now it’s time to implement your first node to interact with the robot! This node will be responsible for receiving a goal position to drive towards and send motor commands to the robot to drive towards it."}),"\n",(0,n.jsx)(s.p,{children:"To set a position for the robot to drive to, we will be using Foxglove’s 3D panel publish feature, which allows you to click on the 3D map and publish a message. In Foxglove under panel settings for the 3D panel, you should see the following listed under the Publish tab:"}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:b})}),"\n",(0,n.jsx)(s.p,{children:"This is where you select the message type and topic to publish to when you click on the 3D map. If you use our configuration file, these will be automatically selected to geometry_msgs::msg::Point and “/goal_point” topic."}),"\n",(0,n.jsx)(s.p,{children:"In order to publish that message, click on the circle icon at the top right of the 3D panel (shown below) and click on a point on the map."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:E})}),"\n",(0,n.jsx)(s.p,{children:"You can verify that your point is published by adding a Raw Message widget and point it to the “/goal_pose” topic and watch as a message appears when you click on the circle icon and click on the map."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:C})}),"\n",(0,n.jsxs)(s.p,{children:["The purpose of the control node is to subscribe to this topic and drive the robot towards the point you clicked on the map.\nYou will be writing code in the control node, so go ahead open up ",(0,n.jsx)(s.code,{children:"src/robot/control/src/control_node.cpp"})," in the wato_asd_training repo. Now, to receive these messages, create a new subscriber in the Control node (a blank node called Control has been setup already in the repo, you may edit that node) and make it subscribe to geometry_msgs::msg::PointStamped messages in the “/goal_point” message. Then, create a callback and print out the x,y,z values of that ",(0,n.jsx)(s.a,{href:"https://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/PointStamped.html",children:"PointStamped"})," message (reference link) using RCLCPP_INFO."]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.1"})," Create a subscriber that receives geometry_msgs::msg::PointStamped messages from the “/goal_point” topic. Verify you receive the messages by printing out the x,y,z coordinates to the console."]})}),"\n",(0,n.jsx)(s.p,{children:"Now that you can receive the goal pose, the next step is to control the robot. Firstly, we need a timer callback that will run the control loop on repeat. To do this, create a timer that runs every 100ms and calls a blank function for now."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.2"})," Create a timer that calls a blank callback function every 100ms for control. You can verify it works by printing a random message using RCLCPP_INFO."]})}),"\n",(0,n.jsx)(s.p,{children:"Inside this timer, we will be using a simple proportional controller. A proportional controller is the first term in PID control, which simply multiplies a constant scalar to the error, where error is the distance between your current point and desired point."}),"\n",(0,n.jsx)(s.p,{children:"Command = Kp * error"}),"\n",(0,n.jsx)(s.p,{children:"In this case, we have two error terms: x and y. The x error will define forward movement, and the y error will define angular movement."}),"\n",(0,n.jsx)(s.p,{children:"However, there is one problem. Currently, the point message is defined in the world coordinate frame, while we want the error in the robot’s relative coordinate frame to do relative movements. In order to do this, we will take advantage of ROS2’s transform system: TF2"}),"\n",(0,n.jsx)(s.h3,{id:"transforms",children:"Transforms"}),"\n",(0,n.jsxs)(s.p,{children:["Transforms define coordinate frames for geometry objects. One example could be a coordinate relative to the world versus relative to the robot, or a coordinate relative to an arm of the robot versus the base of the robot. Here is an illustration of a complex robot with multiple transforms (shown by red,blue,green axis markers):\n",(0,n.jsx)(s.img,{placeholder:"blur",src:Q})]}),"\n",(0,n.jsx)(s.p,{children:"Luckily, the ROS2 library has an easy way for dealing with transforms. The gazebo environment we provided automatically publishes the transform of the robot, which allows us to easily convert"}),"\n",(0,n.jsx)(s.p,{children:"a point in world space to robot space. Once the point is in robot space, we can get the relative x and y offset from the robot’s position to the point."}),"\n",(0,n.jsx)(s.p,{children:"To access ROS2’s transform system, you must create a tf_buffer and tf_listener using the following syntax:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In header file "})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"unique_ptr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf2_ros"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Buffer"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf_buffer_;"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"shared_ptr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf2_ros"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TransformListener"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf_listener_;"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In node constructor"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf_buffer_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"make_unique"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"get_clock"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"());"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf_listener_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"make_shared"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf_buffer_);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Then, to access the transform between two frames, use the following syntax:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TransformStamped transform;"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"try"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" {"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" transform "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"tf_buffer_"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"lookupTransform"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"TO_FRAME"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"FROM_FRAME"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf2"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TimePointZero);"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"} "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"catch"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf2"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TransformException "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ex) {"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"RCLCPP_INFO"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"get_logger"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"()"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"Could not transform '}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"%s"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"ex"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"what"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"());"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where “TO_FRAME” is the frame you want to transform the point into, and “FROM_FRAME” is the frame the point originated in."}),"\n",(0,n.jsx)(s.p,{children:"Then to transform a point using that frame:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"auto"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" transformed_point "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"PointStamped"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"();"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf2"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"doTransform"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(original_point"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" transformed_point"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" transform);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where original_point is the point in the FROM_FRAME and transformed_point is that same point in the TO_FRAME."}),"\n",(0,n.jsx)(s.p,{children:"We will be leveraging this code to transform our global point published to /goal_point into the robot’s frame. The goal_point will be in the frame “sim_world” (which is our global frame in this case) and the robot’s frame is “robot”."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.2"})," Create a tf_buffer and tf_listener in your control node. Then, in the control callback, get the transform from “sim_world” to “robot” and transform the goal_point to the robot frame. You may need to store goal_point in an instance variable in the Node class to access it in the timer callback after it has been received from the subscriber."]})}),"\n",(0,n.jsx)(s.p,{children:"Now that the point is transformed, the x coordinate relates to the robot’s forward motion while the y coordinate relates to the robot’s side to side motion (angular). Therefore, an easy proportional control loop may be derived where the linear command is proportional to the X component and the angular command is proportional to the Y component."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.3"})," Create two variables called Kp_linear and Kp_angular which will be the scalars for the proportional loop, initialize them to 0.5 as a good starting guess (these can be tuned later). Multiply Kp_linear by the X component of the goal_point transformed in the robot frame and Kp_angular by the Y component of the goal_point transformed in the robot frame. These will be your control signals to send to the robot."]})}),"\n",(0,n.jsxs)(s.p,{children:["Now that you have the control signals, you must publish them to the robot. By default, the robot takes in control signals in the form of a ",(0,n.jsx)(s.a,{href:"https://docs.ros.org/en/melodic/api/geometry_msgs/html/msg/Twist.html",children:"geometry_msgs::msg::Twist"})," message, which has a linear and angular velocity associated with it. The simulated robot is setup to receive Twist messages in the “/cmd_vel” topic, so you can directly send commands to the robot by creating a publisher in the control node that publishes geometry_msgs::msg::Twist messages to the “/cmd_vel” topic. Once the subscriber is made, you can then package up your linear and angular velocity commands inside of a Twist message (linear velocity will use the x component of the linear Vector3 of Twist and angular velocity will use the z component of the angular Vector3 of Twist. The rest will remain 0) and publish to the robot to control it!"]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.4"})," Create a publisher that publishes geometry_msgs::msg::Twist messages to the “/cmd_vel” topic. In your control timer, create a new Twist message, set the linear x component to the result of the linear proportional loop and the angular z component to the result of the angular proportional loop. Then, your robot should drive towards the goal_point message when you publish in Foxglove!"]})}),"\n",(0,n.jsx)(s.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,n.jsx)(s.p,{children:"Congratulations! You have now successfully controlled a robot using ROS2! The fundamentals of publishers, subscribers, and a few tricks you learned like Foxglove and transforms will be extremely valuable in your development in the WATO ASD software stack."}),"\n",(0,n.jsxs)(s.p,{children:["Please contact the Director of ASD (Eddy Zhou) on discord, showing proof of completion and 2 subteams you are interested in joining. ",(0,n.jsx)(s.a,{href:"https://www.watonomous.ca/roles",children:"Link to subteams here"}),"."]}),"\n",(0,n.jsx)(I.UW,{type:"warning",emoji:"⭐",children:(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"Welcome to WATonomous! :))"})})})]})}let D={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:s}=Object.assign({},(0,a.a)(),e.components);return s?(0,n.jsx)(s,{...e,children:(0,n.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/onboarding/asd_general_onboarding.mdx",route:"/onboarding/asd_general_onboarding",timestamp:1726189146e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"General Autonomous Software Onboarding - ASD Assignment",headings:B},pageNextRoute:"/onboarding/asd_general_onboarding",nextraLayout:i.ZP,themeConfig:r.Z};var T=(0,t.j)(D)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=871)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[785],{871:function(e,s,o){(window.__NEXT_P=window.__NEXT_P||[]).push(["/onboarding/asd_general_onboarding",function(){return o(679)}])},679:function(e,s,o){"use strict";o.r(s),o.d(s,{__toc:function(){return B},default:function(){return T}});var n=o(5893),t=o(2673),i=o(7913),r=o(4102);o(9128);var a=o(2643),l={src:"/_next/static/media/infrastructure.9106ed3d.jpg",height:303,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAAqoT/xAAaEAACAwEBAAAAAAAAAAAAAAACAwEEEQAS/9oACAEBAAE/ADrMNiIO03Ak9zI9d//EABcRAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQIBAT8AuI//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==",blurWidth:8,blurHeight:5},c={src:"/_next/static/media/container_up.599b81e2.jpg",height:74,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAEACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABgEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAjgf/xAAXEAADAQAAAAAAAAAAAAAAAAAAAgUx/9oACAEBAAE/AKWqf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:1},A={src:"/_next/static/media/vscode_docker_extension.c0d85d3e.jpg",height:349,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAngP/xAAbEAACAQUAAAAAAAAAAAAAAAABAgMAERITIf/aAAgBAQABPwDcVikuiMyyYKxHQAK//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:5},h={src:"/_next/static/media/changes.99ce1ab3.jpg",height:394,width:623,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAIEV/8QAGBABAAMBAAAAAAAAAAAAAAAAAQACAxT/2gAIAQEAAT8Ac7c910VNGf/EABYRAQEBAAAAAAAAAAAAAAAAAAEAMf/aAAgBAgEBPwB1v//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:5},d={src:"/_next/static/media/changes_outside.8bf0243b.jpg",height:395,width:625,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAn4P/xAAaEAACAgMAAAAAAAAAAAAAAAABAgADEiEj/9oACAEBAAE/AHrbGzodMZ//xAAWEQEBAQAAAAAAAAAAAAAAAAABACH/2gAIAQIBAT8AcW//xAAVEQEBAAAAAAAAAAAAAAAAAAAAAf/aAAgBAwEBPwCP/9k=",blurWidth:8,blurHeight:5},p={src:"/_next/static/media/git_status.c1c8ac78.jpg",height:145,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAIACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABgEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAjgf/xAAZEAABBQAAAAAAAAAAAAAAAAADAAQRIVL/2gAIAQEAAT8AcWIk5X//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/AH//xAAVEQEBAAAAAAAAAAAAAAAAAAAAAf/aAAgBAwEBPwCP/9k=",blurWidth:8,blurHeight:2},u={src:"/_next/static/media/foxglove.d856c80a.jpg",height:290,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAIgV/8QAHBAAAgICAwAAAAAAAAAAAAAAAQIDBAUSABEh/9oACAEBAAE/AMXDFdzENWXcK25JRuj4Of/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:5},x={src:"/_next/static/media/profiles.9298bbba.jpg",height:185,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAMACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAngP/xAAaEAABBQEAAAAAAAAAAAAAAAABAAIDETNB/9oACAEBAAE/AJcw7tlf/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:3},g={src:"/_next/static/media/foxglove_1.0e31fef7.jpg",height:117,width:368,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAMACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABgEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAjwf/xAAbEAACAQUAAAAAAAAAAAAAAAACAwABEiGS0f/aAAgBAQABPwBiAvpk9y7P/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:3},m={src:"/_next/static/media/foxglove_2.5d945360.jpg",height:252,width:379,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAI4V/8QAGxAAAQQDAAAAAAAAAAAAAAAAAQACERIUM8L/2gAIAQEAAT8Ah+UTc7Ol/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:5},j={src:"/_next/static/media/foxglove_3.87436e4b.jpg",height:277,width:479,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAT/2gAMAwEAAhADEAAAAIwUP//EABkQAAMAAwAAAAAAAAAAAAAAAAECAwATIf/aAAgBAQABPwBBellG9uvn/8QAGBEAAgMAAAAAAAAAAAAAAAAAAQIAAxH/2gAIAQIBAT8ArRWB0T//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==",blurWidth:8,blurHeight:5},k={src:"/_next/static/media/foxglove_studio.34cdd874.jpg",height:354,width:623,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABwEBAQAAAAAAAAAAAAAAAAAAAAL/2gAMAwEAAhADEAAAAJwKf//EABoQAAIDAQEAAAAAAAAAAAAAAAIDAQQRAKH/2gAIAQEAAT8Am09FmGKYQsCdEt3ye//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:5},y={src:"/_next/static/media/layout.8060e8f5.jpg",height:756,width:940,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAYACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAJAUf//EABwQAAICAgMAAAAAAAAAAAAAAAECAxIAIQQFEf/aAAgBAQABPwCLsYuCTNIjNFQgpVWFiNGp0fM//8QAFhEAAwAAAAAAAAAAAAAAAAAAAAES/9oACAECAQE/AKZ//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:6},v={src:"/_next/static/media/grid.ea05b56b.jpg",height:903,width:1600,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABwEBAQAAAAAAAAAAAAAAAAAAAgX/2gAMAwEAAhADEAAAAJwKI//EABoQAAICAwAAAAAAAAAAAAAAAAECAxEABDL/2gAIAQEAAT8AbZSJ3LRFqo9EZ//EABoRAAICAwAAAAAAAAAAAAAAAAEDAAIEERL/2gAIAQIBAT8AxUrYsm9dnqf/xAAXEQEAAwAAAAAAAAAAAAAAAAABAAIR/9oACAEDAQE/ALOM/9k=",blurWidth:8,blurHeight:5},w={src:"/_next/static/media/grid_2.2ef23278.jpg",height:507,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAgACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAwT/2gAMAwEAAhADEAAAAIQUF//EABkQAAIDAQAAAAAAAAAAAAAAABESAAEhMf/aAAgBAQABPwAUpOtyf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:8},f={src:"/_next/static/media/goal.f8fee143.jpg",height:352,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAYACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABgEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAAArwj/AP/EABoQAAEFAQAAAAAAAAAAAAAAAAMAAQISFBH/2gAIAQEAAT8A1D0sTkrXX//EABURAQEAAAAAAAAAAAAAAAAAAAAB/9oACAECAQE/AK//xAAVEQEBAAAAAAAAAAAAAAAAAAAAAf/aAAgBAwEBPwCP/9k=",blurWidth:8,blurHeight:6},b={src:"/_next/static/media/publish.a2a1af8d.jpg",height:133,width:482,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAIACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAIgV/8QAGxAAAgEFAAAAAAAAAAAAAAAAAQIEABIhIjH/2gAIAQEAAT8Aj7RmY5N3a//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:8,blurHeight:2},E={src:"/_next/static/media/goal_pose.d4fa3091.jpg",height:259,width:109,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAgAAwMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABgEBAQAAAAAAAAAAAAAAAAAAAgP/2gAMAwEAAhADEAAAAIYVP//EABgQAQEAAwAAAAAAAAAAAAAAAAIBACJB/9oACAEBAAE/AChTN+Z//8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8Aj//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z",blurWidth:3,blurHeight:8},C={src:"/_next/static/media/data.4e5d0659.jpg",height:288,width:491,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAUACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAAnAT/xAAbEAABBAMAAAAAAAAAAAAAAAACAAERIQMEE//aAAgBAQABPwDayk/OKkbX/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFhEAAwAAAAAAAAAAAAAAAAAAAAEx/9oACAEDAQE/AFD/2Q==",blurWidth:8,blurHeight:5},Q={src:"/_next/static/media/transforms.e5968bb5.jpg",height:471,width:512,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAcACAMBIgACEQEDEQH/xAAoAAEBAAAAAAAAAAAAAAAAAAAABwEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAKcJP//EAB0QAAMAAAcAAAAAAAAAAAAAAAECBAADBRESQWH/2gAIAQEAAT8AnaldSqnOceCAFSy7+nvH/8QAGREAAQUAAAAAAAAAAAAAAAAAAAECERJx/9oACAECAQE/AGradP/EABURAQEAAAAAAAAAAAAAAAAAAAAB/9oACAEDAQE/AI//2Q==",blurWidth:8,blurHeight:7},I=o(9013);let B=[{depth:2,value:"Introduction",id:"introduction"},{depth:2,value:"Server Access",id:"server-access"},{depth:2,value:"ASD Training Repository",id:"asd-training-repository"},{depth:3,value:"Our Tech stack",id:"our-tech-stack"},{depth:3,value:"ROS2",id:"ros2"},{depth:3,value:"Docker",id:"docker"},{depth:3,value:"Docker Compose and wato",id:"docker-compose-and-wato"},{depth:3,value:"What's up with the long image names?",id:"whats-up-with-the-long-image-names"},{depth:3,value:"A Visual Understanding",id:"a-visual-understanding"},{depth:3,value:"On startup",id:"on-startup"},{depth:2,value:"Training overview",id:"training-overview"},{depth:3,value:"Set up",id:"set-up"},{depth:4,value:"Watod Orchestration",id:"watod-orchestration"},{depth:4,value:"watod-config.sh",id:"watod-configsh"},{depth:4,value:"./watod up",id:"watod-up"},{depth:3,value:"Dev containers",id:"dev-containers"},{depth:3,value:"Up a container",id:"up-a-container"},{depth:3,value:"Enter the container",id:"enter-the-container"},{depth:3,value:"Make changes inside the container",id:"make-changes-inside-the-container"},{depth:3,value:"Git add, commit, and push!",id:"git-add-commit-and-push"},{depth:2,value:"Foxglove",id:"foxglove"},{depth:3,value:"Port Forwarding",id:"port-forwarding"},{depth:3,value:"Using Foxglove",id:"using-foxglove"},{depth:3,value:"Viewing ROS data through foxglove",id:"viewing-ros-data-through-foxglove"},{depth:2,value:"Hands-on Training",id:"hands-on-training"},{depth:3,value:"The Basis of Robot navigation",id:"the-basis-of-robot-navigation"},{depth:3,value:"Goal",id:"goal"},{depth:3,value:"ROS",id:"ros"},{depth:3,value:"Publisher",id:"publisher"},{depth:3,value:"Subscriber",id:"subscriber"},{depth:3,value:"Timers",id:"timers"},{depth:3,value:"Printing",id:"printing"},{depth:3,value:"Control",id:"control"},{depth:3,value:"Transforms",id:"transforms"},{depth:2,value:"Conclusion",id:"conclusion"}];function _createMdxContent(e){let s=Object.assign({h1:"h1",p:"p",a:"a",h2:"h2",strong:"strong",ol:"ol",li:"li",code:"code",h3:"h3",img:"img",ul:"ul",h4:"h4",pre:"pre",span:"span"},(0,a.a)(),e.components);return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(s.h1,{children:"General Autonomous Software Onboarding - ASD Assignment"}),"\n","\n",(0,n.jsx)(I.UW,{type:"error",emoji:"️❗",children:(0,n.jsxs)(s.p,{children:["You must complete ",(0,n.jsx)(s.a,{href:"/onboarding/asd_watcloud_dev",children:"ASD - Developing with WATcloud"})," before proceeding with this guide."]})}),"\n",(0,n.jsx)(s.h2,{id:"introduction",children:"Introduction"}),"\n",(0,n.jsx)(s.p,{children:"Congratulations! WATonomous has recognized your talent, and we would like to get you ready to contribute to the team."}),"\n",(0,n.jsx)(s.p,{children:"The Autonomous Software Division (ASD) assignment is designed to train a newcomer robotics programing at WATonomous from the ground up."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"The onboarding assignment consists of the following:"})}),"\n",(0,n.jsxs)(s.ol,{children:["\n",(0,n.jsx)(s.li,{children:(0,n.jsx)(s.strong,{children:"Accessing the WATonomous Server Cluster (WATcloud)"})}),"\n",(0,n.jsx)(s.li,{children:(0,n.jsx)(s.strong,{children:"Getting used to ROS2 - Docker robotics infrastructure"})}),"\n",(0,n.jsx)(s.li,{children:(0,n.jsx)(s.strong,{children:"Writing your own control program to direct a robot in simulation"})}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["Should you finish this assignment, you will have all the skills necessary to contribute towards building a real autonomous vehicle. Note, this assignment will not cover ML concepts. However, ML is used throughout robot autonomy, so we highly recommend you learn ML on your own time. ",(0,n.jsx)(s.a,{href:"https://mega.nz/folder/gXdVhJID#XCXqwUR-9Y43slf_LJaUDw",children:"Here’s some ML resources for you"})," (DO NOT SHARE)."]}),"\n",(0,n.jsx)(I.UW,{type:"info",emoji:"ℹ️",children:(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"After completing this assignment, you will become an official member of WATonomous with a dedicated watonomous email."})})}),"\n",(0,n.jsxs)(s.p,{children:["Please contact the Director of ASD (",(0,n.jsx)(s.a,{href:"e23zhou@watonomous.ca",children:"Eddy Zhou"}),") on discord, showing proof of completion and ",(0,n.jsx)(s.strong,{children:"two"})," subteams you are interested in joining. ",(0,n.jsx)(s.a,{href:"https://www.watonomous.ca/roles",children:"Link to subteams here"}),"."]}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"The ASD leads are here to help"})," so feel free to ask any questions about the onboarding assignment in the asd-questions channel on discord. This document is pretty long, but rest assured that a large majority of the assignment is a gigantic walk through."]}),"\n",(0,n.jsx)(s.p,{children:"Good Luck!"}),"\n",(0,n.jsx)(s.h2,{id:"server-access",children:"Server Access"}),"\n",(0,n.jsxs)(s.p,{children:["Server access steps have been moved to ",(0,n.jsx)(s.a,{href:"https://wiki.watonomous.ca/onboarding/asd_watcloud_dev",children:"here"}),". Please let the WATcloud leads know if you have any issues."]}),"\n",(0,n.jsx)(s.p,{children:"Once you've familiarized yourself"}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 1.0"})," Create a custom SLURM interactive development job. The SLURM job must have VRAM. You can specify the job to run for 2 hours so that you don't get kicked out while you are developing."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 2.0"})," Connect VS Code to a WATcloud SLURM job. Clone the ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_asd_training",children:"ASD Training Repository"})," into your WATcloud user’s home directory (~/). Open your cloned repository in VS Code. If you aren’t familiar with git, here’s a ",(0,n.jsx)(s.a,{href:"https://www.google.com/url?q=https://education.github.com/git-cheat-sheet-education.pdf&sa=D&source=docs&ust=1725499216448502&usg=AOvVaw1rF47Ac-OyVH_LDB6B4S3t",children:"cheatsheet"}),"."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 2.0.1"})," ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_monorepo",children:(0,n.jsx)(s.strong,{children:"STAR THE WATO_MONOREPO"})}),". It’s voluntary, but it will help us legitimize the repository overtime :)."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 2.1"})," In the opened ASD Training Repo, create a new branch titled ",(0,n.jsx)(s.code,{children:"_training"}),"."]})}),"\n",(0,n.jsx)(s.h2,{id:"asd-training-repository",children:"ASD Training Repository"}),"\n",(0,n.jsxs)(s.p,{children:["In the previous section, you would have cloned the ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_asd_training",children:"ASD Training Repository"})," into your user’s home directory on the server. ",(0,n.jsx)(s.strong,{children:"Please keep this folder open as you read."})]}),"\n",(0,n.jsxs)(s.p,{children:["The repository you just cloned contains a barebones setup of WATonomous’ current ASD infrastructure. It closely mirrors the infrastructure of the ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_monorepo",children:"wato_monorepo"}),", which is the central repository for all of the code that goes into the car. You can read up on the reasoning behind having a monorepo ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_monorepo/blob/main/docs/monorepo.md",children:"here"}),"."]}),"\n",(0,n.jsx)(s.h3,{id:"our-tech-stack",children:"Our Tech stack"}),"\n",(0,n.jsxs)(s.p,{children:["Our autonomy stack contains a multitude of coding tools and libraries (PyTorch, TensorFlow, OpenCV, Numpy, Scikit, Foxglove, Gymnasium, etc.), but the three ",(0,n.jsx)(s.strong,{children:"MOST-PROMINENT, REOCCURING TOOLS"})," you must know are ",(0,n.jsx)(s.a,{href:"https://docs.ros.org/en/humble/index.html",children:"ROS2"}),", ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/",children:"Docker"}),", and ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/",children:"Docker Compose"}),"."]}),"\n",(0,n.jsx)(s.p,{children:"These software tools enable cool AI tools and algorithms to communicate properly in a robotic system. Permutations of these open source tools are used throughout the cutting-edge robotics industry. It is especially prevalent in R&D, where new tools need to be integrated and tested at an alarming rate."}),"\n",(0,n.jsx)(s.h3,{id:"ros2",children:"ROS2"}),"\n",(0,n.jsxs)(s.p,{children:["The ",(0,n.jsx)(s.a,{href:"https://docs.ros.org/en/humble/index.html",children:"Robotics Operating System 2"})," (ROS2) is the second iteration of open source tools and libraries used for quickly building robot applications. It helps us intuitively do interprocess communication without the need to dig extremely deep into low-level programming. ROS2 is the monorepo’s main communication interface."]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.0"})," Identify locations in the code where ROS2 is being used."]})}),"\n",(0,n.jsx)(s.h3,{id:"docker",children:"Docker"}),"\n",(0,n.jsxs)(s.p,{children:["For our ASD stack, we use ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/",children:"Docker"})," to containerize all of our code. Docker can be thought of as a lightweight virtual machine, allowing us to create separate environments (containers) for running code. This makes our codebase portable and modular."]}),"\n",(0,n.jsxs)(s.p,{children:["Docker uses docker files (.Dockerfile extension) to configure and set up each container. Dockerfiles generally start with a machine base, and commands are specified to setup the machine, install dependencies, and setup the code workspace. Here is an ",(0,n.jsxs)(s.a,{href:"https://github.com/WATonomous/wato_monorepo/blob/main/docker/samples/cpp/aggregator.Dockerfile",children:["example dockerfile from the ",(0,n.jsx)(s.code,{children:"wato_monorepo"})]}),"."]}),"\n",(0,n.jsxs)(s.p,{children:["Generally the Dockerfiles for your modules will be set up already by your team leads. However, Docker is still a very useful tool to learn, and you can reference the ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/get-started/",children:"getting started"})]}),"\n",(0,n.jsx)(s.p,{children:"documentation for more details if interested. Those who can understand our monorepo down to the docker-level are much valued :)."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.1.0"})," Identify locations in the code where Docker is being used."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.1.1"})," What is the difference between a Dockerfile, a Docker Image, and a Docker Container? Refer to online references."]})}),"\n",(0,n.jsx)(s.h3,{id:"docker-compose-and-wato",children:"Docker Compose and wato"}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.a,{href:"https://docs.docker.com/compose/",children:"Docker Compose"})," is a utility for managing a codebase with multiple docker components. It provides an intuitive yaml interface to configure, build, and run docker containers. With a single Docker Compose command, you can startup multiple containers at once."]}),"\n",(0,n.jsx)(s.p,{children:"In our ASD training repo, and our monorepo, you will make great use of a software tool known as ‘watod’. Under the hood, watod is a wrapper around Docker Compose. You can use watod just like how you would use Docker Compose."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.0"})," Identify locations where Docker Compose is being used. What does WATO call these Docker Compose files? Hint: directory name"]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.1"})," Identify environment variables in a docker-compose file (Hint: they start with a $). How are these environment variables set during runtime? The next question can clear up this question."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.2"})," Identify any files prefixed with ‘watod’. Take a closer look at ‘watod-setup-env.sh’, what is it doing? What script do you run to run ‘watod-setup-env.sh’? How does this relate to the environment variables found in the docker-compose file?"]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.3"})," In your own words, explain what watod does. How does it interface with our docker-compose files?"]})}),"\n",(0,n.jsx)(s.h3,{id:"whats-up-with-the-long-image-names",children:"What's up with the long image names?"}),"\n",(0,n.jsxs)(s.p,{children:["You may have noticed docker image names along the lines of “git.uwaterloo.ca:5050/watonomous/wato_monorepo/…”. This long image name refers to a docker image stored in a ",(0,n.jsx)(s.a,{href:"https://docs.docker.com/registry/",children:"Docker Registry"}),"."]}),"\n",(0,n.jsx)(s.p,{children:"Building docker images from scratch can take an extremely long time. So our team takes advantage of Docker Registries to quickly pull pre-built images. This saves us a lot of time."}),"\n",(0,n.jsx)(s.p,{children:"In the ASD Training Assignment, docker registries are disabled. This is why you see the manifest error when building. No fret, we disabled registries on purpose. In the actual wato_monorepo, you’ll have to learn how to use registries."}),"\n",(0,n.jsx)(s.h3,{id:"a-visual-understanding",children:"A Visual Understanding"}),"\n",(0,n.jsx)(s.p,{children:"Below is a possible way to visualize how our ASD infrastructure works:"}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:l})}),"\n",(0,n.jsxs)(s.p,{children:["The entire system communicates within itself using ROS2 messages. All the cool algorithms for perception, planning, control, etc. are wrapped in their own ROS2 nodes to function concurrently. Various nodes share ",(0,n.jsx)(s.code,{children:"docker containers"})," if they require the same libraries and tools to function (eg. two nodes may require the same version of PyTorch, so they share the same docker container)."]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 3.2.3"})," In your own words, describe an analogy to real life that closely resembles the ASD infrastructure."]})}),"\n",(0,n.jsx)(s.h3,{id:"on-startup",children:"On startup"}),"\n",(0,n.jsx)(s.p,{children:"The visualization of the tech stack above represents our code in a running state. On startup, watod (docker-compose under the hood) is used to orchestrate the startup of each and every docker container, ROS2 node, and core algorithms. It carefully and automatically builds out the entire software architecture piece by piece until you end up with the visualization above."}),"\n",(0,n.jsx)(s.h2,{id:"training-overview",children:"Training overview"}),"\n",(0,n.jsxs)(s.p,{children:["Now that you have a decent understanding of our tech stack. We can now move on to the final part of the ASD training. This part will be the most time consuming, and it will also contain an extremely limited amount of hand holding. ",(0,n.jsx)(s.strong,{children:"Our team is here to help, so if you have any questions, feel free to ask on Discord!"})]}),"\n",(0,n.jsx)(s.p,{children:"The provided training repository contains the following:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:["A ",(0,n.jsx)(s.a,{href:"https://gazebosim.org/home",children:"Gazebo server"}),", which is a robotics simulator allowing us to interact with and write code for a virtual robot"]}),"\n",(0,n.jsxs)(s.li,{children:["A ",(0,n.jsx)(s.a,{href:"https://app.foxglove.dev/signin",children:"Foxglove"})," websocket, which streams ROS data over a websocket to be viewed using the Foxglove ROS visualization dashboard."]}),"\n",(0,n.jsx)(s.li,{children:"ROS2, Docker, and WATO backend infrastructure."}),"\n",(0,n.jsx)(s.li,{children:"Sample ROS2 nodes in C++ and Python that demonstrate custom message passing."}),"\n",(0,n.jsx)(s.li,{children:"A barebones ROS2 node which you will write your code in."}),"\n"]}),"\n",(0,n.jsx)(s.p,{children:"The end result of the training will be some custom-written ROS2 nodes which will interact with various sensors on the robot, perform some computation/processing, and output commands to control the robot. All of this will be done using ROS2 as a framework."}),"\n",(0,n.jsx)(s.h3,{id:"set-up",children:"Set up"}),"\n",(0,n.jsx)(s.p,{children:"In previous sections, we made you analyze the ASD Training code. Now is the time to learn how to use it."}),"\n",(0,n.jsx)(s.h4,{id:"watod-orchestration",children:"Watod Orchestration"}),"\n",(0,n.jsxs)(s.p,{children:["As you may or may not know, ",(0,n.jsx)(s.code,{children:"watod"})," is used to orchestrate our entire tech stack together. It sets any necessary environment variables that our ",(0,n.jsx)(s.code,{children:"docker compose"})," profiles would need on startup, and then calls the docker compose command to begin building all the docker containers."]}),"\n",(0,n.jsxs)(s.p,{children:["Oftentimes, you don’t need to run the entire software pipeline to begin development, so you may only need to run a minimum slice of the software pipeline to begin. The powerful thing about ",(0,n.jsx)(s.code,{children:"watod"})," is that you can configure what profiles (docker-compose files) to start up. This equates to only starting only a portion of the entire stack as opposed to starting up the whole thing."]}),"\n",(0,n.jsx)(s.h4,{id:"watod-configsh",children:"watod-config.sh"}),"\n",(0,n.jsxs)(s.p,{children:["To configure what profiles to startup, we use ",(0,n.jsx)(s.code,{children:"watod-config.sh"})," Specifically the ",(0,n.jsx)(s.code,{children:"ACTIVE_PROFILES"})," field."]}),"\n",(0,n.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## ----------------------- Watod2 Configuration File Override ----------------------------"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## ACTIVE PROFILES CONFIGURATION"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## List of active profiles to run, defined in docker-compose.yaml."})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"##"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## Possible values:"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## - vis_tools : starts tools for data visualization (foxglove)"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## - gazebo : starts robot simulator (gazebo)"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## - samples : starts up sample nodes for reference (optional)"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## - robot : starts up robot nodes"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"ACTIVE_PROFILES"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"vis_tools gazebo samples"'})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## Name to append to docker containers. DEFAULT = "})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:'# COMPOSE_PROJECT_NAME=""'})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## Tag to use. Images are formatted as : with forward slashes replaced with dashes."})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"## DEFAULT = "})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:'# TAG=""'})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsxs)(s.p,{children:["Your watod-config.sh would have ACTIVE_PROFILES commented out, so please populate this variable just like above. ",(0,n.jsx)(s.code,{children:"vis_tools"}),", ",(0,n.jsx)(s.code,{children:"gazebo"}),", and ",(0,n.jsx)(s.code,{children:"samples"})," are all profiles."]}),"\n",(0,n.jsx)(I.UW,{type:"error",emoji:"️❗",children:(0,n.jsx)(s.p,{children:"NOTE: Once you are a full ASD member, please leave the watod-config.sh unchanged when you make a Pull Request."})}),"\n",(0,n.jsx)(s.h4,{id:"watod-up",children:"./watod up"}),"\n",(0,n.jsxs)(s.p,{children:["After you’ve changed your ",(0,n.jsx)(s.code,{children:"watod-config.sh"}),", simply startup the training stack by entering"]}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.code,{children:"./watod up"})}),"\n",(0,n.jsxs)(s.p,{children:["in your command line while inside the ",(0,n.jsx)(s.code,{children:"./wato_asd_training/"})," directory. You should then see docker compose building all the Docker images present in each of the profiles you specified. You could startup each of the three profiles alone if you wanted to. Use ",(0,n.jsx)(s.code,{children:"Ctrl + C"})," to stop watod."]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0"})," Startup all three profiles in the training repo (vis_tools, gazebo, and samples). After you’ve done that, stop ",(0,n.jsx)(s.code,{children:"watod"})," and startup each profile alone. Do this again with two of the three profiles."]})}),"\n",(0,n.jsx)(s.h3,{id:"dev-containers",children:"Dev containers"}),"\n",(0,n.jsx)(s.p,{children:"Dev containers are a very important aspect of WATonomous’ development cycle. When you run ./watod up, you spin up multiple containers that you can develop in. Inside these containers, you can change code, debug, and develop to your heart’s content, and all changes made inside the dev container will propagate out to your host machine."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"The typical WATonomous development cycle looks like this:"})}),"\n",(0,n.jsxs)(I.Rg,{children:[(0,n.jsx)(s.h3,{id:"up-a-container",children:"Up a container"}),(0,n.jsxs)(s.p,{children:["Up a container using ./watod up\n",(0,n.jsx)(s.img,{placeholder:"blur",src:c})]}),(0,n.jsx)(s.h3,{id:"enter-the-container",children:"Enter the container"}),(0,n.jsxs)(s.p,{children:["Enter the container using VScode Docker extension\n",(0,n.jsx)(s.img,{placeholder:"blur",src:A})]}),(0,n.jsx)(s.h3,{id:"make-changes-inside-the-container",children:"Make changes inside the container"}),(0,n.jsxs)(s.p,{children:["Make changes inside the container and debug with ros2 tools\n",(0,n.jsx)(s.img,{placeholder:"blur",src:h})]}),(0,n.jsxs)(s.p,{children:["Changes in the container automatically propagate out (and vice versa)\n",(0,n.jsx)(s.img,{placeholder:"blur",src:d})]}),(0,n.jsx)(s.h3,{id:"git-add-commit-and-push",children:"Git add, commit, and push!"}),(0,n.jsx)(s.p,{children:"Outside the container, Git add, commit, and push"})]}),"\n",(0,n.jsx)(s.p,{children:"The main benefit to dev containers is that you can use linters and tools installed inside the container. For example, you cannot install numpy in the WATcloud host, but you can install numpy in a container and play around with it inside that container."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.1"})," Enter the transformer container and change its ",(0,n.jsx)(s.a,{href:"https://github.com/WATonomous/wato_asd_training/blob/074cb7400fcfdc12130b520c606044ec16083e48/src/samples/cpp/transformer/include/transformer_core.hpp#L20",children:"BUFFER_CAPACITY"}),"."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.2"})," Rebuild the transformer inside the container using colcon build ",(0,n.jsx)(s.strong,{children:"under the ~/ament_ws directory"}),". Source your changes with source install/setup.bash."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.3"})," Launch the transformer node with ros2 launch transformer transformer.launch.py. You should notice your transformer buffer fill up faster or slower based on the new BUFFER_CAPACITY you specified."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.4"})," Outside the container, check that your changes to the transformer have propagated out using git status. You should see something like the following…"]})}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:p})}),"\n",(0,n.jsx)(s.p,{children:"You can also to ./watod up outside your container, and the changes you made inside the container should reflect in the robot logs."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.0.5"})," ",(0,n.jsx)(s.strong,{children:"BONUS"})," when doing ./watod up, the transformer node spins up automatically. Can you think of a way to not make the node spin up? Hint: you need to stop the node from launching, but you have to keep the container running for an indefinite period."]})}),"\n",(0,n.jsx)(s.h2,{id:"foxglove",children:"Foxglove"}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:u})}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.a,{href:"https://foxglove.dev/",children:"Foxglove"})," is an open source data visualization tool for robotics. Founded by a couple of folks at ",(0,n.jsx)(s.a,{href:"https://webviz.io/",children:"Cruise"}),", Foxglove’s main advantage is its ability to visualize server-side data."]}),"\n",(0,n.jsx)(s.p,{children:"You may have heard of RViz, which is ROS’s native visualization software. As powerful as this tool can be, RViz is not great for server-side development. In order to use RViz on our server, you’d have to spin up a VNC docker container. This is what our team has done in the past, but it was terrible. RViz would run at no faster than 3fps."}),"\n",(0,n.jsx)(s.h3,{id:"port-forwarding",children:"Port Forwarding"}),"\n",(0,n.jsxs)(s.p,{children:["In a nutshell, ",(0,n.jsx)(s.a,{href:"https://www.coeosolutions.com/news/what-is-port-forwarding",children:"Port Forwarding"})," establishes a connection between a communication endpoint on one device in a private network to another device on another network. We use Port Forwarding to view Graphic User Interfaces (GUIs) and stream data to your local machine. The world of networking is scary, so we will try to make it as straightforward as possible."]}),"\n",(0,n.jsxs)(s.p,{children:["A recurring pattern for Port Forwarding is: ",(0,n.jsx)(s.code,{children:"“software A is streaming data out of some port ####, I want to read this data myself, so I will forward port #### to my machine to read it”"}),". If software A is in a Docker container on a server host machine, then you have to forward the port from the Docker container to the host machine, and then from the host machine to your local machine."]}),"\n",(0,n.jsx)(s.p,{children:"You don’t have to do much port forwarding yourself, but it’s good to know the gist of it as we move forward."}),"\n",(0,n.jsx)(s.h3,{id:"using-foxglove",children:"Using Foxglove"}),"\n",(0,n.jsxs)(s.ol,{children:["\n",(0,n.jsxs)(s.li,{children:["Make sure you have the ",(0,n.jsx)(s.strong,{children:"gazebo"})," and ",(0,n.jsx)(s.strong,{children:"vis_tools"})," profiles running"]}),"\n",(0,n.jsx)(s.li,{children:"Have some sort of web browser on your local machine"}),"\n",(0,n.jsx)(s.li,{children:"Have the asd_training_config downloaded on your local machine (this is in the training repo)"}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["Once you have the two profiles running, you should notice an auto-forwarded port in your VS Code.\n",(0,n.jsx)(s.img,{placeholder:"blur",src:x})]}),"\n",(0,n.jsx)(s.p,{children:"This port is where foxglove is streaming its data from. You don’t have to do any forwarding yourself, as we’ve done it for you ;P."}),"\n",(0,n.jsxs)(s.p,{children:["To view this data, go to the Foxglove website, create an account ",(0,n.jsx)(s.code,{children:"(with your personal email, we need to hash out sponsorship from Foxglove before you can use your watonomous email)"}),", and either visualize the data through your web browser or a local download of Foxglove."]}),"\n",(0,n.jsx)(s.h3,{id:"viewing-ros-data-through-foxglove",children:"Viewing ROS data through foxglove"}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"To view the data coming from the forwarded port"}),", in the Foxglove dashboard click Visualize Data -> Open Connection -> Foxglove Websocket and set it as the port that was forwarded. In the example above, that would be ",(0,n.jsx)(s.code,{children:"ws://localhost:33401"}),". It will be different for everyone."]}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.img,{placeholder:"blur",src:g}),"\n",(0,n.jsx)(s.img,{placeholder:"blur",src:m}),"\n",(0,n.jsx)(s.img,{placeholder:"blur",src:j})]}),"\n",(0,n.jsx)(s.p,{children:"You can now proceed to open your Foxglove Studio to begin viewing the robot simulation."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.1"})," Up the ASD Training Repo and view your robot simulation in Foxglove on your local machine."]})}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:k})}),"\n",(0,n.jsx)(s.p,{children:"Your Foxglove Studio should look something like this. Don’t worry if it looks slightly different. We will fix that now. In the ASD Training Repo, we’ve provided a configuration file that sets up your Foxglove Studio to look exactly like how we want it to look. If you haven’t yet, download the configuration file from the repo."}),"\n",(0,n.jsx)(s.p,{children:"You then need to load this file into your local Foxglove Studio."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:y})}),"\n",(0,n.jsx)(s.p,{children:"Reload your Foxglove, and you should see something like the following."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:v})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.2"})," Upload the ASD Foxglove config to your local foxglove."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.3"})," Interact with Foxglove Studio. You can move around the robot with the Teleop Panel. Here are a couple of subtasks to get you acquainted."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"Deliverable 4.3.1"})})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.3.2"})," ",(0,n.jsx)(s.strong,{children:"BONUS:"})," View the list of Topics. What is a Topic? This can be a great opportunity to get an initial idea of ROS Topics."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 4.3.2"})," ",(0,n.jsx)(s.strong,{children:"BONUS:"})," The visualized data is, in-reality, a bunch of numbers. Foxglove (and RViz) do some post processing to make it look intuitive. View the ",(0,n.jsx)(s.code,{children:"/lidar"})," topic’s raw data through a new Raw Message Panel."]})}),"\n",(0,n.jsx)(s.h2,{id:"hands-on-training",children:"Hands-on Training"}),"\n",(0,n.jsxs)(s.p,{children:["Now that your development environment is set up, we can begin hands-on training. You can always refer back to the previous steps for reference. ",(0,n.jsx)(s.strong,{children:"Kind reminder that our team is here to help, so if you ever have any questions, please feel free to ask in the ASD-Questions channel :)."})]}),"\n",(0,n.jsx)(s.p,{children:"Originally, the intent of this training was to code up a semi-complete navigation system to make the robot rather-intelligently navigate to a destination of your choice. However, for the sake of not blowing up your mind, we have trimmed the assignment down to at least touch all the fundamental concepts we would like you to learn. If you would like to do the whole assignment, feel free to reach out, and we can give you more info on how the robot should navigate the map."}),"\n",(0,n.jsx)(s.h3,{id:"the-basis-of-robot-navigation",children:"The Basis of Robot navigation"}),"\n",(0,n.jsxs)(s.p,{children:["Robot navigation is a very large field of study, so let’s be more specific. The navigation problem we are most concerned with here at WATonomous is: ",(0,n.jsx)(s.strong,{children:"given an end position, and no prior knowledge of the environment, navigate to that end position."})]}),"\n",(0,n.jsxs)(s.p,{children:["In Foxglove Studio, you should see an environment that closely relates to the problem above.\n",(0,n.jsx)(s.img,{placeholder:"blur",src:w})]}),"\n",(0,n.jsx)(s.p,{children:"Here, the end position can be specified by the user to be anywhere on the map."}),"\n",(0,n.jsx)(s.p,{children:"In an ideal environment, where obstacles don’t move, robot navigation can be quite simple. A naive approach to this problem could be: Occupancy, Immediate Trajectory, and Execution. We provide a brief description of them below:"}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Occupancy:"})," A map of squares indicating where the robot can and cannot go."]}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Immediate Trajectory:"})," The planned trajectory to take to get to the end position."]}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Execution:"})," Following the planned trajectory as best as we can."]}),"\n",(0,n.jsx)(s.p,{children:"In this system, the occupancy around the robot is found continuously. Given this geometric representation, we can produce a trajectory using the Immediate Trajectory planner. This trajectory is then Executed for a period of time. The whole process repeats continuously until an endpoint is reached."}),"\n",(0,n.jsx)(s.p,{children:"This is not much different to the real autonomy stack of the car. A higher fidelity Occupancy is found using Perception. Immediate trajectories need to factor in the complexity of the road with Navigation. And we mist execute on this trajectory with Control."}),"\n",(0,n.jsx)(s.p,{children:"To formalize, Perception would receive sensor messages and do processing. Navigation would receive the processed sensor data and figure out a set of actions to take. Control would receive the actions and perform them, finally sending commands to the motor. In fact, WATonomous’s software stack follows a very similar architecture, but with more node subdivision within each group to perform more complex computations."}),"\n",(0,n.jsx)(s.h3,{id:"goal",children:"Goal"}),"\n",(0,n.jsx)(s.p,{children:"The Foxglove simulation consists of a robot within an environment. The robot has three wheels, two motors, a camera, and a 1-dimensional laser scanner."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:f})}),"\n",(0,n.jsx)(s.p,{children:"You can freely control the robot’s movement within the environment using the Foxglove Teleop panel."}),"\n",(0,n.jsxs)(s.p,{children:["As you may have noticed, the Teleop control of the robot is pretty terrible. It oversteers, stops late, and has a ton of inertia. ",(0,n.jsx)(s.strong,{children:"What if we could simply instruct the robot to move to a specific point on the map instead?"})]}),"\n",(0,n.jsx)(s.p,{children:"The goal of your hands-on training is to do just that! By specifying a goal on the foxglove map, your robot should naively navigate to that point on the map."}),"\n",(0,n.jsx)(I.UW,{type:"error",emoji:"️❗",children:(0,n.jsx)(s.p,{children:"Note: It is important to know that the node you are about to create does not allow the robot to avoid obstacles. It will simply follow the straight-line trajectory to the point you specified.\nAs naive as this sounds, this is actually the basis of robot control. You will see later how this simple control algorithm you’ll make is used with the rest of the navigation stack to achieve obstacle avoidance!"})}),"\n",(0,n.jsx)(s.h3,{id:"ros",children:"ROS"}),"\n",(0,n.jsx)(s.p,{children:"Robot Operating System (ROS) is a popular framework for robotics codebases. At its core, it is a message broker – a system which manages and passes data between programs. This allows for modularity, management of complex data types, and interoperability. What makes ROS so powerful is the ability to easily interface libraries and tools that were built on top of ROS to bring advanced capabilities and interactive visualizations to your software stack. Some examples that we will be using are:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"Foxglove, a web-based ROS dashboard for visualizing data and interacting with the robot"}),"\n",(0,n.jsx)(s.li,{children:"Geometry messages, a ROS package which includes a lot of standard geometry data types like Pose, Point, Twist, Quaternion, etc."}),"\n",(0,n.jsx)(s.li,{children:"Transforms 2 (Tf2), a ROS package which allows easy transformations between coordinate frames"}),"\n"]}),"\n",(0,n.jsx)(s.p,{children:"The basic building block of the ROS framework are nodes. Nodes are responsible for performing a specific set of tasks, and interact with other nodes by receiving (subscribing) or sending out (publishing) messages. You can think of ROS like a network, with many nodes interacting with each other through ROS to achieve the overall function of the software stack."}),"\n",(0,n.jsx)(s.p,{children:"On the technical side, ROS nodes are just c++ or python programs with the ROS library. The ROS framework then manages the building, running, and message passing of each node behind the scenes. The ROS library provides the syntax for creating a node, subscribing to messages, sending out messages, and native objects for ROS messages."}),"\n",(0,n.jsx)(I.UW,{type:"error",emoji:"️❗",children:(0,n.jsx)(s.p,{children:"Note: This document will give a quick overview of ROS2 code, however we highly recommend going through the ROS2 Getting Started documentation which is much more detailed and explains ROS2 from start to finish. There may be a steep learning curve, so if you are confused throughout these training modules, feel free to ask the team leads for an explanation on discord!"})}),"\n",(0,n.jsx)(s.p,{children:"Here is an example ROS node in C++:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"#include"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"transformer_node.hpp"'})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"() : "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"Node"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"example_node"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"){"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Subscribe to a topic"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" subscription_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"create_subscription"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"/example_topic0"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"20"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bind"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"subscription_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"placeholders"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"_1));"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Publish to a topic"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" publisher_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"create_publisher"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"/example_topic1"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"20"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:");"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Subscription callback: gets called when a message from the subscription comes in"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"subscription_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"SharedPtr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" msg)"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Print to the console"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"RCLCPP_INFO"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"get_logger"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"()"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"Received message…"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:");"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" …"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Publish a message"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"publisher_"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"publish"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(msg);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Boilerplate code for starting up the node (gets called by ROS framework)"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"int"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"main"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"int"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" argc"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"char"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"**"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" argv)"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"init"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(argc"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" argv);"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"spin"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"make_shared"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:">());"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"shutdown"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"();"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:";"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"Let’s break it down."})}),"\n",(0,n.jsx)(s.p,{children:"First, we have the constructor of our node, which inherits the Node class from the ROS library (rclcpp). There we pass the name of our node, “example_node”."}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"() : "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"Node"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"example_node"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"){"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" …"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.h3,{id:"publisher",children:"Publisher"}),"\n",(0,n.jsx)(s.p,{children:"Inside the constructor, we initialize our publishers and subscribers. Here is the syntax to create a publisher with the rclcpp library:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In header file:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Publisher<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"MESSAGE_TYPE"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"SharedPtr publisher_;"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In node constructor:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"publisher_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"create_publisher"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"MESSAGE_TYPE"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"/TOPIC"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"20"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:");"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where MESSAGE_TYPE is the message you are publishing (i.e. geometry_msgs::msg::Pose for a Pose message) and “/TOPIC” is the topic you are publishing to. The 20 at the end is the buffer size, 20 is a good number for that.\nYou can then publish a message to the publisher using:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"publisher_"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"publish"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(msg);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where msg is an object of the type you specified (i.e. geometry_msgs::msg::Pose())."}),"\n",(0,n.jsx)(s.h3,{id:"subscriber",children:"Subscriber"}),"\n",(0,n.jsx)(s.p,{children:"Next, here is the syntax to create a subscriber:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In header file:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Subscription<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"MESSAGE_TYPE"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"SharedPtr subscriber_;"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In node constructor:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"subscriber_"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"create_subscription"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"MESSAGE_TYPE"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"/TOPIC"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"20"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bind"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"CALLBACK"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"placeholders"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"_1));"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where MESSAGE_TYPE is the message in the topic you want to subscribe to (i.e. geometry_msgs::msg::Pose), “/TOPIC” is the topic to subscribe to, and CALLBACK is a reference to a member function that will get called when a new message comes in (i.e. ExampleNode::subscription_callback). Again, the 20 is the buffer size, and std::placeholders::_1 is boilerplate code for calling the callback with an argument."}),"\n",(0,n.jsx)(s.p,{children:"When a message is received by the subscriber, it calls the callback function with a pointer to the message. Here is an example callback function you would define in your node class:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In header file:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"subscription_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"SharedPtr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" msg);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//Implementation:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ExampleNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"subscription_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Pose"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"SharedPtr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" msg)"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"{"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" …"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"The ::SharedPtr is a special type of smart pointer, but it is functionally the same as a normal pointer and can be dereferenced and accessed using ->. Values of the message can be accessed just like a normal C++ object, such as “msg->position.x, msg->position.y, msg->position.z” to access the coordinates of the Pose message."}),"\n",(0,n.jsxs)(s.p,{children:["You can reference the property names of any message by searching up the message followed by “ros2” on google. For example, here are the docs for the Pose message: ",(0,n.jsx)(s.a,{href:"http://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/Pose.html",children:"docs for message"}),", and you can see all the variables you can access in it."]}),"\n",(0,n.jsx)(s.h3,{id:"timers",children:"Timers"}),"\n",(0,n.jsx)(s.p,{children:"Timers are tasks that execute in a repeated loop. Example use cases for timers are control loops which consistently update motor commands based on observations. The syntax to create a timer is the following:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//In header file:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"rclcpp"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TimerBase"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"SharedPtr timer_;"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"timer_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"();"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//In node constructor:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"timer_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"create_wall_timer"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"chrono"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"milliseconds"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(delay_ms)"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bind"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"YourNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"timer_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"));"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//Callback implementation:"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"void"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" YourNode"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"timer_callback"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(){"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where delay_ms is the time between timer calls in milliseconds, and YourNode::timer_callback is the callback function that is called when the timer executes."}),"\n",(0,n.jsx)(s.h3,{id:"printing",children:"Printing"}),"\n",(0,n.jsxs)(s.p,{children:["To print to the console, use RCLCPP_INFO followed by a string. You can pass additional parameters to the RCLCPP_INFO and they get added to the string using c++ sprintf ",(0,n.jsx)(s.a,{href:"https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm",children:"format"}),". For example, adding %f to the string will add in a floating point number to the print, which is passed in as a parameter to RCLCPP_INFO. The following code segment shows printing position.x to the console."]}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"RCLCPP_INFO"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"get_logger"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"()"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"Received message… x='}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"%f"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"position"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"x);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Will output “Received message… x=10.5” or whatever position.x is"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsxs)(s.p,{children:["These are all of the core functionalities to writing a node in ROS! As an exercise, try implementing a publisher and subscriber in C++ using one of the empty nodes available in the training repo, such as ",(0,n.jsx)(s.code,{children:"src/robot/control/src/control_node.cpp"})]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 5.1"})," Write a timer that executes every second that will call a callback function and print out a message of your choice to the console. Verify that it works by running ./watod up and viewing the console printouts."]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 5.2"})," Create a publisher that publishes a std_msgs::msg::String message to the “/example_string” topic. Modify the timer callback function you wrote in Deliverable 5.1 to include publishing of a string message to your publisher with a message of your choice. Verify the messages are being sent by adding a “raw message” widget to foxglove and set it to your new topic name. Congratulations, you published your first message!"]})}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 5.3"})," Write a subscriber that receives nav_msgs::msg::Odometry messages from the “/model/robot/odometry” topic (should be automatically available via Gazebo when you run the training repo) and prints its x,y,z coordinates to the console. Reference the ros2 documentation for the Odometry message properties. Congratulations, you subscribed to your first message!"]})}),"\n",(0,n.jsx)(s.p,{children:"You have now successfully written a node to publish and subscribe to messages, which is the core functionality of ROS! You can now write nodes to interact with robots via subscribing and publishing ROS messages."}),"\n",(0,n.jsx)(s.h3,{id:"control",children:"Control"}),"\n",(0,n.jsx)(s.p,{children:"Now it’s time to implement your first node to interact with the robot! This node will be responsible for receiving a goal position to drive towards and send motor commands to the robot to drive towards it."}),"\n",(0,n.jsx)(s.p,{children:"To set a position for the robot to drive to, we will be using Foxglove’s 3D panel publish feature, which allows you to click on the 3D map and publish a message. In Foxglove under panel settings for the 3D panel, you should see the following listed under the Publish tab:"}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:b})}),"\n",(0,n.jsx)(s.p,{children:"This is where you select the message type and topic to publish to when you click on the 3D map. If you use our configuration file, these will be automatically selected to geometry_msgs::msg::Point and “/goal_point” topic."}),"\n",(0,n.jsx)(s.p,{children:"In order to publish that message, click on the circle icon at the top right of the 3D panel (shown below) and click on a point on the map."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:E})}),"\n",(0,n.jsx)(s.p,{children:"You can verify that your point is published by adding a Raw Message widget and point it to the “/goal_pose” topic and watch as a message appears when you click on the circle icon and click on the map."}),"\n",(0,n.jsx)(s.p,{children:(0,n.jsx)(s.img,{placeholder:"blur",src:C})}),"\n",(0,n.jsxs)(s.p,{children:["The purpose of the control node is to subscribe to this topic and drive the robot towards the point you clicked on the map.\nYou will be writing code in the control node, so go ahead open up ",(0,n.jsx)(s.code,{children:"src/robot/control/src/control_node.cpp"})," in the wato_asd_training repo. Now, to receive these messages, create a new subscriber in the Control node (a blank node called Control has been setup already in the repo, you may edit that node) and make it subscribe to geometry_msgs::msg::PointStamped messages in the “/goal_point” message. Then, create a callback and print out the x,y,z values of that ",(0,n.jsx)(s.a,{href:"https://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/PointStamped.html",children:"PointStamped"})," message (reference link) using RCLCPP_INFO."]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.1"})," Create a subscriber that receives geometry_msgs::msg::PointStamped messages from the “/goal_point” topic. Verify you receive the messages by printing out the x,y,z coordinates to the console."]})}),"\n",(0,n.jsx)(s.p,{children:"Now that you can receive the goal pose, the next step is to control the robot. Firstly, we need a timer callback that will run the control loop on repeat. To do this, create a timer that runs every 100ms and calls a blank function for now."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.2"})," Create a timer that calls a blank callback function every 100ms for control. You can verify it works by printing a random message using RCLCPP_INFO."]})}),"\n",(0,n.jsx)(s.p,{children:"Inside this timer, we will be using a simple proportional controller. A proportional controller is the first term in PID control, which simply multiplies a constant scalar to the error, where error is the distance between your current point and desired point."}),"\n",(0,n.jsx)(s.p,{children:"Command = Kp * error"}),"\n",(0,n.jsx)(s.p,{children:"In this case, we have two error terms: x and y. The x error will define forward movement, and the y error will define angular movement."}),"\n",(0,n.jsx)(s.p,{children:"However, there is one problem. Currently, the point message is defined in the world coordinate frame, while we want the error in the robot’s relative coordinate frame to do relative movements. In order to do this, we will take advantage of ROS2’s transform system: TF2"}),"\n",(0,n.jsx)(s.h3,{id:"transforms",children:"Transforms"}),"\n",(0,n.jsxs)(s.p,{children:["Transforms define coordinate frames for geometry objects. One example could be a coordinate relative to the world versus relative to the robot, or a coordinate relative to an arm of the robot versus the base of the robot. Here is an illustration of a complex robot with multiple transforms (shown by red,blue,green axis markers):\n",(0,n.jsx)(s.img,{placeholder:"blur",src:Q})]}),"\n",(0,n.jsx)(s.p,{children:"Luckily, the ROS2 library has an easy way for dealing with transforms. The gazebo environment we provided automatically publishes the transform of the robot, which allows us to easily convert"}),"\n",(0,n.jsx)(s.p,{children:"a point in world space to robot space. Once the point is in robot space, we can get the relative x and y offset from the robot’s position to the point."}),"\n",(0,n.jsx)(s.p,{children:"To access ROS2’s transform system, you must create a tf_buffer and tf_listener using the following syntax:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In header file "})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"unique_ptr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf2_ros"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"Buffer"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf_buffer_;"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"shared_ptr"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf2_ros"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TransformListener"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf_listener_;"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// In node constructor"})}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf_buffer_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"make_unique"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"get_clock"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"());"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf_listener_ "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" std"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"make_shared"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tf_buffer_);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Then, to access the transform between two frames, use the following syntax:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TransformStamped transform;"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"try"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" {"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" transform "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"tf_buffer_"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"lookupTransform"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"TO_FRAME"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"FROM_FRAME"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf2"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TimePointZero);"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"} "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"catch"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf2"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"TransformException "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ex) {"})]}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"RCLCPP_INFO"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"("}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"->"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"get_logger"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"()"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"Could not transform '}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"%s"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"'}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"ex"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"."}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"what"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"());"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"}"})}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where “TO_FRAME” is the frame you want to transform the point into, and “FROM_FRAME” is the frame the point originated in."}),"\n",(0,n.jsx)(s.p,{children:"Then to transform a point using that frame:"}),"\n",(0,n.jsx)(s.pre,{"data-language":"c++","data-theme":"default",children:(0,n.jsxs)(s.code,{"data-language":"c++","data-theme":"default",children:[(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"auto"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" transformed_point "}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" geometry_msgs"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"msg"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"PointStamped"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"();"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "}),"\n",(0,n.jsxs)(s.span,{className:"line",children:[(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" tf2"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:"::"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"doTransform"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"(original_point"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" transformed_point"}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,n.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" transform);"})]}),"\n",(0,n.jsx)(s.span,{className:"line",children:" "})]})}),"\n",(0,n.jsx)(s.p,{children:"Where original_point is the point in the FROM_FRAME and transformed_point is that same point in the TO_FRAME."}),"\n",(0,n.jsx)(s.p,{children:"We will be leveraging this code to transform our global point published to /goal_point into the robot’s frame. The goal_point will be in the frame “sim_world” (which is our global frame in this case) and the robot’s frame is “robot”."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.2"})," Create a tf_buffer and tf_listener in your control node. Then, in the control callback, get the transform from “sim_world” to “robot” and transform the goal_point to the robot frame. You may need to store goal_point in an instance variable in the Node class to access it in the timer callback after it has been received from the subscriber."]})}),"\n",(0,n.jsx)(s.p,{children:"Now that the point is transformed, the x coordinate relates to the robot’s forward motion while the y coordinate relates to the robot’s side to side motion (angular). Therefore, an easy proportional control loop may be derived where the linear command is proportional to the X component and the angular command is proportional to the Y component."}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.3"})," Create two variables called Kp_linear and Kp_angular which will be the scalars for the proportional loop, initialize them to 0.5 as a good starting guess (these can be tuned later). Multiply Kp_linear by the X component of the goal_point transformed in the robot frame and Kp_angular by the Y component of the goal_point transformed in the robot frame. These will be your control signals to send to the robot."]})}),"\n",(0,n.jsxs)(s.p,{children:["Now that you have the control signals, you must publish them to the robot. By default, the robot takes in control signals in the form of a ",(0,n.jsx)(s.a,{href:"https://docs.ros.org/en/melodic/api/geometry_msgs/html/msg/Twist.html",children:"geometry_msgs::msg::Twist"})," message, which has a linear and angular velocity associated with it. The simulated robot is setup to receive Twist messages in the “/cmd_vel” topic, so you can directly send commands to the robot by creating a publisher in the control node that publishes geometry_msgs::msg::Twist messages to the “/cmd_vel” topic. Once the subscriber is made, you can then package up your linear and angular velocity commands inside of a Twist message (linear velocity will use the x component of the linear Vector3 of Twist and angular velocity will use the z component of the angular Vector3 of Twist. The rest will remain 0) and publish to the robot to control it!"]}),"\n",(0,n.jsx)(I.UW,{type:"default",emoji:"✏️",children:(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.strong,{children:"Deliverable 6.4"})," Create a publisher that publishes geometry_msgs::msg::Twist messages to the “/cmd_vel” topic. In your control timer, create a new Twist message, set the linear x component to the result of the linear proportional loop and the angular z component to the result of the angular proportional loop. Then, your robot should drive towards the goal_point message when you publish in Foxglove!"]})}),"\n",(0,n.jsx)(s.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,n.jsx)(s.p,{children:"Congratulations! You have now successfully controlled a robot using ROS2! The fundamentals of publishers, subscribers, and a few tricks you learned like Foxglove and transforms will be extremely valuable in your development in the WATO ASD software stack."}),"\n",(0,n.jsxs)(s.p,{children:["Please contact the Director of ASD (Eddy Zhou) on discord, showing proof of completion and 2 subteams you are interested in joining. ",(0,n.jsx)(s.a,{href:"https://www.watonomous.ca/roles",children:"Link to subteams here"}),"."]}),"\n",(0,n.jsx)(I.UW,{type:"warning",emoji:"⭐",children:(0,n.jsx)(s.p,{children:(0,n.jsx)(s.strong,{children:"Welcome to WATonomous! :))"})})})]})}let D={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:s}=Object.assign({},(0,a.a)(),e.components);return s?(0,n.jsx)(s,{...e,children:(0,n.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/onboarding/asd_general_onboarding.mdx",route:"/onboarding/asd_general_onboarding",timestamp:1726352057e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"General Autonomous Software Onboarding - ASD Assignment",headings:B},pageNextRoute:"/onboarding/asd_general_onboarding",nextraLayout:i.ZP,themeConfig:r.Z};var T=(0,t.j)(D)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=871)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/onboarding/asd_watcloud_dev-7f8279904ea7777e.js b/_next/static/chunks/pages/onboarding/asd_watcloud_dev-7f8279904ea7777e.js new file mode 100644 index 0000000..a392c12 --- /dev/null +++ b/_next/static/chunks/pages/onboarding/asd_watcloud_dev-7f8279904ea7777e.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[375],{457:function(e,s,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/onboarding/asd_watcloud_dev",function(){return n(1570)}])},1570:function(e,s,n){"use strict";n.r(s),n.d(s,{__toc:function(){return u},default:function(){return x}});var o=n(5893),t=n(2673),i=n(7913),a=n(4102);n(9128);var r=n(2643),l={src:"/_next/static/media/watcloud_afar.6f5294d1.jpg",height:2244,width:2904,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAYACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAsAP/xAAZEAEAAgMAAAAAAAAAAAAAAAABAAMCIWH/2gAIAQEAAT8AcbCw2dn/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwCP/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:6},c={src:"/_next/static/media/vscode_ssh.c0aa57be.gif",height:720,width:1280,blurWidth:0,blurHeight:0},h={src:"/_next/static/media/slurm_dev.0e2a590e.gif",height:720,width:1280,blurWidth:0,blurHeight:0},d=n(9013);let u=[{depth:2,value:"Why WATcloud? What is WATcloud?",id:"why-watcloud-what-is-watcloud"},{depth:2,value:"A Look from Afar",id:"a-look-from-afar"},{depth:3,value:"How does WATcloud share compute resources fairly?",id:"how-does-watcloud-share-compute-resources-fairly"},{depth:3,value:"So how does remote development actually work?",id:"so-how-does-remote-development-actually-work"},{depth:3,value:"Job Scheduling vs Interactive Development",id:"job-scheduling-vs-interactive-development"},{depth:2,value:"Setting up WATcloud for ASD",id:"setting-up-watcloud-for-asd"},{depth:2,value:"General Setup",id:"general-setup"},{depth:3,value:"[Local Machine] Clone the wato_asd_tooling repository",id:"local-machine-clone-the-wato_asd_tooling-repository"},{depth:3,value:"[Local Machine] Generate an SSH config",id:"local-machine-generate-an-ssh-config"},{depth:3,value:"[Local Machine] Setup VScode for SSH",id:"local-machine-setup-vscode-for-ssh"},{depth:3,value:"[Local Machine] Setup Agent Forwarding",id:"local-machine-setup-agent-forwarding"},{depth:3,value:"[Host Machine] Confirm Agent Forwarding Works",id:"host-machine-confirm-agent-forwarding-works"},{depth:2,value:"Setup for Interactive Development",id:"setup-for-interactive-development"},{depth:3,value:"One-time Setup",id:"one-time-setup"},{depth:3,value:"[Host Machine] Add your public key into the SLURM node's authorized keys",id:"host-machine-add-your-public-key-into-the-slurm-nodes-authorized-keys"},{depth:3,value:"[Host Machine] Add the following to your bashrc in your SLURM node",id:"host-machine-add-the-following-to-your-bashrc-in-your-slurm-node"},{depth:3,value:"[Local Machine] Build the Computer you desire!",id:"local-machine-build-the-computer-you-desire"},{depth:3,value:"[Local Machine] Start a SLURM Dev Session",id:"local-machine-start-a-slurm-dev-session"},{depth:3,value:"[Local Machine] Setup SSH for SLURM",id:"local-machine-setup-ssh-for-slurm"},{depth:3,value:"[Local Machine] Stop the SLURM Dev Session",id:"local-machine-stop-the-slurm-dev-session"},{depth:3,value:"Starting a SLURM Dev Session Regularly",id:"starting-a-slurm-dev-session-regularly"},{depth:3,value:"[Local Machine][Optional] Build the Computer you desire!",id:"local-machineoptional-build-the-computer-you-desire"},{depth:3,value:"[Local Machine] Start a SLURM Dev Session",id:"local-machine-start-a-slurm-dev-session-1"},{depth:3,value:"[Local Machine] Connect to the SLURM Dev Session with VScode",id:"local-machine-connect-to-the-slurm-dev-session-with-vscode"},{depth:2,value:"Setup for Job Scheduling",id:"setup-for-job-scheduling"}];function _createMdxContent(e){let s=Object.assign({h1:"h1",p:"p",a:"a",h2:"h2",strong:"strong",h3:"h3",ul:"ul",li:"li",code:"code",img:"img",pre:"pre",span:"span"},(0,r.a)(),e.components);return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.h1,{children:"Developing with WATcloud"}),"\n","\n",(0,o.jsx)(d.UW,{type:"error",emoji:"️❗",children:(0,o.jsxs)(s.p,{children:["You must complete your ",(0,o.jsx)(s.a,{href:"https://cloud.watonomous.ca/docs/compute-cluster/getting-access",children:"Cluster Access Form"})," before proceeding with this guide."]})}),"\n",(0,o.jsx)(s.p,{children:"Here, we discuss setting up WATcloud to be used for software development in the Autonomous Software Division."}),"\n",(0,o.jsx)(s.h2,{id:"why-watcloud-what-is-watcloud",children:"Why WATcloud? What is WATcloud?"}),"\n",(0,o.jsxs)(s.p,{children:["Due to the high computational requirements of many aspects of the ASD stack, WATO has a large server infrastructure for remote development ",(0,o.jsx)(s.a,{href:"https://cloud.watonomous.ca/",children:"WATcloud"}),". In this section, you will learn to connect to WATcloud on VS code. Connecting to a server to do remote development is not only a crucial aspect of software development at WATonomous, but is also a very common practice in the industry."]}),"\n",(0,o.jsx)(d.UW,{type:"info",emoji:"ℹ️",children:(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.strong,{children:"Fun Fact:"})," WATcloud closely mimics server infrastructures used by OpenAI, NASA, Nvidia, and more!"]})}),"\n",(0,o.jsx)(s.h2,{id:"a-look-from-afar",children:"A Look from Afar"}),"\n",(0,o.jsx)(s.h3,{id:"how-does-watcloud-share-compute-resources-fairly",children:"How does WATcloud share compute resources fairly?"}),"\n",(0,o.jsxs)(s.p,{children:["WATcloud relies heavily on a resource management tool known as ",(0,o.jsx)(s.a,{href:"https://slurm.schedmd.com/overview.html",children:"SLURM"}),". SLURM ensures that all resources in WATcloud are shared in a fair and well-managed manner."]}),"\n",(0,o.jsx)(s.p,{children:'For the everyday developer, you can imagine SLURM as a "build your own computer" tool. You specify to SLURM what compute resources you want (CPU, RAM, GPU, memory, time limit, etc.) and SLURM will build a compute node with the resources it has on hand.'}),"\n",(0,o.jsx)(s.h3,{id:"so-how-does-remote-development-actually-work",children:"So how does remote development actually work?"}),"\n",(0,o.jsx)(s.p,{children:"Remote development for a WATonomous member typically consists of a local machine, host machine, SLURM node, and a docker container. They are defined below:"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"Local Machine"})," Your personal computer."]}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"Host Machine"})," The computer you connect to. In the case of WATcloud, this is the SLURM login node."]}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"SLURM Node"})," Used to manage compute resources. It creates SLURM Jobs according to your needs."]}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"SLURM Job"}),' An "imaginary computer" that is created by WATcloud. You specify to WATcloud what compute you need by running commands in the SLURM login node.']}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"Docker Container"})," An isolated coding environment."]}),"\n"]}),"\n",(0,o.jsx)(s.p,{children:"To do remote development in the Autonomous Software Division, the process can be summed up by the image below:"}),"\n",(0,o.jsx)(s.p,{children:(0,o.jsx)(s.img,{placeholder:"blur",src:l})}),"\n",(0,o.jsx)(d.UW,{type:"warning",emoji:"⚠️",children:(0,o.jsxs)(s.p,{children:["As shown in the image, ",(0,o.jsx)(s.strong,{children:"there are two ways to use a SLURM node"}),"."]})}),"\n",(0,o.jsx)(s.h3,{id:"job-scheduling-vs-interactive-development",children:"Job Scheduling vs Interactive Development"}),"\n",(0,o.jsx)(s.p,{children:"Use job scheduling when you want to run a command for a very long time (>1 day long). Use interactive development when you are actively making changes to your code and testing it."}),"\n",(0,o.jsx)(s.p,{children:"For most WATonomous members, you would use job scheduling for tasks like training neural networks, large data processing, numerical optimization, etc. On the other hand, you would use interactive development when you are coding/testing ROS2 nodes, interacting with / visualizing live data, making code changes in general, etc."}),"\n",(0,o.jsx)(s.h2,{id:"setting-up-watcloud-for-asd",children:"Setting up WATcloud for ASD"}),"\n",(0,o.jsx)(d.UW,{type:"warning",emoji:"⚠️",children:(0,o.jsx)(s.p,{children:"This section is experimental. Please let us know of any issues on our Discord"})}),"\n",(0,o.jsx)(s.p,{children:"Dealing with SSH can be quite foreign to alot of new developers. Thankfully, we provide a series of helper scripts that will make setup for WATcloud easier on you."}),"\n",(0,o.jsx)(s.h2,{id:"general-setup",children:"General Setup"}),"\n","\n",(0,o.jsx)(s.p,{children:"This section is required so that you have proper access to our server cluster."}),"\n",(0,o.jsxs)(d.Rg,{children:[(0,o.jsx)(s.h3,{id:"local-machine-clone-the-wato_asd_tooling-repository",children:"[Local Machine] Clone the wato_asd_tooling repository"}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsx)(s.code,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"git"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"clone"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"git@github.com:WATonomous/wato_asd_tooling.git"})]})})}),(0,o.jsx)(s.h3,{id:"local-machine-generate-an-ssh-config",children:"[Local Machine] Generate an SSH config"}),(0,o.jsxs)(s.p,{children:["If you have never created an ",(0,o.jsx)(s.code,{children:"~/.ssh/config"})," file before, do that now. Note, we assume that all your SSH files are stored under ",(0,o.jsx)(s.code,{children:"~/.ssh"})]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsx)(s.code,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"touch"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"~/.ssh/config"})]})})}),(0,o.jsx)(s.p,{children:"Generate a WATcloud SSH config. Follow the prompts whenever you get them."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"ssh_helpers/generate_ssh_config.sh"})]})]})}),(0,o.jsx)(s.p,{children:"You should now be able to connect our cluster using these commands:"}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"tr-ubuntu3"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"derek3-ubuntu2"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"delta-ubuntu2"})]})]})}),(0,o.jsx)(s.h3,{id:"local-machine-setup-vscode-for-ssh",children:"[Local Machine] Setup VScode for SSH"}),(0,o.jsxs)(s.p,{children:["To do this, download the ",(0,o.jsx)(s.code,{children:"Remote - SSH"})," VScode Extension. After that, you should be able to attach VScode to any of the machines."]}),(0,o.jsx)(s.p,{children:(0,o.jsx)(s.img,{src:c})}),(0,o.jsx)(s.h3,{id:"local-machine-setup-agent-forwarding",children:"[Local Machine] Setup Agent Forwarding"}),(0,o.jsx)(s.p,{children:"Agent forwarding lets us carry our identity onto other machines that we connect to. What this means is, you can use git commands on other machines without having to create an SSH key on each machine you connect to."}),(0,o.jsx)(s.p,{children:"Setup agent forwarding with our helper script. Follow the prompts whenever you get them."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"ssh_helpers/configure_agent_forwarding.sh"})]})]})}),(0,o.jsx)(s.h3,{id:"host-machine-confirm-agent-forwarding-works",children:"[Host Machine] Confirm Agent Forwarding Works"}),(0,o.jsxs)(s.p,{children:["You should now be able to use git on all the WATcloud machines you connect to. Confirm by running the following ",(0,o.jsx)(s.strong,{children:"inside a WATcloud machine you connected to"}),"."]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsx)(s.code,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"-T"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"git@github.com"})]})})})]}),"\n",(0,o.jsx)(d.UW,{type:"default",emoji:"✏️",children:(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.strong,{children:"Deliverable"})," Get SSH and SSH Agent Forwarding working."]})}),"\n",(0,o.jsx)(s.h2,{id:"setup-for-interactive-development",children:"Setup for Interactive Development"}),"\n",(0,o.jsx)(s.p,{children:"Unlike job scheduling, SLURM was not built to handle interactive development. Luckily we have a team of very talented individuals, and we managed to make interactive development work nonetheless :)."}),"\n",(0,o.jsx)(s.p,{children:"Creating an interactive development environment entails starting an SSH server inside the SLURM node, some wacky SSH key sharing, a netcast proxycommand, as well as pointing docker to a persistent filesystem. You don't have to do that though. You just need to do the following."}),"\n",(0,o.jsx)(s.h3,{id:"one-time-setup",children:"One-time Setup"}),"\n",(0,o.jsx)(s.p,{children:"Follow these steps if you are setting up the SLURM dev sessions for the first time, or you were using past solutions for SLURM that WATO provided."}),"\n",(0,o.jsxs)(d.Rg,{children:[(0,o.jsx)(s.h3,{id:"host-machine-add-your-public-key-into-the-slurm-nodes-authorized-keys",children:"[Host Machine] Add your public key into the SLURM node's authorized keys"}),(0,o.jsx)(s.p,{children:"Copy your public key on your local computer and paste it into the authorized_keys file on the SLURM node."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"your_local_machine$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"cat"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"~/.ssh/your_key.pub"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" (copy "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"the"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"output"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"of"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"this"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:")"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"your_local_machine$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" [tr-ubuntu3 "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"or"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"derek3-ubuntu2]"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"watcloud_slurm_node$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"touch"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"~/.ssh/authorized_keys"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"watcloud_slurm_node$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"nano"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"~/.ssh/authorized_keys"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" (paste "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"you"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"rpublic"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"key"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"into"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"here"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:")"})]})]})}),(0,o.jsx)(s.h3,{id:"host-machine-add-the-following-to-your-bashrc-in-your-slurm-node",children:"[Host Machine] Add the following to your bashrc in your SLURM node"}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsx)(s.code,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"watcloud_slurm_node$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"nano"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"~/.bashrc"})]})})}),(0,o.jsxs)(s.p,{children:["Add the following to the end of your ",(0,o.jsx)(s.code,{children:"~/.bashrc"})," file."]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"if"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" [[ "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"$('}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"hostname"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:')"'}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"slurm"'}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" ]]; "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"then"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" XDG_RUNTIME_DIR"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"${XDG_RUNTIME_DIR"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":-/"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tmp"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"/"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"run}"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" XDG_CONFIG_HOME"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"${XDG_CONFIG_HOME"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":-/"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"tmp"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"/"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:"config}"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" DOCKER_HOST"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"unix://${XDG_RUNTIME_DIR}/docker.sock"'})]}),"\n",(0,o.jsx)(s.span,{className:"line",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"fi"})})]})}),(0,o.jsx)(s.h3,{id:"local-machine-build-the-computer-you-desire",children:"[Local Machine] Build the Computer you desire!"}),(0,o.jsxs)(s.p,{children:["Use your favorite text editor to edit ",(0,o.jsx)(s.code,{children:"wato_asd_tooling/session_config.sh"}),"."]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"nano"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"session_config.sh"})]})]})}),(0,o.jsx)(s.p,{children:"The file itself contains descriptions of all of the parameters and how to set them."}),(0,o.jsx)(s.h3,{id:"local-machine-start-a-slurm-dev-session",children:"[Local Machine] Start a SLURM Dev Session"}),(0,o.jsx)(s.p,{children:"Run the helper script to startup a SLURM Dev Node. Follow all the prompts carefully."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"start_interactive_session.sh"})]})]})}),(0,o.jsx)(d.UW,{type:"error",emoji:"\uD83D\uDEAB",children:(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.strong,{children:"DO NOT START MORE THAN ONE DEV NODE."})," You have a chance of corrupting your docker filesystem. Starting more than one dev node is like building multiple computers. It is NOT the same as creating multiple terminals."]})}),(0,o.jsx)(s.h3,{id:"local-machine-setup-ssh-for-slurm",children:"[Local Machine] Setup SSH for SLURM"}),(0,o.jsxs)(s.p,{children:["Run this last helper script ",(0,o.jsx)(s.strong,{children:"LOCALLY"}),". Follow the prompts carefully."]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"ssh_helpers/setup_slurm_ssh.sh"})]})]})}),(0,o.jsx)(s.h3,{id:"local-machine-stop-the-slurm-dev-session",children:"[Local Machine] Stop the SLURM Dev Session"}),(0,o.jsxs)(s.p,{children:["Stop the interactive session by hitting ",(0,o.jsx)(s.code,{children:"Ctrl+C"}),"."]})]}),"\n",(0,o.jsx)(s.h3,{id:"starting-a-slurm-dev-session-regularly",children:"Starting a SLURM Dev Session Regularly"}),"\n",(0,o.jsx)(s.p,{children:"Once you have done the one-time setup, connecting to a SLURM Dev Session is easy."}),"\n",(0,o.jsxs)(d.Rg,{children:[(0,o.jsx)(s.h3,{id:"local-machineoptional-build-the-computer-you-desire",children:"[Local Machine][Optional] Build the Computer you desire!"}),(0,o.jsxs)(s.p,{children:["Use your favorite text editor to edit ",(0,o.jsx)(s.code,{children:"wato_asd_tooling/session_config.sh"}),"."]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"nano"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"session_config.sh"})]})]})}),(0,o.jsx)(s.h3,{id:"local-machine-start-a-slurm-dev-session-1",children:"[Local Machine] Start a SLURM Dev Session"}),(0,o.jsx)(s.p,{children:"Run the helper script to startup a SLURM Dev Node. Follow all the prompts carefully."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"start_interactive_session.sh"})]})]})}),(0,o.jsx)(s.h3,{id:"local-machine-connect-to-the-slurm-dev-session-with-vscode",children:"[Local Machine] Connect to the SLURM Dev Session with VScode"}),(0,o.jsx)(s.p,{children:"You can connect to the SLURM Dev Session using the VScode ssh extension. The remote host is called asd-dev-session by default."}),(0,o.jsx)(s.p,{children:(0,o.jsx)(s.img,{src:h})})]}),"\n",(0,o.jsx)(d.UW,{type:"default",children:(0,o.jsxs)(s.p,{children:["And you're good to go! Whenever you want to startup a SLURM Dev Node, start one up by running ",(0,o.jsx)(s.code,{children:"start_interactive_session.sh"}),", and then SSH into the SLURM node through VScode."]})}),"\n",(0,o.jsx)(s.h2,{id:"setup-for-job-scheduling",children:"Setup for Job Scheduling"}),"\n",(0,o.jsxs)(s.p,{children:["There is no setup. Creating an SLURM job is really easy. It was what SLURM was designed for. You can view docs on SLURM in the ",(0,o.jsx)(s.a,{href:"https://cloud.watonomous.ca/docs/compute-cluster/slurm",children:"WATcloud documentation"}),"."]}),"\n",(0,o.jsx)(d.UW,{type:"default",emoji:"✏️",children:(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.strong,{children:"Deliverable"})," Run a SLURM batch job with 2 CPUs that counts to 60."]})})]})}let p={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:s}=Object.assign({},(0,r.a)(),e.components);return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/onboarding/asd_watcloud_dev.mdx",route:"/onboarding/asd_watcloud_dev",timestamp:1726352057e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Developing with WATcloud",headings:u},pageNextRoute:"/onboarding/asd_watcloud_dev",nextraLayout:i.ZP,themeConfig:a.Z};var x=(0,t.j)(p)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=457)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/onboarding/asd_watcloud_dev-9e5669c2bda3b049.js b/_next/static/chunks/pages/onboarding/asd_watcloud_dev-9e5669c2bda3b049.js deleted file mode 100644 index 1c5b3c2..0000000 --- a/_next/static/chunks/pages/onboarding/asd_watcloud_dev-9e5669c2bda3b049.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[375],{457:function(e,s,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/onboarding/asd_watcloud_dev",function(){return n(1570)}])},1570:function(e,s,n){"use strict";n.r(s),n.d(s,{__toc:function(){return u},default:function(){return g}});var o=n(5893),t=n(2673),i=n(7913),a=n(4102);n(9128);var r=n(2643),l={src:"/_next/static/media/watcloud_afar.6f5294d1.jpg",height:2244,width:2904,blurDataURL:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAYACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAsAP/xAAZEAEAAgMAAAAAAAAAAAAAAAABAAMCIWH/2gAIAQEAAT8AcbCw2dn/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwCP/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=",blurWidth:8,blurHeight:6},c={src:"/_next/static/media/vscode_ssh.c0aa57be.gif",height:720,width:1280,blurWidth:0,blurHeight:0},h={src:"/_next/static/media/slurm_dev.0e2a590e.gif",height:720,width:1280,blurWidth:0,blurHeight:0},d=n(9013);let u=[{depth:2,value:"Why WATcloud? What is WATcloud?",id:"why-watcloud-what-is-watcloud"},{depth:2,value:"A Look from Afar",id:"a-look-from-afar"},{depth:3,value:"How does WATcloud share compute resources fairly?",id:"how-does-watcloud-share-compute-resources-fairly"},{depth:3,value:"So how does remote development actually work?",id:"so-how-does-remote-development-actually-work"},{depth:3,value:"Job Scheduling vs Interactive Development",id:"job-scheduling-vs-interactive-development"},{depth:2,value:"Setting up WATcloud for ASD",id:"setting-up-watcloud-for-asd"},{depth:2,value:"General Setup",id:"general-setup"},{depth:3,value:"[Local Machine] Clone the wato_asd_tooling repository",id:"local-machine-clone-the-wato_asd_tooling-repository"},{depth:3,value:"[Local Machine] Generate an SSH config",id:"local-machine-generate-an-ssh-config"},{depth:3,value:"[Local Machine] Setup VScode for SSH",id:"local-machine-setup-vscode-for-ssh"},{depth:3,value:"[Local Machine] Setup Agent Forwarding",id:"local-machine-setup-agent-forwarding"},{depth:3,value:"[Host Machine] Confirm Agent Forwarding Works",id:"host-machine-confirm-agent-forwarding-works"},{depth:2,value:"Setup for Interactive Development",id:"setup-for-interactive-development"},{depth:3,value:"One-time Setup",id:"one-time-setup"},{depth:3,value:"[Host Machine] Add your public key into the SLURM node's authorized keys",id:"host-machine-add-your-public-key-into-the-slurm-nodes-authorized-keys"},{depth:3,value:"[Local Machine] Build the Computer you desire!",id:"local-machine-build-the-computer-you-desire"},{depth:3,value:"[Local Machine] Start a SLURM Dev Session",id:"local-machine-start-a-slurm-dev-session"},{depth:3,value:"[Local Machine] Setup SSH for SLURM",id:"local-machine-setup-ssh-for-slurm"},{depth:3,value:"[Local Machine] Stop the SLURM Dev Session",id:"local-machine-stop-the-slurm-dev-session"},{depth:3,value:"Starting a SLURM Dev Session Regularly",id:"starting-a-slurm-dev-session-regularly"},{depth:3,value:"[Local Machine][Optional] Build the Computer you desire!",id:"local-machineoptional-build-the-computer-you-desire"},{depth:3,value:"[Local Machine] Start a SLURM Dev Session",id:"local-machine-start-a-slurm-dev-session-1"},{depth:3,value:"[Local Machine] Connect to the SLURM Dev Session with VScode",id:"local-machine-connect-to-the-slurm-dev-session-with-vscode"},{depth:2,value:"Setup for Job Scheduling",id:"setup-for-job-scheduling"}];function _createMdxContent(e){let s=Object.assign({h1:"h1",p:"p",a:"a",h2:"h2",strong:"strong",h3:"h3",ul:"ul",li:"li",code:"code",img:"img",pre:"pre",span:"span"},(0,r.a)(),e.components);return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.h1,{children:"Developing with WATcloud"}),"\n","\n",(0,o.jsx)(d.UW,{type:"error",emoji:"️❗",children:(0,o.jsxs)(s.p,{children:["You must complete your ",(0,o.jsx)(s.a,{href:"https://cloud.watonomous.ca/docs/compute-cluster/getting-access",children:"Cluster Access Form"})," before proceeding with this guide."]})}),"\n",(0,o.jsx)(s.p,{children:"Here, we discuss setting up WATcloud to be used for software development in the Autonomous Software Division."}),"\n",(0,o.jsx)(s.h2,{id:"why-watcloud-what-is-watcloud",children:"Why WATcloud? What is WATcloud?"}),"\n",(0,o.jsxs)(s.p,{children:["Due to the high computational requirements of many aspects of the ASD stack, WATO has a large server infrastructure for remote development ",(0,o.jsx)(s.a,{href:"https://cloud.watonomous.ca/",children:"WATcloud"}),". In this section, you will learn to connect to WATcloud on VS code. Connecting to a server to do remote development is not only a crucial aspect of software development at WATonomous, but is also a very common practice in the industry."]}),"\n",(0,o.jsx)(d.UW,{type:"info",emoji:"ℹ️",children:(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.strong,{children:"Fun Fact:"})," WATcloud closely mimics server infrastructures used by OpenAI, NASA, Nvidia, and more!"]})}),"\n",(0,o.jsx)(s.h2,{id:"a-look-from-afar",children:"A Look from Afar"}),"\n",(0,o.jsx)(s.h3,{id:"how-does-watcloud-share-compute-resources-fairly",children:"How does WATcloud share compute resources fairly?"}),"\n",(0,o.jsxs)(s.p,{children:["WATcloud relies heavily on a resource management tool known as ",(0,o.jsx)(s.a,{href:"https://slurm.schedmd.com/overview.html",children:"SLURM"}),". SLURM ensures that all resources in WATcloud are shared in a fair and well-managed manner."]}),"\n",(0,o.jsx)(s.p,{children:'For the everyday developer, you can imagine SLURM as a "build your own computer" tool. You specify to SLURM what compute resources you want (CPU, RAM, GPU, memory, time limit, etc.) and SLURM will build a compute node with the resources it has on hand.'}),"\n",(0,o.jsx)(s.h3,{id:"so-how-does-remote-development-actually-work",children:"So how does remote development actually work?"}),"\n",(0,o.jsx)(s.p,{children:"Remote development for a WATonomous member typically consists of a local machine, host machine, SLURM node, and a docker container. They are defined below:"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"Local Machine"})," Your personal computer."]}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"Host Machine"})," The computer you connect to. In the case of WATcloud, this is the SLURM login node."]}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"SLURM Node"})," Used to manage compute resources. It creates SLURM Jobs according to your needs."]}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"SLURM Job"}),' An "imaginary computer" that is created by WATcloud. You specify to WATcloud what compute you need by running commands in the SLURM login node.']}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"Docker Container"})," An isolated coding environment."]}),"\n"]}),"\n",(0,o.jsx)(s.p,{children:"To do remote development in the Autonomous Software Division, the process can be summed up by the image below:"}),"\n",(0,o.jsx)(s.p,{children:(0,o.jsx)(s.img,{placeholder:"blur",src:l})}),"\n",(0,o.jsx)(d.UW,{type:"warning",emoji:"⚠️",children:(0,o.jsxs)(s.p,{children:["As shown in the image, ",(0,o.jsx)(s.strong,{children:"there are two ways to use a SLURM node"}),"."]})}),"\n",(0,o.jsx)(s.h3,{id:"job-scheduling-vs-interactive-development",children:"Job Scheduling vs Interactive Development"}),"\n",(0,o.jsx)(s.p,{children:"Use job scheduling when you want to run a command for a very long time (>1 day long). Use interactive development when you are actively making changes to your code and testing it."}),"\n",(0,o.jsx)(s.p,{children:"For most WATonomous members, you would use job scheduling for tasks like training neural networks, large data processing, numerical optimization, etc. On the other hand, you would use interactive development when you are coding/testing ROS2 nodes, interacting with / visualizing live data, making code changes in general, etc."}),"\n",(0,o.jsx)(s.h2,{id:"setting-up-watcloud-for-asd",children:"Setting up WATcloud for ASD"}),"\n",(0,o.jsx)(d.UW,{type:"warning",emoji:"⚠️",children:(0,o.jsx)(s.p,{children:"This section is experimental. Please let us know of any issues on our Discord"})}),"\n",(0,o.jsx)(s.p,{children:"Dealing with SSH can be quite foreign to alot of new developers. Thankfully, we provide a series of helper scripts that will make setup for WATcloud easier on you."}),"\n",(0,o.jsx)(s.h2,{id:"general-setup",children:"General Setup"}),"\n","\n",(0,o.jsx)(s.p,{children:"This section is required so that you have proper access to our server cluster."}),"\n",(0,o.jsxs)(d.Rg,{children:[(0,o.jsx)(s.h3,{id:"local-machine-clone-the-wato_asd_tooling-repository",children:"[Local Machine] Clone the wato_asd_tooling repository"}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsx)(s.code,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"git"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"clone"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"git@github.com:WATonomous/wato_asd_tooling.git"})]})})}),(0,o.jsx)(s.h3,{id:"local-machine-generate-an-ssh-config",children:"[Local Machine] Generate an SSH config"}),(0,o.jsxs)(s.p,{children:["If you have never created an ",(0,o.jsx)(s.code,{children:"~/.ssh/config"})," file before, do that now. Note, we assume that all your SSH files are stored under ",(0,o.jsx)(s.code,{children:"~/.ssh"})]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsx)(s.code,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"touch"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"~/.ssh/config"})]})})}),(0,o.jsx)(s.p,{children:"Generate a WATcloud SSH config. Follow the prompts whenever you get them."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"ssh_helpers/generate_ssh_config.sh"})]})]})}),(0,o.jsx)(s.p,{children:"You should now be able to connect our cluster using these commands:"}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"tr-ubuntu3"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"derek3-ubuntu2"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"delta-ubuntu2"})]})]})}),(0,o.jsx)(s.h3,{id:"local-machine-setup-vscode-for-ssh",children:"[Local Machine] Setup VScode for SSH"}),(0,o.jsxs)(s.p,{children:["To do this, download the ",(0,o.jsx)(s.code,{children:"Remote - SSH"})," VScode Extension. After that, you should be able to attach VScode to any of the machines."]}),(0,o.jsx)(s.p,{children:(0,o.jsx)(s.img,{src:c})}),(0,o.jsx)(s.h3,{id:"local-machine-setup-agent-forwarding",children:"[Local Machine] Setup Agent Forwarding"}),(0,o.jsx)(s.p,{children:"Agent forwarding lets us carry our identity onto other machines that we connect to. What this means is, you can use git commands on other machines without having to create an SSH key on each machine you connect to."}),(0,o.jsx)(s.p,{children:"Setup agent forwarding with our helper script. Follow the prompts whenever you get them."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"ssh_helpers/configure_agent_forwarding.sh"})]})]})}),(0,o.jsx)(s.h3,{id:"host-machine-confirm-agent-forwarding-works",children:"[Host Machine] Confirm Agent Forwarding Works"}),(0,o.jsxs)(s.p,{children:["You should now be able to use git on all the WATcloud machines you connect to. Confirm by running the following ",(0,o.jsx)(s.strong,{children:"inside a WATcloud machine you connected to"}),"."]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsx)(s.code,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"-T"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"git@github.com"})]})})})]}),"\n",(0,o.jsx)(d.UW,{type:"default",emoji:"✏️",children:(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.strong,{children:"Deliverable"})," Get SSH and SSH Agent Forwarding working."]})}),"\n",(0,o.jsx)(s.h2,{id:"setup-for-interactive-development",children:"Setup for Interactive Development"}),"\n",(0,o.jsx)(s.p,{children:"Unlike job scheduling, SLURM was not built to handle interactive development. Luckily we have a team of very talented individuals, and we managed to make interactive development work nonetheless :)."}),"\n",(0,o.jsx)(s.p,{children:"Creating an interactive development environment entails starting an SSH server inside the SLURM node, some wacky SSH key sharing, a netcast proxycommand, as well as pointing docker to a persistent filesystem. You don't have to do that though. You just need to do the following."}),"\n",(0,o.jsx)(s.h3,{id:"one-time-setup",children:"One-time Setup"}),"\n",(0,o.jsx)(s.p,{children:"Follow these steps if you are setting up the SLURM dev sessions for the first time, or you were using past solutions for SLURM that WATO provided."}),"\n",(0,o.jsxs)(d.Rg,{children:[(0,o.jsx)(s.h3,{id:"host-machine-add-your-public-key-into-the-slurm-nodes-authorized-keys",children:"[Host Machine] Add your public key into the SLURM node's authorized keys"}),(0,o.jsx)(s.p,{children:"Copy your public key on your local computer and paste it into the authorized_keys file on the SLURM node."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"your_local_machine$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"cat"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"~/.ssh/your_key.pub"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" (copy "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"the"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"output"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"of"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"this"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:")"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"your_local_machine$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"ssh"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" [tr-ubuntu3 "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"or"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"derek3-ubuntu2]"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"watcloud_slurm_node$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"touch"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"~/.ssh/authorized_keys"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"watcloud_slurm_node$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"nano"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"~/.ssh/authorized_keys"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" (paste "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"you"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"rpublic"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"key"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"into"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"here"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:")"})]})]})}),(0,o.jsx)(s.h3,{id:"local-machine-build-the-computer-you-desire",children:"[Local Machine] Build the Computer you desire!"}),(0,o.jsxs)(s.p,{children:["Use your favorite text editor to edit ",(0,o.jsx)(s.code,{children:"wato_asd_tooling/session_config.sh"}),"."]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"nano"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"session_config.sh"})]})]})}),(0,o.jsx)(s.p,{children:"The file itself contains descriptions of all of the parameters and how to set them."}),(0,o.jsx)(s.h3,{id:"local-machine-start-a-slurm-dev-session",children:"[Local Machine] Start a SLURM Dev Session"}),(0,o.jsx)(s.p,{children:"Run the helper script to startup a SLURM Dev Node. Follow all the prompts carefully."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"start_interactive_session.sh"})]})]})}),(0,o.jsx)(d.UW,{type:"error",emoji:"\uD83D\uDEAB",children:(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.strong,{children:"DO NOT START MORE THAN ONE DEV NODE."})," You have a chance of corrupting your docker filesystem. Starting more than one dev node is like building multiple computers. It is NOT the same as creating multiple terminals."]})}),(0,o.jsx)(s.h3,{id:"local-machine-setup-ssh-for-slurm",children:"[Local Machine] Setup SSH for SLURM"}),(0,o.jsxs)(s.p,{children:["Run this last helper script ",(0,o.jsx)(s.strong,{children:"LOCALLY"}),". Follow the prompts carefully."]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"ssh_helpers/setup_slurm_ssh.sh"})]})]})}),(0,o.jsx)(s.h3,{id:"local-machine-stop-the-slurm-dev-session",children:"[Local Machine] Stop the SLURM Dev Session"}),(0,o.jsxs)(s.p,{children:["Stop the interactive session by hitting ",(0,o.jsx)(s.code,{children:"Ctrl+C"}),"."]})]}),"\n",(0,o.jsx)(s.h3,{id:"starting-a-slurm-dev-session-regularly",children:"Starting a SLURM Dev Session Regularly"}),"\n",(0,o.jsx)(s.p,{children:"Once you have done the one-time setup, connecting to a SLURM Dev Session is easy."}),"\n",(0,o.jsxs)(d.Rg,{children:[(0,o.jsx)(s.h3,{id:"local-machineoptional-build-the-computer-you-desire",children:"[Local Machine][Optional] Build the Computer you desire!"}),(0,o.jsxs)(s.p,{children:["Use your favorite text editor to edit ",(0,o.jsx)(s.code,{children:"wato_asd_tooling/session_config.sh"}),"."]}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"nano"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"session_config.sh"})]})]})}),(0,o.jsx)(s.h3,{id:"local-machine-start-a-slurm-dev-session-1",children:"[Local Machine] Start a SLURM Dev Session"}),(0,o.jsx)(s.p,{children:"Run the helper script to startup a SLURM Dev Node. Follow all the prompts carefully."}),(0,o.jsx)(s.pre,{"data-language":"bash","data-theme":"default",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"default",children:[(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"cd"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"wato_asd_tooling"})]}),"\n",(0,o.jsxs)(s.span,{className:"line",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"bash"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-color-text)"},children:" "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:"start_interactive_session.sh"})]})]})}),(0,o.jsx)(s.h3,{id:"local-machine-connect-to-the-slurm-dev-session-with-vscode",children:"[Local Machine] Connect to the SLURM Dev Session with VScode"}),(0,o.jsx)(s.p,{children:"You can connect to the SLURM Dev Session using the VScode ssh extension. The remote host is called asd-dev-session by default."}),(0,o.jsx)(s.p,{children:(0,o.jsx)(s.img,{src:h})})]}),"\n",(0,o.jsx)(d.UW,{type:"default",children:(0,o.jsxs)(s.p,{children:["And you're good to go! Whenever you want to startup a SLURM Dev Node, start one up by running ",(0,o.jsx)(s.code,{children:"start_interactive_session.sh"}),", and then SSH into the SLURM node through VScode."]})}),"\n",(0,o.jsx)(s.h2,{id:"setup-for-job-scheduling",children:"Setup for Job Scheduling"}),"\n",(0,o.jsxs)(s.p,{children:["There is no setup. Creating an SLURM job is really easy. It was what SLURM was designed for. You can view docs on SLURM in the ",(0,o.jsx)(s.a,{href:"https://cloud.watonomous.ca/docs/compute-cluster/slurm",children:"WATcloud documentation"}),"."]}),"\n",(0,o.jsx)(d.UW,{type:"default",emoji:"✏️",children:(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.strong,{children:"Deliverable"})," Run a SLURM batch job with 2 CPUs that counts to 60."]})})]})}let p={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:s}=Object.assign({},(0,r.a)(),e.components);return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/onboarding/asd_watcloud_dev.mdx",route:"/onboarding/asd_watcloud_dev",timestamp:1726189146e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"Developing with WATcloud",headings:u},pageNextRoute:"/onboarding/asd_watcloud_dev",nextraLayout:i.ZP,themeConfig:a.Z};var g=(0,t.j)(p)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=457)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/onboarding/vp_general_onboard-6b966bc9a65115d9.js b/_next/static/chunks/pages/onboarding/vp_general_onboard-30ddbb1624efcb3b.js similarity index 99% rename from _next/static/chunks/pages/onboarding/vp_general_onboard-6b966bc9a65115d9.js rename to _next/static/chunks/pages/onboarding/vp_general_onboard-30ddbb1624efcb3b.js index 2e1bcba..673b9eb 100644 --- a/_next/static/chunks/pages/onboarding/vp_general_onboard-6b966bc9a65115d9.js +++ b/_next/static/chunks/pages/onboarding/vp_general_onboard-30ddbb1624efcb3b.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[740],{4816:function(e,n,o){(window.__NEXT_P=window.__NEXT_P||[]).push(["/onboarding/vp_general_onboard",function(){return o(3406)}])},3406:function(e,n,o){"use strict";o.r(n),o.d(n,{__toc:function(){return d}});var t=o(5893),a=o(2673),r=o(7913),i=o(4102);o(9128);var s=o(2643);let d=[{depth:3,value:"What is WATonomous? (Short form: WATO)",id:"what-is-watonomous-short-form-wato"},{depth:3,value:"Past Achievements",id:"past-achievements"},{depth:3,value:"Current Plan",id:"current-plan"},{depth:3,value:"Impact, Goals",id:"impact-goals"},{depth:2,value:"A Bit About Vehicle Platform...",id:"a-bit-about-vehicle-platform"},{depth:3,value:"Interfacing",id:"interfacing"},{depth:3,value:"Power systems",id:"power-systems"},{depth:3,value:"Onboarding Challenges:",id:"onboarding-challenges"}];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",hr:"hr",h3:"h3",h2:"h2",blockquote:"blockquote",strong:"strong",em:"em",ul:"ul",li:"li",a:"a"},(0,s.a)(),e.components);return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{children:"WATO-VP Onboarding Document"}),"\n",(0,t.jsx)(n.p,{children:"Last modified: September 11th, 2024"}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"what-is-watonomous-short-form-wato",children:"What is WATonomous? (Short form: WATO)"}),"\n",(0,t.jsx)(n.p,{children:"Started in summer 2017, WATonomous is the University of Waterloo's first student-run autonomous car team. We are an agile group of developers, engineers, and businessmen looking to lead the next generation of robotic systems and their applications. Ultimately, we just want to prove that you don't need a room full of current FAANG employees to build a self-driving car! All you need for a self-driving car is a self-driven individual ;)"}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"past-achievements",children:"Past Achievements"}),"\n",(0,t.jsx)(n.p,{children:"Up till 2023, we had a Chevrolet, and we are proud to have finished 2nd place in the SAE AutoDrive Challenge, an international competition to build a Level 4 autonomous vehicle in four years!"}),"\n",(0,t.jsx)(n.p,{children:"Following the competition, we continued our path towards perfect autonomy, completing research papers on various autonomous vehicle technologies including action classification, control, and environment modeling. We have a track record of submitting to prestigious research conferences like ICRA, ICVES, and ITS."}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"current-plan",children:"Current Plan"}),"\n",(0,t.jsx)(n.p,{children:"We have a brand new car, a Kia Soul EV! We are planning on reaching level 5 autonomy all from scratch! Progress has been made over the past year, and we are aiming to be able to test track it by the end of summer 2025!"}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"impact-goals",children:"Impact, Goals"}),"\n",(0,t.jsx)(n.p,{children:"We want to prove that a group of dedicated students, and NOT industry professionals, can work together and create a self-driving automotive system that can compete with the likes of the WAYMO taxi and Google Self-Driving Car."}),"\n",(0,t.jsx)(n.p,{children:"For the end of the Fall 2024 term, we hope to have the majority of our vehicle platforming work completed and to have the car (along with its peripheral systems) operate under its own power. We are also expecting a substantial computing power upgrade to the onboard systems that we hope to have ready by the Winter 2024 term."}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"a-bit-about-vehicle-platform",children:"A Bit About Vehicle Platform..."}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Directors"}),": Xander Hayhoe, Mahir Mahota"]}),"\n"]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"interfacing",children:"Interfacing"}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsxs)(n.em,{children:[(0,t.jsx)(n.strong,{children:"Leads"}),":"]})," Mariam ElSahhar"]}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsxs)(n.em,{children:[(0,t.jsx)(n.strong,{children:"Current Projects"}),":"]})}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"Most components on the interfacing PCBs are soldered. We do need to add connectors and completely resolder a whole new board."}),"\n",(0,t.jsx)(n.li,{children:"Boards and all assembled hardware logic need to be thoroughly tested (based on predetermined test parameters) before implementation into the car."}),"\n",(0,t.jsx)(n.li,{children:"Install DBW system with the joysticking tele-drive module for user overriding."}),"\n",(0,t.jsx)(n.li,{children:"Migrate EXISTING Joystick controller code from ROS1 to ROS2."}),"\n",(0,t.jsx)(n.li,{children:"Implement arbiter and MUX logic for the vehicle architecture, signals control."}),"\n",(0,t.jsx)(n.li,{children:"Run final tests of the ass"}),"\n"]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"power-systems",children:"Power systems"}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.strong,{children:"Leads"})}),": Benjamin Liu"]}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.strong,{children:"Current Projects:"})}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"For the end of October, we hope to have the car’s sensor rack and compute module running off of the internal 12V battery. In the car’s current state, these systems along with other peripherals run off of external power sources such as a wall outlet. To reach a minimal viable product, we need this crucial step to be completed."}),"\n"]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"onboarding-challenges",children:"Onboarding Challenges:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Interfacing Challenge"}),": Do the ASD Assignment, as it uses the ROS2 Stack, which VP will do a lot of work with! ",(0,t.jsx)(n.a,{href:"https://docs.google.com/document/d/1AO1wNM4qa_GpoK7cK_W0Uw-AMHI5p-IsOJXWxOdIJB4/edit#heading=h.s0i0mec3io06",children:"WATO ASD Assignment"})]}),"\n"]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.em,{children:"Note"}),": Instead of sending proof of completion to Eddy, please send the proof to ",(0,t.jsx)(n.a,{href:"mailto:amwhayho@watonomous.ca",children:"Xander"}),"; Discord: @Xander1452."]}),"\n"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Electrical Challenge"}),": Take the Electrical Safety and Awareness course offered on LEARN to all Waterloo students (self-enrollment)."]}),"\n"]})]})}let l={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,s.a)(),e.components);return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/onboarding/vp_general_onboard.mdx",route:"/onboarding/vp_general_onboard",timestamp:1726189146e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"WATO-VP Onboarding Document",headings:d},pageNextRoute:"/onboarding/vp_general_onboard",nextraLayout:r.ZP,themeConfig:i.Z};n.default=(0,a.j)(l)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=4816)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[740],{4816:function(e,n,o){(window.__NEXT_P=window.__NEXT_P||[]).push(["/onboarding/vp_general_onboard",function(){return o(3406)}])},3406:function(e,n,o){"use strict";o.r(n),o.d(n,{__toc:function(){return d}});var t=o(5893),a=o(2673),r=o(7913),i=o(4102);o(9128);var s=o(2643);let d=[{depth:3,value:"What is WATonomous? (Short form: WATO)",id:"what-is-watonomous-short-form-wato"},{depth:3,value:"Past Achievements",id:"past-achievements"},{depth:3,value:"Current Plan",id:"current-plan"},{depth:3,value:"Impact, Goals",id:"impact-goals"},{depth:2,value:"A Bit About Vehicle Platform...",id:"a-bit-about-vehicle-platform"},{depth:3,value:"Interfacing",id:"interfacing"},{depth:3,value:"Power systems",id:"power-systems"},{depth:3,value:"Onboarding Challenges:",id:"onboarding-challenges"}];function _createMdxContent(e){let n=Object.assign({h1:"h1",p:"p",hr:"hr",h3:"h3",h2:"h2",blockquote:"blockquote",strong:"strong",em:"em",ul:"ul",li:"li",a:"a"},(0,s.a)(),e.components);return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{children:"WATO-VP Onboarding Document"}),"\n",(0,t.jsx)(n.p,{children:"Last modified: September 11th, 2024"}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"what-is-watonomous-short-form-wato",children:"What is WATonomous? (Short form: WATO)"}),"\n",(0,t.jsx)(n.p,{children:"Started in summer 2017, WATonomous is the University of Waterloo's first student-run autonomous car team. We are an agile group of developers, engineers, and businessmen looking to lead the next generation of robotic systems and their applications. Ultimately, we just want to prove that you don't need a room full of current FAANG employees to build a self-driving car! All you need for a self-driving car is a self-driven individual ;)"}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"past-achievements",children:"Past Achievements"}),"\n",(0,t.jsx)(n.p,{children:"Up till 2023, we had a Chevrolet, and we are proud to have finished 2nd place in the SAE AutoDrive Challenge, an international competition to build a Level 4 autonomous vehicle in four years!"}),"\n",(0,t.jsx)(n.p,{children:"Following the competition, we continued our path towards perfect autonomy, completing research papers on various autonomous vehicle technologies including action classification, control, and environment modeling. We have a track record of submitting to prestigious research conferences like ICRA, ICVES, and ITS."}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"current-plan",children:"Current Plan"}),"\n",(0,t.jsx)(n.p,{children:"We have a brand new car, a Kia Soul EV! We are planning on reaching level 5 autonomy all from scratch! Progress has been made over the past year, and we are aiming to be able to test track it by the end of summer 2025!"}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"impact-goals",children:"Impact, Goals"}),"\n",(0,t.jsx)(n.p,{children:"We want to prove that a group of dedicated students, and NOT industry professionals, can work together and create a self-driving automotive system that can compete with the likes of the WAYMO taxi and Google Self-Driving Car."}),"\n",(0,t.jsx)(n.p,{children:"For the end of the Fall 2024 term, we hope to have the majority of our vehicle platforming work completed and to have the car (along with its peripheral systems) operate under its own power. We are also expecting a substantial computing power upgrade to the onboard systems that we hope to have ready by the Winter 2024 term."}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"a-bit-about-vehicle-platform",children:"A Bit About Vehicle Platform..."}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Directors"}),": Xander Hayhoe, Mahir Mahota"]}),"\n"]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"interfacing",children:"Interfacing"}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsxs)(n.em,{children:[(0,t.jsx)(n.strong,{children:"Leads"}),":"]})," Mariam ElSahhar"]}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsxs)(n.em,{children:[(0,t.jsx)(n.strong,{children:"Current Projects"}),":"]})}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"Most components on the interfacing PCBs are soldered. We do need to add connectors and completely resolder a whole new board."}),"\n",(0,t.jsx)(n.li,{children:"Boards and all assembled hardware logic need to be thoroughly tested (based on predetermined test parameters) before implementation into the car."}),"\n",(0,t.jsx)(n.li,{children:"Install DBW system with the joysticking tele-drive module for user overriding."}),"\n",(0,t.jsx)(n.li,{children:"Migrate EXISTING Joystick controller code from ROS1 to ROS2."}),"\n",(0,t.jsx)(n.li,{children:"Implement arbiter and MUX logic for the vehicle architecture, signals control."}),"\n",(0,t.jsx)(n.li,{children:"Run final tests of the ass"}),"\n"]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"power-systems",children:"Power systems"}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.strong,{children:"Leads"})}),": Benjamin Liu"]}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.strong,{children:"Current Projects:"})}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"For the end of October, we hope to have the car’s sensor rack and compute module running off of the internal 12V battery. In the car’s current state, these systems along with other peripherals run off of external power sources such as a wall outlet. To reach a minimal viable product, we need this crucial step to be completed."}),"\n"]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"onboarding-challenges",children:"Onboarding Challenges:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Interfacing Challenge"}),": Do the ASD Assignment, as it uses the ROS2 Stack, which VP will do a lot of work with! ",(0,t.jsx)(n.a,{href:"https://docs.google.com/document/d/1AO1wNM4qa_GpoK7cK_W0Uw-AMHI5p-IsOJXWxOdIJB4/edit#heading=h.s0i0mec3io06",children:"WATO ASD Assignment"})]}),"\n"]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.em,{children:"Note"}),": Instead of sending proof of completion to Eddy, please send the proof to ",(0,t.jsx)(n.a,{href:"mailto:amwhayho@watonomous.ca",children:"Xander"}),"; Discord: @Xander1452."]}),"\n"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Electrical Challenge"}),": Take the Electrical Safety and Awareness course offered on LEARN to all Waterloo students (self-enrollment)."]}),"\n"]})]})}let l={MDXContent:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}=Object.assign({},(0,s.a)(),e.components);return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(_createMdxContent,{...e})}):_createMdxContent(e)},pageOpts:{filePath:"pages/onboarding/vp_general_onboard.mdx",route:"/onboarding/vp_general_onboard",timestamp:1726352057e3,pageMap:[{kind:"Meta",data:{index:"Welcome to the Wiki",about:"About WATonomous",onboarding:"Onboarding","Official Website":{title:"Official Website ↗",type:"page",href:"https://www.watonomous.ca",newWindow:!0},"WATcloud Website":{title:"WATcloud Website ↗",type:"page",href:"https://cloud.watonomous.ca/docs",newWindow:!0},finance:"Finance"}},{kind:"MdxPage",name:"about",route:"/about"},{kind:"Folder",name:"finance",route:"/finance",children:[{kind:"MdxPage",name:"creating_personal_purchases",route:"/finance/creating_personal_purchases"},{kind:"MdxPage",name:"creating_purchase_requests",route:"/finance/creating_purchase_requests"},{kind:"Meta",data:{creating_personal_purchases:"Creating Personal Purchases",creating_purchase_requests:"Creating Purchase Requests"}}]},{kind:"MdxPage",name:"finance",route:"/finance"},{kind:"MdxPage",name:"index",route:"/"},{kind:"Folder",name:"onboarding",route:"/onboarding",children:[{kind:"Meta",data:{asd_watcloud_dev:"ASD - Developing with WATcloud",asd_general_onboarding:"ASD - General Onboarding",vp_general_onboard:"VP - General Onboarding"}},{kind:"MdxPage",name:"asd_general_onboarding",route:"/onboarding/asd_general_onboarding"},{kind:"MdxPage",name:"asd_watcloud_dev",route:"/onboarding/asd_watcloud_dev"},{kind:"MdxPage",name:"vp_general_onboard",route:"/onboarding/vp_general_onboard"}]},{kind:"MdxPage",name:"onboarding",route:"/onboarding"}],flexsearch:{codeblocks:!0},title:"WATO-VP Onboarding Document",headings:d},pageNextRoute:"/onboarding/vp_general_onboard",nextraLayout:r.ZP,themeConfig:i.Z};n.default=(0,a.j)(l)}},function(e){e.O(0,[318,630,774,888,179],function(){return e(e.s=4816)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/about.html b/about.html index 5984e55..5b77402 100644 --- a/about.html +++ b/about.html @@ -11,11 +11,11 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
About WATonomous

About WATonomous

+
About WATonomous

About WATonomous

WATonomous is the autonomous vehicle design team at the University of Waterloo. We are an agile group of developers, engineers, businessmen, and marketers looking to lead the next generation of robotic systems and their applications to society.

Our Big Audacious Goal

Members of WATonomous center around a singular goal:

💡

To show the world a bunch of students can build a self-driving car!

-

... and in doing so, drive ourselves to become better people :).


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file +

... and in doing so, drive ourselves to become better people :).


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file diff --git a/finance.html b/finance.html index 9249ffa..54aad7a 100644 --- a/finance.html +++ b/finance.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
Finance

Finance System

+
Finance

Finance System

Instructions on how to use the WATonomous Finance System, which is used to track funding, our account balances, and manage and reimburse purhcases.

Creating and Submitting a Personal Purchase Request Contains details for members on how to purchase items using their own money and get reimbursed for it. (go-to)

-

Creating and Submitting a Purchase Request Contains details for members on how to purchase items using WATonomous cash or directly from one of our funds. (go-to)


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file +

Creating and Submitting a Purchase Request Contains details for members on how to purchase items using WATonomous cash or directly from one of our funds. (go-to)


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file diff --git a/finance/creating_personal_purchases.html b/finance/creating_personal_purchases.html index a6109bb..82ae9f2 100644 --- a/finance/creating_personal_purchases.html +++ b/finance/creating_personal_purchases.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
Creating Personal Purchases

Creating Personal Purchases

+

Made with using and
by WATonomous and WATcloud.
\ No newline at end of file +

Made with using and
by WATonomous and WATcloud.
\ No newline at end of file diff --git a/finance/creating_purchase_requests.html b/finance/creating_purchase_requests.html index ecca1a7..41109af 100644 --- a/finance/creating_purchase_requests.html +++ b/finance/creating_purchase_requests.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
Creating Purchase Requests

Creating Purchase Requests

+
Creating Purchase Requests

Creating Purchase Requests

  1. Go to https://finance-frontend.watonomous.ca/ (opens in a new tab)
  2. Create New Ticket > Ticket Type: UW Finance Purchase
  3. @@ -20,4 +20,4 @@
  4. Once this has been completed, the item will be sent to the finance coordinator. They will purchase the item, in which case the status will be ORDERED
  5. Once the item has arrived, the item will be transitioned to READY_FOR_PICKUP, and the pick up instructions will give you next steps
  6. Once completed, the item should be in the PICKED_UP state, completing the flow.
  7. -

Made with using and
by WATonomous and WATcloud.
\ No newline at end of file +

Made with using and
by WATonomous and WATcloud.
\ No newline at end of file diff --git a/index.html b/index.html index 334e9a1..fc2bc74 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
Welcome to the Wiki

Welcome to the Wiki

+

How to Edit

Editing the WATonomous wiki is easy. Simply click Edit this page on the far right of each page. Note, you must be part of the WATonomous Organization to be able to edit.

-

You can use regular react components, or choose from a plethora of pre-built components and tools here (opens in a new tab).


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file +

You can use regular react components, or choose from a plethora of pre-built components and tools here (opens in a new tab).


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file diff --git a/onboarding.html b/onboarding.html index c35a4ce..fbaed78 100644 --- a/onboarding.html +++ b/onboarding.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
Onboarding

Onboarding

+
Onboarding

Onboarding

WATonomous onboarding guides. The following are quick jists of what each onboarding guide is for:

ASD - Developing with WATcloud Contains setup steps for getting access to WATcloud as well as how to use WATcloud in the Autonomous Software Division. (go-to)

-

ASD - General Onboarding Contains a walk-through that teaches you the basics of ROS2, Docker, Docker Compose, and the rest of our robotics software stack. (go-to)


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file +

ASD - General Onboarding Contains a walk-through that teaches you the basics of ROS2, Docker, Docker Compose, and the rest of our robotics software stack. (go-to)


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file diff --git a/onboarding/asd_general_onboarding.html b/onboarding/asd_general_onboarding.html index fb937fc..6f501fb 100644 --- a/onboarding/asd_general_onboarding.html +++ b/onboarding/asd_general_onboarding.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
ASD - General Onboarding

General Autonomous Software Onboarding - ASD Assignment

+
ASD - General Onboarding

General Autonomous Software Onboarding - ASD Assignment

️❗

You must complete ASD - Developing with WATcloud before proceeding with this guide.

Introduction

@@ -368,4 +368,4 @@

Conclusion

Congratulations! You have now successfully controlled a robot using ROS2! The fundamentals of publishers, subscribers, and a few tricks you learned like Foxglove and transforms will be extremely valuable in your development in the WATO ASD software stack.

Please contact the Director of ASD (Eddy Zhou) on discord, showing proof of completion and 2 subteams you are interested in joining. Link to subteams here (opens in a new tab).

-

Welcome to WATonomous! :))


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file +

Welcome to WATonomous! :))


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file diff --git a/onboarding/asd_watcloud_dev.html b/onboarding/asd_watcloud_dev.html index d5b2f7d..b384662 100644 --- a/onboarding/asd_watcloud_dev.html +++ b/onboarding/asd_watcloud_dev.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
ASD - Developing with WATcloud

Developing with WATcloud

+
ASD - Developing with WATcloud

Developing with WATcloud

️❗

You must complete your Cluster Access Form (opens in a new tab) before proceeding with this guide.

Here, we discuss setting up WATcloud to be used for software development in the Autonomous Software Division.

@@ -57,7 +57,11 @@

[Host Machine] Add your public key into the SLURM node's authorized keys

Copy your public key on your local computer and paste it into the authorized_keys file on the SLURM node.

your_local_machine$ cat ~/.ssh/your_key.pub (copy the output of this)
 your_local_machine$ ssh [tr-ubuntu3 or derek3-ubuntu2]
 watcloud_slurm_node$ touch ~/.ssh/authorized_keys
-watcloud_slurm_node$ nano ~/.ssh/authorized_keys (paste you rpublic key into here)

[Local Machine] Build the Computer you desire!

Use your favorite text editor to edit wato_asd_tooling/session_config.sh.

cd wato_asd_tooling
+watcloud_slurm_node$ nano ~/.ssh/authorized_keys (paste you rpublic key into here)

[Host Machine] Add the following to your bashrc in your SLURM node

watcloud_slurm_node$ nano ~/.bashrc

Add the following to the end of your ~/.bashrc file.

if [[ "$(hostname)" == *"slurm"* ]]; then 
+    export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/tmp/run}
+    export XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-/tmp/config}
+    export DOCKER_HOST="unix://${XDG_RUNTIME_DIR}/docker.sock"
+fi

[Local Machine] Build the Computer you desire!

Use your favorite text editor to edit wato_asd_tooling/session_config.sh.

cd wato_asd_tooling
 nano session_config.sh

The file itself contains descriptions of all of the parameters and how to set them.

[Local Machine] Start a SLURM Dev Session

Run the helper script to startup a SLURM Dev Node. Follow all the prompts carefully.

cd wato_asd_tooling
 bash start_interactive_session.sh
🚫

DO NOT START MORE THAN ONE DEV NODE. You have a chance of corrupting your docker filesystem. Starting more than one dev node is like building multiple computers. It is NOT the same as creating multiple terminals.

[Local Machine] Setup SSH for SLURM

Run this last helper script LOCALLY. Follow the prompts carefully.

cd wato_asd_tooling
 bash ssh_helpers/setup_slurm_ssh.sh

[Local Machine] Stop the SLURM Dev Session

Stop the interactive session by hitting Ctrl+C.

@@ -69,4 +73,4 @@

💡

And you're good to go! Whenever you want to startup a SLURM Dev Node, start one up by running start_interactive_session.sh, and then SSH into the SLURM node through VScode.

Setup for Job Scheduling

There is no setup. Creating an SLURM job is really easy. It was what SLURM was designed for. You can view docs on SLURM in the WATcloud documentation (opens in a new tab).

-
✏️

Deliverable Run a SLURM batch job with 2 CPUs that counts to 60.


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file +
✏️

Deliverable Run a SLURM batch job with 2 CPUs that counts to 60.


Made with using and
by WATonomous and WATcloud.
\ No newline at end of file diff --git a/onboarding/vp_general_onboard.html b/onboarding/vp_general_onboard.html index 9e54cbb..969076c 100644 --- a/onboarding/vp_general_onboard.html +++ b/onboarding/vp_general_onboard.html @@ -11,7 +11,7 @@ --nextra-primary-hue: 204deg; --nextra-primary-saturation: 100%; } -
VP - General Onboarding

WATO-VP Onboarding Document

+

Made with using and
by WATonomous and WATcloud.
\ No newline at end of file +

Made with using and
by WATonomous and WATcloud.
\ No newline at end of file