Thursday, February 16, 2023

LIDAR experiements

  

 Working on the lidar I discovered that you first need to create the build directory before you cd into it.

$ git clone https://github.com/YDLIDAR/YDLidar-SDK.git
$ cd YDLidar-SDK/build

$ cmake ..

$ make

$ sudo make install

 

lidar is -60mm in the x dimension. 0 in the y. 1/2 height of robot in the z.


It can be launched with 

roslaunch ydlidar_ros_driver X4.launch

roslaunch ydlidar_ros_driver lidar_view.launch

Then you can view the results in rviz.

The next step is for me to establish the transforms from the lidar to the center of the robot. I can do that by defining a static tf broadcast or by specifying that in the URDF (
Unified Robotics Description Format) file. Then I can make the visualization better.




Sunday, January 8, 2023

odometry implemented!

 Hey guys, I haven't been keeping up with the blog, because I've been progressing too fast to write down what I've done. I have finally completed the odometry node on the robot and implemented a tf broad cast on it.

 I know that odometry isn't a reliable method for localization, so I've also bought an IMU. After some test I was able to connect it to the Arduino Mega using the SDA pins. I haven't calibrated it yet so I don't know how accurate it will be, but I can combine the readings from the IMU and Odometry to implement an Extended Kalman filter, which should make the pose estimation a lot better.

I'm very worried about the Serial communication between the Mega and the Pi. There are a lot of parameters here. It seems to be working so far though. 

ALSO! I finally bought a lidar, which sits on the robot like a little hat! This will let me map the environment and navigate obstacles. I'm really excited to use it!

Thursday, September 15, 2022

New Enclosure!

 

Hey it's been a while! I've had a lot more time to work on the robot recently, so I've been making so much progress that I haven't been able to write it all down.
Last time I left off, I was having problems getting the robot to respond quickly over serial communication. It turns out that the 10 AA batteries that I was using as my main power source were draining to quickly. 
I decided to fix the issue by finally getting a lipo battery. I chose a 3 cell battery, which is rated at 11.1V.
Lipo batteries are dangerous and should not be discharged below a certain rate. With the addition of the kinect I also realized that I need something that would provide a stable 12V. I'm sure the kinect has an internal voltage regulator, but I still want to be able to provide a steady voltage. This would also make sure the motors are operating at their rated voltage.
In order to solve the problems above I bought a buck-boost converter and a low voltage cut-off circuit. The battery came with really large wires so I also had to buy an xt60 connector. I'm also going to add a more robust power switch. Eventually the circuit will look like this
Battery >> connector >> power switch >> low voltage protection circuit >> buck boost converter.
Currently I just have the low voltage protection circuit in there with the connector and the switch.
Since cost is a driving factor for me, I chose the cheapest low voltage protection circuit I could find, which relied on a potentiometer to adjust the cut offs. This proved to be really annoying to set and very unreliable. In the future I'm going to pick something that provides me with more information even if it costs extra.
Because I don't have a power supply, I had to put some resistors between the battery and the circuit, measure the output with my voltmeter and then twist the adjustment screw on the potentiometer. The process was excruciating, but I did manage to set the cutoff point at about 11 V.  Now that I have a few extra pieces of gear it's time to start thinking how I'm going to fit all of that on the robot.
Enclosure:
In the past I made a second platform for the robot and elevated it with columns. Going forward I wanted to make the robot more presentable and take advantage of the space more. I plan to put the kinect and lidar on the second level, so I needed another approach.
Here are the objectives of the enclosure
  1. Make the robot look more professional
  2. Hold the lipo battery securely
  3. Provide space for the low voltage circuit
  4. Provide space for the buck boost converter
  5. House the arduino and the motor shield
  6. House the raspberry pi
  7. House the raspberry pi battery
  8. Provide access to the raspberry pi battery power button
  9. Provide access to the arduino usb port
  10. Mount at least 4 ultra sound distance senors
  11. Provide room on the second level for the kinect and lidar.
As you can see it's a tall order. When I added the adafruit motor shield I had to screw down the arduino. The existing holes in the enclosure weren't easy to line up against. As a result the board is positioned in such a way that when you plug the USB cable in, it sticks out past the edges of the platform. This isn't ideal as I hoped the whole thing could be completely enclosed. However this was actually convenient for development, because I could access the cable from the side.
The enclosure took me more iterations that I would have liked. My biggest issue was lining up holes in the 3d model with holes on the actual chassis, since the chassis is from china, I didn't get a drawing of where the holes were and had to rely on my imperfect measurements made with a caliper. 
But eventually I was able to print the enclosure! Here is what the end product looks like!

 
I had to create a cut out for the arduino cable and profile cutouts for the distance sensors. The lipo battery is fixed with a ushaped bracket, same with the raspberry pi battery. The pi is mounted on a plate that inserts into the battery bracket. This enables it to be removed if need be. 
I also had to get a usb-C cable with a 90-degree bend so that it wouldn't interfere with the lipo battery.
Overall, it fits really nice! 
To attach the roof of the enclosure to the walls, I added a lip to the walls. The lips houses a nut inserted into a cutout. This nut allows the roof to the secured with screws. The roof also has a large opening in the middle to feed wires through from future additions such as the lidar or the kinect. 
Another part of this issue was getting the enclosure to print in a way that would look beatiful. Since the walls are mostly flat it made sense to lay them down, in the print configuration, but that resulted in some ugly layers. What I ended up doing instead is printing all the pieces upright. This took wayy more time, but the result was phenomenal! I originally intended for the enclosure to be a mint colour, but I ran out and had to use purple silk PLA. This gives the robot a distinct shine!
Overall, I'm really happy with the progress so far. All of the wire mess can be hidden now and protected from my cats.
    
PID:
After making the enclosure I started driving the robot around again, and I was disappointed with the accuracy of the control. I drove the robot forward and the told it to stop. The robot overcorrected by driving backwards. This told me that there is still work that needs to be done on the PID tuning.
This stuff is the bane of my existence. I realized I may be a bit out of my depth as I've previously tried a lot of different tuning approaches, and I still couldn't get a reliable result. I decided to find a PID library and found the Arduino PID library by Brett. 
This library is intended to replicate what is being used in industry and has numerous improvements over the regular PID algorithm. I replaced my code with the PID from the library and was still having trouble.
The first thing that I did right was to correct the low pass filter I was using on my velocity measurements. As they say garbage in garbage out. I adjusted the cut off frequency and the sampling rate of the filter, which provided significantly more accurate results. That got me thinking that I also had to do the same for the PID controller. One of the improvements that the library implements is that the controller runs at a set interval to increase the performance. After playing around with the PID constants I was finally able to get the controller to follow a sine wave! 
I was excited to try it with the rest of the code, however when I tried to setup the serial node on the pi to connect to the arduino I got the error
wrong checksum or topic id
I looked online and one of the solutions is to increase the delay in the arduino loop. Yesterday I was able to fix this problem by increasing the time between evaluations of the PID loop. I think it has some interrupts in there that must be screwing up my message reception. I still get the error occasionally, but at least it's able to connect. 
I tried to drive the robot around, but there were HUGE spikes in output! The thing will lurch forward like a monster that is out for blood! This is disappointing, because I'm not worse than when I started in terms of PID control. The PID library that I'm using wasn't designed for robots, but rather fridges or cruise control in a car. So, I'm going to investigate if there's something I can do to get smoother performance. The library offers quite a few sophisticated tools that I can take advantage of, such as adjusting constants on the fly. 
If I can't figure it out, I might try another library or just go back to my code. 
 
 Here's a video of the robot startling my cat:
https://www.youtube.com/shorts/RhPg2kjIFW0

Thursday, June 2, 2022

Kinect progress

I bought a kinect! One of my coworkers needed some parts 3D printed for his sched, so I helped him out and made 20$. I reinvested that money into my project and bought a kinect for the xbox360.

I had to get a power adapter from amazon for another 20$ and that enabled me to connect my kinect to my pc. It was really easy to get it working with windows, which allowed me to check its functionality.

IR, rgb camera, depth sensor, microphones, tilt motor and accelerometer all seem to be working correctly. It was even able to do some facial recognition and speach interpretation!

Then I moved onto ubuntu. I'm using the libfreenect drivers and the freenect_stack for ros kinetic. Following tutorials I was able to get it to work and output a 3d projection of my living room in rviz.



Monday, May 23, 2022

Serial problems

 So last time I left the robot, I was able to control it with a keyboard, but it had issues stopping.


Today I revisited the issue. 

I started by Tuning the PID controller on the Arduino, that proved really unproductive, so I started wondering if this is a "garbage in=garbage out" problem. The lowpass filter I got from Curiores (https://gist.github.com/curiores), wasnt performing the way I wanted, do I found another signal filtering library by Martin Bloedorn (https://github.com/MartinBloedorn/libFilter) which seemed to work a lot better. Probably because its a higher order filter.

I also noticed that my the battery voltage dropped quickly and determined how well the PID worked. So I might need a more reliable battery source than just 12 NiMH batteries. 

I also bought a kinect recently for 20$ in hopes of putting it on the robot to do fancy visualizations. It will also require 12 volts, so I'll need to share the power between the motors and the kinect.

Note to self: to start the kinematics node do

rosrun Kinematics kinematics.py

its a capital K

So after I filtered the signal and started all the nodes on the pi, I was able to drive it with the keyboard and have it stop, however there is still some funky business going on.

  • The robot really likes to turn. In fact it likes it so much that it doesn't stop turning.
  • Sometimes it won't obey the commands, as if it read commands from somewhere else. 
  • The wheels behave independently of each other.

The serial node only works at 57600Hz, even though the arduino is supposed to be running at 230400Hz. When I was testing on the Arduino alone none of this seemed to be happening, so I think the issue lies in the serial interface, so the next step is to configure some logging. Or I could just wing it and try to get the robot to follow a path. Or just give up on the velocity aspect and only tune for position.



Wednesday, May 11, 2022

ROS Problems

 

When integrating a bunch of different components there are certainly some expected difficulties. The way that ROS nodes work is by sending each other messages that are published to certain topics.

A single node can have multiple subscribers and publishers in it that can send and receive messages.

Each message has a certain type and a definition file that controls what is sent in the message.

I ran into a problem when trying to send over a list of 4 integers that would represent velocities on the 4 motors.

ROS has a message type for a multi-dimensional array, but not a single array. To send a multiple dimension array message, you need to specify a bunch of parameters such as dimensions which I didn’t want to bother with.

So, I set out to create my own custom message type. To do so I first needed to create a package, then create a message file on my raspberry pi. This was relatively easy, however I quickly realized that I also needed to generate a header file for the Arduino to understand the message. This is where the trouble began.

I followed the tutorial for header file generation, but it didn’t work!

What I was missing is that instead of rosclient I should have used rosArduino in the command. And the destination needs to be empty of ros lib. After fixing a couple of other issues I was finally able to generate the h file.

Then I learned how to use scp to copy the file from the raspberry pi onto windows, so I could put it into the Arduino library.

After doing all that I was ready to test.

To my great disappointment, the raspberry pi couldn’t sync to the Arduino. Something about my custom message was messing it up, and I just kept the same error over and over again.

Finally, I gave up and decided to use the overkill MultiArrayFloat32 message. Float64 is not accepted by the Arduino!

When I tested before I just used the rospub command and published an empty message or a couple of integers, but MultiArray was a whole other beast.

I couldn’t figure out the format for the life of me. BUT. If you push tab it autocompletes it for you!

After I learned that I was able to publish a list of integers to the robot and it turned on the motors!

Huge success!

Next, I included a publisher node into my kinematics node, which published a Multi array message. Publishing messages that way was a lot easier as I was able to use the attributes of the message object.

PID tuning

One thing that is really important in robotics is PID. Which stand for proportional, integral, differential methods of control.

Essentially it is a method for controlling the system based on the error.  For me this was essential, because my motors spin even after you cut off the power. I started with making a PID controller to control position.

I created a controller function and started graphing the target position as well as the actual.

I frequently ran into the problem where the wheel would oscillate around zero, never fully stopping.

This was super annoying and disheartening because I wanted my robot to be really precise. Eventually I ended up putting in a tolerance of 5% and telling the robot to shut off the motors if it got within that margin. This improved the issue drastically.

However, the wheels performed differently. At this point I was considering making a controller for each individual wheel and creating their own constants, but that seemed too time consuming.

The good news was that on the ground the robot wheels didn’t oscillate, because of the friction. Although the robot didn’t really drive straight.

I got a 3d printer

 

Some big updates to the robot!

I have finally bought a 3d printer and have been busy making parts. I designed columns to support a second deck and secured them to the chassis with m3 bolts and nuts. I used a clever technique where the nut sits inside a recessed space and are held in place by the material, this way it acts as a threaded insert and doesn’t require a wrench. Then I printed a 2-part deck for the robot, my printer wasn’t big enough to do the whole thing in one go.

So now I can finally mount the raspberry pi and its battery securely. The battery sits inside a pocket and the raspberry pi sits on top of that. I don’t have standoffs, so I decided to just screw it on and use nuts for spacers. Might need to add head sinks to the pi too, hopefully it won’t melt the plastic or anything.

So now that everything is secured, I can test the driving capability without worrying about everything falling off. Woohoo!

On the electronics side, I figured out how to get a reliable reading from all 4 encoder, so now I have a position count.

I have also made a PID controller, to control the motor. Otherwise, its impossible to get them to stop when you want. It seems that they keep spinning past the cutoff time.

The key insight is that you can make a  template in C, that can be applied to the same function with different parameters. This allowed me to clean up the code significantly.

So, after I figured all of that out, I managed to set a position target for each wheel. The PID performance was still a bit inconsistent, but it kind of worked. The problem is that I don’t want the robot to move at maximum speed towards each target and I’m not sure how to use the kinematic equations with a position target. Plus with speed I could use the teleop_twist_keyboard node, which would let me set a speed with a keyboard and pass the twist message to my kinematics equations which could then pass a motor velocity to the Arduino.

So now I am trying to get a reading for speed. And oh boy that is a can of worms and a half. Basically the most reliable method is to count the number of pulses from the encoder within a specified time interval

d/delta(t)

The problem is that my encoders are incremental. Which means that they go up in discrete steps. Think of it as walking up a staircase instead of a hill.

And that creates a TON of noise, when you try to calculate velocity. The typical solution to this problem is to implement a low pass filter that will filter out the high frequencies and keep the low ones. I tried an update equation from a low pass filter of 25Hz, and that made the signal a lot better. But I have this problem where my filtered velocity doesn’t reach the target, and the unfiltered velocity peaks at the target.

I have no idea why this is happening. Maybe I’m setting a target too high? I can experiment with slower velocities? Creating a low pass filter on the Arduino feels like trying to paint the Mona Lisa with blood spurting out of your cut off finger. Very painful and ugly.

So I’m considering just passing the encoder readings to the pi and letting it do the complicated signal processing and maybe even the control. I’m just worried it won’t be fast enough. The whole point of the Arduino was to have fast low level control, which it seems I can’t do without a better filter! AAAAA!

It sounds like I have to get back to the complicated math of my 4th year control systems course, and get into stuff like Discrete Fourier Transform and Bode diagrams.

Hopefully, I don’t need to get to much into the weeds, I just want the speed control to work.

I am also experimenting with a higher baud rate, might be faster.

In more happy news though, I got the teleop twist to work! I figured out how to use a MultiArray message to communicate to the Arduino and now I can drive the robot with my keyboard on my laptop!

It has an issue with stopping for some reason. It doesn’t like to come to a complete stop. And there seems to be a delay between the keyboard input and the wheels.

NEW Project Autonomous underwater vehicle!

  I have been more and more interested in Autonomous Underwater Vehicles and ocean exploration. I live on the coast with direct access to th...