Thursday, 29 October 2015

I'm spinning around - Line following

The second autonomous challenge I decided to focus on was the Line Follower challenge, using the Pololu QTR-8RC reflective sensor I posted about earlier. As the sensor needs to be very close to the ground, its not something I can leave mounted all the time (The obstacle course would not be kind to it) so I needed it to be easy to add/remove.  Previously I had the QTR-8RC connected directly to the Raspberry Pi using 11 jumper leads, but that number of cables doesn't lend itself to quick attaching and/or detaching.

To try and make the sensor a little more self contained I decided to use an Arduino as a buffer device, with the sensor connected to the Arduino with the 11 jumper leads, and the Arduino connecting to the Raspberry Pi over I2C, in a similar manner to the motor driver.
The QTR-8RC sensor connected to an Arduino.
As I'm already successfully using an Adafruit Metro 328 for controlling the motors I decided to get a second one, but this time going with the 'Mini' variant to save space. Getting the  QTR-8RC sensor up and running was fairly trivial, as its just a case of grabbing the QTRSensors library written by Pololu and following the instructions. For communicating between the Arduino and the Raspberry Pi I copied the I2C code from my MotorDriver script.

For testing my Line Following capabilities I, obviously, needed to attach the sensor to the robot and, after digging around in my various boxes of supplies, discovered a couple of large bolts that would hold the sensor in the correct position. A few holes drilled later, plus a couple of blobs of blu-tac, and the sensor and Arduino was successfully mounted.


As the circuit is currently wired up on a bread board I've actually mounted everything on the rear of the chassis where there was still plenty of room. Obviously its more traditional to have line following sensors on the front of the robot, but it only takes a few tweaks in the code to make it drive backwards, and there's still plenty of time to move it before PiWars. In theory the best place for the sensor would be in the middle of the robot, as that is the point it turns around, but I don't currently have a good way of mounting it there.

With the hardware sorted it was time to turn my attention to the software. First task was to create a new 'Sensor' class for talking to the QTR-8RC sensor. As this was now controlled by the Arduino via I2C, this was a fairly trivial task (SensorQTR8RC.h) as I could just copy an already implemented I2C sensor and tweak the code.

Next was to add some actual logic to connect the sensor readings to the motor. Again this is mostly a case of copying an existing 'ThoughtProcess', tweaking it to initialize the correct sensor, sit in a loop trying to get the 'line' to stay in the centre of the sensor and we have ourselves a working ThoughtProcess_LineFollower module yes? Well turns out it wasn't quite that easy....

Other than the usual coding issues (e.g. copy and pasting a one second sleep, turning left when I should be turning right) a major problem was the Arduino resetting itself after a couple of seconds, causing the loss of calibration data and making the robot just see 'white' and drive in a straight line. My initial thoughts was that it was related to the known issue with how the I2C hardware in the Raspberry Pi deals with clock stretching (badly), so I spent a few hours switching over to the pigpio library's bit banging  I2C implementation, which correctly handles clock stretching.

I moved the QTR-8RC sensor over to the newly created 'I2CExternal' class and started running some tests. Initial results were not encouraging,  with the Arduino resetting after 20-30 requests, leaving me in the same situation I was earlier. Whilst running further tests I noticed that the Arduino was now resetting whenever I was checking if it existed, which seemed to be due to how I'd changed the 'I2C::exists()' API to work on both the 'internal' and 'external' I2C buses.

This, happily, was a 100% reproducible test case and I started to review the I2C handling code running on the Arduino. Using the time-honoured approach of  sticking an early 'return' statement in the receiveData call to determine at what point the crash was occurring. I, fairly quickly, tracked the issue to the 'for' loop that attempts to determine which command was being requested, and I spotted that I was using 'sizeof(supportedI2Ccmd)' which returns the total size of the array in bytes, instead of '(sizeof(supportedI2Ccmd) / sizeof(supportedI2Ccmd[0]))' which returns the actual number of entries in the array. Meaning I was massively reading off the end of the array and accessing memory that I didn't 'own', apparently annoying the Arduino enough to restart.

So with that change made (and ported across to the Motor Driver Arduino code) I placed my robot back onto my test track (Last years course scaled by 50%) and ended up with...



Now the robot doesn't get close to making its way around the course, but it is definitely trying to track and follow the line, which is a good starting point and something I can build and improve on over the next few weeks.

So with that in a 'working' state I'll be moving onto the next autonomous challenge, the Straight-line speed test.

Leo

No comments:

Post a Comment