Skip to content

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 utexas with 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.

  1. 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
    
  2. Install the tools the flash uses:
    sudo apt install -y python-is-python3 jq
    
  3. Turn off the firewall, which blocks the flashing tools:
    sudo ufw disable
    

Flash

  1. Put a jumper between the Orin's FC_REC and GND pins, which starts it in recovery mode.
  2. Connect the Orin's USB-C port to the flashing computer.
  3. Start SDK Manager: sdkmanager &
  4. Insert the SD card or NVMe disk into the Orin, then plug in the Orin's round power plug.
  5. 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 (utav01 for car 1) and the password is the one the course staff use for the cars. Choose the storage, then click Flash.
  6. 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.
  7. Remove the FC_REC jumper.

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).

  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 utexas with your EID and password. You take your login back off the car in step 5.

    utexas doesn'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
  2. Install JetPack's components. Open a terminal with Ctrl+Alt+T, then:

    sudo apt update
    sudo apt upgrade
    sudo apt install nvidia-jetpack
    
    If apt update complains that a file "is not valid yet", the Orin's clock is wrong (it has no clock battery). Run sudo timedatectl set-ntp true, wait a minute, and try again.

  3. Check the version:
    dpkg-query -W nvidia-jetpack     # nvidia-jetpack  7.2.1-b49
    cat /etc/nv_tegra_release        # # R39 (release), REVISION: 2.1, ...
    
    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: apt upgrade installs any newer 7.2 release NVIDIA has put out, and every car has to be on the same one (see JetPack version).
  4. Note the Wi-Fi MAC address. You send it to the course staff in step 5, so the car can join utexas-iot:
    ip link show wlP1p1s0
    
    It's the xx:xx:xx:xx:xx:xx after link/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.key on 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:

  1. sudo wg show on the car lists a latest handshake, and you can reach the car from your laptop at its VPN address, with WireGuard on.
  2. 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 2
    
    nmcli connection show lists 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:

  1. Shut the Orin down with sudo poweroff.
  2. 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.
  3. Connect the battery and turn the car on.
  4. Run the setup script again with --check:

    sudo ~/ot_orin_ros2/scripts/bootstrap.sh --check
    
    Its Lidar and motor controller step 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.

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:

  1. Check that the chip answers:
    sudo i2cdetect -y -r 7      # the MPU6050 shows up as 68
    
    If i2cdetect isn't found, install it with sudo apt install -y i2c-tools. If nothing shows up at 68, check the IMU's wiring before going on.
  2. Turn the IMU on in this car's car.lua, then rebuild:
    cd ~/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_automata
    
    The second line removes any fuse_imu line that's already there and adds fuse_imu = true; at the end, so it's safe to run twice. car.lua holds 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.