Skip to content

Latest commit

 

History

History
656 lines (488 loc) · 43.3 KB

README.md

File metadata and controls

656 lines (488 loc) · 43.3 KB

DIYDuckie

This repo contains instructions for building a Duckiebot DB21M 'clone' from discrete modules available on Aliexpress or Amazon (or Ebay...) Duckiebot is a ROS-based robot used to teach autonomous robot navigation and behavior as part of the Duckietown curriculum and MOOC.

If you like the idea of a DIY Duckiebot, please star this repo, it will help me keep going. Thanks!

DIYDuckie cardboard prototype DIYDuckie cardboard prototype

Video of DIYDuckie's first drive

Status

  • Created a proof of concept by mounting parts on a pizza box. All components worked; I could echo rostopic messages and see the parts in http://duckiebot.local/health/components (insert your duckiebot name for 'duckiebot'). Dashboard shows video and telmetry. Motors move when using the dts duckiebot keyboard_control command.
  • Did some initial layout experiments for a two-layer chassis using paper-cutouts of the parts. Found that a 12cm wide x 17cm long chassis would work. This could be made smaller if we can get rid of the second PCA9685; but that will have to wait until I have time to make a pull request to the project to support delivering PWM directly from the Jetson Nano.
  • Created prototype using a cardboard chassis so I could finalize component placement and wire routing. The cardboard layers make it easy to cut holes and fine-tune placements. I have a layout that works. I've tried a couple of power solutions.
    • I first tried a 5v/5amp buck converter and an RC LIPO battery. I've used that solution in other projects but here it did not work because routing the 5v into the Jetson Nano via the 40pin header would not boot the Nano. This is apparently a common issue when trying to power via the 40 pin header using a buck converter. So we really need to power the Jetson via the USB-C connector if we are using a battery.
    • Next I tried two USB batteries; one to power the Jetson Nano, the other to power everthing else. This worked better, but still had a problem. The Jetson Nano booted and ran well. However, the USB batteries I am using would not see enough power pulled from the other modules and would shutdown. If I ran the motors then everything was fine becaue enough power was being pulled to keep the battery awake. But 10 seconds after I stopped the motors, the battery would go to sleep. The other issue is these USB batteries are heavy, so the it's not great to require two of them.
    • I have two other buck-converter solutions on order that will terminate in USB connections, so I can feed the Jetson Nano via the USB-C connector. I'm hoping I only need a single RC LIPO battery because the battery is the single heaviest component.
  • I solved the power issue by using an Anker PowerCore 13000 USB Battery. This battery outputs 3 amps and has two USB ports, which made it easy to route power to the USB-C port on the Jetson Nano and to supply separate power for the Sensors, LEDs, and OLED screen.
  • I created a new bottom platform using 1/8" hobby plywood. I kept the upper platform in cardboard because it is plenty stiff and it is lighter than the plywood. The battery resides on the bottom platform. The remaining task is to mount the RGB LEDs, but that will be simple compared to everything else.
  • I've been able to test the DIYDuckie in this new prototype and it works well. It can be remote-controlled using the dts duckiebot keyboard_control <duckiename> command and video can be streamed in the dashboard.
  • The next step is to modify the ROS code so apply the physical configuration of this robot; wheel radius/circumference, wheel base, min/max rpm of motors, number of encoder ticks per rotation. This is all so the kinematic model and speed control will work correctly. I think I may also need to set the offset of the camera from the robot center.

To do

  • Finalize the power system. That will then allow us to finalize the length on the stand-offs between the two layers.
  • Optimize the wire routing; use more solid core wiring so routing can be precise.
  • Create a hobby-board version of the chassis using the final component positions and wire routing. This will be the first fully functional version and so I will add a lot more documentation and images on the build process.
  • fork the dt-duckiebot-interface repo and modify it to allow PWM to be generate directly from the Nano. Update the configuration to support a diydb model.
  • redesign the chassis without the second PCA9685.
  • Create a 3D printable chassis

Parts

Related Repositories and Configuration

I reviewed the duckietown repos at https://github.com/duckietown to determine what hardware is used.

https://github.com/duckietown/dt-duckiebot-interface This repo includes a number of packages to interface to hardware. This is where we will find the info on what we need to purchase to build a duckiebot from parts. What I don't see here is any driver for the IMU that is part of the DB21 duckiebot. So at this point I don't know which IMU is used, but it may not matter because I also cannot see here it would be used anyway. I would expect a node to read from the IMU and public imu messages and another node to use a Kalman Filter or other way to combine the IMU with encoder values to create pose estimates. So at this point I do not think the IMU is necessary, but I'll keep looking.

All the other hardware components can be easily obtained. I would look at all the config associated with the drivers below in order to figure out how to configure them. If need be, the configuration could be updated to match your hardware.

I would recommend a motor with the encoder on the motor's drive shaft rather than the output shaft because you will get many more ticks per revolution of the wheel. However,

I think generic gear motors with optical encoders would work if you tweak the config to handle the lower ticks/revolution of the wheel. See my project for sources for the motors, encoder wheels and optocouplers here: https://github.com/Ezward/Esp32CameraRover2/blob/master/docs/building_the_rover.md

You will probably need to modify the wheel encoder config in the duckietown/dt-duckiebot-interface container with the pulse per wheel revolution for your motors/encoders. My motor has 7 ppr (pulses-per-revolution) of the motor shaft and there is a 150:1 gear reduction on the output shaft, so each time the output shaft (and so the wheel) turns once, the motor shaft turns 150 times. So the total pulses-per-wheel-revolution = 7 * 150. The nominal PPR for the DB21m duckiebot is 135 pulses-per-wheel revolution; so I had to edit the config. 1. ssh into the duckiebot; ssh [email protected] where DUCKIEBOT is the name of you duckiebot. Default password in 'quackquack' 2. list the containers running on the duckiebot; docker ps 3. find the duckietown/dt-duckiebot-interface container and get it's id (in the leftmost column), it will be a 12 digit hexadecimal number. 4. open a bash shell in the container; docker exec -it CONTAINER /bin/bash where CONTAINER is the 12 digit hex id of the container. 5. Once in a shell in the container, cd into the wheel encoder package; cd wheel_encoder/config/wheel_encoder_node 6. Once in the wheel encoder package config folder, edit the files left_wheel.yaml and right_wheel.yaml and change the resolution value. Note that if you change the gpio pin that reads the encoder, you will also need to edit this file and update the gpio value. I did not have to do this because I used the defaults (as specified in wiring charts below). So my left_wheel.yaml ends up looking like this;

gpio: 18
resolution: 1050
configuration: "left"
publish_frequency: 30

An important thing to note in this file is that the gpio is based on the broadcom SOC gpio number of the RaspberryPi, not the pin number on the board. More on this below in the wiring section.

I found out the IMU is the MPU9250, which is very easy to get. However, it won't be used in this class. There is no code in the repositories that use it, but I do see that it tries to identify the IMU on startup (go to http://<duckiebot>.local/health/components to see the components that the code is looking for and if it found them).

Power

We use two separate 5v USB batteries; one supplies the power to the Jetson Nano and the other supplies the power to the LEDs and the Motors.

The Jetson Nano is a 3.3v device; it's pins output 3.3v and when in input mode, it expects 3.3v logic. It is NOT 5v tolerant. This is important to note that the Jetson Nano is the I2C bus master; so it will be reading from the I2C bus. Therefore, that bus must be 3.3v. All the peripheral modules described below can operate their logic at 3.v or 5v, but we choose 3.3v to be compatible with the Nano.

The exception is the TB6612FNG. Due to power issues on the 3.3v rail (see below) we chose to power the TB6612FNG VCC using 5v. This works because the TB6612FNG only takes inputs from the Jetson Nano and does not output anything back to the Nano, so 3.3v inputs are fine.

We did find that we needed to enhance the power to the 3.3v devices. This was done because we were pulling too much power from the 3.3v rail and it was causing the OLED to flicker. We had to add another 3.3v source to provide enough power; so one 3.3v rail is provided by the 3.3v output from the Jetson Nano, the other 3.3v rail is supplied by an AMS1117 buck converter that takes the 5v source and brings it down to 3.3v. Power was organized as shown below (we could probably balance the 3.3v a little more, but it seems to work ok like this):

  • 3.3v rail from Nano 3.3v pin
    • VCC - PCA9685 at address 0x40 providing PWM for RGB LEDs
    • VCC - VL53L0X TOF Distance Sensor
  • 3.3v rail from AMS-1117 buck converter OUT pin
    • VCC - PCA9685 at address 0x60 that produces PWM for motors
    • VCC - OLED display
    • VCC - Motor A
    • VCC - Motor B
  • 5v from USB battery 1
    • VIN - PCA9685 at address 0x40 providing PWM for RGB LEDs
    • VIN - PCA9685 at address 0x60 that produces PWM for motors
    • VM - TB6612FNG motor voltage input
    • VCC - TB6612FNG logic
    • VIN - AMS1117 buck converter
  • 5v from USB Battery 2
    • Jetson Nano USB-C connector

Wiring

1. PCA9685 PWM Controller

The PCA9685 can control up to 16 PWM devices like servos, motor controllers, LEDs or almost anything that uses a PWM signal.

Duckiebot uses this device to control 5 RGB LEDs. Each LED has 3 control pins, so we end up using 15 channels.

This section is only about wiring the PCA9685 to the Jetson Nano. There will be a separate section describing the wiring of the LEDs.

  • On I2C bus 1, address 0x40
  • On jetson nano, I2C bus 1
    • SDA is pin 03
    • SCL is pin 05
  • Wiring
    • All GND must be common ground.
    • External 3.3v power may be on shared bus.
    • SDA and SCL may be through a shared bus rather than a direct connection between nano and PCA9685
---
    Nano   ... PCA9685 ... 5v
    pin-03 <---> SDA
    pin-05 <---> SCL
    GND-06 <---> GND 
    3v3-01 <---> VCC
                 VIN <---> 5v
                 GND <---> GND
---
  • checking connections
    • ssh into the Duckiebot and use i2cdetect to read bus 1. A device should exits at address 0x40
---
    $ i2cdetect -y -r 1
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    70: UU -- -- -- -- -- -- --                         
---

Notes

2. RGB LEDs

The RGB LEDs are driven by the PCA9685. It takes 3 PWM channels to drive each LED. The system supports 5 LEDs.

  • RGB_LED
    This class is used to drive a single rgb led using 3 channels on the PCA9685.
  • I'm using RGB LED modules with built-in resistors, so they are very easy to add with no soldering; just use dupont jumper wires. Like these https://www.aliexpress.com/item/32833188535.html
  • Wiring (using module above)
---
    PCA9685 <--> RGB LED
    pwm-0   <--> Front Left Red
    pwm-1   <--> Front Left Green
    pwm-2   <--> Front Left Blue
    GND     <--> GND
    pwm-3   <--> Rear Left Red
    pwm-4   <--> Rear Left Green
    pwm-5   <--> Rear Left Blue
    GND     <--> GND
    pwm-6   <--> Top / Front Middle Red
    pwm-7   <--> Top / Front Middle Green
    pwm-8   <--> Top / Front Middle Blue
    GND     <--> GND
    pwm-9   <--> Rear Right Red
    pwm-10  <--> Rear Right Green
    pwm-11  <--> Rear Right Blue
    GND     <--> GND
    pwm-12  <--> Rear Left Red
    pwm-13  <--> Rear Left Green
    pwm-14  <--> Rear Left Blue
    GND     <--> GND
---

3. OLED Display

The display is based on the SSD1306 chipset.

  • On I2C bus 1, address 0x3C
  • On jetson nano, I2C bus 1
    • SDA is pin 03
    • SCL is pin 05
  • Wiring
    • All GND must be common ground.`
    • SDA and SCL may be through a shared bus rather than a direct connection between nano and PCA9685`
---
    Nano   ... OLED
    pin-03 <-> SDA
    pin-05 <-> SCL
    GND-06 <-> GND 
    3v3-01 <-> VCC
---
  • checking connections

    • ssh into the duckiebot and use i2cdetect to read bus 1. A new device should exits at address 0x3C
---
    $ i2cdetect -y -r 1
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- -- 
    40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    70: UU -- -- -- -- -- -- --                         
---

4. VL53L0X TOF Distance Sensor

The VL53L0X is a time-of-flight distance sensor using a 904nm laser emitter and detector. It can measure distances up to 2 meters, which is plenty for Duckiebot.

  • On I2C bus 1, address 0x29 // see not below on discrepancy in config
  • Wiring
    • All GND must be common ground.
    • SDA and SCL may be through a shared bus rather than a direct connection between nano and PCA9685
---
    Nano   ... VL53L0X
    pin-03 <-> SDA
    pin-05 <-> SCL
    GND-06 <-> GND 
    3v3-01 <-> VCC
---

  • Related Repos
  • Checking Connections
    ssh into the duckiebot and use i2cdetect to read bus 1. A new device should exist at address 0x29
---
    $ i2cdetect -y -r 1
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    20: -- -- -- -- -- -- -- -- -- 29 -- -- -- -- -- -- 
    30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- -- 
    40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    70: UU -- -- -- -- -- -- --                         
---

  • Notes
    • Here is a blog post on using the with a Jetson TK1. The pinouts for the board are the same as on a Nano.

5. TB6612FNG Dual H-Bridge Motor Controller

This is used to control the 2 wheels on the duckiebot.

  • Related Repositories
    • HatV3 allows for two potential configurations for motor control; one uses GPIO pins for the direction signals and the other uses the PCA9685 PWM controller to generate direction signals (by using a 100% duty cycle to logic high and 0% duty cycle for logic low)
    • hat_driver/hat.py
      hat.py makes it appear that one motor is using PWM mode and the other is using GPIO mode. That is strange, but I've confirmed that is how it works.
    • The code to control a single motor is here: hat_driver/motor.py#L111
    • wheels_driver/dagu_wheels_driver.py instantiates 2 Motors;

I figured out how the motor driver is wired and it is a little strange (see duckietown/dt-duckiebot-interface#53) It appears that there is a second PCA9685 that is used to control the motors. It's I2C address is set to 0x60 (this is done by closing the jumper pad 1+A5 with solder, see https://learn.adafruit.com/16-channel-pwm-servo-driver/chaining-drivers).

I really have no idea why one motor uses GPIO pins for direction and the other uses the PCA9685. It makes no sense (which makes me question my understanding of it).

  • NOTE: I could see using the PCA9685 to generate the PWM values for the motor controller, since it creates a high resolution, rock solid PWM signal which offloads that work from the Jetson nano. I even think you could mix LEDs and motor pwm; they just need to operate on the same PWM frequency. What I don't understand is why two different ways to support the direction pins for the two motors. If we only have the one PCA9685, then just use GPIO pins for direction of both motors; that is only 4 pins and they are available. On the other hand, if there is a second PCA9685 when there are plenty of channels on that, so why not use those for both motors? Finally, the Jetson Nano does support 2 hardware PWM pins, so why not use those? So the configuration is weird. I think it is probably this way because the PWM class is written assuming a PCA9685. I think it would be worth a pull request to modify the code to allow the Nano or RaspberryPi 4 to generate the PWM, since both have two hardware PWM pins available; that would allow the DIY Duckiebot to drop the second PCA9685. For now I've included the wiring for the default configuration. I've also annotated an alternative that would use the the second PCA9685 exclusively; you would need to change the default config to enable this mode.

Note: the TB6612FNG has a standby pin( STBY) that must be driven high to enable output to the motors, so below you will see it connected to +3.3v for this purpose.

In the wiring notes in this document, we are using the pin numbers as labelled on the Nano gpio header, also called board pin numbers. It is important to understand that the Duckiebot software configuration related to the encoder input pins in wheel_encoder/config/wheel_encoder_node/left_wheel.yaml and wheel_encoder/config/wheel_encoder_node/right_wheel.yaml are not using board numbering, but instead are using the Broadcom SOC gpio numbering as specified in RaspberryPi pinouts. The Jetson Nano is not based on the Broadcom SOC, but the Jetson Nano version of the GPIO library accepts the broadcom values and maps these values to the correct pins on the board, so the Duckiebot code still works on a Nano. The default configuration uses broadcom gpio-18 and gpio-19, which are board header pin-12 and pin-35 respectively. If you change wiring from what is shown below, then you will need to change the wheel encoder software configuration and you will have to figure out the broadcom gpio numbers for the board pins you use. For convenience, here is a small table that maps the board pin numbers (as used in the motor wiring diagrams below) to the Broadcom SOC gpio numbers (as used in the wheel encoder config).

    Board Pin ... BCM (Broadcom gpio) pin
       pin-12  =  gpio-18
       pin-13  =  gpio-27
       pin-15  =  gpio-22
       pin-35  =  gpio-19
Motor 1 (Left wheel)
---
    Nano ... PCA9685x60 ... TB6612FNG ... Motor A ... 5v ... 3.3v ... GND
                             VM  <------------------> 5v
                             VCC <------------------> 5v
                             GND <----------------------------------> GND 
                             STBY <------------------------> 3.3v
               ch-10 <-----> A-IN-1 
               ch-09 <-----> A-IN-2
               ch-08 <-----> PWM-A
                             A-OUT-1 <--> M2 motor +
                             A-OUT-2 <--> M1 motor -
    pin-35 <----------------------------> enc-2
    pin-15 <----------------------------> enc-1
                                          VCC <------------> 3.3v
                                          GND <--------------------> GND
---
Motor 2 (Right Wheel)
---
    Nano ... PCA9685x60 ... TB6612FNG ... Motor B ... 5v ... 3.3v ... GND
                             VM  <------------------> 5v
                             VCC <------------------> 5v
                             GND <----------------------------------> GND 
                             STBY <------------------------> 3.3v
               ch-13 <-----> PWM-B
    pin-33 <---------------> B-IN-1
    pin-31 <---------------> B-IN-2
                             A-OUT-1 <--> M2 motor +
                             A-OUT-2 <--> M1 motor -
    pin-13 <----------------------------> enc-2
    pin-12 <----------------------------> enc-1
                                          VCC <------------> 3.3v
                                          GND <--------------------> GND
---
Motor2 (Right wheel: modified to only use PCA9685 - requires configuration change)
---
    Nano ... PCA9685x60 ... TB6612FNG ... Motor B ... 5v ... 3.3v ... GND
                             VM  <------------------> 5v
                             VCC <------------------> 5v
                             GND <----------------------------------> GND 
                             STBY <------------------------> 3.3v
               ch-03 <-----> B-IN-1
               ch-02 <-----> B-IN-2
               ch-01 <-----> PWM-B
                             A-OUT-1 <--> M2 motor +
                             A-OUT-2 <--> M1 motor -
    pin-13 <----------------------------> enc-2
    pin-12 <----------------------------> enc-1
                                          VCC <------------> 3.3v
                                          GND <--------------------> GND
---

Add a second PCA9685 on bus 1 at address 0x60

The default PWM channels are configured to be on a PCA9685 at address 0x60. The first PCA9685 that we added is dedicated to 5 RGB LEDs, so 15 of the 16 available PWM channels are occupied, so there is not room on that PWM controller anyway. So we need to add a second PCA9685 in order to support the default HAT configuration for the motor controller.

By default the PCA9685 is at address 0x40. However, there are a set of open jumper pads that can be used to alter the address by closing (soldering) the jumper pads. The duckiebot software is setup to address this at 0x60. Each pad adds a power of 2 to the default address of 0x40. We can change the address of a PCA9685 to 0x60 by closing the solder pad marked A5, which adds 2^5 = 0x20 to the address.

See https://learn.adafruit.com/16-channel-pwm-servo-driver/chaining-drivers

I then used jumper wires to carry across the 5v and GND from the original PCA9685 header (for LEDs) to the second PCA9685 header to get it power.

Hookup of VCC, GND, SDL and SCL are the same as the original PCA9685.

With this second PCA9685 in place, we can operate the motor controller as it is configured in the code by default.

After the second PCA9685 is in place, the i2c bus should look like this:

---
    $ i2cdetect -y -r 1
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    20: -- -- -- -- -- -- -- -- -- 29 -- -- -- -- -- -- 
    30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- -- 
    40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    70: 70 -- -- -- -- -- -- --   
---

Generating PWM from the Jetson Nano

The duckiebot code always assumes that PWM is generated by a PCA9685. However, if we were to generate PWM via the Jetson Nano GPIO header pins, then we would not need the second PCA9685. This would require extending the HAT controller to configure and control hardware PWM pins on the Nano or RaspberryPi. Both the Jetson Nano and RaspberryPi4 support two hardware PWM pins. On the Jetsion Nano, these must be configured.

For now, I will use the second PCA9685 so we can use the Duckiebot code as-is. Once I get that working, I will look at creating a pull request that would allow the Duckiebot to use two PWM pins on the Jetson and RaspberryPi4.

Configure Jetson Expansion Header for PWM

  • ssh into the duckiebot and run this command sudo /opt/nvidia/jetson-io/jetson-io.py. It should show the Jetson Expansion Header Tool that allows you to change GPIO pin functions (see below).

  • If your Jetson expansion header configuration does not show any PWM pins, then you will need to enable them.

---
     =================== Jetson Expansion Header Tool ===================
     |                                                                    |
     |                                                                    |
     |                        3.3V ( 1)  ( 2) 5V                          |
     |                        i2c2 ( 3)  ( 4) 5V                          |
     |                        i2c2 ( 5)  ( 6) GND                         |
     |                      unused ( 7)  ( 8) uartb                       |
     |                         GND ( 9)  (10) uartb                       |
     |                      unused (11)  (12) unused                      |
     |                      unused (13)  (14) GND                         |
     |                      unused (15)  (16) unused                      |
     |                        3.3V (17)  (18) unused                      |
     |                      unused (19)  (20) GND                         |
     |                      unused (21)  (22) unused                      |
     |                      unused (23)  (24) unused                      |
     |                         GND (25)  (26) unused                      |
     |                        i2c1 (27)  (28) i2c1                        |
     |                      unused (29)  (30) GND                         |
     |                      unused (31)  (32) unused                      |
     |                      unused (33)  (34) GND                         |
     |                      unused (35)  (36) unused                      |
     |                      unused (37)  (38) unused                      |
     |                         GND (39)  (40) unused                      |
     |                                                                    |
      ====================================================================
---

Choose Configure the 40 pin expansion header to activate pwm0 and pwm1:

---
     =================== Jetson Expansion Header Tool ===================
     |                                                                    |
     |                                                                    |
     |                        3.3V ( 1)  ( 2) 5V                          |
     |                        i2c2 ( 3)  ( 4) 5V                          |
     |                        i2c2 ( 5)  ( 6) GND                         |
     |                      unused ( 7)  ( 8) uartb                       |
     |                         GND ( 9)  (10) uartb                       |
     |                      unused (11)  (12) unused                      |
     |                      unused (13)  (14) GND                         |
     |                      unused (15)  (16) unused                      |
     |                        3.3V (17)  (18) unused                      |
     |                      unused (19)  (20) GND                         |
     |                      unused (21)  (22) unused                      |
     |                      unused (23)  (24) unused                      |
     |                         GND (25)  (26) unused                      |
     |                        i2c1 (27)  (28) i2c1                        |
     |                      unused (29)  (30) GND                         |
     |                      unused (31)  (32) pwm0                        |
     |                        pwm2 (33)  (34) GND                         |
     |                      unused (35)  (36) unused                      |
     |                      unused (37)  (38) unused                      |
     |                         GND (39)  (40) unused                      |
     |                                                                    |
      ====================================================================
---

After enabling, pwm0 is pin-32 and pwm1 is pin-33.

6. Gear Motor and Quadrature Encoder

The motors I am using are n=N20 gear motors with hall effect quadrature encoders; like this https://www.aliexpress.com/item/4000759558143.html

The connector is labelled. They come with a wire harness that plugs into the connector. The wire color corresponds to the following:

---
    PIN   WIRE    PURPOSE
    M2:   red:    motor power '+'
    VCC:  black:  encoder power, 3.3v to 5v
    C2:   yellow: encoder output signal A
    C1:   green:  encoder output signal B
    GND:  blue:   encode power GND
    M1:   white:  motor power '-'
---

Encoder VCC should be in range 3.3v to 5V. This will also determine the output value of the encoder signals A and B, so for our purposes we want those to be 3.3v so the Jetson Nano can read them safely. So we need to provide 3.3v for encoder VIN. This can be done using the 3.3v output pin in the Jetson Nano header.

Motor power can be whatever the motor is rated for. In the case of these motors, that is 6v. I will actually be using 5v because that is what I have readily available without having to introduce a voltage converter. Remember that the Jetson Nano and the encoder must share a common ground.