JetBot using L298N PWM Motor

Anand Mohan
6 min readJul 3, 2020

--

NVIDIA AI IoT Platform Project

An open source NVIDIA project where you can build a miniature driving bot with the help of IoT and AI. Use the below link to know more about the project.

https://diyrobocars.com/2019/08/12/adventures-with-the-nvidia-jetbot-and-jetracer/
Image from diyrobocars.com

Note: I have used a four wheels chassis car (two wheels was not available with me) and need RPi.GPIO python library to run this project and need a network connection.

Here, JetBot with NVIDIA Jetson Nano uses DC Motor + Stepper FeatherWing which will let you use 2 x bi-polar stepper motors or 4 x brushed DC motors. The the motor driver can with replaced with PWM motor driver which is much cheaper (Rs 200). Follow the below steps to build the product:

  1. Setup the Jetson Nano with JetPack

Let’s get into the software part first. Click Here for the instructions to download the customized JetPack for flashing into the Jetson Device and to complete the initial software setup of the Jetbot program.

Inside the jetbot directory (that you downloaded from the link), there will be a file named “setup.py”. Remove the Adafruit packages from the installation as we are not using Adafruit motor driver. So it’s kind of unnecessary to install. The file content will be looking like this at the end:

import glob
import subprocess
from setuptools import setup, find_packages, Extension
def build_libs():
subprocess.call(['cmake', '.'])
subprocess.call(['make'])
build_libs()setup(
name='jetbot',
version='0.4.0',
description='An open-source robot based on NVIDIA Jetson Nano',
packages=find_packages(),
install_requires=[],
# 'Adafruit_MotorHat',
# 'Adafruit-SSD1306',

package_data={'jetbot': ['ssd_tensorrt/*.so']},
)

After running this code, you initial setup of this project is complete. TO proceed further, We have to jump into the hardware setup now.

2. Hardware Connection

Now, power off and unplug the Jetson device and start connecting it with the motor driver.

https://developer.nvidia.com/embedded/jetson-nano-developer-kit https://robu.in/product/l298-based-motor-driver-module-2a/
Jetson Nano image from developer.nvidia.com and L298N motor driver from robu.in

Use the below diagram to connect the devices accordingly. Here, Pin 32 and 33 has been used for PWM ouput and rest Pins for L298N inputs. You a 2A power bank for Jetson and 1500mAh Lithium ion battery for the motor.

Hardware connection between Jetson Nano and L298N Motor Driver

Add a Logitech camera (or any other) to the Jetson module for this project. So, the finally the whole hardware setup will be looking like this:

Hardware Setup of the JetBot Project

Now it’s time to play around the codes to make with work.

3. Coding Part

Now power on the jetson nano module and connect with a monitor and keyboard and mouse. Go to the jetbot/jetbot/ directory where you can see files like motor.py, robot.py, camera.py which are used to control the different parts of the car. We have to change the code in motor and robot and camera python files alone to add L298N motor driver to work with this project.

Inside robot.py file, change the Adafruit motor code to PWM motor code. Pins 35 and 36 is connected to the two left motors and Pins 37 and 38 is connected to the two right motors through the motor driver. The PWM speed ranges from 0(no speed) to 100(full speed).

Play around with the pin number and code for two wheel chassis car.

import time
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
class Robot():
def __init__(self, *args, **kwargs):
super(Robot, self).__init__(*args, **kwargs)
self.left_motor = [35,36]
self.right_motor = [37,38]
self.left_speed = 0
self.right_speed = 0
GPIO.setup(32,GPIO.OUT)
GPIO.setup(33,GPIO.OUT)
self.pwm=[GPIO.PWM(32,50),GPIO.PWM(33,50)]
GPIO.setup(self.left_motor[0],GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(self.right_motor[0],GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(self.left_motor[1],GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(self.right_motor[1],GPIO.OUT,initial=GPIO.LOW)
self.pwm[0].start(0)
self.pwm[1].start(0)
def set_motors(self, left_speed=1.0, right_speed=1.0):
GPIO.output(self.left_motor[0],GPIO.HIGH)
GPIO.output(self.right_motor[0],GPIO.HIGH)
self.left_speed = ((left_speed - (-1))/2)*100
self.right_speed = ((right_speed - (-1))/2)*100
print()
print()
self.pwm[0].ChangeDutyCycle(self.left_speed)
self.pwm[1].ChangeDutyCycle(self.right_speed)

def forward(self, speed=1.0, duration=None):
GPIO.output(self.left_motor[0],GPIO.HIGH)
GPIO.output(self.right_motor[0],GPIO.HIGH)
GPIO.output(self.left_motor[1],GPIO.LOW)
GPIO.output(self.right_motor[1],GPIO.LOW)
self.speed = ((speed - (-1))/2)*100
self.pwm[0].ChangeDutyCycle(self.speed)
self.pwm[1].ChangeDutyCycle(self.speed)
def backward(self, speed=1.0):
GPIO.output(self.left_motor[0],GPIO.LOW)
GPIO.output(self.right_motor[0],GPIO.LOW)
GPIO.output(self.left_motor[1],GPIO.HIGH)
GPIO.output(self.right_motor[1],GPIO.HIGH)
self.speed = ((speed - (-1))/2)*100
self.pwm[0].ChangeDutyCycle(self.speed)
self.pwm[1].ChangeDutyCycle(self.speed)
def left(self, speed=1.0):
GPIO.output(self.left_motor[0],GPIO.LOW)
GPIO.output(self.right_motor[0],GPIO.HIGH)
GPIO.output(self.left_motor[1],GPIO.HIGH)
GPIO.output(self.right_motor[1],GPIO.LOW)
self.speed = ((speed - (-1))/2)*100
self.pwm[0].ChangeDutyCycle(self.speed)
self.pwm[1].ChangeDutyCycle(self.speed)
def right(self, speed=1.0):
GPIO.output(self.left_motor[0],GPIO.HIGH)
GPIO.output(self.right_motor[0],GPIO.LOW)
GPIO.output(self.left_motor[1],GPIO.LOW)
GPIO.output(self.right_motor[1],GPIO.HIGH)
self.speed = ((speed - (-1))/2)*100
self.pwm[0].ChangeDutyCycle(self.speed)
self.pwm[1].ChangeDutyCycle(self.speed)
def stop(self):
GPIO.output(self.left_motor[0],GPIO.LOW)
GPIO.output(self.right_motor[0],GPIO.LOW)
GPIO.output(self.left_motor[1],GPIO.LOW)
GPIO.output(self.right_motor[1],GPIO.LOW)
self.left_speed = 0
self.right_speed = 0
self.pwm[0].ChangeDutyCycle(self.left_speed)
self.pwm[1].ChangeDutyCycle(self.right_speed)

Delete the motor.py file as it is not necessary as all the required code for the motor is implemented in the robot.py file.

Inside the camera.py code, change the highlighted code for gstreaming depending upon the camera. Its like a flow from the camera end to the jetson end. Play around with it as there are changes in the flow if you are using Logitech camera instead of Logitech camera. Refer this link if you come across any issues while making this stream flow.

import traitlets
from traitlets.config.configurable import SingletonConfigurable
import atexit
import cv2
import threading
import numpy as np
class Camera(SingletonConfigurable):

value = traitlets.Any()

# config
width = traitlets.Integer(default_value=224).tag(config=True)
height = traitlets.Integer(default_value=224).tag(config=True)
fps = traitlets.Integer(default_value=21).tag(config=True)
capture_width = traitlets.Integer(default_value=640).tag(config=True)
capture_height = traitlets.Integer(default_value=480).tag(config=True)
def __init__(self, *args, **kwargs):
self.value = np.empty((self.height, self.width, 3), dtype=np.uint8)
super(Camera, self).__init__(*args, **kwargs)
try:
self.cap = cv2.VideoCapture(self._gst_str(), cv2.CAP_GSTREAMER)
re, image = self.cap.read()if not re:
raise RuntimeError('Could not read image from camera.')
self.value = image
self.start()
except:
self.stop()
raise RuntimeError(
'Could not initialize camera. Please see error trace.')
atexit.register(self.stop)def _capture_frames(self):
while True:
re, image = self.cap.read()
if re:
self.value = image
else:
break

def _gst_str(self):
return 'v4l2src ! video/x-raw, width=640, height=480, format=(string)YUY2, framerate=(fraction)30 ! nvvidconv ! video/x-raw(memory:NVMM) ! nvvidconv ! video/x-raw, width=(int)224, height=(int)224, format=(string)BGRx ! videoconvert ! appsink'

def start(self):
if not self.cap.isOpened():
self.cap.open(self._gst_str(), cv2.CAP_GSTREAMER)
if not hasattr(self, 'thread') or not self.thread.isAlive():
self.thread = threading.Thread(target=self._capture_frames)
self.thread.start()
def stop(self):
if hasattr(self, 'cap'):
self.cap.release()
if hasattr(self, 'thread'):
self.thread.join()

def restart(self):
self.stop()
self.start()

Now the coding part is completely done. Using a WiFi adapter connected to the Jetson module, connect a PC system and Jetson module to the same network to test the product remotely.

If you installed the customized JetPack given in the site above, then power up the Jetson Nano using power bank and connect to the module’s Jupyterlab using module’s WiFi IP address from the PC System. If you have used other versions of JetPack, please install jupyterlab server and configure it to access remotely.

Before running the model, open the terminal in jupyterlab and run the below command with “sudo” privilege:

# Enable Pin 32 / PWM0
busybox devmem 0x700031fc 32 0x45
busybox devmem 0x6000d504 32 0x2
# Enable Pin 33 / PWM2
busybox devmem 0x70003248 32 0x46
busybox devmem 0x6000d100 32 0x00

It is used to enable the PWM pins, else the JetBot wont move. Each time you start the Jetson module, please do run this code to run the model.

The video of the working model is shown below:

JetBot (Running Phase)

Use this link to play around with the project like collision avoidance, road following and also come up with creative ideas. All the best.

Hope this information was useful to you. Feel free to use the comment section.

Don’t forget to give a clap if you like this blog. Also do follow for more similar stuffs.

Thank you.

--

--

Anand Mohan

Engineer in the field of Data Science and Artificial Intelligence.