The code for this section is located in navigation_bot_06 part of an archive. Note that archive includes some additional folders, like maps and worlds, that are used by all projects and therefore are located outside of them. Also note that we use ROS2 Galactic now.
In this tutorial, I am going to follow automaticaddison.com, while he is following https://www.youtube.com/watch?v=ySlU5CIXUKE&ab_channel=CaraSperbeck, and there is a chance that is not the end of a line. Also, they use ROS 1, while I use ROS2. Anyway, both sources are greatly recommended.
That said, I have to mention that "you can not simply take a floor plan and convert it to a map", which is something authors of the sources (above) do not address. See, a normal floor plan has doors, and they can be opened, closed or half-opened. The approach we use doesn't take it into consideration. Also, furniture can create an obstacle for a robot, or maybe not. For example, a vacuum cleaner can move under the table, except for the spots where table's legs are. That last problem can be solved by drawing "obstacles" (table's legs) with one color and non-obstacles (table's top) with another. Then we can take it into consideration when converting... Again, in this article the issue is ignored.
A floor plan we are going to use is a simple .png file. In "maps" folder, add a B&W image you want to convert:
First of all, we need to make sure the image is in binary format. Here is a Python file, credits to automaticaddison.com; we place it in utils folder that is located in the src folder of our workspace, outside navigation_bot_06 folder. This way any project (like futire navigation_bot_XXX) can use it:
convert_png_to_binary.py:
import cv2 # Import OpenCV # read the image file img = cv2.imread('../maps/floor_plans/maze_01.png') ret, bw_img = cv2.threshold(img, 220, 255, cv2.THRESH_BINARY) # converting to its binary form bw = cv2.threshold(img, 240, 255, cv2.THRESH_BINARY) cv2.imwrite("../maps/floor_plans/maze_01_binary.png", bw_img)
Note that maps folder should have floor_plans subfolder:
$ cd $HOME/SnowCron/ros_projects/harsh/src/maps $ mkdir floor_plans
To run this script, we need to:
$ cd $HOME/SnowCron/ros_projects/harsh/src/utils $ python3 convert_png_to_binary.py
In utils folder, create convert_png_to_map.py file
import numpy as np import cv2 import math import os.path # The name of your floor plan you want to convert to a ROS map: strSrcFileName = "../maps/floor_plans/maze_01_binary.png" image = cv2.imread(strSrcFileName) strDstFileName = "maze_01_binary" strDstLocation = "../maps/" # We scale the map, so it is 40 meters wide img_width_meters = 40 width, height, _ = image.shape x1 = [0,width, 0,0] y1 = [0,0, 0,height] deltax = img_width_meters dx = math.sqrt((x1[1]-x1[0])**2 + (y1[1]-y1[0])**2)*.05 sx = deltax / dx deltay = img_width_meters * float(height) / width dy = math.sqrt((x1[3]-x1[2])**2 + (y1[3]-y1[2])**2)*.05 sy = deltay/dy res = cv2.resize(image, None, fx=sx, fy=sy, interpolation = cv2.INTER_CUBIC) # Convert to grey res = cv2.cvtColor(res, cv2.COLOR_BGR2GRAY) #cv2.imwrite("KEC_BuildingCorrected.pgm", res ); # cv2.imshow("Image2", res) completeFileNameMap = os.path.join(strDstLocation, strDstFileName +".pgm") completeFileNameYaml = os.path.join(strDstLocation, strDstFileName +".yaml") cv2.imwrite(completeFileNameMap, res ) yaml = open(completeFileNameYaml, "w") yaml.write("image: " + strDstFileName + ".pgm\n") yaml.write("resolution: 0.050000\n") yaml.write("origin: [" + str(-1) + "," + str(-1) + ", 0.000000]\n") yaml.write("negate: 0\noccupied_thresh: 0.65\nfree_thresh: 0.196") yaml.close()
This Python file is based on the https://automaticaddison.com/, the difference is, it calculates size of a map without user input. Ideally, it should take command line parameters for that, but right now everything is hardcoded.
$ colcon build --packages-select navigation_bot_06 $ source install/setup.bash $ ros2 launch navigation_bot_06 main_simulation_launch.py world:=src/worlds/maze.sdf slam:=False