Car Bring-Up
This page takes a new car's Jetson Orin Nano from a blank disk to a car that drives. You flash JetPack onto the Orin, finish its install on the car, run one setup script that does almost everything else, and then do a few jobs by hand.
These steps may have been completed for you
Prior to the FRI course, these steps have been completed for you, but are included here for completeness.
You do this at the car
Apart from flashing in step 1, all of this setup happens at the car itself: on the keyboard and mouse plugged into the Orin, and on the car's touchscreen. Every command on this page goes into a terminal on the car's desktop (open one with Ctrl+Alt+T), unless a step says otherwise. You don't need SSH or VS Code, and until the car is registered they can't reach it anyway.
The setup script lives in the ut-av/ot_orin_ros2 repository, which is private to the course staff. You need access to it to set up a car.
Before you start
You'll need:
- An x86 computer running Ubuntu, for flashing.
- A USB keyboard and mouse to plug into the Orin. Most of the setup happens on the car's own touchscreen.
- A Wi-Fi network the car can join straight away, such as
utexaswith your EID. The car's own network,utexas-iot, only lets it on once it's registered. - Your GitHub account, with access to
ut-av/ot_orin_ros2, and a phone or laptop with a web browser. - The Orin's wall power adapter. The first setup takes more than an hour, so run it on wall power rather than the battery.
Until the setup has run and the car is registered, you can't reach it from your laptop over the network. Campus Wi-Fi doesn't let your laptop connect to the car, and the car's VPN, which gets around that, isn't working yet. So you work at the car itself until then, or over a USB-C cable from your laptop (see the alternative in step 3).
Names and addresses
Each car has a number, and its name comes from that number:
| Car 1 | Car 20 | |
|---|---|---|
| Car name, hostname and login account | utav01 |
utav20 |
| WireGuard VPN address | 10.1.0.101 |
10.1.0.120 |
Cars set up before this naming use the account orin and hostnames like orin07 (the examples in the tutorials), with the same VPN addresses.
1. Flash the Orin
Every car runs JetPack 7.2.1. The whole fleet has to be on the same version; see JetPack version for why.
Prepare the flashing computer
Flashing needs an x86 computer running Ubuntu, with NVIDIA's SDK Manager.
- Install SDK Manager:
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb sudo dpkg -i cuda-keyring_1.1-1_all.deb sudo apt-get update sudo apt-get -y install sdkmanager - Install the tools the flash uses:
sudo apt install -y python-is-python3 jq - Turn off the firewall, which blocks the flashing tools:
sudo ufw disable
Flash
- Put a jumper between the Orin's
FC_RECandGNDpins, which starts it in recovery mode. - Connect the Orin's USB-C port to the flashing computer.
- Start SDK Manager:
sdkmanager & - Insert the SD card or NVMe disk into the Orin, then plug in the Orin's round power plug.
- Configure the flash in SDK Manager:
- Step 1: target hardware Jetson Orin Nano [8GB developer kit], JetPack 7.2.1, plus DeepStream and GXF Runtime.
- Step 2: check every box, including Jetson Platform Services, and accept the terms.
- Step 3: choose Pre-Config. The username is the car's name (
utav01for car 1) and the password is the one the course staff use for the cars. Choose the storage, then click Flash.
- When the flash finishes, the Orin restarts into its desktop. SDK Manager then offers to install JetPack's SDK components (CUDA, TensorRT and the rest), which needs the Orin on the internet. Skip it, and if it has already stopped with an error such as "no internet connection", click Finish and Exit. Step 2 installs the components from the Orin itself.
- Remove the
FC_RECjumper.
2. Finish the install on the car
Plug a USB keyboard and mouse into the Orin, and log in on the car's touchscreen with the car's account (utav01 for car 1).
-
Get online. Join a Wi-Fi network from the menu in the top-right corner of the screen. Use one that lets the car on straight away, such as
utexaswith your EID and password. You take your login back off the car in step 5.utexasdoesn't just ask for a username and password: it opens a window with several security fields. Fill them in like this:Field Value Security WPA & WPA2 Enterprise Authentication Tunneled TLS Anonymous identity leave empty Domain leave empty CA certificate (None) CA certificate password leave empty No CA certificate is required checked Inner authentication MSCHAPv2 (no EAP) Username your EID Password your UT password -
Install JetPack's components. Open a terminal with
Ctrl+Alt+T, then:Ifsudo apt update sudo apt upgrade sudo apt install nvidia-jetpackapt updatecomplains that a file "is not valid yet", the Orin's clock is wrong (it has no clock battery). Runsudo timedatectl set-ntp true, wait a minute, and try again. - Check the version:
R39 revision 2.1 is L4T 39.2.1, which is JetPack 7.2.1. If you see a newer version, stop and ask the course staff:
dpkg-query -W nvidia-jetpack # nvidia-jetpack 7.2.1-b49 cat /etc/nv_tegra_release # # R39 (release), REVISION: 2.1, ...apt upgradeinstalls any newer 7.2 release NVIDIA has put out, and every car has to be on the same one (see JetPack version). - Note the Wi-Fi MAC address. You send it to the course staff in step 5, so the car can join
utexas-iot:It's theip link show wlP1p1s0xx:xx:xx:xx:xx:xxafterlink/ether.
3. Run the setup script
scripts/bootstrap.sh sets up everything else: the operating system, the desktop, the VPN, Docker, airfield, the roboracer_ws code, the car's hardware, and the build.
Get the script onto the car
The repository is private, so the car needs your GitHub login to clone it. The GitHub CLI can log in with a one-time code, which needs no browser on the car. In the car's terminal:
sudo apt install -y gh
gh auth login
Answer its questions with GitHub.com, HTTPS, Yes (to authenticate Git with your GitHub credentials) and Login with a web browser. It prints a one-time code. On your phone or laptop, open github.com/login/device, type in the code and approve. If gh says it couldn't open a browser, ignore that: it finishes on its own once you approve. Then:
gh repo clone ut-av/ot_orin_ros2 ~/ot_orin_ros2
gh auth logout
Logging out takes your GitHub access back off the shared car. The setup doesn't need it: everything else it downloads is public.
An alternative: over the USB-C cable from your laptop
The Orin's USB-C port doubles as a network cable: plug your laptop into it, and the car is at 192.168.55.1. You can then type into the car from your laptop instead of its keyboard. The car still needs its own internet connection, so join Wi-Fi on its desktop first (step 2).
ssh -A utav01@192.168.55.1
-A lends the car your laptop's SSH key for this session. If that key is on your GitHub account (see the SSH guide), the car can clone with it. Run the setup inside tmux, so a dropped cable doesn't stop it:
git clone git@github.com:ut-av/ot_orin_ros2.git ~/ot_orin_ros2
sudo apt install -y tmux
tmux new -s setup
sudo ~/ot_orin_ros2/scripts/bootstrap.sh utav01
If the connection drops, reconnect and run tmux attach -t setup. Some laptops and USB-C adapters keep dropping it; if yours does, use the car's keyboard instead.
Every car is 192.168.55.1 over the cable, and each has its own SSH identity. So from the second car on, your laptop refuses to connect with REMOTE HOST IDENTIFICATION HAS CHANGED!. Make it forget the old car, then connect again:
ssh-keygen -R 192.168.55.1
On Linux or macOS, scripts/remote_setup.sh does all of this from your own clone of the repository, and the car needs no GitHub access: it copies the repository to the car and runs the setup there. From the ot_orin_ros2 folder on your laptop:
./scripts/remote_setup.sh utav01 # over USB
./scripts/remote_setup.sh utav01 10.1.0.101 -- --check # later, over the VPN
It doesn't use tmux, so if the cable drops, run it again. It leaves its copy in /tmp on the car, which a restart empties, so later runs go through remote_setup.sh too.
Run it
In the car's terminal:
sudo ~/ot_orin_ros2/scripts/bootstrap.sh utav01
sudo asks for the car's password once, and after that the script asks nothing. The first run takes an hour or more, mostly for the build at the end. Leave it running: don't close the terminal or log out. It keeps going when the screen locks or goes dark.
Every step checks first and only changes what's missing or wrong, so it's safe to run on a new car, on a half-set-up car, or again later to repair one. If it stops with an error, for example after a failed download, run the same command again. It carries on from where it stopped.
What it sets up
The script runs in stages, in this order:
| Stage | What it does |
|---|---|
system |
Hostname, Wi-Fi (utexas-iot), each network card on its own MAC address, DNS, clock sync, keeps JetPack at the flashed version, host packages, passwordless sudo, no crash pop-ups, fast SSH logins, a 16 GB swap file, the MAXN_SUPER power mode, jetson_clocks at boot, and the dialout/i2c groups. |
desktop |
Logs in automatically on the touchscreen, shows the car's name, IPs and load, adds the "UT Automata GUI" and "Shutdown" buttons, turns the screen upside down, and turns off screen lock and pop-ups. |
vpn |
The car's WireGuard connection to robofleet, at the address that comes from its name. |
docker |
Docker, NVIDIA's container runtime (so containers can use the GPU and cameras), and the docker group. |
airfield |
airfield, its dependency recipes, tab-completion, then airfield doctor. |
workspace |
Clones ~/roboracer_ws and its ROS package repositories with their submodules, writes the .air file, and checks every repository against GitHub. |
hardware |
The Hokuyo lidar's network port, the game-controller driver, the camera connector, and a check of the device group numbers the containers use. It also looks for the lidar, the motor controller and a paired controller. |
build |
The car's base image, then scripts/build for the ROS packages. |
Options
sudo ~/ot_orin_ros2/scripts/bootstrap.sh [<car-name>] [--check] [--only a,b] [--skip a,b] [--update] [--user NAME]
| Option | What it does |
|---|---|
<car-name> |
utavXX or atXX. Sets the hostname and the VPN address. You can leave it out when the hostname or the login account already has that form. |
--check |
Changes nothing. Runs every check and prints what would change. |
--only a,b / --skip a,b |
Runs only these stages, or all but these. |
--update |
Also brings airfield, its recipes, ~/roboracer_ws and the ROS package repositories up to the newest commit of their branch, then rebuilds the ROS packages. A repository with uncommitted changes or commits of its own is left alone. |
--user NAME |
The car's login account. The default is the account that ran sudo. |
Reading the output
The end of the output has up to four lists:
- Steps that changed something.
- Reboot when it's convenient, with the reason for each.
- Still to do by hand: things the script found but couldn't fix itself.
- Once per car: the jobs in step 5.
Everything a real run prints is also saved on the car in /var/log/ot_orin_ros2.log. /var is a system folder, not part of roboracer_ws. To see the end of the last run:
tail -40 /var/log/ot_orin_ros2.log
or open it in VS Code with code /var/log/ot_orin_ros2.log and press Ctrl+End.
4. Reboot
If the power mode changed, reboot with the command the summary prints, for example:
sudo nvpmodel -m 2 --force
This switches to the new power mode and reboots in one go. A plain sudo reboot keeps the old power mode. Otherwise sudo reboot is fine. The reboot also applies the camera connector, auto-login and group changes.
5. Finish the per-car jobs
These need a person, at the car's keyboard like the rest of the setup. The setup script lists them at the end of every run, leaving out the ones it can see are done: a paired controller, and a lidar and motor controller it found. You don't have to wait for the registration before doing the others.
Register the car on robofleet
The car's VPN connection is set up, but the robofleet server only accepts cars it knows, and utexas-iot only lets on devices whose MAC address is registered. Send the course staff member who runs robofleet one message with:
- the car's name,
- its Wi-Fi MAC address, from step 2,
- its VPN address (
10.1.0.1XX), - its WireGuard public key, from
sudo cat /etc/wireguard/wg-public.keyon the car.
The script prints the address and key at the end of its run. Copy the key exactly: it's 44 characters long and ends with =. Every device on the VPN has its own key pair: the car's public key is not the one in your laptop's WireGuard app, and it's safe to send because the private half never leaves the car. A reflashed car makes a new key pair, so it has to be registered again.
Once the car is registered:
sudo wg showon the car lists alatest handshake, and you can reach the car from your laptop at its VPN address, with WireGuard on.- Move the car onto
utexas-iot, and delete the network you joined in step 2, so your login doesn't stay on a shared car. At the car's keyboard:sudo nmcli connection up utexas-iot sudo nmcli connection delete utexas # the network you joined in step 2nmcli connection showlists the networks the car has saved.
Check the lidar and motor controller
The Hokuyo lidar and the motor controller (VESC) run off the car's battery, so the setup itself doesn't need them. Once the first run is done on wall power:
- Shut the Orin down with
sudo poweroff. - Switch the Orin from the wall adapter to the car's power. Push the round plug all the way in: a loose one cuts the power.
- Connect the battery and turn the car on.
-
Run the setup script again with
--check:Itssudo ~/ot_orin_ros2/scripts/bootstrap.sh --checkLidar and motor controllerstep should find:- the lidar: a Hokuyo answering at
192.168.0.10, or an RPLIDAR C1 on USB, - the motor controller at
/dev/ttyACM0.
The reminders to pair a controller and to calibrate are still listed at the end. That's expected until you do them.
- the lidar: a Hokuyo answering at
A car with an RPLIDAR C1 usually needs its scan turned around; see Scan direction. A Hokuyo needs nothing.
Pair each game controller
See Pair a controller.
Drive it for the first time
Put the car up on a box so its wheels are off the ground. Turn the controller on first, because the joystick pane only looks for it at startup. Then:
cd ~/roboracer_ws
airfield project up teleop
In the joystick pane, look for this line:
Driver 'playstation' reports the standard layout; not remapping it
If it isn't there, don't drive: the controller's sticks may be mixed up. Stop the plan with airfield project down teleop.
Hold L1 to drive. The left stick steers and the right stick up/down sets the speed. Let go of L1 and the car stops. Driving with the controller lists the other buttons.
Until you calibrate it, the car uses the fleet's standard steering and speed values, which is fine for driving around. If it pulls to one side, trim the center with the D-pad. The IMU light on the touchscreen stays red until you turn the IMU on, and for good on a car without one.
When you're done:
airfield project down teleop
Calibrate the driving
Follow Car Calibration in the Navigation tutorial. It measures the steering and speed values for this car and saves them in ~/roboracer_ws/packages/ut_automata/config/car.lua. The VESC page explains what each value means.
Turn on the IMU
This step is optional. Without it the car drives normally, and takes its heading from the wheels and steering. If the car has an MPU6050 IMU (every car built from the assembly guide does), turn it on, so the heading comes from the gyro instead. In a terminal on the car:
- Check that the chip answers:
If
sudo i2cdetect -y -r 7 # the MPU6050 shows up as 68i2cdetectisn't found, install it withsudo apt install -y i2c-tools. If nothing shows up at68, check the IMU's wiring before going on. - Turn the IMU on in this car's
car.lua, then rebuild:The second line removes anycd ~/roboracer_ws/packages/ut_automata/config sed -i '/^fuse_imu/d' car.lua 2>/dev/null; echo 'fuse_imu = true;' >> car.lua grep fuse_imu car.lua # fuse_imu = true; cd ~/roboracer_ws && scripts/build ut_automatafuse_imuline that's already there and addsfuse_imu = true;at the end, so it's safe to run twice.car.luaholds this car's own settings and isn't in git, so the change stays on this car.
The IMU page explains what the driver measures at startup, and how to check that it's working.
6. Check and launch
Run the setup script again with --check. It changes nothing, and every line should say ok apart from anything you still have to do by hand:
sudo ~/ot_orin_ros2/scripts/bootstrap.sh --check
Then launch the navigation stack:
cd ~/roboracer_ws
scripts/up
See Airfield for what scripts/up starts and how to stop it.
Keeping a car up to date
To bring a car's code up to the newest version and rebuild it:
sudo ~/ot_orin_ros2/scripts/bootstrap.sh --update
Code that's behind GitHub can fail to build, and a normal run warns when it finds some. --update never throws away work: a repository with uncommitted changes or commits that aren't on GitHub is left as it is, with a warning.
The script never switches the branch of a repository that already exists, because someone's work might be on it. It prints the command to switch instead.
--update doesn't update the setup script itself. To get its newest version first, log in to GitHub the same way as in step 3:
gh auth login
git -C ~/ot_orin_ros2 pull
gh auth logout
JetPack version
Every car runs JetPack 7.2.1 (Jetson Linux / L4T 39.2.1). The build makes a base image that copies the car's exact L4T version, and roboracer_ws's airfield.yaml names that one image, so the whole fleet has to match.
To check a car's version:
dpkg-query -W -f='${Version}\n' nvidia-l4t-core # 39.2.1-...
NVIDIA puts new 7.2 releases in the same place apt installs from. Until the setup script runs, sudo apt upgrade installs the newest one, which is why step 2 checks the version straight after. The setup script then holds the JetPack packages, so later upgrades leave them alone. Don't remove that hold: a newer release no longer matches the base image.
MAC addresses
Each car's network cards use their own MAC addresses, and nothing in the setup sets one. Older versions of the setup script made the Wi-Fi card claim another device's MAC address (spoofing). The system stage removes that setting if a car still has it. If a car then can't join utexas-iot, register its own Wi-Fi MAC address for the network rather than borrowing another device's.
Troubleshooting
The script stopped with an error
It prints ERROR: command failed ... during: <step>, then any to-dos it had noted before the failure. The first error message above that line says why, and everything is also in /var/log/ot_orin_ros2.log. If the failure is in the build and the run warned that code is behind GitHub, run again with --update.
"No route to host" at the car's Wi-Fi address
Campus Wi-Fi doesn't let your laptop connect to a car at its Wi-Fi address. Use the car's VPN address once it's registered. Before that, work at the car's keyboard or over the USB-C cable (see step 3).
"REMOTE HOST IDENTIFICATION HAS CHANGED!"
Reflashing gives a car a new SSH identity, and your laptop remembers the old one for that address. Over the USB-C cable this happens with every new car, because they're all 192.168.55.1. If you're sure it's the car you meant, make your laptop forget the old identity, then connect again and accept the new one:
ssh-keygen -R 10.1.0.101 # or 192.168.55.1 over the USB-C cable
On Windows, run it in the same program you connect from: PowerShell and WSL each keep their own list.
Reaching a car before it's registered on robofleet
A new car isn't on the VPN until it's registered. If it's already on utexas-iot, for example a reflashed car whose MAC address is still registered, you can reach it through a car that is on the VPN: cars on utexas-iot can reach each other. In your laptop's ~/.ssh/config:
Host utav01
HostName <utav01's Wi-Fi address>
User utav01
ProxyJump utav19
Once it's registered, change HostName to its VPN address and delete the ProxyJump line.
For other problems with a running car, see the Troubleshooting Guide.