A Project Report on “Design and Simulation of Last Mile Delivery Robot” Submitted in partial fulfilment of the requirements for the award of degree of BACHELOR OF ENGINEERING In MECHANICAL ENGINEERING Submitted by Mr. Ghure Mohd. Muneer (7117015) Mr. Hamza Shabbir Husein (7117020) Mr. Ammaar Shahid Solkar (7218075) Mr. Khatib Salman Mohammed Rafi (7114027) Under the guidance of Er. Parvez Waghoo Department of Mechanical Engineering M. H. SABOO SIDDIK COLLEGE OF ENGINEERING 8, SABOO SIDDIK POLYTECHNIC ROAD, MUMBAI - 400 008 (A.Y 2020-21) A Project Report on “Design and Simulation of Last Mile Delivery Robot” Submitted in partial fulfilment of the requirements for the award of degree of BACHELOR OF ENGINEERING In MECHANICAL ENGINEERING Submitted by Mr. Ghure Mohd. Muneer (7117015) Mr. Hamza Shabbir Husein (7117020) Mr. Ammaar Shahid Solkar (7218075) Mr. Khatib Salman Mohammed Rafi (7114027) Under the guidance of Er. Parvez Waghoo Department of Mechanical Engineering M. H. SABOO SIDDIK COLLEGE OF ENGINEERING 8, SABOO SIDDIK POLYTECHNIC ROAD, MUMBAI - 400 008 (A.Y 2020-21) i CERTIFICATE This is to certify that the project entitled “Design and Simulation of Last Mile Delivery Robot” is a bonafide work of Mr. Ghure Mohd. Muneer (7117015), Mr. Hamza Shabbir Husein (7117020), Mr. Ammaar Shahid Solkar (7218075), Mr. Khatib Salman Mohammed Rafi (7114027) submitted to the University of Mumbai in partial fulfillment of the requirement for the award of the degree of Undergraduate in Mechanical Engineering. _________________ Prof. Parvez Waghoo (Guide) _______________________ _______________________ Dr. Javed Shaikh (HOD - Mechanical) Dr. Ganesh Kame (I/C Principal) _______________________ _______________________ Internal Examiner External Examiner i Project Report Approval for B. E. This project report entitled “Design and Simulation of Last Mile Delivery Robot” by Mr. Ghure Mohd. Muneer, Mr. Hamza Shabbir Husein, Mr. Ammaar Shahid Solkar, Mr. Khatib Salman Mohammed Rafi is approved for the degree of Mechanical Engineering. Examiners 1.--------------------------------------------- 2.--------------------------------------------- Date: Place: Mumbai ii DECLARATION We declare that this written submission represents our ideas in our own words and where others' ideas or words have been included, we have adequately cited and referenced the original sources. We also declare that we have adhered to all principles of academic honesty and integrity and have not misrepresented or fabricated or falsified any idea/data/fact/source in my submission. We understand that any violation of the above will be cause for disciplinary action by the Institute and can also evoke penal action from the sources which have thus not been properly cited or from whom proper permission has not been taken when needed. Name Roll No. Ghure Mohd. Muneer 7117015 Hamza Shabbir Husein 7117020 Ammaar Shahid Solkar 7218075 Khatib Salman Mohammed Rafi 7114027 Date: iii Signature ABSTRACT E-commerce has become an indispensable part of our life today. An important aspect of ecommerce is the fast, economical and safe delivery of the product to the customer right at their doorstep i.e. the last mile delivery. The last mile delivery contributes to a major proportion of the overall shipping costs and logistics companies have been looking to reduce this cost by using robots and drones. Several companies have built delivery robots which can deliver packages to the customers in open areas. The limitation of these robots however is that they cannot deliver the package at the door of the customer in an apartment building, at the most delivering it at the lobby. This leaves out a major proportion of the customers using ecommerce to benefit from this technology. Here we have proposed a method, using which, a robot can deliver packages not only at the lobby of an apartment building but also right at the doorstep of the customer at any floor accessible by an elevator. We have developed a method which uses grid maps to map multiple floors and perform deliveries. The developed method has been successfully implemented in ROS, a popular robotics framework and simulated using Gazebo, a multi-robot physics simulator. The implementation can be used as a starting point for more robust and generalized methods for indoor delivery robots The following report contains an explanation of the work done. The first three chapters discuss the need for this project, existing literature and designs on delivery robots. In chapter 4 we lay out the parameters we considered during the design process. Chapter 5 contains details on the planning of the project and chapter 6 contains the architecture of the designed system for mapping and delivery. Chapter 7 aims to give a brief overview of the major tools used during the development process. The chapters 8 through 10 contain details of the implementation and work done. Chapter 11 lays out the result and in chapter 12 we discuss the conclusions and the areas to which can be improved. Finally chapter 13 contains the references used and is followed by Appendix 1 which contains some important parts of the code base. Keywords - Last Mile Delivery, Mobile Robotics, Autonomous Navigation, Multifloor Navigation, Multi-floor Mapping. iv CONTENTS Certificate i Approval Sheet ii Declaration iii Abstract iv Contents v List of Figures viii List of Tables ix Chapter 1 Introduction 1.1 Problem Statement 1 1.2 Project Goals 2 1.3 Project Objectives 2 1.4 Project Overview 3 Chapter 2 Literature Review 2.1 Introduction 4 2.2 Navigation methods 4 2.3 Image Processing 5 Chapter 3 Existing Designs 3.1 Introduction 7 3.2 Savioke 7 3.3 Starship Technologies 8 3.4 Kiwibot 9 Chapter 4 Design Parameters 4.1 Objective 10 4.2 Mechanical Design Parameters 10 4.3 Software Design Parameters 12 Chapter 5 Methodology 5.1 Work Breakdown Structure 13 5.2 PERT Analysis 14 i Chapter 6 Architecture 6.1 Introduction 18 6.2 Mapping 19 6.3 Delivery 20 6.4 Discussion on Map Security 21 Chapter 7 Technologies 7.1 Introduction 23 7.2 ROS (Robot Operating System) 23 7.3 Gazebo 24 7.4 Computer Vision 26 Chapter 8 Implementation 8.1 Introduction 28 8.2 ROS Packages 29 8.3 Robot Description 30 8.4 Perception 32 8.5 Elevator 34 8.6 Simulation Environments 35 8.7 Mapping 38 8.8 Navigation 41 8.9 Vision 47 8.10 Delivery 48 Chapter 9 Mechanical Design 9.1 Introduction 51 9.2 Motor Torque & Power Calculations 51 9.3 Components Suggestion 55 9.4 Concept Design Sketches 58 Chapter 10 Image Processing 10.1 Introduction 59 10.2 Necessity 60 10.3 Working 61 ii Chapter 11 Results 64 Chapter 12 Conclusion 12.1 Conclusion 68 12.2 Future Work 69 Chapter 13 References 70 Appendix I 72 iii LIST OF FIGURES Fig. No. Title Page No. Fig 2.1 Image processing of a sign to obtain text 6 Fig 2.2 Objects as seen from camera (right) and objects detected after image processing (left) 6 Fig 3.1 Savioke Relay Robot 8 Fig 3.2 Starship Technologies Robot 8 Fig 3.3 Kiwibot Robot 9 Fig 5.1 Work Breakdown Structure 14 Fig 5.2 Critical Path 16 Fig 6.1 Mapping Architecture 19 Fig 6.2 Delivery Architecture 20 Fig 7.1 ROS Logo 23 Fig 7.2 Gazebo logo 24 Fig 7.3 Gazebo features 25 Fig 7.4 Object detection and labelling 26 Fig 8.1 ROS package distribution of project 28 Fig 8.2 DeliveryBot simulation model 30 Fig 8.3 GUI for DeliveryBot control 31 Fig 8.4 DeliveryBot dropping package 31 Fig 8.5 rqt_robot_steering plugin for control of DeliveryBot’s motion 31 Fig 8.6 Visualization of PointCloud2 laser data from simulation environment (left) in RViz (right) 33 Fig 8.7 Visualization of depth cloud data from simulation environment (left) in RViz (right) 33 Fig 8.8 Visualization of camera data from left, right, and front cameras respectively 34 Fig 8.9 GUI for Elevator control 34 Fig 8.10 CAD model of elevator (left), transparent visualization of elevator model in gazebo (center), visualization of elevator model with door open in gazebo (right) 35 Fig 8.11 Simulation environment of building in Gazebo 36 iv Fig 8.12 Close up of simulation environment of building in Gazebo 36 Fig 8.13 CAD model of another simulation environment with ramp and stairs based on real building 37 Fig 8.14 A grid map 38 Fig 8.15 Multi layered map composed of grid maps for multi-floor map 39 Fig 8.16 Mapping GUI 39 Fig 8.17 Created map with marked points 41 Fig 8.18 Figure showing different types of costmaps and plans 42 Fig 8.19 Door detection and number plate recognition 47 Fig 8.20 Delivery GUI 48 Fig 8.21 Multi-floor navigation illustration 49 Fig 8.22 Flow of delivery process 50 Fig 9.1 MATLAB Script for Torque Requirement Estimation 52 Fig 9.2 Torque Estimation Results 53 Fig 9.3 MATLAB Script for motor power estimation 54 Fig 9.4 Motor Power Estimation Results 54 Fig 9.5 Components Packaging Concept Sketch 57 Fig 9.6 Shape Study 57 Fig 9.7 Concept Exploration 58 Fig 9.8 Packaging Concept 58 Fig 10.1 Door and apartment number detection in Python 61 Fig 10.2 Door and apartment number detection implementation in ROS 61 Fig 11.1 Mapping a building 65 Fig 11.2 DeliveryBot dropped at building for delivery 65 Fig 11.3 DeliveryBot waiting for elevator 66 Fig 11.4 DeliveryBot dropping package at door 66 Fig 11.5 DeliveryBot waiting to be picked up after delivering package 67 v LIST OF TABLES Table No. Title Page No. Table 5.1 Duration of work 15 Table 5.2 Slack table 17 Table 9.1 Processing Board Options 55 Table 9.2 Depth Camera Options 55 Table 9.3 Laser Scanner Options 56 vi Chapter 1 Introduction 1.1 Problem Statement The last mile delivery market accounted to $ 1.99 B in 2018 and is expected to grow at a CAGR of 16.7% during the forecast period 2019 – 2027, to account to $ 7.69 B by 2027 [1]. The requirement for better and optimized last mile delivery methods is increasing rapidly as more and more customers are taking their shopping online for food, grocery, medicines, electronics, furniture, etc. Consumers want faster deliveries and want them more frequently. The timeliness of deliveries is getting more important as the online shopping culture becomes mainstream and customer satisfaction is very important even in the delivery aspect. Last mile delivery costs account for 53% of the total cost of shipping [2] and in order to stay profitable while providing deliveries at lower costs, newer methods and technologies have to be developed and adopted. To do this several companies and startups are exploring ways to use drones, automated ground vehicles, autonomous vehicles droids and others to fulfill this requirement. Companies like StarShip, Eliport, Delivers AI and OTSAW have started delivery services using ground vehicles mainly working using GPS and delivering in open areas. Companies like Amazon, UPS, Wing are working with drones to execute deliveries where they drop the packages at the customer's house. One important limitation of these drones and AGVs is that they currently only work for open areas and places with suburban housing. They cannot deliver to the customers' doors in buildings and high-rises either requiring them to collect the packages from the lobby or outright leaving them out of the target customer base. Closing the last mile delivery 1 gap is an important topic to be able to completely automate the delivery processes. Exploring the limitations that current products and methods used for package delivery face in delivering to buildings and developing a proof of concept to overcome it is the goal of this project. 1.2 Project Goals A last mile delivery robot for delivering to apartments in buildings will require certain critical functionalities. These functionalities are listed below: 1. Ability to navigate autonomously in crowded indoor areas as well as outdoor areas. 2. Ability to communicate with elevators in the building where the robot is executing the delivery. 3. Ability to autonomously operate and ride elevators to go from one floor to another while carrying packages. 4. Ability to navigate in areas with little to none prior mapping. 5. Ability to safely deliver packages to the customer’s house or office by either delivering the package in a safe storage box outside the customer’s house or by going into the customers house by using IoT, dropping the package in the house and leaving. 6. Ability to communicate with the central or decentralized server to get delivery details and packages. 7. Keep all invested parties up to date on the status of the package by providing on time updates. 8. Ability to work as a swarm of robots in collaboration with delivery vans which can transport these robots from the delivery sites to the warehouses. 9. Have a likeable appearance and not cause any discomfort or harm to the humans it encounters. These goals are some of the qualities and functionalities a delivery robot for buildings should have. 1.3 Project Objectives The objectives of this project are to implement some of the goals in some form or other to develop a proof of concept for the delivery robot. Some goals stated previously will require technologies which are considered state of the art at the time of writing and a major time commitment so they are reduced to a simpler and currently implementable level. Taking these factors and time constraint into consideration, the following are the objectives of this project: 2 1. Develop a simulation environment for the delivery robot that resembles the environment the robots will face in the real world. 2. Develop the simulation version of the delivery robot which can be simulated in the simulation environment and can be directly deployed on hardware with little to no changes. 3. Implement navigation techniques in the delivery robot to allow the robot to navigate autonomously while avoiding obstacles. 4. Develop software to allow robots to map buildings which it can later use during navigation. 5. Develop a method for multi-floor navigation of the delivery robot. 6. Explore concepts for storage and delivery mechanisms. 7. Explore concept designs for the delivery robots. 8. Select hardware components which will be required to build a physical prototype of the robot. 1.4 Project Overview In this project we develop and simulate a delivery robot which can be dropped outside the building by an autonomous delivery van from where it can go to the required apartment door by navigating the hallways and operating the elevators. Important functionality which are focused upon are the ability to plan the path from the dropped location to the delivery apartment door, perform multi-floor navigation, navigate the area while avoiding obstacles, operating elevators to move between floors, verify that the package is being delivered to the correct location and then dropping the package. This is developed in the industry standard for robot middleware Robot Operating System (ROS) [3] and simulated in a multi-robot physics simulation software Gazebo [4]. We have developed a method to create multi-floor maps using grid maps and perform multi-floor navigation using these maps using the ROS navigation stack [5]. 3 Chapter 2 Literature Review 2.1 Introduction Before we discuss our approach to solving the problem statement, we first have to look into existing literature in order to familiarize ourselves with the current progress or solutions found within certain domains. By conducting a literature review, we are able to identify the methods used to gain a conclusion, and also understand the strength and weaknesses or the opinions of researchers on a topic. We are also able to justify our choice of design, as well as how we aim to improve on existing research. 2.2 Navigation methods 2.2.1 Navigation tuning guide Kaiyu Zheng [6] discusses the tuning process of the ROS navigation stack which is responsible for producing safe paths for the robot to navigate in the environment. The author first discusses how to obtain and set the maximum velocity and maximum acceleration of the robot in the navigation stack. After that the author discusses the different types of global planners available and their differences. The author then explains the global planner parameters and their effects on the produced paths and also suggests values for the parameters which the author found to be acceptable after tuning. The author selected Dynamic Window Approach (DWA) [7] local 4 planner for the navigation stack. We used the DWA planner in the initial version of the robot then switched to Timed Elastic Band (TEB) [8] local planner due to the latter having some advantages which we required. The author also discusses costmap parameters, recovery behaviours, localization and problems. We found this paper to be extremely valuable in our initial versions and saved us a lot of time. 2.2.2 Multi-floor mapping with uncertainty James Ellingson et al. [9] outline their research on autonomous robots where they developed a robot capable of multi-floor navigation. They used a turtlebot [10] as their robot base with a RGB-Depth (RGB-D) camera for perception. They described their method of tackling elevator banks at their campus for navigation. As they were sending the commands to the robot through a base computer over Wi-Fi, they reported problems with the robot losing Wi-Fi connectivity which they solved by putting the robot in autonomous mode while it is in the elevator. The authors suggest the need of a method to change map after changing the floor and using a robotics manipulator to operate the elevator buttons in the future work section. 2.3 Image Processing Here we will look into the various research papers which have been selected for our project based on the findings of the papers. 2.3.1 Text Detection & Recognition Muhammad Sami et al. discuss methods to extract text from indoor sign boards to aid in navigation of a robot [11]. The authors discuss the two steps of information extraction from an image, first by understanding the type of objects in the image captured, and next interpreting the letters and symbols present (if any). The focus of the paper is to discuss current ongoing research related to text recognition and the proposed algorithms which can yield high accuracy and maximum results. The authors also discuss in depth the process they have selected for detecting text. While we are not primarily concerned with the color of the signage as it does not aid in our solution and can free up processing power, we do learn about shape and text extraction. Based on the paper’s findings, we have decided to move forward with our approach for object detection, image processing. We have also chosen to implement Optical Character Recognition for text detection as per the findings of the paper. 5 Fig 2.1 Image processing of a sign to obtain text 2.3.2 Object Recognition Chae, Hee-Won, et al. discuss the implementation of object recognition by analyzing their surface appearance using depth sensors [12]. Currently, there is a vast amount of research being done in the field of object detection using depth sensors, primarily for indoor navigation. The authors discuss how to recognize objects which are intended for Simultaneous Localization and Mapping (SLAM). Since most robots travel on the ground, this method aims to also reject the floor surface, allowing for faster processing and detection. This process is also applied for other surfaces, such as walls. Further, the authors also discuss the algorithm they have used in order to detect the object and its depth, i.e. distance from the robot. While we will not be implementing such an algorithm in our project, the purpose of studying this paper is to understand how object detection can be achieved and to understand how the robot should perceive and interact with objects, as well as how to fine tune processing so that high accuracy is achieved to separate objects from other surfaces. Fig 2.2 Objects as seen from camera (right) and objects detected after image processing (left) 6 Chapter 3 Existing Designs 3.1 Introduction In this chapter we discuss the autonomous ground robots currently being used for deliveries in gated areas like universities and suburban neighborhoods. 3.2 Savioke Founded in San Jose, California in 2012, Savioke robots have gained popularity as friendly and sophisticated robots that are primarily used as indoor delivery bots in hotels and residences [13]. These robots are designed such that they are excellent at navigating winding corridors and highways, however, outside that environment, they are very vulnerable. Their most popular model is ‘Relay’. The robot is primarily used in hotels to deliver small and lightweight items to rooms on different floors of the hotel. The robot is designed such that it performs superbly to deliver items, however as a parcel delivery bot it is not good enough. The cargo hold of the robot, which is designed for small items, cannot fit a majority of package sizes that are ordered by consumers from various online E-tailers. Another design flaw is the overall shape of the robot. Being a tall and slim robot, such a design is easily vulnerable to toppling over due to a high center of gravity. To overcome this the footprint of the robot has to be substantially larger, which increases its overall width and thus faces difficulty in avoiding obstacles or humans. It’s 7 very minimal ground clearance also disables it on uneven terrain. The robot operates on ROS which is what we will be implementing in our delivery robot. Fig 3.1 Savioke Relay robot 3.3 Starship Technologies Founded in San Francisco in 2014, Starship technologies[14] is the largest autonomous delivery robot maker to-date. The robot has found success as a food and grocery delivery robot. Even with large wheels and clearance, it is unable to drive on uneven terrain and cannot overcome obstacles higher than a sidewalk. Current iteration of robot is well suited to navigate large sidewalks and suburban neighborhoods, but is unable to navigate in an indoor environment due to it being dependent on GPS location to move about. Its design is such that it can carry a substantial variety of package sizes and weights, while still maintaining a decent footprint such that it doesn't take up a large amount of space. The implementation of cameras on both the front and back allow for a 360-degree view of its surroundings, thus allowing it to easily navigate and avoid obstacles. Fig 3.2 Starship Technologies robot 8 3.4 Kiwibot Founded in Berkeley in 2017, it is one of the more recent entries into the robot delivery space[15]. The company operates primarily on college campuses, delivering food to students either at college or hostels. The goal of the company is to minimize the number of humans delivering and rather allow a person to oversee five to six KiwiBots. The main advantage the company has are the cheap food options that it provides to its customers, thereby allowing it to grow its user base. One of the perks of the robot is its smaller footprint than the competition, allowing it to easily avoid and go around obstacles while taking up less space on the floor as it moves. Currently, the robot can only deliver up to the main floor of a residence, requiring the customer to leave their homes to retrieve their order. The small size of the robot also means it can't always handle uneven surfaces, and require input from operators from time to time. Fig 3.3 Kiwibot robot 9 Chapter 4 Design Parameters 4.1 Objective To create a prototype design of a robot that can navigate from the main entrance of a residential complex to the door of the delivery target utilizing the ramps and elevators present in the buildings. The robot should be able delivery packages to the door without any person having to interact with it. 4.2 Mechanical Design Parameters These are some of the parameters taken into account before starting the design process. The influence of the parameters and the assumptions made to exclude them will depend on the time constraints of the project. 1. Gradients: The robot should be able to climb gradients it encounters in its path without toppling. These gradients will generally be the wheelchair ramps at the entrance of the lobby for the building. Standards for these have to be found and the robot should be designed accordingly. 2. Elevators: To be able to deliver products in buildings, the robot will have to use the elevator to reach floors other than the ground floor. For this the robot should have some 10 means to interact with the elevator. This consideration will also affect the height of the robot to make it easier to reach the elevator control panel. 3. Footprint: The robot footprint should not be very large so as to not inconvenience people coming in its way. This consideration goes hand in hand with elevator and gradient considerations since to make the footprint smaller, the height will have to be increased which will in turn increase the toppling tendency. A suitable compromise will have to be reached. 4. Package Size: The robot has to be designed to accommodate several packages. The capacity of each package compartment has to be decided based on package sizes of frequently ordered goods. To get this data, product dimensions of high and frequently sold goods can be web scraped from e-commerce websites. Variations of the robot can be made to carry fewer packages of larger goods. 5. Coexistence: To ensure that the robot does not pose danger to any person or animal it encounters in its way it should have no sharp corners or exposed moving parts as much as possible along with sensing capability to stop when it comes in contact with someone. The aesthetics of the robots also have to be designed in such a way that they are accepted by a vast majority of people and not feel uncomfortable in the presence of a robot. 6. Range and Battery: Increasing operating time of the robot will require an increase of the battery size which in turn will increase the weight of the robot and decrease space available for cargo. A suitable compromise has to be reached between operating time, battery size and operating costs. The vans in which the robots will arrive also need to have charging docks available for the robots. 7. Privacy and Security: There will be concerns regarding privacy and security with the robots having some information of the delivery target and the fear of being hacked. These have to be addressed and the architecture has to be designed in such a way that only the minimal amount of information required is given to the robot and major data such as the map of the housing complex does not leave the complex. 8. Delivery Mechanism: The delivery mechanism should be such that it does not harm the package during delivery and be able to cycle through packages in the cargo. 11 4.3 Software Design Parameters Software plays a very important role in the robots and has to be designed carefully for efficient functioning. Some important parameters taken into account are listed below. 1. Map Creation: Creating a map is an important part of the navigation. SLAM (Simultaneous localization and mapping) technology has not yet advanced so much as to allow quick and reliable navigation without maps. The map creation process has to be easy and as much automated as possible. Places where operator input is required have to be carefully designed to minimize human errors and time wastage. 2. Navigation: The navigation module of the robot is one of the most important modules in the robot. This module has to be able to plan and navigate efficiently taking the shortest safest path to the destination to minimize the delivery times. It has to be reliable and self-correcting. Care has to be taken that the module also consumes as less computation resources as possible to allow use of lower end and cheaper hardware to be used in the robots. 3. Obstacle Avoidance: Obstacle avoidance is another module which has to be designed carefully. This module is responsible for avoiding any static and dynamic obstacles which come in the way of the robot. This module prevents accidents from happening and makes the robots feel safe to the humans around the robots. 4. Image Recognition: This module provides the robot with a way to perceive and recognize the objects it encounters in the world. This module works and provides information to both navigation and obstacle avoidance modules and also helps in delivery verification. This is a resource intensive module and tradeoffs will have to be made between functionality and resource requirements. 5. Delivery Module: This module is supposed to take care of the application layer of the delivery robot. The module has to receive delivery details, create a plan from these details and provide information to the navigation module to create the path, perform verification before delivering the package, etc. 6. Hardware Control: This module provides an interface between the hardware and the software. The navigation instructions like heading and orientation and operation of joints are converted into motor actuation and signals by this module. 12 Chapter 5 Methodology 5.1 Work Breakdown Structure In order to realize our project, we have to list down every task that needs to be completed. The below chart shows the 4 primary domains of our project and also sub-divides it into different categories: 1. Mechanical Design 2. Environmental Design 3. ROS 4. Imaging 13 Fig 5.1 Work Breakdown Structure 5.2 PERT Analysis To ensure that each task is completed on time and any delay does not affect the overall output of the project, we have used Program Evaluation and Review Technique (PERT) to understand the interdependencies of the work components and find the minimum time within which the project can be completed. The table below lists out each task and its duration of work (in days). 14 Task Description Depends Optimistic on Time (Days) Most Likely Time (Days) A Design Requirements - 5 7 8 7 B Site Survey - 3 7 8 7 C [ROS] Sensors - 4 7 8 7 D Apartment Identification - 10 14 18 14 E Components Selection A 3 4 7 5 F Floor Env B 3 5 7 5 G [ROS] Mapping C, F 4 5 6 5 H Elevator Identification D 10 14 16 14 I Interaction Mechanism E 7 10 12 10 J Storage Mechanism I 7 10 12 10 K Robot Design J 15 20 25 20 L Lobby Env F 3 5 6 5 M Complex Env L 3 5 6 5 N Elevators Env M 3 5 6 5 O Env Files Launch N 1 2 3 2 P [ROS] Navigation M, G 3 5 7 5 Q [ROS] Controller P 4 5 7 6 R Elevator Identification H 10 14 16 14 S Actors Identification R 7 10 12 10 T [ROS] Interaction K, O, H 10 14 16 14 U Integration T, S, Q 10 20 25 20 Table 5.1 Duration of work 15 Pessimistic Expected Time (Days) Time (Days) Below diagram illustrates the various paths that can be taken to complete the project. Based on the priority of tasks that need to be completed, the critical path for the given project is: A - E I - J - K -Y - U Fig 5.2 Critical path To ensure that each task is started at the earliest, we need to know the earliest start, the number of days to the latest start such that further tasks are least affected due to the slack. The below table illustrates that, where black indicates slack days are available, and red indicates that no slack days are available. 16 Task Earliest Start Latest Start Slack (Days) A 0 0 0 B 0 23 23 C 0 23 23 D 0 14 14 E 7 7 0 F 7 30 23 G 7 51 44 H 14 28 14 I 12 12 0 J 22 22 0 K 32 32 0 L 12 35 23 M 17 40 23 N 22 45 23 O 27 50 23 P 17 56 39 Q 22 61 39 R 28 48 20 S 42 56 14 T 52 52 0 U 66 66 0 Table 5.2 Slack table Based on the PERT discussed above, we have come to a final expected duration of the project, from start to end, which is 84 days. However, the duration is subjected to change due to factors that are not in our control. 17 Chapter 6 Architecture 6.1 Introduction The working of the delivery robot is divided in two different phases. The first phase is mapping and the other part is the actual delivering phase. Before a robot can deliver to a building, the building first has to be mapped out by the robot. This mapping includes creating a floor plan and marking important locations on the created map. This map helps the robot to navigate when it comes back later to deliver a package. The mapping has to be only done once and updated only if there is a significant change in the landscape or structure of the building. 18 6.2 Mapping Fig 6.1 Mapping architecture The mapping phase is the first phase in the process. When a building or complex agrees to allow delivery robots to perform deliveries, the vendor will then need to map out the building or complex and add it to its database. The vendor can send one robot with an operator to the site to carry out the mapping process. The operator has to control the robot using a remote control device and wait for the robot to create a complete map of the area. The mapping (gmapping)[16][17][18] package utilizes the sensor information from sensors like Light Detection and Ranging sensor (LiDAR), Depth Cameras and odometry to create an area map. During the mapping the operator can mark important locations like entry points, elevators, pickup and drop points and room locations on the map. The robot only needs to map out the lobby area and only one floor area if all floors are identical. If the layout of the floors change, Different maps will have to be created for different floors. Once the map is complete, the map is tagged with a unique id which will be used to identify the site and the map during the delivery phase. This map is then sent to the mapping server of the vendor and stored in the database for later use. 19 6.3 Delivery Once the mapping phase is complete the building can be delivered to by any robot in the fleet. The overview of the working and dependencies are shown in the figure below. Fig 6.2 Delivery architecture When a delivery is requested, the package is put into the robot and the delivery van drops the robot at the set drop zone for the building. From here the robot's delivery client communicates with the vendor's delivery server from where it receives the delivery details such as the floor number, room number, building map, etc. The delivery details are then sent to the path planner which uses the map to create a path which the robot has to follow to reach the desired apartment. Once the path planning is complete, the plan is sent to the move base which is the module which executes the plan. The move base receives the sensor data from the world which it utilizes to avoid obstacles. When the robot has to interact with an elevator the Elevator controller module kicks in which communicates with the elevator API for the building. In this way the robot can request the elevator to open or close doors and go to specific floors. Once the robot reaches the apartment, it runs a verification check to see if it is in front of the correct location. This is done by running an object detection and Optical Character Recognition (OCR) 20 program to get the room number from the number plate of the door. Once the location is verified the robot drops the package in front of the door and sends a verification message to the customer as well as the vendor. This completes the delivery of the package. The robot then takes a similar route back to its drop location from where it will be picked up by the delivery van. 6.4 Discussion on map security Since the map created by the robot contains important information about the site, security becomes a big concern when saving them for later use and deciding who can access these maps. There are several alternatives as to where maps can be stored depending on the level of control required. Some are discussed in the following subsections. 6.4.1 Map with Robot This is the easiest way to store maps after being created. The maps are created on the robot and annotated by a user using an external device. When the map creation is complete the maps can be stored on the storage system on the robot. This makes it so that only the robot which was used during the map creation can be used for delivery in that location. This provides good security as the map is not shared on a central database and no vendor has access to the map. This poses a serious limitation to the generality of the delivery robots. Storing maps on the robot makes it so that only that specific robot can be used for delivery. This method can be used if the business model is such that the building or housing complex purchases robots for internal delivery use and can be accessed by vendors by allowing the vendors to drop packages in the robot along with the delivery information. This also reduces the chances of monopoly being created by a single vendor. This requires building complexes to make a capital investment of purchasing robots and then bear the cost of maintaining them. This method does not require a significant amount of digital storage for saving the maps since the robot will only have a store a handful of maps. 6.4.2 Map with Building An alternative to storing maps on the robot is to store the maps within the IoT infrastructure of the building. This is assuming the building already has an IoT infrastructure which provides communication lines between the site elevators, doors and other IoT devices. This infrastructure can be leveraged to store maps of the building. In this alternative method, any vendor can send their delivery robots to the building or complex with the delivery details and 21 the robot can receive the map for the building IoT Access Point. This eliminates the need for the buildings to have their own robots and reduces the capital investment and maintenance cost while at the same time not giving up the map to any specific vendor. With this method the map can still potentially be stored by the vendor permanently when the robot receives the map but then it comes under the umbrella of regulations. This method is a compromise between security and convenience. This method does not require storage space on the robot for storing maps since the map is received during the beginning of delivery and then forgotten once the delivery is complete. 6.4.3 Map with Vendor Storing maps on robots posed limitations of investment in purchasing and maintaining a robot while providing convenience and preventing vendor monopoly in the robotic delivery segment. Storing maps with the IoT infrastructure on the other hand did not face the limitation of having to purchase robots as the map data can be accessed by any vendor executing a delivery. This poses a technical challenge as the API of the IoT infrastructure and the system in place to communicate with these robots will have to be standardized across buildings and vendors. This is a difficult task and will take careful planning and collaborative effort from Delivery vendors, IoT vendors and Builders to execute successfully. A third alternative to the two aforementioned methods is to have the map available with the vendor. Since a vendor will be investing in getting the maps created for the building they can argue that they have the right to not share the map with other vendors. This will then require that all vendors separately create their own maps and store them individually on their own central databases. The maps can be stored on the vendor’s map database from where the map can be sent to the robot during delivery along with the delivery details. This however exposes the map with several vendors a breach of data of any of which will leave the building vulnerable. 22 Chapter 7 Technologies 7.1 Introduction The three major technologies that we are using are ROS, Gazebo and Computer Vision. A brief description of their capabilities are provided in the following pages. 7. 2 ROS (Robot Operating System) Fig 7.1 ROS Logo The Robot Operating System (ROS) is a flexible framework for writing robot software. It is a collection of tools, libraries, and conventions that aim to simplify the task of creating complex and robust robot behavior across a wide variety of robotic platforms. Why? Because creating truly robust, general-purpose robot software is hard. From the robot's perspective, problems that seem trivial to humans often vary wildly between instances of tasks and environments. Dealing with these variations is so hard that no single individual, laboratory, or institution can hope to do it on their own. As a result, ROS was built from the ground up to encourage collaborative robotics software development. For example, one laboratory might have experts 23 in mapping indoor environments, and could contribute a world-class system for producing maps. Another group might have experts at using maps to navigate, and yet another group might have discovered a computer vision approach that works well for recognizing small objects in clutter. ROS was designed specifically for groups like these to collaborate and build upon each other's work, as is described throughout this site. 7.2.1 Reason for using ROS: ROS makes it easier to build robotic applications faster without having to reinvent the wheel and build core robotic functionality from scratch saving precious time. ROS is also used in a large number of robots such as the PR2, Turtlebots and such. 7.3 Gazebo Fig 7.2 Gazebo Logo Robot simulation is an essential tool in every roboticist's toolbox. A well-designed simulator makes it possible to rapidly test algorithms, design robots, perform regression testing, and train AI systems using realistic scenarios. Gazebo offers the ability to accurately and efficiently simulate populations of robots in complex indoor and outdoor environments. At your fingertips is a robust physics engine, high-quality graphics, and convenient programmatic and graphical interfaces. Best of all, Gazebo is free with a vibrant community. 24 Fig 7.3 Gazebo features 7.3.1 Reason for using Gazebo: Creating physical prototypes is expensive. Gazebo provides us with a realistic simulation environment where main functionality can be tested. Gazebo integrates with ROS to provide simulation which can be directly deployed on hardware with minimal to no changes as the simulator can simulate actuators available on the market. 25 7.4 Computer Vision Fig 7.4 Object detection and labelling Computer vision is an interdisciplinary scientific field that deals with how computers can gain high-level understanding from digital images or videos. From the perspective of engineering, it seeks to understand and automate tasks that the human visual system can do. Computer vision tasks include methods for acquiring, processing, analyzing and understanding digital images, and extraction of high-dimensional data from the real world in order to produce numerical or symbolic information, e.g. in the forms of decisions. Understanding in this context means the transformation of visual images (the input of the retina) into descriptions of the world that make sense to thought processes and can elicit appropriate action. This image understanding can be seen as the disentangling of symbolic information from image data using models constructed with the aid of geometry, physics, statistics, and learning theory. The scientific discipline of computer vision is concerned with the theory behind artificial systems that extract information from images. The image data can take many forms, such as video sequences, views from multiple cameras, multidimensional data from a 3D scanner, or medical scanning device. The technological discipline of computer vision seeks to apply its theories and models to the construction of computer vision systems. [19] 7.4.1 Reason for using Computer Vision: Cameras provide a high amount of information as opposed to sensors based on light or ultrasound. However, to be able to utilize this rich information, a high amount of processing is required which is done by computer vision techniques. Depth cameras provide even higher amounts of information with point cloud data and imaging which can be used to recreate a 3d 26 world from the data. Computer vision has evolved over the years and has extensive use in robotics applications and will help us reduce the number and types of sensors required. 27 Chapter 8 Implementation 8.1 Introduction The firmware and simulation of the delivery bot is done in ROS and Gazebo. In ROS the functionality is divided into packages where each package handles one functionality of the robot. As such for Delivery Bot the whole project is divided into three categories and then further divided into packages. Fig 8.1 ROS package distribution of the project 28 8.2 ROS Packages 8.2.1 Delivery Bot This meta package contains all the packages required to give the desired functionality to the delivery robot. 1. deliverybot_control - Control contains the code to provide control of the hardware actuators. Also it provides the ability to simulate and control these actuators in the Gazebo simulation. 2. deliverybot_delivery - Delivery provides the application level functionality for planning and executing deliveries. 3. deliverybot_description - Description contains the Universal Robot Description Format (urdf) description of the robot. It also contains the meshes for the visual and the collision geometry of the robot. 4. deliverybot_gui - GUI contains the graphical user interfaces for different functionality of the robot. 5. deliverybot_mapping - Mapping functionality to create and load maps for navigation using map_server. 6. deliverybot_navigation - Navigation provides basic navigation ability. 7. deliverybot_vision - Vision provides image processing and recognition abilities for various tasks. 8.2.2 Elevator This meta package contains packages to simulate an elevator in gazebo. 1. elevator_controls - Controls package provides the code to control the elevator in the simulation. 2. elevator_description - Description provides the urdf description of the elevator and contains the meshes for visual and collision geometry. 3. elevator_gui - GUI provides graphical user interfaces for controlling the elevator during simulation. 8.2.3 Simulations This meta package contains the package with the simulations for the project. 29 1. deliverybot_simulations - Simulations package contains the code to simulate the robot and environment in Gazebo. 8.3 Robot Description Fig 8.2 DeliveryBot simulation model A simple robot is used for simulations. It contains a LIDAR sensor, a depth camera, 3 side cameras, a door and a pusher for pushing the package out of the robot. The door and pusher are actuated. The door has a revolute joint which can be controlled to open and close the door. The pusher has a prismatic joint to move the package out of the robot. The wheels are actuated and a differential drive plugin is used for their control. To control the delivery bot joints, these services are provided. /dbot/open_bot_door /dbot/close_bot_door /dbot/set_pusher_in /dbot/set_pusher_out /dbot/deliver_package These services can either directly be called programmatically or by using a GUI for easier use. 30 Fig 8.3 GUI for DeliveryBot control Fig 8.4 DeliveryBot dropping package To move the robot, we have to publish Twist messages to the cmd_vel topic. The Twist message is composed of two vectors one each for Linear and Angular. The differential drive plugin converts this Twist message into individual speeds for left and right wheels. The Twist message can be published manually, by a GUI or by a navigation node. To publish manually, we have to publish a message to /dbot/cmd_vel. To use a GUI, we can use rqt_robot_steering. Fig 8.5 rqt_robot_steering plugin for control of DeliveryBot’s motion 31 The GUI will mostly be used when creating a map or when the robot gets stuck and has to be manually controlled. The rest of the time it will be published to by the navigation node based to the planned path. 8.4 Perception Robot perception is a very important aspect which is crucial for the robot to understand its environment. We have provided three main different types of sensors to the robot which are 2D LIDAR, 3D depth camera and RGB cameras. The 2D LIDAR is an important sensor used for mapping and navigation. The depth camera helps to perceive objects and obstacles which are missed by the LIDAR and identify them. The RGB cameras are used to keep a tab on the environment when the robot is out for delivery. The specifications of the sensors are listed below. 1. 2D LIDAR i) Max Distance - 10.0 m ii) Min Distance - 0.1 m iii) Samples - 360 iv) Angle - 180° 2. Depth Camera i) Resolution - 640 x 480 ii) Format - R8G8B8 iii) Horizontal FOV - 60° 3. RGB Camera i) Resolution - 800 x 800 ii) Format -R8G8B8 iii) Horizontal FOV - 80° The output from these sensors is visualized using RViz (ROS Visualization) a visualization tool for sensory data. 32 Fig 8.6 Visualization of PointCloud2 laser data from simulation environment (left) in RViz (right) Fig 8.7 Visualization of depth cloud data from simulation environment (left) in RViz (right) 33 Fig 8.8 Visualization of camera data from left, right, and front cameras respectively 8.5 Elevator Simulating an elevator is an essential part of the project since the robot has to ride in the elevators to move to different floors during delivery. Initially, we used an elevator plugin which is provided by Gazebo. This plugin had the downside of having very low amount of control available to the user. To overcome this, we created our own elevator model and added controls to it such that it can be controlled using a GUI or by calling service which allows to change floors, open doors and close doors. These services act as the API assumed at the start of the project. To use the elevator in the simulation, we went for a multi-robot approach to allow the elevator to be controlled using ROS. In this approach both the actual robot and the elevator are considered as robots and we can control them using ROS. Fig 8.9 GUI for elevator control The services which are advertised for control are /elevator/open_elevator_doors /elevator/close_elevator_doors /elevator/elevator_goto_floor 34 These services provide the functionality for the elevator. The GUI uses these services in the background and calls them on button press. Fig 8.10 CAD model of elevator (left), transparent visualization of elevator model in gazebo (center), visualization of elevator model with door open in gazebo (right) 8.6 Simulation Environment To simulate the robot, we need an environment which resembles the actual environment the robot will face during the delivery but the detail on the environment has to be reduced to be able to simulate it at a sufficient speed. We created several different environments of buildings with elevators to give the robot an environment to execute deliveries. The 3D models for the environment can be created in any CAD modelling software. We used Onshape[20] and Blender[21] for modelling the environments. Blender can be used for modelling as well as to apply textures to the environment so that the cameras can get realistic looking images instead of solid colors. These models can then be composed into a ‘.world’ file which can be launched in the Gazebo simulator. The ‘.world’ files have a 3d model for the environment, some other object models for obstacles and the elevator robot to act as a part of the environment. 35 Fig 8.11 Simulation environment of building in Gazebo Fig 8.12 Close up of simulation environment of building in Gazebo 36 Fig 8.13 CAD model of another simulation environment with ramp and stairs based on real building 37 8.7 Mapping 8.7.1 Mapping Mechanism We use occupancy grid maps created from laser scan data as the basic method of storing maps. There are several map creation packages available for ROS which require different types of data input and different assumptions. We use gmapping for our map building. Gmapping requires odometry and laser scan data as input for creating maps. Occupancy grid maps represent an area by having different pixel values depending on the type of the area on the map. The ones used here are obstacle, free and unknown. How much distance a pixel represents depends on the resolution when creating the map (ex: - 1px = 5cm). Fig 8.14 A grid map (White - Free, Black - Obstacle, Grey - Unknown) These occupancy grid maps are good enough for navigation when the robot is already in the area of the map but we run into problems when we have to change maps like when a robot changes the floor using an elevator. To be able to create multi floor maps we add another layer to the map and stack 2d grid maps with important points marked on the map. To allow multi floor navigation we created a node which uses the map_server node to load different layers of the map. When the robot changes a floor for example, from lobby to floor we use the map loading service to load our floor map. In this way by using the robot’s odometry we can localize the robot in the new map without any issues and have a multi floor map server. 38 Fig 8.15 multi layered map composed of grid maps for multi-floor map 8.7.2 Mapping Implementation Now that we have the underlying mechanism for creating maps, we developed a method to mark important points on the map which will be useful during the navigation. To create a map of a building the user has to use the GUI which provides the required interface to create a map. Fig 8.16 Mapping GUI Steps to create a map 1. To create a map first, we input the building name in the building and hit create. This will create a new map if a map of the same name does not exist. If a map of the same name exists, it will load the files from that map to edit. 2. Once the map is created or loaded we can start the map server by pressing the start button next to the server label. This will run the gmapping node and start creating the 39 map. The operator can drive the robot around the location till they are satisfied with the occupancy grid map. Once the operator is satisfied they can press the lobby or floor button depending on the location and it will create the .yaml and .pgm files for the map. 3. The GUI has buttons to mark the location of the elevator, lobby and rooms which can be pressed during the mapping process to mark the locations. For the room marking on the room number has to be marked along with the scheme so that the room and floor number can be extracted from the room number during the delivery process. 4. The server has to be cleared before starting a new map section such as the lobby or floor. The building.yaml file for each building/map is created which contains the details of the marked locations. This file is used when creating the navigation plan during the delivery phase. Building Name - test_building building: {floors: 3, name: test_building, scheme: F0R} elevator: in: {a: -1.61853604817792, x: 0.6934827549218775, y: 0.08420044952974128} out: {a: 1.595608575754123, x: 0.3409216603784452, y: 2.2235174763106094} lobby: drop: {a: 1.574721731693996, x: 8.999215828701779, y: 17.49701041263829} pickup: {a: -1.5457419755519313, x: 9.178838972561948, y: 16.942039005619698} rooms: 1: {a: -0.06342197332648702, x: 3.3880842476089565, y: 3.0718819998791416} 2: {a: 0.060238990106272636, x: 3.606012970188086, y: 7.5487842424071205} 3: {a: 3.1305571781020154, x: -3.116973458806227, y: 7.5779168592677575} 4: {a: -3.1382769460531232, x: -3.194904404283088, y: 3.002927451759802} 40 Fig 8.17 Created map with marked points 8.7.3 Implementation Limitations The current implementation is sufficient as a proof of concept however this implementation is restrictive to a certain type of buildings which have identical floors and only two different categories of locations, floor and lobby. This implementation also is not very user friendly for end use so the GUI will have to be redesigned to be used with touch screens for easier and intuitive usage where the occupancy grid map can be directly displayed on the screen and the locations can be marked directly using touch. Provision to handle more elevators and different categories of marked locations can be added to make mapping and navigation easier. 8.8 Navigation For basic navigation we used the ROS navigation stack. It provides the basic navigation functionality required for a robot. The navigation stack takes odometry and sensor information and outputs velocity commands to the mobile base. The navigation stack has to be configured to work for each robot. These configurations are done using a series of config files. The config files are a set of costmap files, the global planner and the local planner. 41 8.8.1 Costmaps A costmap is a grid map where each cell is assigned a specific value or cost: higher cost indicates a smaller distance between the robot and an obstacle. Path-finding is done by a planner which uses a series of different algorithms to find the shortest path while avoiding obstacles. Optimization of autonomous driving at close proximity is done by the local costmap and local planer whereas the full path is optimized by the global costmap and global planner. Together these components find the most optimized path given a navigation goal []. Correctly configuring costmaps is essential to ensure that the navigation is smooth and the robot maintains an appropriate distance from obstacles while also not deviating too much from the path and taking a longer detour. The local costmap is the costmap for a short distance around the robot created using sensor information and updated as obstacles come in the way. The global costmap is created using the map. The global costmap is used when creating the global path that is the main path that the robot has to follow to get to the destination. It however does not account for the obstacles that come in the way. For this the local costmap comes into the picture where the local costmap takes into account the obstacles and allows for creation of a local path which can avoid the obstacles. Fig 8.18 Figure showing different types of costmaps and plans 42 The common costmap parameters set for our robot are as follows. costmap_common_params.yaml robot_base_frame: dbot/base_link max_obstacle_height: 20.0 robot_radius: 0.2 static_layer: map_topic: /map subscribe_to_updates: true obstacle_layer: observation_sources: laser #depth laser: topic: /dbot/scan sensor_frame: dbot/sensor_laser observation_persistence: 0.0 expected_update_rate: 5.0 data_type: LaserScan clearing: true marking: true max_obstacle_height: 20.0 min_obstacle_height: -20.0 obstacle_range: 4.0 raytrace_range: 6.0 voxel_layer: observation_sources: depth depth: topic: /dbot/kinect/depth/points sensor_frame: dbot/kinect observation_persistence: 2.0 expected_update_rate: 5.0 data_type: PointCloud2 clearing: true 43 marking: false max_obstacle_height: 20.0 min_obstacle_height: -0.02 obstacle_range: 3.0 raytrace_range: 6.0 inflated_layer: inflation_radius: 4.0 cost_scaling_factor: 3.0 This configuration file contains the details of the behaviour of the costmap, the details of the sensor information sources and layers of the costmap. 8.8.2 Global Planner For global planning, we use the global_planner package. The global_planner offers various algorithms for path planning which can be configured using the global planner config params. The lethat_cost, neutral_cost and cost_factor are important params which affect the final plan and have to be tuned correctly. global_planner_params.yaml GlobalPlanner: allow_unknown: true default_tolerance: 0.2 visualize_potential: true use_dijkstra: true use_quadratic: true use_grid_path: false old_navfn_behavior: false lethal_cost: 253 neutral_cost: 66 cost_factor: 0.55 publish_potential: true orientation_mode: 1 outline_map: true orientation_window_size: 1 44 8.8.3 Local Planner The local planner produces velocity commands to send to a mobile base using a global plan to follow and a costmap. There are several local planner packages available like dwa_local_planner, dwb_local_planner, teb_local_planner, etc all having different requirements and tradeoffs. Initially we used the dwa_local_planner which was fast and required lower computation but struggled when the goal was directly opposite to the starting orientation and in spot rotation. Later we switched to the teb_local_planner. The underlying method called Timed Elastic Band locally optimizes the robot's trajectory with respect to trajectory execution time, separation from obstacles and compliance with kinodynamic constraints at runtime. teb_base_local_planner_param.yaml TebLocalPlannerROS: odom_topic: /dbot/odom map_frame: /map # Trajectory teb_autosize: True dt_ref: 0.3 dt_hysteresis: 0.1 global_plan_overwrite_orientation: True max_global_plan_lookahead_dist: 3.0 feasibility_check_no_poses: 5 # Robot max_vel_x: 0.4 max_vel_x_backwards: 0.2 max_vel_theta: 0.3 acc_lim_x: 0.5 acc_lim_theta: 0.5 min_turning_radius: 0.0 footprint_model: # types: "point", "circular", "two_circles", "line", "polygon" type: "radius" radius: 0.15 # for type "circular" line_start: [-0.3, 0.0] # for type "line" line_end: [0.3, 0.0] # for type "line" front_offset: 0.2 # for type "two_circles" front_radius: 0.2 # for type "two_circles" rear_offset: 0.2 # for type "two_circles" rear_radius: 0.2 # for type "two_circles" 45 vertices: [ [0.25, -0.05], [0.18, -0.05], [0.18, -0.18], [-0.19, 0.18], [-0.25, 0], [-0.19, 0.18], [0.18, 0.18], [0.18, 0.05], [0.25, 0.05] ] # for type "polygon" # GoalTolerance xy_goal_tolerance: 0.2 yaw_goal_tolerance: 0.1 free_goal_vel: False # Obstacles min_obstacle_dist: 0.3 include_costmap_obstacles: True costmap_obstacles_behind_robot_dist: 1.0 obstacle_poses_affected: 30 costmap_converter_plugin: "" costmap_converter_spin_thread: True costmap_converter_rate: 5 # Optimization no_inner_iterations: 5 no_outer_iterations: 4 optimization_activate: True optimization_verbose: False penalty_epsilon: 0.1 weight_max_vel_x: 2 weight_max_vel_theta: 1 weight_acc_lim_x: 1 weight_acc_lim_theta: 1 weight_kinematics_nh: 1000 weight_kinematics_forward_drive: 1 weight_kinematics_turning_radius: 1 weight_optimaltime: 1 weight_obstacle: 50 weight_dynamic_obstacle: 10 # not in use yet alternative_time_cost: False # not in use yet # Homotopy Class Planner enable_homotopy_class_planning: True enable_multithreading: True simple_exploration: False max_number_classes: 4 roadmap_graph_no_samples: 15 roadmap_graph_area_width: 5 h_signature_prescaler: 0.5 h_signature_threshold: 0.1 obstacle_keypoint_offset: 0.1 obstacle_heading_threshold: 0.45 46 visualize_hc_graph: False 8.9 Vision The vision module uses object detection and optical character recognition to identify doors and read number plates to verify before completing the delivery process. When the robot reaches the marked point for the room on the floor, before delivering the package considering that there is no human to receive the package, the robot will use the vision package to photograph from the front camera and run number plate detection and door detection. If the robot correctly detects the door and the number plate to be correct, it will execute the package delivery and drop the package at the door. If any of the conditions are not met, the robot will go into corrective routine which may have an exploration algorithm if the robot is on the correct floor, a rerouting algorithm if the robot is on the incorrect floor and if all fails the robot can be put into manual mode and be controlled by a remote human operator. Details of the implementation of the vision package are given in the chapter 10. Fig 8.19 Door detection and number plate recognition 47 8.10 Delivery 8.10.1 Local Planner The delivery process is handled by the deliverybot_delivery package. This package uses the maps created during the mapping process, the navigation and path planning functionality and the delivery details to plan to deliver a package from the lobby of the building to the door of the target apartment. Fig 8.20 Delivery GUI In this simulation the delivery GUI is used to input details of the delivery such as the building name, room number and floor number. These details are sent to the delivery server where the node creates the plan which is to be followed by the delivery robot. The planner gets the map description file and uses all the marked locations along with the grid maps to create a global navigation path. 48 Fig 8.21 Multi-floor navigation illustration The planner also has to take into account the waiting for the elevators and verification process during the delivery. Once the path is created the robot starts executing the plan. The plan is essentially an array composed of actions and navigation goals. The execution of the plan is the executing of all actions one by one. The different types of actions are: 1. ELEVATOR_DOOR_OPEN 2. ELEVATOR_DOOR_CLOSE 3. DELIVER_PACKAGE 4. CHANGE_FLOOR 5. CLEAR_COSTMAP 6. WAIT 7. MOVE 8. MAP The elevator actions are used to communicate with the elevator and request the doors to be opened or closed. The CHANGE_FLOOR action is also related to the elevator and requests the elevator to go to a certain floor. The CLEAR_COSTMAP is an implementation requirement 49 for the simulation which clears the costmap on changing the floor if they are not automatically cleared. The MOVE action sends navigation goals to different locations. The MAP action changes the maps when the robot has to move from one mapped area to another. In the DELIVER_PACKAGE action, the robot runs the verification process and executes a predetermined sequence of actions to drop the package out of the robot and in front of the apartment door. Fig 8.22 Flow of delivery process 50 Chapter 9 Mechanical Design 9.1 Introduction This chapter looks into the calculations for drive motors, component suggestion for the physical prototype of the robot, concept sketches. Before starting the mechanical design some assumptions and considerations which area taken are listed below: 1. Robot is Differential Drive 2. Maximum gradient is 6 degrees 3. Gross Vehicle Weight with packages is 30 kg 4. Maximum speed is 2 m/s and normal speed is 1.5 m/s (Human walking speed) 9.2 Motor Torque and Power Calculations Motor torque calculations were done using a MATLAB script to allow tweaking of parameters. The procedure is taken from the EML2322L Laboratory Document [22]. 9.2.1 MATLAB script for torque calculations 51 Fig 9.1 MATLAB Script for Torque Requirement Estimation 52 9.2.2 Result for torque calculation Fig 9.2 Torque Estimation Results 53 9.2.3 MATLAB script for motor power calculation Fig 9.3 Matlab Script for motor power estimation 9.2.4 Result for motor power calculation Fig 9.4 Motor Power Estimation Results 54 9.3 Components Suggestion The processing board and sensors are components on which the performance of the robot depends. Some components suggested for creating the physical prototype are listed below. 9.3.1 Processing Board Name Price Description Comparison Docs Raspberry Pi 4 (8GB) ₹ 7,666.00 https://www.raspberrypi.org /products/raspberry-pi-4model-b/specifications/ https://build5nines.com/rasp berry-pi-4-vs-nvidia-jetsonnano-developer-kit/ Nvidia Jetson Nano ₹ 10,549.00 https://developer.nvidia.com /embedded/jetson-nanodeveloper-kit Table 9.1 Processing Board Options 9.3.2 Depth Camera Name Price Image specifications Intel RealSense LiDAR Depth Camera L515 ₹ 41,399.00 1. 30FPS Depth at 1024×768 (XGA) 2. 30FPS Color at 1920×1080 (FHD) Intel RealSense Depth Camera D435i with IMU ₹ 25,969.00 1. Depth Output Approx. 10m Resolution: up to 1280 x 720 active stereo depth resolution 2. Frame Rate: Up to 90 fps Intel D455 ₹ 1280 × 720 @ 30fps RealSense Depth 29,649.00 848 × 480 @ 90fps Camera Intel RealSense ₹ FOV: Two Fisheye lenses Tracking Camera 25,999.00 with combined 163±5° T265 (Non Depth, Feature Camera) Table 9.2 Depth Camera Options 55 Depth Range Type 0.25 to 9m Lidar Active IR Stereo Recommended Active Range: IR From 0.4 to 6 m Stereo 9.3.3 Laser Scanner Name Price Range Specifications YDLIDAR X2 360 Degree ROS Scanner for Navigation, Collision Avoidance ₹ 7,999.00 0.1 8m 1. Scanning Frequency:7Hz 2. Ranging Frequency: 3000Hz 3. 360-degree two-dimensional rangefinder 4. Adjustable motor speed(7Hz recommend) 5. UART Communication at 3.3v 6. Laser power meets Class I laser safety standards YDLIDAR X4 360 Degree ROS Scanner for Navigation, Collision Avoidance 10M ₹ 10,999.00 0.1210 m 1. Small distance error, stable performance, and high accuracy 2. 10m Ranging distance 3. Strong resistance to ambient light interference 4. Low power consumption, small size, and long life 5. Laser power meets Class I laser safety standards 6. Adjustable motor speed frequency differ from 6Hz~12Hz 7. Ranging frequency up to 5K Hz RP LIDAR A2M8 360 ₹ degrees Laser Range Finder- 29,499.00 12 Meter Range 18 m 1. 4cm Ultra-thin 2. Ideal for all kinds of the service robot. 3. 8000 times sample rate, the highest in the current economical LIDAR industry 4. 360-degree laser range scanning 5. Low Noise, Brushless Motor New Non-contact Drive 6. 12m Range Radius 7. OPTMAG Original Design, prolong the life-span 8. Class 1 Laser Safety Standard 9. High-speed RPVision Range Engine: RPVision 2.0 10. Five years ultra-long life. Table 9.3 Laser Scanner Options 56 9.4 Concept Design Sketches Some concept design sketches are shown here Fig 9.5 Components Packaging Concept Sketch Fig 9.6 Shape Study 57 Fig 9.7 Concept Exploration Fig 9.8 Packaging Concept 58 Chapter 10 Image Processing 10.1 Introduction In order for the robot to be able to understand the tasks it is performing, it needs a real world view. Since we aim to let the robot make its own decisions, such as avoiding obstacles, navigating its environment, it needs to be able to visualize everything around it. If we were to analyze the way the human eyes work, we can see how they are able to adapt to dynamic changes in an instant, and aid the human brain in understanding its environment thus guiding us in what we see and do. However, a robot is unable to do this on its own. Thus, one of the main challenges in the robotics domain is to extend the visual ability to robots so that they can also visualize their environment. The primary way to do this is with cameras. A camera is a device which contains an image sensor, that captures pictures, and converts into a digital format that can be interpreted by a computer. The use of cameras allows the robot to collect visual data about its environment and by using mathematical processes/ algorithms, it is able to generate an image which it understands, along with other relevant information such as colors and edges. This is necessary, since what a human sees is interpreted from light entering the eyes from different objects, a camera sees its environment as a collection of pixels, each having 3 values, ranging from 0 to 255, pertaining to each of the color spectrum (Red, Green and Blue). 59 In this chapter, we will go over the necessity of image detection and processing, then compare various papers to decide the ideal algorithm to implement, then we discuss the working of image processing in the delivery bot. 10.2 Necessity Since our robot should be able to navigate itself from the buildings entrance, along with overcoming various hurdles (elevators, ramps), avoid obstacles (both human and non-living) and detect its environment in order to successfully deliver its package, we need to develop an application that allows the robot to analyze the problem it is facing and find the optimum solution in real time. Let's discuss the problems the robot will face in its operation. 10.2.1 Overcoming Objects/Obstacles As the robot is not a sentient being, it is not able to distinguish various objects in its environment. If we are to implement autonomous delivery robots, they should be able to overcome challenges that it will face. In order for the robot to be able to distinguish between, say, an elevator and a hallway plant, we have to train the robots to understand what they see. This is the fundamental part of image processing, where we train models to be able to distinguish various objects. In our application, we propose to implement models that allow the robot to identify objects in its surroundings, and be able to react accordingly. 10.2.2 Door Detection This is a subset of object detection, but it is an important part of the delivery process. The main focus of this chapter is on image processing for door detection. While the robot is navigating along its chosen route, it should be able to read the various door/apartment numbers and check whether it matches that listed on the delivery order. Using its camera, the robot obtains the digital image, and using image processing, it both checks whether the object visible is a door, and if any alphanumeric characters are visible. By further fine tuning the model, we are able to optimize the character recognition by the process of elimination. The process we have implemented in our robot is similar to that used in Automatic Number Plate Recognition (ANPR), which uses trained models and image processing to capture a vehicle’s number plate for further action. 60 Fig 10.1 Door and apartment number detection in Python Fig 10.2 Door and apartment number detection implementation in ROS 10.3 Working As covered in previous sections, the robot can successfully enter and navigate the building. However, in order to deliver the product to the recipient, robot should be able to know where 61 the recipient's location is. Primary locations where image capture and processing is required are: Ramps and stairs, Elevator and its buttons, building signage, and door/apartment numbers. We first begin with image capture. We propose installation of the camera on the front of the robot, which gives it the required field of view. As a summary, we have chosen to use Intel RealSense Depth Camera D435i, which has a 3um x 3um image sensor, and is able to output a resolution of 1280x720, while also being able to use Inertial Measurement Unit to map out its surroundings and know the relative position of objects with respect to the location of the robot. The camera also allows depth perception, allowing the robot to detect objects as far as 3 meters. The camera is able to output images continuously, up to 90fps, thus allowing for smoother output for image recognition and processing. Once the image is captured by the camera, it is outputted to the computer, where it undergoes image processing. From the literature review, we have chosen to implement the YOLOv3[23] algorithm, which is a real-time object detection algorithm, particularly for constant video or images. We have also chosen to use predefined classes and weights which had been trained to detect doors by MiguelARD [24][25]. We have gone this route as creating custom classes and weights requires a lot of compute power and is out of the scope of this project. The algorithm is able to convert the video into still images, which undergoes scaling, resizing and cropping to obtain images with high confidence scores. It then undergoes post-processing, where it uses the vectors obtained to draw bounding boxes around the objects detected, and further label them as given in the class. The output obtained are still images which have every object present bounded by a label box. This gives the computer information as to what objects it sees and is interacting with in its environment. Next we move on to character recognition. While in some cases, the robot may only require to know what objects are in its environment, it also needs to be able to read any alphanumeric text, either as signage or door numbers. Using the OpenCV library, we implement Optical Character Recognition(OCR), which takes the image obtained with labelled objects for further processing. Here, the program first filters the image for noise. Using edge detection, it generates each edge present in the object, which will aid in contour detection. The program also applies a mask to the image to further ease detection in the forthcoming steps. The images generated are passed to the OCR library, using the set default language of the text, detects any alphanumeric text is present in the image. The final output is the original image along with a 62 bounding box with labels specifying the type of object detected and the text on the object, if applicable. The source code for door and door number detection is available on github[26]. 63 Chapter 11 Results We successfully simulated the delivery robot system for delivering packages to the doorstep of customers who live in apartment buildings in Gazebo and a video of this demo can be viewed on YouTube [27]. The robot was able to navigate around obstacles, ride in elevators, and deliver the package to the correct room. The mapping of the simulated worlds was done using the mapping GUI we created before running deliveries. Once the map was created the robot was ready to perform deliveries. The deliveries were executed using the Delivery GUI. The delivery process used the maps created during mapping for path planning and navigation. The source code of the project is openly available on Github[28]. Screenshots of the simulation at various stages can be seen in the series of images below. 64 Fig 11.1 Mapping a building Fig 11.2 DeliveryBot dropped at building for delivery 65 Fig 11.3 DeliveryBot waiting for elevator Fig 11.4 DeliveryBot dropping package at door 66 Fig 11.5 DeliveryBot waiting to be picked up after delivering package 67 Chapter 12 Conclusion 12.1 Conclusion In this project we tackled the limitation of delivery robots which cannot deliver to the customer’s door in residential buildings with an important assumption that the robot has some way of communicating with the elevator in the building which can be either hardware or software. We created and simulated a robot capable of multi-floor navigation and package delivery. To do so we implemented a multi-floor mapping and navigation method using 2D grid maps in ROS and simulated the whole system in Gazebo. Delivery robots capable of delivering packages to the door of customers living in residential buildings will help to bridge the final gap in the last mile delivery process and help in achieving automation of the complete process from warehouse to the customer doorstep. Along with ecommerce site deliveries, these robots can also be used by local businesses like restaurants, medical stores, grocery stores, etc. Automation of this process can help reduce the delivery costs to both the vendors and the customers. It will also help to make the process more efficient and reduce delivery times allowing faster deliveries. These robots can even help reduce traffic on the streets by completing deliveries during the night time. 68 12.2 Future Work There are several areas of this project that can be improved either mainly to make the methods more general or improve performance. 1. The current planning process has a fixed structure for the plan created that is the drop point, elevator and the apartment door. In the real world the robot may have to go through more waypoints to reach the elevator or door. The mapping and planning process can be generalized to provide more flexibility in the choice of waypoints. 2. We assume there is some means for the robot to communicate with the elevator. This can be hardware based i.e. by using a manipulator to push the elevator buttons or software based as demonstrated in the project where the robot can wirelessly communicate with the elevator. More work can be done in this area to build these systems. The software method is preferred as it will allow simpler designs for the robots and better communication. 3. We suggest robots containing the packages will be dropped off at the building by autonomous vans for the delivery. These robots can be operated in fleets to maximize their impact and methods have to be developed to optimize the paths and assignment of the autonomous vans. This is a suggested area for future work. 69 Chapter 13 References Weblink, [1] Last Mile Delivery Market to 2027 - Global Analysis and Forecasts by Technology [Online]. Available: https://www.theinsightpartners.com/reports/last-mile-delivery-market (April 05 2021) [2] Report on Last Mile delivery challenge [Online]. Available: https://www.capgemini.com/ research/the-last-mile-delivery-challenge. (April 05, 2021) [3] Robot Operating System (ROS) [Online]. Available: https://www.ros.org/about-ros/ [4] Gazebo Sim [Online]. Available: http://gazebosim.org/ [5] ROS Navigation Stack [Online]. Available: http://wiki.ros.org/navigation [7] DWA Local Planner. [Online]. Available: http://wiki.ros.org/dwa_local_planner [8] TEB Local Planner [Online]. Available: http://wiki.ros.org/teb_local_planner [10] TurtleBot Product Line [Online]. Available: http://wiki.ros.org/Robots/TurtleBot [13] Savioke Website [Online]. Available: https://www.savioke.com/ [14] Starship Website [Online]. Available: https://www.starship.xyz/ [15] Kiwibot Website [Online]. Available: https:// www.kiwibot.com/ [18] Gmapping implementation for ROS. Available: http://wiki.ros.org/gmapping [19] Wikipedia entry for Computer Vision [Online] . Available: https://en.wikipedia.org/ wiki/Computer_vision Book, [6] Kaiyu Zheng, “ROS Navigation Tuning Guide” in Robot Operating System (ROS) The Complete Reference, Volume 6, 2016/09, Springer International Publishing [16] Giorgio Grisetti, Cyrill Stachniss, and Wolfram Burgard: Improved Techniques for Grid Mapping with Rao-Blackwellized Particle Filters, IEEE Transactions on Robotics, Volume 23, pages 34-46, 2007 [17] Giorgio Grisetti, Cyrill Stachniss, and Wolfram Burgard: Improving Grid-based SLAM with Rao-Blackwellized Particle Filters by Adaptive Proposals and Selective Resampling, In Proc. of the IEEE International Conference on Robotics and Automation (ICRA), 2005 70 Conferences and Conference Proceedings, [9] Ellingson, J., & Nepal, K., & McGill, M. R., & Hoffmann, M. J. (2013, June), Multi-floor Mapping and Navigation with Uncertainty Paper presented at 2013 ASEE Annual Conference & Exposition, Atlanta, Georgia. 10.18260/1-2--22302 [11] Sami, Muhammad, et al. "Text detection and recognition for semantic mapping in indoor navigation." 2015 5th International Conference on IT Convergence and Security (ICITCS). IEEE, 2015. [12] Chae, Hee-Won, et al. "Object recognition for SLAM in floor environments using a depth sensor." 2016 13th International Conference on Ubiquitous Robots and Ambient Intelligence (URAI). IEEE, 2016. [23] Joseph Redmon & Ali Farhadi, “YOLOv3: An Incremental Improvement” arXiv: 1804.02767 [cs.CV], 2018. [24] Miguel Arduengo, & Carme Torras, & Luis Sentis, “Robust and Adaptive Door Operation with a Mobile Robot” arXiv: 1902.09051[cs.RO], 2020. Lecture Notes, [22] EML2322L – MAE Design and Manufacturing LaboratoryDrive Wheel Motor Torque Calculations [PDF]. Available: https://mae.ufl.edu/designlab/motors/ EML2322L%20Drive%20Wheel%20Motor%20Torque%20Calculations.pdf Software [20] OnShape, PTC Inc. Available: https://www.onshape.com/en/ [21] Blender 2.9, The Blender Foundation. Available: https://www.blender.org/ [24] Door Detection Dataset [Online]. Available: https://github.com/MiguelARD/DoorDetectDataset [26] Door_Number_Detector [Online]. Available: https://github.com/Hamdrive/ Door_Number_Detector [28] DeliveryBot, A delivery robot with multi-floor navigation capabilities [Online]. Commit: 2862f904d97e2f724ae5b7cc773dbe84bcb0ff86. Available: https://github.com/ammaar8/ DeliveryBot Online Video, [27] Ammaar Solkar, DeliveryBot Demo Video | ROS (Apr. 26, 2021). Accessed: May 2, 2021 [Online Video]. Available: https://youtu.be/9i83VW3t_Xk Patents, 1. Adrian Canoso et al., inventor; Jan. 03, 2017, Mobile delivery robot with interior cargo space, US patent no. 9535421B1. 2. Ahti Heinla et al., inventor; Aug. 16, 2018, Method and system for autonomous or semiautonomous delivery, US patent no. US20180232839A1. 71 Appendix - I This appendix provides the code files for some important functions in the ROS packages used in simulating this project. The source code is available on Github[27]. The codebase is divided into three parts namely, 1. Deliverybot 2. Elevator 3. Simulation The deliverybot metapackage contains the packages required for the working of the delivery robot. It contains the packages for control, delivery, description, gui, mapping and navigation. The elevator metapackage contains the packages required for the working of the elevator created for running simulations. It contains the description, control and gui packages. The simulations metapackage contains the delvierybot_simulations package which is used for running all the gazebo simulations. Only important files which contain core functionality are shown here to maintain brevity. 1. Deliverybot 1.1 deliverybot_control scripts/deliverybot_hw_controller.py #!/usr/bin/env python3 import rospy import sys from std_msgs.msg import Float64 from sensor_msgs.msg import JointState from std_srvs.srv import Empty, EmptyResponse global ns ns = rospy.get_namespace() print("Controller started with ns " + ns) DOOR_OPEN = 0.51 DOOR_CLOSED = -1.57075 PUSHER_OUT = 0.24 72 PUSHER_IN = 0.0 TOLERANCE_DOOR = 0.05 TOLERANCE_PUSHER = 0.01 door_pub = rospy.Publisher( rospy.names.ns_join(ns, "door_position_controller/command"), Float64, queue_size=10 ) pusher_pub = rospy.Publisher( rospy.names.ns_join(ns, "pusher_position_controller/command"), Float64, queue_size=10 ) def open_bot_door(req): rospy.loginfo("Opening Bot Door") door_pub.publish(DOOR_OPEN) return EmptyResponse() def close_bot_door(req): rospy.loginfo("Closing Bot Door") door_pub.publish(DOOR_CLOSED) return EmptyResponse() def pusher_out(req): rospy.loginfo("Pushing Package Out") pusher_pub.publish(PUSHER_OUT) return EmptyResponse() def pusher_in(req): rospy.loginfo("Retracting Pusher") pusher_pub.publish(PUSHER_IN) return EmptyResponse() def deliver_package(req): global DOOR_CLOSED, DOOR_OPEN, PUSHER_IN, PUSHER_IN, TOLERANCE_DOOR,TOLERANCE_PUSHER DOOR_STATE = None PUSHER_STATE = None def check_door(): msg = rospy.wait_for_message(rospy.names.ns_join(ns, 'joint_states'), JointState) DOOR_STATE = msg.position[0] return DOOR_STATE 73 def check_pusher(): msg = rospy.wait_for_message(rospy.names.ns_join(ns, 'joint_states'), JointState) PUSHER_STATE = msg.position[1] return PUSHER_STATE DOOR_STATE = check_door() PUSHER_STATE = check_pusher() while DOOR_STATE < DOOR_OPEN - TOLERANCE_DOOR: DOOR_STATE = check_door() open_bot_door(req) while PUSHER_STATE < PUSHER_OUT - TOLERANCE_PUSHER: PUSHER_STATE = check_pusher() pusher_out(req) while PUSHER_STATE > PUSHER_IN + TOLERANCE_PUSHER: PUSHER_STATE = check_pusher() pusher_in(req) while DOOR_STATE > DOOR_CLOSED + TOLERANCE_DOOR: DOOR_STATE = check_door() close_bot_door(req) rospy.loginfo("Package Drop Complete") return EmptyResponse() if __name__ == "__main__": rospy.init_node("deliverybot_hw_controller") rospy.Service('close_bot_door', Empty, close_bot_door) rospy.Service('open_bot_door', Empty, open_bot_door) rospy.Service('set_pusher_out', Empty, pusher_out) rospy.Service('set_pusher_in', Empty, pusher_in) rospy.Service('deliver_package', Empty, deliver_package) rospy.spin() 1.2 deliverybot_delivery scripts/delivery_client.py #!/usr/bin/env python3 import actionlib import deliverybot_navigation.msg import tkinter as tk import rospy class Application(tk.Frame): def __init__(self, master=None): 74 tk.Frame.__init__(self, master) self.client = actionlib.SimpleActionClient( 'delivery_server', deliverybot_navigation.msg.DeliverAction ) self.client.wait_for_server() self.master = master self.pack() self.create_widgets() def create_widgets(self): widget_frame = tk.LabelFrame(self, text="Delivery GUI") widget_frame.grid_columnconfigure(0, weight=1) widget_frame.grid_rowconfigure(0, weight=1) widget_frame.pack(fill='x', pady=5) building_name_var = tk.StringVar() floor_var = tk.StringVar() room_var = tk.StringVar() self.status_label = tk.Label(widget_frame, text="Idle") self.status_label.grid(row=0, column=0, columnspan=2, sticky="ew") building_name_label = tk.Label(widget_frame, text="Building Name") building_name_label.grid(row=1, column=0, sticky="ew") building_name_entry = tk.Entry(widget_frame, textvariable=building_name_var) building_name_entry.grid(row=1, column=1, sticky="ew") floor_label = tk.Label(widget_frame, text="Floor") floor_label.grid(row=2, column=0, sticky="ew") floor_entry = tk.Entry(widget_frame, textvariable=floor_var) floor_entry.grid(row=2, column=1, sticky="ew") room_label = tk.Label(widget_frame, text="Room") room_label.grid(row=3, column=0, sticky="ew") room_entry = tk.Entry(widget_frame, textvariable=room_var) room_entry.grid(row=3, column=1, sticky="ew") self.send_goal_button = tk.Button( widget_frame, text="Deliver", command=lambda: self.send_delivery_goal( building_name_var.get(), int(floor_var.get()), int(room_var.get()) ) ) self.send_goal_button.grid(row=4, column=0, sticky="ew") def reset(): building_name_entry.delete(0, tk.END) floor_entry.delete(0, tk.END) 75 room_entry.delete(0, tk.END) reset_btn = tk.Button(widget_frame, text="Reset", command = reset) reset_btn.grid(row=4, column=1, sticky="ew") def send_delivery_goal(self, building_name, floor, room): self.client.wait_for_server() goal = deliverybot_navigation.msg.DeliverGoal( building_name, floor, room) self.client.send_goal(goal) self.status_label.config(text="Running Delivery") self.send_goal_button["state"] = "disabled" self.master.update() self.client.wait_for_result() self.send_goal_button["state"] = "normal" self.status_label.config(text="Idle") if __name__ == "__main__": rospy.init_node("delivery_client_gui") root = tk.Tk() root.title("Delivery GUI") root.resizable(False, False) app = Application(master=root) app.mainloop() scripts/delivery_server.py #!/usr/bin/env python import rospy import sys import os import yaml import tf_conversions import actionlib import rosparam from std_srvs.srv import Empty from elevator_controls.srv import ElevatorFloorGoal from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal from deliverybot_navigation.srv import MapFilePath import deliverybot_navigation.msg class DeliveryServer(object): 76 _feedback = deliverybot_navigation.msg.DeliverFeedback() _result = deliverybot_navigation.msg.DeliverResult() def __init__(self, name): self._action_name = name self._as = actionlib.SimpleActionServer( self._action_name, deliverybot_navigation.msg.DeliverAction, execute_cb = self.execute_cb, auto_start = False ) self.dbot_deliver = rospy.ServiceProxy('/dbot/deliver_package', Empty) self.change_map = rospy.ServiceProxy('/load_map', MapFilePath) self.el_open_door = rospy.ServiceProxy('/elevator/open_elevator_doors', Empty) self.el_close_door = rospy.ServiceProxy('/elevator/close_elevator_doors', Empty) self.el_change_floor = rospy.ServiceProxy('/elevator/elevator_goto_floor', ElevatorFloorGoal) self.clear_costmap = rospy.ServiceProxy('/move_base/clear_costmaps', Empty) self._as.start() rospy.loginfo("Delivery server started") def move(self, x, y, a): client = actionlib.SimpleActionClient('move_base', MoveBaseAction) client.wait_for_server() goal = MoveBaseGoal() goal.target_pose.header.frame_id = "map" goal.target_pose.header.stamp = rospy.Time.now() goal.target_pose.pose.position.x = x goal.target_pose.pose.position.y = y goal.target_pose.pose.position.z = 0.0 quat = tf_conversions.transformations.quaternion_from_euler( 0.0, 0.0, a ) goal.target_pose.pose.orientation.x goal.target_pose.pose.orientation.y goal.target_pose.pose.orientation.z goal.target_pose.pose.orientation.w 77 = = = = quat[0] quat[1] quat[2] quat[3] client.send_goal(goal) wait = client.wait_for_result() rospy.loginfo("Sent Goal") if not wait: rospy.logerr("Action server not available!") rospy.signal_shutdown("Action server not available!") else: return client.get_result() def load_map(self, map_path): rospy.loginfo("Waiting for service /load_map") self.change_map.wait_for_service() self.change_map.call(map_path) rospy.loginfo("Map Changed.") def execute_action(self, action): # action = (type: string, value: object) if action[0] == "ELEVATOR_DOOR_OPEN": result = self.el_open_door.call() elif action[0] == "ELEVATOR_DOOR_CLOSE": result = self.el_close_door.call() elif action[0] == "DELIVER_PACKAGE": result = self.dbot_deliver.call() elif action[0] == "CHANGE_FLOOR": result = self.el_change_floor.call(action[1]) elif action[0] == "CLEAR_COSTMAP": result = self.clear_costmap.call() elif action[0] == "WAIT": pass else: rospy.logwarn("Action type " + str(action[0]) + " not recognized.") return result def load_building_description(self, building_name): # TODO - replace map folder with DB map_folder = os.path.abspath(os.environ.get('MAPS_FOLDER_PATH')) with open(os.path.join(map_folder, building_name, 'building.yaml')) as f: yaml_desc = yaml.safe_load(f) return yaml_desc def generate_plan(self, goal): path = [] rospy.loginfo("Generating Plan") building_desc = self.load_building_description(goal.building_name) # load lobby map path.append(( 78 "MAP", ( os.path.abspath(os.path.join(os.environ.get('MAPS_FOLD ER_PATH'), goal.building_name, 'lobby.yaml')) ) )) # elevator sequence path.append(( "MOVE", ( building_desc["elevator"]["out"]["x"], building_desc["elevator"]["out"]["y"], building_desc["elevator"]["out"]["a"] ) )) path.append(( "ACTION", ( "CHANGE_FLOOR", 0 ) )) path.append(( "ACTION", ( "ELEVATOR_DOOR_OPEN", ) )) path.append(( "MOVE", ( building_desc["elevator"]["in"]["x"], building_desc["elevator"]["in"]["y"], building_desc["elevator"]["in"]["a"] ) )) path.append(( "ACTION", ( "ELEVATOR_DOOR_CLOSE", ) )) path.append(( "ACTION", ( "CHANGE_FLOOR", goal.floor ) )) path.append(( "ACTION", ( 79 "ELEVATOR_DOOR_OPEN", ) )) path.append(( "MAP", ( os.path.abspath(os.path.join(os.environ.get('MAPS_FOLD ER_PATH'), goal.building_name, 'floor.yaml')) ) )) path.append(( "MOVE", ( building_desc["elevator"]["out"]["x"], building_desc["elevator"]["out"]["y"], -building_desc["elevator"]["out"]["a"] ) )) path.append(( "ACTION", ( "ELEVATOR_DOOR_CLOSE", ) )) path.append(( "MOVE", ( building_desc["rooms"][goal.room]["x"], building_desc["rooms"][goal.room]["y"], building_desc["rooms"][goal.room]["a"] ) )) path.append(( "ACTION", ( "DELIVER_PACKAGE", ) )) path.append(( "MOVE", ( building_desc["elevator"]["out"]["x"], building_desc["elevator"]["out"]["y"], building_desc["elevator"]["out"]["a"] ) )) path.append(( "ACTION", ( "CHANGE_FLOOR", goal.floor ) 80 )) path.append(( "ACTION", ( "ELEVATOR_DOOR_OPEN", ) )) path.append(( "ACTION", ( "CLEAR_COSTMAP", ) )) path.append(( "MOVE", ( building_desc["elevator"]["in"]["x"], building_desc["elevator"]["in"]["y"], building_desc["elevator"]["in"]["a"] ) )) path.append(( "ACTION", ( "ELEVATOR_DOOR_CLOSE", ) )) path.append(( "ACTION", ( "CHANGE_FLOOR", 0 ) )) path.append(( "ACTION", ( "ELEVATOR_DOOR_OPEN", ) )) path.append(( "MAP", ( os.path.abspath(os.path.join(os.environ.get('MAPS_FOLD ER_PATH'), goal.building_name, 'lobby.yaml')) ) )) path.append(( "MOVE", ( building_desc["elevator"]["out"]["x"], building_desc["elevator"]["out"]["y"], 81 -building_desc["elevator"]["out"]["a"] ) )) path.append(( "ACTION", ( "ELEVATOR_DOOR_CLOSE", ) )) path.append(( "MOVE", ( building_desc["lobby"]["pickup"]["x"], building_desc["lobby"]["pickup"]["y"], building_desc["lobby"]["pickup"]["a"] ) )) rospy.loginfo("Completed Generating Plan") return path def execute_cb(self, goal): rospy.loginfo("Goal Received.") success = True plan = self.generate_plan(goal) for action_type, action_value in plan: if action_type == "MAP": self.load_map(action_value) elif action_type == "ACTION": self.execute_action(action_value) elif action_type == "MOVE": self.move(*action_value) else: rospy.logerr("Unknown action type found in plan.") if success: rospy.loginfo("Successfully delivered.") self._as.set_succeeded(self._result) if __name__ == "__main__": rospy.init_node("delivery_server") server = DeliveryServer(rospy.get_name()) rospy.spin() 1.3 deliverybot_mapping scripts/mapping_gui.py #!/usr/bin/env python 82 import import import import import import import import import import Tkinter as tk rospkg roslaunch rospy os geometry_msgs.msg tf2_ros tf_conversions yaml subprocess class MappingGUI(tk.Frame): def __init__(self, master=None): tk.Frame.__init__(self, master=master) self.master = master self.BUILDING_NAME = None self.map_server_node = None self.rospack = rospkg.RosPack() self.tfBuffer = tf2_ros.Buffer() self.tfListener = tf2_ros.TransformListener(self.tfBuffer) self.PKG_DIR = self.rospack.get_path('deliverybot_mapping') self.MAPS_DIR = os.path.join(self.PKG_DIR, 'maps') self.LAUNCH_DIR = os.path.join(self.PKG_DIR, 'launch') self.map = { "building": { "name": None, "scheme": None, "floors": None, }, "lobby":{ "pickup": None, "drop": None, }, "elevator":{ "in": None, "out": None, }, "rooms":{ } } self.pack(expand=True, fill="both") self.create_widgets() def save_yaml(self): 83 with open(os.path.join(self.MAPS_DIR, self.BUILDING_NAME, 'building.yaml'), 'w') as f: yaml.dump(self.map, f) def connect_map(self, building_name): self.BUILDING_NAME = building_name self.map["building"]["name"] = building_name if os.path.isdir(os.path.join(self.MAPS_DIR, building_name)): with open(os.path.join(self.MAPS_DIR, building_name, 'building.yaml'), 'r') as f: self.map = yaml.safe_load(f) rospy.loginfo("Building " + building_name + " loaded.") else: os.mkdir(os.path.join(self.MAPS_DIR, building_name)) with open(os.path.join(self.MAPS_DIR, building_name, 'building.yaml'), 'w') as f: yaml.dump(self.map, f) rospy.loginfo("Building " + building_name + " created.") def start_map_server(self): uuid = roslaunch.rlutil.get_or_generate_uuid(None, False) roslaunch.configure_logging(uuid) self.map_server_node = roslaunch.parent.ROSLaunchParent( uuid, [ os.path.join(self.LAUNCH_DIR, "map_server.launch") ] ) self.map_server_node.start() rospy.loginfo("Map Server started") def kill_map_server(self): self.map_server_node.shutdown() self.map_server_node = None rospy.loginfo("Map Server shutdown") def save_map_lobby(self): uuid = roslaunch.rlutil.get_or_generate_uuid(None, False) roslaunch.configure_logging(uuid) cli_args = [ os.path.join(self.LAUNCH_DIR, "map_saver_lobby.launch"), str("location:=" + os.path.join(self.MAPS_DIR, self.BUILDING_NAME)) ] roslaunch_args = cli_args[1:] 84 roslaunch_file = [ ( roslaunch.rlutil.resolve_launch_arguments(cli_args)[0], roslaunch_args ) ] map_saver_lobby = roslaunch.parent.ROSLaunchParent( uuid, roslaunch_file ) map_saver_lobby.start() rospy.loginfo("Lobby map saved.") def save_map_floor(self): uuid = roslaunch.rlutil.get_or_generate_uuid(None, False) roslaunch.configure_logging(uuid) cli_args = [ os.path.join(self.LAUNCH_DIR, "map_saver_floor.launch"), str("location:=" + os.path.join(self.MAPS_DIR, self.BUILDING_NAME)) ] roslaunch_args = cli_args[1:] roslaunch_file = [ ( roslaunch.rlutil.resolve_launch_arguments(cli_args)[0], roslaunch_args ) ] map_saver_lobby = roslaunch.parent.ROSLaunchParent( uuid, roslaunch_file ) map_saver_lobby.start() rospy.loginfo("Floor map saved.") def mark_pickup(self): print("Pickup Marked") trans = self.tfBuffer.lookup_transform( 'map', 'dbot/base_link', rospy.Time() ) self.map["lobby"]["pickup"] = { "x": trans.transform.translation.x, "y": trans.transform.translation.y, "a": tf_conversions.transformations.euler_from_quaternion( [ trans.transform.rotation.x, trans.transform.rotation.y, 85 trans.transform.rotation.z, trans.transform.rotation.w, ] )[2] } self.save_yaml() def mark_drop(self): print("Drop Marked") trans = self.tfBuffer.lookup_transform( 'map', 'dbot/base_link', rospy.Time() ) self.map["lobby"]["drop"] = { "x": trans.transform.translation.x, "y": trans.transform.translation.y, "a": tf_conversions.transformations.euler_from_quaternion( [ trans.transform.rotation.x, trans.transform.rotation.y, trans.transform.rotation.z, trans.transform.rotation.w, ] )[2] } self.save_yaml() def mark_elevator_in(self): print("Elevator In Marked") trans = self.tfBuffer.lookup_transform( 'map', 'dbot/base_link', rospy.Time() ) self.map["elevator"]["in"] = { "x": trans.transform.translation.x, "y": trans.transform.translation.y, "a": tf_conversions.transformations.euler_from_quaternion( [ trans.transform.rotation.x, trans.transform.rotation.y, trans.transform.rotation.z, trans.transform.rotation.w, ] )[2] } self.save_yaml() 86 def mark_elevator_out(self): print("Elevator Out Marked") trans = self.tfBuffer.lookup_transform( 'map', 'dbot/base_link', rospy.Time() ) self.map["elevator"]["out"] = { "x": trans.transform.translation.x, "y": trans.transform.translation.y, "a": tf_conversions.transformations.euler_from_quaternion( [ trans.transform.rotation.x, trans.transform.rotation.y, trans.transform.rotation.z, trans.transform.rotation.w, ] )[2] } self.save_yaml() def add_room_coods(self, room_number): print("Marked Room", room_number) trans = self.tfBuffer.lookup_transform( 'map', 'dbot/base_link', rospy.Time() ) self.map["rooms"][int(room_number)] = { "x": trans.transform.translation.x, "y": trans.transform.translation.y, "a": tf_conversions.transformations.euler_from_quaternion( [ trans.transform.rotation.x, trans.transform.rotation.y, trans.transform.rotation.z, trans.transform.rotation.w, ] )[2] } self.save_yaml() def add_floor_count(self, count): print("Floor Count Added", count) trans = self.tfBuffer.lookup_transform( 'map', 'dbot/base_link', rospy.Time() 87 ) self.map["building"]["floors"] = int(count) self.save_yaml() def add_naming_scheme(self, scheme): print("Floor Count Added", scheme) trans = self.tfBuffer.lookup_transform( 'map', 'dbot/base_link', rospy.Time() ) self.map["building"]["scheme"] = scheme self.save_yaml() def create_widgets(self): self.var_room_number = tk.StringVar() self.var_building_name = tk.StringVar() self.var_wing_name = tk.StringVar() self.var_floors = tk.StringVar() self.var_scheme = tk.StringVar() def add_room_coods_helper(): if (self.var_room_number.get() != ""): self.add_room_coods(self.var_room_number.get()) room_entry.delete(0, tk.END) mapping_frame = tk.LabelFrame(self, text="Mapping") mapping_frame.grid_columnconfigure(0, weight=1, uniform="something") mapping_frame.grid_rowconfigure(0, weight=1, uniform="something") mapping_frame.pack(side="top", expand=True, fill="both") #1 building_name_label = tk.Label(mapping_frame, text="Bldg Name") building_name_label.grid(column=0, row=0, sticky="ew") #2 building_name_entry = tk.Entry(mapping_frame, bd=2, textvariable=self.var_building_name) building_name_entry.grid(column=1, row=0, sticky="ew") #3 building_name_btn = tk.Button(mapping_frame, text="Create", command=lambda: self.connect_map(self.var_building_name.get())) building_name_btn.grid(column=2, row=0, sticky="EW") #4 label_server = tk.Label(mapping_frame, text="Server") label_server.grid(column=0, row=1, sticky="EW") #5 88 server_start_btn = tk.Button(mapping_frame, text="Start", command=self.start_map_server) server_start_btn.grid(column=1, row=1, sticky="EW") #6 server_kill_btn = tk.Button(mapping_frame, text="Clear", command=self.kill_map_server) server_kill_btn.grid(column=2, row=1, sticky="EW") #7 label_area = tk.Label(mapping_frame, text="Area") label_area.grid(column=0, row=2, sticky="EW") #8 lobby_btn = tk.Button(mapping_frame, text="Lobby", command=self.save_map_lobby) lobby_btn.grid(column=1, row=2, sticky="EW") #9 floor_btn = tk.Button(mapping_frame, text="Floor", command=self.save_map_floor) floor_btn.grid(column=2, row=2, sticky="EW") #10 label_elevator = tk.Label(mapping_frame, text="Elevator") label_elevator.grid(column=0, row=3, sticky="EW") #11 elevator_out_btn = tk.Button(mapping_frame, text="OUT", command=self.mark_elevator_out) elevator_out_btn.grid(column=1, row=3, sticky="EW") #12 elevator_in_btn = tk.Button(mapping_frame, text="IN", command=self.mark_elevator_in) elevator_in_btn.grid(column=2, row=3, sticky="EW") #13 label_pickup = tk.Label(mapping_frame, text="Lobby") label_pickup.grid(column=0, row=4, sticky="EW") #14 drop_btn = tk.Button(mapping_frame, text="Mark Drop", command=self.mark_drop) drop_btn.grid(column=2, row=4, sticky="EW") #15 pickup_btn = tk.Button(mapping_frame, text="Mark Pickup", command=self.mark_pickup) pickup_btn.grid(column=1, row=4, sticky="EW") #16 label_room = tk.Label(mapping_frame, text="Room") label_room.grid(column=0, row=5, sticky="EW") #17 room_entry = tk.Entry(mapping_frame, bd=2, textvariable=self.var_room_number, width=4) room_entry.grid(column=1, row=5, sticky="EW") #18 mark_room_btn = tk.Button(mapping_frame, text="Mark", command=add_room_coods_helper) mark_room_btn.grid(column=2, row=5, sticky="EW") #19 89 building_floors_label = tk.Label(mapping_frame, text="Floors") building_floors_label.grid(column=0, row=6, sticky="ew") #20 building_floors_entry = tk.Entry(mapping_frame, bd=2, textvariable=self.var_floors) building_floors_entry.grid(column=1, row=6, sticky="ew") #21 building_floors_btn = tk.Button(mapping_frame, text="Save", command=lambda: self.add_floor_count(self.var_floors.get())) building_floors_btn.grid(column=2, row=6, sticky="EW") #22 building_scheme_label = tk.Label(mapping_frame, text="Scheme") building_scheme_label.grid(column=0, row=7, sticky="ew") #23 building_scheme_entry = tk.Entry(mapping_frame, bd=2, textvariable=self.var_scheme) building_scheme_entry.grid(column=1, row=7, sticky="ew") #24 building_scheme_btn = tk.Button(mapping_frame, text="Save", command=lambda: self.add_naming_scheme(self.var_scheme.get())) building_scheme_btn.grid(column=2, row=7, sticky="ew") if __name__ == "__main__": rospy.init_node("dbot_mapping", anonymous=True) root = tk.Tk() root.resizable(False, False) app = MappingGUI(master=root) tk.mainloop() 1.4 deliverybot_naivgation launch/amcl.launch <launch> <!-- Arguments --> <arg name="scan_topic" <arg name="initial_pose_x" <arg name="initial_pose_y" <arg name="initial_pose_a" default="/dbot/scan"/> default="8.998051126081153"/> default="-17.496187924233098"/> default="1.5891208177771623"/> <!-- AMCL --> <node pkg="amcl" type="amcl" name="amcl"> <param <param <param <param <param name="min_particles" name="max_particles" name="kld_err" name="update_min_d" name="update_min_a" value="500"/> value="3000"/> value="0.02"/> value="0.20"/> value="0.20"/> 90 <param name="resample_interval" <param name="transform_tolerance" <param name="recovery_alpha_slow" <param name="recovery_alpha_fast" <param name="initial_pose_x" initial_pose_x)"/> <param name="initial_pose_y" initial_pose_y)"/> <param name="initial_pose_a" initial_pose_a)"/> <param name="gui_publish_rate" value="1"/> value="0.5"/> value="0.00"/> value="0.00"/> value="$(arg value="$(arg value="$(arg value="50.0"/> <remap <param <param <param <param <param <param <param <param <param <param from="scan" name="laser_max_range" name="laser_max_beams" name="laser_z_hit" name="laser_z_short" name="laser_z_max" name="laser_z_rand" name="laser_sigma_hit" name="laser_lambda_short" name="laser_likelihood_max_dist" name="laser_model_type" to="$(arg scan_topic)"/> value="6"/> value="180"/> value="0.5"/> value="0.05"/> value="0.05"/> value="0.5"/> value="0.2"/> value="0.1"/> value="2.0"/> value="likelihood_field"/> <param <param <param <param <param <param <param name="odom_model_type" name="odom_alpha1" name="odom_alpha2" name="odom_alpha3" name="odom_alpha4" name="odom_frame_id" name="base_frame_id" value="diff"/> value="0.02"/> value="0.02"/> value="0.02"/> value="0.02"/> value="dbot/odom"/> value="dbot/base_link"/> </node> </launch> 1.5 deliverybot_vision scripts/vision_testing.py #!/usr/bin/env python3 from __future__ import print_function import roslib roslib.load_manifest('deliverybot_vision') import sys import os 91 import rospy import easyocr from cv2 import cv2 import numpy as np from std_msgs.msg import String from sensor_msgs.msg import Image from cv_bridge import CvBridge, CvBridgeError class image_converter: def __init__(self): self.image_pub = rospy.Publisher('door_number_image', Image, queue_size=10) self.bridge = CvBridge() self.ocr_reader = easyocr.Reader(['en'], gpu=True) self.image_sub = rospy.Subscriber('/dbot/upper_camera/upper_camera_image', Image, self.callback) dir_path = os.path.dirname(os.path.realpath(__file__)) self.yolo_weights = os.path.join(dir_path, "weights_configs/yolo-obj.weights") self.yolo_config = os.path.join(dir_path, "weights_configs/yolo-obj.cfg") self.yolo = cv2.dnn.readNet(self.yolo_weights, self.yolo_config) with open(os.path.join(dir_path, "weights_configs/obj.names"), "rt") as f: self.classes = [line.strip() for line in f.readlines()] self.layer_names = self.yolo.getLayerNames() self.output_layers = [self.layer_names[i[0] - 1] for i in self.yolo.getUnconnectedOutLayers()] def callback(self, data): try: cv_image = self.bridge.imgmsg_to_cv2(data, 'bgr8') except CvBridgeError as e: print(e) res = self.run_ocr(cv_image) res = self.run_detection(res) cv2.waitKey(10) try: self.image_pub.publish(self.bridge.cv2_to_imgmsg(res, 'bgr8')) except CvBridgeError as e: print(e) def run_detection(self, image): try: 92 height, width, _ = image.shape blob = cv2.dnn.blobFromImage(image, 0.00392, (416, 416), (0, 0, 0), True, crop=False) self.yolo.setInput(blob) outputs = self.yolo.forward(self.output_layers) class_ids = [] confidences = [] boxes = [] for output in outputs: for detection in output: scores = detection[5:] class_id = np.argmax(scores) confidence = scores[class_id] if confidence > 0.5: #Ideally should work with higher tolerance center_x = int(detection[0] * width) center_y = int(detection[1] * height) w = int(detection[2] * width) h = int(detection[3] * height) x = int(center_x - w / 2) y = int(center_y - h / 2) boxes.append([x, y, w, h]) confidences.append(float(confidence)) class_ids.append(class_id) indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4) img = image.copy() for i in range(len(boxes)): if i in indexes: x, y, w, h = boxes[i] label = str(self.classes[class_ids[i]]) cv2.rectangle(img, (x, y), (x + w, y + h), (0,255,0), 3) cv2.putText(img, label, (x, y + 100), cv2.FONT_HERSHEY_PLAIN, 2, (0,0,255), 3) return img except: return image def run_ocr(self, image): try: gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) result: list = self.ocr_reader.readtext(gray) door_number = result[0][1] location = result[0][0] contours = np.array(location).reshape((-1, 1, 2)).astype(np.int32) font = cv2.FONT_HERSHEY_COMPLEX 93 res = cv2.putText( image, text=door_number, org=(contours[0][0][0], contours[1][0][1]+50), fontFace=font, fontScale=1, color=(0,255,0), thickness=2, lineType=cv2.LINE_AA ) res = cv2.rectangle( image, tuple(contours[0][0]), tuple([2][0]), (0,255,0), 3 ) return res except: return image def main(args): ic = image_converter() rospy.init_node('image_converter', anonymous=True) try: rospy.spin() except KeyboardInterrupt: print('Shutting Down') cv2.destroyAllWindows() if __name__ == "__main__": main(sys.argv) 94