Algorithm

Machine learning Model

Soft Actor Critic using Stable Baseline(Reinforcement Learning)
  • I refered this Link for setup.
  • This algorithm is mainly based on Reward function.More points are given when robot follows a path and less point is given when it goes out of the track.
  • Install Tensorflow 1.10.1 version for this algo and use it in different environment.
  • Initially a Variational Auto encoder(VAE) Pretrained agent is used for improving the performance of model along with reward function. Hyperparameters will also affect the performance of model. By changing number of epoch,batch size etc model can be improved.
Linear Model

Switching Between Autonomous and Manual Mode

  • We are using the Donkeycar pacakge in our project.Import the same as shown below.
import donkey as dk
#initialize the vehicle
V = dk.Vehicle()
Check any object detected based on Label Value
    #So we initialise the object of Objectdetectionflag class for setting flag variable when object detected 
    V.add(ObjectDetectionflag(),inputs=['cam/image_array'],outputs=['flag'])
    #keeping the negate value of flag variable
    class Negate():
       def __init__(self):
          pass
       def run(self,flag):
          if flag==True:
             return False
          else:
             return True
    # adding the part to the vehicle
    V.add(Negate(),inputs=['flag'],outputs=['Negate'])
    #Use the run_condition parameter to add part to the vehicle or not this is where webcontroller sitches modes
    V.add(ctr, 
          inputs=['cam/image_array'],
          outputs=['user/angle', 'user/throttle', 'user/mode', 'recording'],
          threaded=True,run_condition='flag')

Note

  • Check the Global.py file for declaration of global variables.

  • Label is obtained from Master i.e. laptop after object is recognised in front of the vehicle.

"""
This class is used for Manual mode operations.Right now robot take decision within 1.5 seconds i.e it moves left then again come back to track after travelling through some distance.
"""
class ValueFetching:
       def __init__(self):
         self.start=None
         self.steering=0.45
         self.throttle=0.6
       def run(self):  
          #this should be called for the first time only 
          if m.count==1:
            self.start=datetime.now()
          m.count=255
          endtime=datetime.now()
          print((endtime-self.start).seconds)
          if (endtime-self.start).seconds>1.5:
              m.count=0
              self.steering=0.45
              m.x=False

          if (endtime-self.start).seconds<1.5:
              self.throttle=0.45
              # changing the steering dynamically based on previous value
              self.steering=self.steering-0.04
              m.x=True
          return self.steering,self.throttle,'user',False
    # add the part to the vehicle when run_condition is reverse that is opposite to webcontroller part.
    #Negate class is used for the above mentioned purpose
    V.add(ValueFetching(),outputs=['user/angle', 'user/throttle', 'user/mode', 'recording'],run_condition='Negate')

Choose between Image zmq and ROS Image Publishing

  • I used both of the methods to send images to rpi. In my opinion Imagezmq method performed well.
  • You can choose any of the methods.
import imagezmq
import socket
import time
#create a part or class which publishes image.Replace tcp address of master by address you get using 
#hostname -I on Master
class ImagePublish:
  def __init__(self):
    self.sender = imagezmq.ImageSender(connect_to="tcp://192.168.1.7:5555")
    self.rpiName = socket.gethostname()
    self.imageHub = imagezmq.ImageHub()


  def run(self,image):
    # send the image only if not none and only if sender is ready
    if(image is not None and self.sender is not None):
        print("Connection successful")
        print(self.sender.send_image(self.rpiName,image))



  def shutdown(self):
    pass

import os
import rospy
from std_msgs.msg import String, Int32, Float32
from sensor_msgs.msg import Image
from cv_bridge import CvBridge, CvBridgeError

# this part should be called in manage.py
class RosPublisher(object):
    '''
    A ROS node to pubish to a Image data stream
    '''
    def __init__(self, node_name, channel_name, stream_type=Image, anonymous=True):
        #replace ip with your host address
        os.environ['ROS_IP'] = '192.168.1.4'

        #run roscore in master before running slave
        os.environ['ROS_MASTER_URI'] = 'http://192.168.1.4:11311/'

        self.pub = rospy.Publisher(channel_name, stream_type,queue_size=10)
        self.bridge = CvBridge()
        rospy.init_node(node_name, anonymous=anonymous)

    def run(self, data):
        '''
        only publish when data stream changes.
        '''
        if data is not None and not rospy.is_shutdown():
            # cvbridge is used to convert open cv or numpy image to sensor image suitable with ros msg passing
            self.pub.publish(self.bridge.cv2_to_imgmsg(data, "bgr8"))

Behaviour Clonning

  • This is a simple CNN followed by deep neural network required for training the images to predict throttle,steering values.
    drop = 0.1

    img_in = Input(shape=input_shape, name='img_in')

    #get the image passed from part initialisation
    x = img_in
   `x = Convolution2D(24, (5,5), strides=(2,2), activation='relu', name="conv2d_1")(x)
    x = Dropout(drop)(x)
    x = Convolution2D(32, (5,5), strides=(2,2), activation='relu', name="conv2d_2")(x)
    x = Dropout(drop)(x)
    x = Convolution2D(64, (5,5), strides=(2,2), activation='relu', name="conv2d_3")(x)
    x = Dropout(drop)(x)
    x = Convolution2D(64, (3,3), strides=(1,1), activation='relu', name="conv2d_4")(x)
    x = Dropout(drop)(x)
    x = Convolution2D(64, (3,3), strides=(1,1), activation='relu', name="conv2d_5")(x)
    x = Dropout(drop)(x)
    #flatten it so that it fits to Deep neural network layer input
    x = Flatten(name='flattened')(x)
    x = Dense(100, activation='relu')(x)
    x = Dropout(drop)(x)
    x = Dense(50, activation='relu')(x)
    #drop some layer helps in performance improvement
    x = Dropout(drop)(x)`

   outputs = []
    #output consists of steering and throttle
    #final model object which is saved as model.h5 file
    for i in range(num_outputs):
        outputs.append(Dense(1, activation='linear', name='n_outputs' + str(i))(x))

    model = Model(inputs=[img_in], outputs=outputs)

  • Predicting the steering and throttle based on linear model output which takes realtime image as input.
    img_arr = img_arr.reshape((1,) + img_arr.shape)
    outputs = self.model.predict(img_arr)
    steering = outputs[0]
    throttle = outputs[1]

Warning

  • Add all the above part in manage.py file by calling-> format is as follows
    V.add(object,inputs=[],outputs=[],threaded or not,run condition)