JetBot using L298N PWM Motor
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.
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:
- 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, Extensiondef 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.
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.
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:
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 npclass 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:
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.