{"id":343,"date":"2013-11-24T21:54:47","date_gmt":"2013-11-24T21:54:47","guid":{"rendered":"http:\/\/localhost:8888\/?page_id=343"},"modified":"2022-07-06T18:40:56","modified_gmt":"2022-07-06T18:40:56","slug":"thermal-imaging","status":"publish","type":"page","link":"https:\/\/www.amzsaki.com\/?page_id=343","title":{"rendered":"Thermal imaging"},"content":{"rendered":"<p><strong>THERMAL IMAGING SCANNING CAMERA<\/strong><\/p>\n<p style=\"text-align: justify;\">The human visual system is sensitive to wavelengths of light from about 400 nanometers (interpreted a violet) to about 700 nanometers (perceived as red).\u00a0 This range of wavelengths is commonly referred to as the visible light and serves as the foundation of the visual spectrum. Although giving rise to a bewildering variety of colours, this range of wavelengths is only a fraction of all detectable wavelengths. From short wavelength gamma-rays to kilometre long radio waves we are potentially exposed to this electromagnetic spectrum on a daily basis. Of particular interest to us are the waves in the infra-red range, from about 2 micro-meters to 22 micro meters. These waves carry radiant heat.<\/p>\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-348 aligncenter\" src=\"\/wp-content\/uploads\/2013\/11\/15_300ms_wm.jpg\" alt=\"15_300ms_wm\" width=\"400\" height=\"464\" \/><\/p>\n<div id=\"id3\">\n<div>\n<div>\n<p style=\"text-align: justify;\">The detection of infra-red waves in the range from 2 to 22 micrometers can be accomplished in a variety of ways; via pyro-electric sensors used in motion detectors to thermopiles, which are more sophisticated in that they can measure static heat sources [1]. Just like a digital photo camera \u2018sees\u2019 and records colour, there are commercial cameras that detect radiant heat and form an image [2]. The only minor problem is that the cost of these thermal imaging cameras is still prohibitively expensive for most of us. However, there are alternatives for the technologically inclined; thermopile sensors are available at a reasonable cost (e.g. around $50-100) that can read one or more (as high as eight) values of radiant heat. As of writing this,\u00a0<a title=\"http:\/\/www.robot-electronics.co.uk\/acatalog\/Thermal_Array_Sensor.html\" href=\"http:\/\/www.robot-electronics.co.uk\/acatalog\/Thermal_Array_Sensor.html\">Devantech<\/a>\u00a0has the\u00a0<a title=\"http:\/\/www.robot-electronics.co.uk\/acatalog\/Thermal_Array_Sensor.html\" href=\"http:\/\/www.robot-electronics.co.uk\/acatalog\/Thermal_Array_Sensor.html\">TPA81<\/a>\u00a0sensor array available and others have successfully used\u00a0<a title=\"http:\/\/www.cheap-thermocam.tk\/\" href=\"http:\/\/www.cheap-thermocam.tk\/\">MELEXIS<\/a>\u00a0products.<\/p>\n<p style=\"text-align: justify;\">What I am presenting on this page is by no means the first implementation of a thermal imaging camera using the TPA81 thermopile sensor. There are a few interesting implementation such as [3,4], which have served as the basis of my contraption. However, I have not seen one with a pan\/tilt combination using this sensor with the hardware available for 3D printing and discussing on how to perhaps improve the resulting image.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div id=\"id4\">\n<div>\n<div>\n<p style=\"text-align: justify;\">The thermal imaging scanning camera was built using a TPA81 thermopile sensor array mounted atop a servo-controlled pan\/tilt head attachable to a tripod. The sensor feeds information to an Arduino Uno microcontroller [5], which, in turn communicates the thermal readings to a computer via a serial connection. The figure below shows the general setup mounted on a tripod.<\/p>\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-344 aligncenter\" src=\"\/wp-content\/uploads\/2013\/11\/MG_8762_display_small.jpg\" alt=\"_MG_8762_display_small\" width=\"400\" height=\"486\" \/><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p style=\"text-align: justify;\">The ensuing discussion will be divided into four parts; the physical hardware, electronics, firmware and post-capture processing software.<\/p>\n<p style=\"text-align: justify;\">\n<div id=\"id5\" style=\"text-align: justify;\">\n<p><strong>Physical Hardware<\/strong><\/p>\n<p>The pan\/tilt mechanism formed by the two servo motors and held together by brackets is quite simple. The lower servo, attached to the tripod via a tripod attachment, supplies the rotational motion to sweep (or pan) across the field of view in the horizontal plane. The upper servo, mounted to the lower bracket via a servo bracket, is attached to one arm of the upper bracket providing the tilt. Two #6 machine screws form the pivot connection between the two brackets.<\/p>\n<\/div>\n<div id=\"id6\">\n<p style=\"text-align: justify;\">The TPA81 was mounted on the top bracket via a small adapter. The various connections holding the brackets together were accomplished using the small screws supplied with the servos.<\/p>\n<p style=\"text-align: justify;\">The realization of the physical hardware can be accomplished in a variety of ways; pre-formed metal brackets are available at various robot supply stores, the brackets can be made at home by bending thin metal plates, or, to go with the emerging \u2018fabrication at home\u2019 trend, printed on a 3D printer. This project uses hardware elements designed on a computer to fit HITEC HS-422 servos and printed on a\u00a0<a title=\"http:\/\/www.makerbot.com\" href=\"http:\/\/www.makerbot.com\/\">Thing-o-matic<\/a>\u00a03D printer using ABS plastic filament providing custom elements that are strong yet relatively inexpensive. The whole physical hardware design is available, of course free of charge, for download and printing on your 3D printer if you so desire. Just browse over to thing\u00a0<a title=\"http:\/\/www.thingiverse.com\/thing:22527\" href=\"http:\/\/www.thingiverse.com\/thing:22527\">#22527<\/a>\u00a0on Thingiverse to get the parts.<\/p>\n<p style=\"text-align: justify;\">The assembly of the hardware should be straight forward. Generally there are no pre-formed mounting holes, so there is a built-in adjustably to suit your needs if you use a different servo or attachment method. Of course, there are various size restrictions since the design used a HS-422 servo.<\/p>\n<p style=\"text-align: justify;\"><strong>Electronics<\/strong><\/p>\n<p style=\"text-align: justify;\">The electronics is quite simple, requiring just a minimum amount of soldering and a few components. Similar projects [3,4] did an excellent job in outlining the micro-controller and communications, so here only the salient features of the electronics will be discussed drawing attention to the particularities of this design. The figure below shows the general schematics of the micro-controller, servos and the TPA81 sensor.<\/p>\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-353 aligncenter\" src=\"\/wp-content\/uploads\/2013\/11\/wiringschematics.png\" alt=\"wiringschematics\" width=\"594\" height=\"498\" \/><\/p>\n<p style=\"text-align: justify;\">The TPA81 thermopile sensor communicates with the Arduino micro-controller via an I2C bus. Although the manufacturer recommends two pull-up resistors in the neighbourhood of 1.8 kOhm to be put on the SDA and SCL lines, other implementers [3,4] make no mention of using them. I have tried without the resistors and no luck. As soon as two 2 kOhm resistors from my spare box were added to the lines, the communication was flawless. The connection to the servos is a standard 5V, ground and signal. My version was put together on a perfboard with pins soldered on so the whole circuit attaches on top of the Arduino like other \u201cshields\u201d.<\/p>\n<p style=\"text-align: justify;\">\n<p style=\"text-align: justify;\"><strong>Firmware<\/strong><\/p>\n<p style=\"text-align: justify;\">The firmware running on the Arduino could not be simpler. After the usual setup of input\/output pins and assignment of control parameters, the micro-controller enters an endless cycle, which contains two nested loops. These two inner loops carry out the positioning of the pan\/tilt mechanism, communication with the TPA81 sensor and feeding the temperature readout to the attached computer. The code below borrows from the great work of [3,4] with a few minor modifications, as will be discussed later.<\/p>\n<pre><span style=\"color: #ffffff;\">#include &lt;Wire.h&gt;\n#define TPA81ADDR (0xd0&gt;&gt;1)\n#include &lt;Servo.h&gt;\n\nServo panServo;\nServo tiltServo;\n\nint tilt=0;\nint pan=0;\nint tiltSteps=65;\nint panSteps=65;\nint tiltIncrement=2;\nint panIncrement=2;\n\nvoid setup()\n{\n\n  tiltServo.attach(9);\n  panServo.attach(10);\n  tiltServo.write(0);\n  panServo.write(0);\n\n  Wire.begin();\n  Serial.begin(9600);\n}\n\nvoid loop()\n{\n  byte b;\n  int i;\n\n  Serial.print(\"begin thermal scan\");\n  Serial.println(\"\");\n  Serial.print(tiltSteps,DEC);\n  Serial.print(\" \");\n  Serial.print(panSteps,DEC);\n  Serial.println(\"\");\n\n  for (pan=0;pan&lt;panIncrement*panSteps;pan+=panIncrement)\u00a0 {\n    panServo.write(pan);\n    delay(150);\n\n    for (tilt=0;tilt&lt;tiltIncrement*tiltSteps;tilt+=tiltIncrement) {\n      tiltServo.write(tilt);\n      delay(150);\n      Wire.beginTransmission(TPA81ADDR);\n      Wire.write(4);\n      Wire.endTransmission();\n      Wire.requestFrom(TPA81ADDR,(int)1);\n\n      while (Wire.available()&lt;1) {\n       ;\n      }\n\n      b=Wire.read(); \/\/ receive a byte as character\n\n\u00a0     Serial.print(b,DEC);\n      Serial.print(\" \");\n    }\n    Serial.println(\"\");\n  }\n\n  Serial.print(\"end thermal scan\");\n  Serial.println(\"\");\n\n  panServo.write(0);\n  tiltServo.write(0);\n}<\/span><\/pre>\n<p style=\"text-align: justify;\">The variables <em>tiltSteps<\/em> and <em>panSteps<\/em> define the number of increments (tiltIncrement and panIncrement) the servos will cover, such that the total angular extent of the \u2018image\u2019 formed will be (<em>tiltSteps<\/em> * <em>tiltIncrement<\/em>) by (<em>panSteps<\/em> * <em>panIncrement<\/em>). It would be relatively easy to add an offset to the starting and ending angles of pan and tilt so that scanning commences not at zero but at an offset, as we will see later. The TPA81 has eight pixels, but the above code reads only one of them (number 4) and sends the reading to the attached computer via a serial link. The mechanism pauses for 150 milliseconds after a new pan\/tilt position is set. Why 150 milliseconds? One reason is that the mechanism has to stabilize itself after a movement; to reduce vibrations due to inertia. The other reason is to allow the TPA81 sensor to take a new reading and make it available for the micro-controller. The implication of changing this number will be discussed later.<\/p>\n<p style=\"text-align: justify;\">If the above firmware is uploaded to the Arduino and a scan is performed, something like the following image is obtained.<\/p>\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-352 aligncenter\" src=\"\/wp-content\/uploads\/2013\/11\/initial.jpg\" alt=\"initial\" width=\"484\" height=\"627\" \/><\/p>\n<p style=\"text-align: justify;\">A couple of things are obvious from the image; one, that it is not that pretty, the other is that there is a considerable amount of fluctuation on the lower end of the temperature scale, in other words, lot of noise. What was this image supposed to represent? Well, on the left, about mid-height was my head, shown as a greenish blob reading about, you guessed it, 36+ degrees Celsius. A bit lower, to the right was my forearm. In the upper section, the red dot is a pot light in the ceiling, fairly hot in comparison to its surroundings. The ambient temperature was about 17-18 degrees Celsius.<\/p>\n<p style=\"text-align: justify;\">Another \u2018limitation\u2019 present in the sensor is that it reads the temperature in an area subtended by about an angle of 6 degrees in both the horizontal and vertical. What this means is that it reads an \u2018average\u2019 temperature of an area within that window of 6 degrees. Of course, the farther the heat \u2018source\u2019, the 6 degree angle covers a larger area! Thus, to read\/sample the surrounding area\u2019s temperature, the sensor should be repositioned by 6 degrees in both the pan and tilt direction to read a completely new reading. However, this translates to only a 15 by 15 pixel image for an area covering 90 by 90 degrees. Not that impressive. To increase the \u2018resolution\u2019, the repositioning of the sensor\u2019s direction could be made less than the 6 degree field of view. However, in this case, the reading covers or averages out the newly sampled area with part of the one sampled in the previous increment. Thus, although images of larger size (e.g. more points sampled) can be made, in fact, the resulting image would not be comparable to an image obtained from a sensor with a smaller field of view. One has to keep this in mind! Thus, to increase the resolution, just sample the space in increments less than the 6 degrees field of view. The above code increments pan\/tilt by 2 degrees and the preceding image was created using such technique.<\/p>\n<p style=\"text-align: justify;\">The other issue of \u2018noise\u2019 in the image can be improved at two places; in the firmware and in post-processing on the computer. The idea is that if one could take multiple samples at the same pan\/tilt location and average their values perhaps a better estimate of the actual temperature could be obtained, thus reducing noise. The most obvious choice would be to make use of all eight pixels on the sensor and store all readings. In essence, this would lead to eight images taken \u2018panIncrement\u2019 apart. These eight images then could be averaged to improve the quality of the final image. I have explored this option but was not satisfied with the results. Why? Even though there is a noticeable improvement in reducing noise, since the average image size rarely exceeds 100 pixels, there is a visible vertical band of up to seven pixels that are a result of averaging one, two, three and up to seven samples before all eight values are available, deteriorating the quality of the whole image. The other alternative, although costly time-wise, is to obtain multiple readings at the same location separated by a time interval. The following four images were taken such a way. Four readings were taken at 50, 75, 100 and 300 milliseconds apart as shown in the figures below, where the top row corresponds to 50 and 75, while the bottom row to 100 and 300 millisecond intervals between readings, respectively.<\/p>\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-362 aligncenter\" src=\"\/wp-content\/uploads\/2013\/11\/Untitled-1.jpg\" alt=\"Untitled-1\" width=\"559\" height=\"689\" \/><\/p>\n<p style=\"text-align: justify;\">Looking at the upper left (50ms) image, the noise at lower temperatures still manifested itself, however, it was considerably less than before. As the time interval between each of the four readings increases, the noise (in the blueish-cyanish colours) was reduced giving a more uniform colour image. Perhaps the 300ms image looks the best. However, this improvement in quality comes at a cost; the scan time for the whole image has substantially increased since we are waiting six times more in comparison to the 50ms case. Thus, as a trade-off, the 100 millisecond pause was considered reasonable and has been used ever since. Finally, the modified code is shown below incorporating the multiple sampling.<\/p>\n<pre><span style=\"color: #ffffff;\">#include &lt;Wire.h&gt;\n\n#define TPA81ADDR (0xd0&gt;&gt;1)\n\n#include &lt;Servo.h&gt;\nServo panServo;\nServo tiltServo;\nint tilt=0;\n\nint pan=0;\n\n\/\/int tiltSteps=24;\n\/\/int panSteps=24;\n\/\/int tiltIncrement=5;\n\/\/int panIncrement=5;\nint tiltSteps=45;\nint panSteps=45;\nint tiltIncrement=2;\nint panIncrement=2;\nint tiltStartAngle=30;\nint panStartAngle=0;\n\nvoid setup()\n{\n  tiltServo.attach(10);\n  panServo.attach(9);\n  tiltServo.write(0);\n  panServo.write(0);\n\n  Wire.begin();\n  Serial.begin(9600);\n}\n\nvoid loop()\n{\n  byte b;\n  int i;\n  Serial.print(\"begin thermal scan\");\n  Serial.println(\"\");\n  Serial.print(tiltSteps,DEC);\n  Serial.print(\" \");\n  Serial.print(panSteps,DEC);\n  Serial.println(\"\");\n\n  for (pan=panStartAngle;pan&lt;(panIncrement*panSteps+panStartAngle);pan+=panIncrement)\u00a0 {\n    panServo.write(pan);\n    delay(150);\n    for (tilt=tiltStartAngle;tilt&lt;(tiltIncrement*tiltSteps+tiltStartAngle);tilt+=tiltIncrement) {\n      tiltServo.write(tilt);\n      \/\/delay(300);\n      \/\/delay(1000);\n      for (i=1;i&lt;=4; i++) {\n        delay(100);\n        Wire.beginTransmission(TPA81ADDR);\n        Wire.write(4);\n        Wire.endTransmission();\n        Wire.requestFrom(TPA81ADDR,(int)1);\n        while (Wire.available()&lt;1) {\n          ;\n        }\n\n        b=Wire.read(); \/\/ receive a byte as character\n\n        Serial.print(b,DEC);\n        Serial.print(\" \");\n      }\n      Serial.println(\"\");\n    }\n  }  \n\n  Serial.print(\"end thermal scan\");\n  Serial.println(\"\");\n  panServo.write(0);\n  tiltServo.write(0);\n}<\/span><\/pre>\n<p><strong>Post-Capture Processing<\/strong><\/p>\n<p style=\"text-align: justify;\">The readings from the sensor, through the Arduino micro-controller, are transmitted to the computer as a set of four number per pan\/tilt sampling location. Their numbers are logged in the serial output window of the Arduino development environment and save into a text file. This text file serves as the input to a C++ command line program that reads the values and stores them in a vector. Once all values are read, the average per sampling location is computed and a PostScript file is written out with the thermal image and a legend, as seen in the preceding images.<\/p>\n<p style=\"text-align: justify;\">As hinted at earlier, another way to improve image quality is to pass the image through a filter to smooth out the noise. There are many potentially useful filters available, but as of this writing, only a median filter was implemented. Description of the median filter can be found in [6] with implementation details at [7]. To see the effect of the median filter, compare the two images below; the averaged, but the unfiltered image is on the left and the averaged and the median filtered image is on the right.<\/p>\n<p style=\"text-align: center;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-371 aligncenter\" src=\"\/wp-content\/uploads\/2013\/11\/Untitled-2.jpg\" alt=\"Untitled-2\" width=\"565\" height=\"348\" \/><\/p>\n<p style=\"text-align: justify;\">The improvement in image quality with respect to noise is noticeable. The areas of higher temperature were delineated from the ambient, bringing out the donut-shaped ring light on the right, to the left of it the laptop used in capturing the image. Left of the laptop is my arm and even farther left is my shoulder. The red dot on the top is the pot light, as before. The warmer area in the lower right puzzled me for a while until I realized that it was the laptop\u2019s power supply. Mystery solved!<\/p>\n<p style=\"text-align: justify;\">I hope that this little article will help you to make your own thermal imaging scanner or clarified a few issues with the building and running such home-brewed contraption. Of course, like everything else you read on the Internet, there might be errors in the text both writing and technical. If you find one, please contact me so I can update this page&#8230;<\/p>\n<p><strong>References<\/strong><\/p>\n<ol>\n<li value=\"1\">1.TPA81 Thermopile Array Technical Specification (<a title=\"http:\/\/www.robot-electronics.co.uk\/htm\/tpa81tech.htm\" href=\"http:\/\/www.robot-electronics.co.uk\/htm\/tpa81tech.htm\">http:\/\/www.robot-electronics.co.uk\/htm\/tpa81tech.htm<\/a>)<\/li>\n<li value=\"2\">2.FLIR (<a title=\"http:\/\/www.flir.com\/ca\/\" href=\"http:\/\/www.flir.com\/ca\/\">http:\/\/www.flir.com\/ca<\/a>)<\/li>\n<li value=\"3\">3.DIY Thermal Imaging System for under\u00a0$200 (<a title=\"http:\/\/spill.tanagram.com\/2010\/11\/24\/diy-thermal-imaging-system-for-under-200\" href=\"http:\/\/spill.tanagram.com\/2010\/11\/24\/diy-thermal-imaging-system-for-under-200\">http:\/\/spill.tanagram.com\/2010\/11\/24\/diy-thermal-imaging-system-for-under-200<\/a>)<\/li>\n<li value=\"4\">4.Thermoscanner (<a title=\"http:\/\/www.designer2k2.at\/index.php?option=com_content&amp;view=article&amp;id=30:thermoscanner&amp;catid=13:arduino&amp;Itemid=40\" href=\"http:\/\/www.designer2k2.at\/index.php?option=com_content&amp;view=article&amp;id=30:thermoscanner&amp;catid=13:arduino&amp;Itemid=40\">http:\/\/www.designer2k2.at\/index.php?option=com_content&amp;view=article&amp;id=30:thermoscanner&amp;catid=13:arduino&amp;Itemid=40<\/a>)<\/li>\n<li value=\"5\">5.Arduino Uno Microcontroller (<a title=\"http:\/\/arduino.cc\/en\/Main\/ArduinoBoardUno\" href=\"http:\/\/arduino.cc\/en\/Main\/ArduinoBoardUno\">http:\/\/arduino.cc\/en\/Main\/ArduinoBoardUno<\/a>)<\/li>\n<li value=\"6\">6.Median filter on Wikipedia (<a title=\"http:\/\/en.wikipedia.org\/wiki\/Median_filter\" href=\"http:\/\/en.wikipedia.org\/wiki\/Median_filter\">http:\/\/en.wikipedia.org\/wiki\/Median_filter<\/a>)<\/li>\n<li value=\"7\">7.Median filter (<a title=\"http:\/\/www.librow.com\/articles\/article-1\" href=\"http:\/\/www.librow.com\/articles\/article-1\">http:\/\/www.librow.com\/articles\/article-1<\/a>)<\/li>\n<\/ol>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>THERMAL IMAGING SCANNING CAMERA The human visual system is sensitive to wavelengths of light from about 400 nanometers (interpreted a<\/p>\n<p><a href=\"https:\/\/www.amzsaki.com\/?page_id=343\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\">Thermal imaging<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":298,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-343","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.amzsaki.com\/index.php?rest_route=\/wp\/v2\/pages\/343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.amzsaki.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.amzsaki.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.amzsaki.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.amzsaki.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=343"}],"version-history":[{"count":9,"href":"https:\/\/www.amzsaki.com\/index.php?rest_route=\/wp\/v2\/pages\/343\/revisions"}],"predecessor-version":[{"id":3542,"href":"https:\/\/www.amzsaki.com\/index.php?rest_route=\/wp\/v2\/pages\/343\/revisions\/3542"}],"up":[{"embeddable":true,"href":"https:\/\/www.amzsaki.com\/index.php?rest_route=\/wp\/v2\/pages\/298"}],"wp:attachment":[{"href":"https:\/\/www.amzsaki.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}