25 Apr 2020
robomagellan
calibration
robots
ros
I’ve made quite a bit of progress on the robomagellan robot recently. Over the past week
I’ve installed the Realsense D435 camera, setup a URDF, and made some serious
progress towards global localization.
Absolute Heading Reference
I’m using an EKF from the
robot_localization package
for outdoor localization. This package can merge the wheel odometry, IMU and GPS data together
into a cohesive global pose and odometry. I’ll post more details about how I’m using
robot_localization in a future post once I finish tuning things.
Since GPS data doesn’t include heading, robot_localization requires an IMU or odometry source
with absolute heading information. While some higher end IMUs may put this information out
directly, none of the IMUs I have offer a reliable absolute heading.
Today’s IMUs typically include a three-axis accelerometer, gyro and magnetometer.
Most robotics people are pretty familiar with gyros and accelerometers, but less so with
magnetometers, especially if they have primarily worked on indoor mobile robots. Magnetometers
measure the geomagnetic field. If there were no disturbances and our magnetometer was
accurately calibrated, then plotting the magnitude vector while rotating the sensor around would
form a sphere with a radius equal to the local field strength. A projection of the magnitude
vector into the horizontal plane gives us the equivelent of a compass bearing.
The imu_filter_madgwick ROS package is used to merge the accelerometer, gyro, and
magnetometer readings into an orientation estimate which we can feed into the EKF. Unfortunately,
the geomagnetic field is not very strong so calibration is quite important. This filter is
able to take in a magnetometer bias, which corresponds to what is often called the
hard iron offset . To get this bias vector I wrote a magnetometer_calibration
node in the robot_calibration package.
This calibration node rotates the robot around slowly while recording the magnetometer
values. It then tries to fit them to a sphere. It outputs the center of that sphere (which
corresponds to our bias or hard iron offset). It doesn’t currently do the more complex
soft iron calibration since there isn’t a good way to merge that into the pipeline anyways.
I’m planning to implement both the soft iron calibration, and the extensions to the filter
node so it can use the soft iron calibration soon.
The output of the node looks something like this:
$ rosrun robot_calibration magnetometer_calibration
[ INFO]: Subscribing to /imu/mag
[ INFO]: Publishing to /cmd_vel
[ INFO]: Rotating robot for 60 seconds.
[ INFO]: Done rotating robot.
[ INFO]: Saving bagfile with 1337 poses.
[ INFO]: Ceres Solver Report: <truncated>
[ INFO]: mag_bias_x: 0.042075341
[ INFO]: mag_bias_y: -0.175981837
[ INFO]: mag_bias_z: -1.057499311
The mag_bias parameters can then be passed to the imu_filter_madgwick
as parameters (along with use_mag parameter being set to true). I still need to add
documentation on the new magnetometer calibration node, but the basics are:
- Topics (remap these to your actual topics):
- /imu/mag - the magnetometer data. Should be a sensor_msgs/MagneticField message.
- /cmd_vel - the commands to the mobile base. geometry_msgs/Twist.
- Parameters
- rotation_velocity - this is the angular velocity to command. Should be relatively
low, but has to be high enough that your robot actually rotates. Defaults to 0.2 rad/s.
- rotation_duration - the robot has to make at least one full revolution in place. If
your robot is really slow, this might need to be longer. Defaults to 60 seconds.
The node also has some other modes, such as manual rotation (in case your IMU isn’t actually
mounted in a robot yet). Right now the best source of documentation is the code itself. I plan
to add documentation to the robot_calibration README later this week and then get a new release
pushed to debians. UPDATE: documentation is now in the
README.
Next Steps
I’m still working on tuning the EKF parameters. I’ve also started to write some GPS-waypoint
navigation code. Both of these topics will be the subject of upcoming posts.
18 Apr 2020
Earlier this month I started blogging again. My blog was hosted on blogger.com,
which was a pretty advanced platform in 2008 when I started blogging.
Now it is pretty dated.
I had a hell of a time trying to put any code blocks in the blog - which for a
software engineer is a real problem.
So this is the new blog. I’ve ported most of the old content over, except for a few
posts which were completely irrelevant (for example, a post which consists of nothing
but a link to a Kickstarter project for Mech Warfare, both of which are since over).
Porting over all of these posts was actually a somewhat fun exercise, I had forgotten
just how much had been posted to this blog over the years. Here’s a few highlights:
In setting up this site, I decided not to bother with a comments section, as the comments
on the old site have generally been a wasteland for some time. If you come across an issue
or are looking for more information, reach out to me by either filing a ticket against
this site on GitHub or
by messaging me on Twitter.
I’ve also updated the domain name to reflect that I might actually post some non-robotics
things here in the future. In that same spirit, most of the existing posts are now
tagged with the new
robots
tag. I’ve had this domain name for a while now, happy to finally put it to use.
Welcome to Robot & Chisel.
10 Apr 2020
robomagellan
national-robotics-week
ros
robots
This is day 6 of my National Robotics Week blog marathon.
See the full set of posts here.
It is really starting to feel like spring here in New Hampshire, so I've been reviving my Robomagellan robot:
The current robot (not shown: Intel D435 sensor)
If you're not familiar with Robomagellan, here is the wikipedia description:
Robomagellan was created by the Seattle Robotics Society and is a small scale autonomous vehicle race in which robots navigate between predefined start and finish points. The start and finish points are usually represented as GPS coordinates and marked by orange traffic cones. In most versions of the competition there are also optional waypoints that the robot can navigate to in order to earn bonus points. The race is usually conducted on mixed pedestrian terrain which can include obstacles such as park benches, curbs, trees, bushes, hills, people, etc..
Unfortunately, there are not many Robomagellan contests happening anymore - but this platform
is still good for me to work on some outdoor navigation. I actually started building this robot
in 2012 when Robomagellan was quite popular. The robot was briefly worked on in 2014 and 2018.
The GitHub contributions view seems to tell this story quite well:
Contribution timeline for Robomagellan
As with any robot that has been developed sporadically over close to a decade, it has gone
through quite a bit of evolution. You can find some of that evolution in the posts tagged
robomagellan,
but here is a summary:
- The computer was originally a Turtlebot laptop. It has since been swapped out for an Intel
NUC. I've previously posted about how I
power the NUC off a 12->19V step up.
- The original version of the Etherbotix was designed for this robot. It now uses the later
Etherbotix design with a plug-in motor driver.
- The robot now has an Adafruit Ultimate
GPS v3. That may change in the near future, as I've been looking at setting up an RTK
solution here on the farm.
- The robot originally used a small chip-level IMU on the Etherbotix, but now uses a
UM-7 for better results. That said, I never
had any luck with the internal UM-7 EKF (even when trying to calibrate it), so there are
probably plenty of cheaper options out there.
- Originally, the main sensor was going to be a UTM-30 on a tilting servo. I've now simplified
that for an Intel D435 depth sensor.
- The robot is still using the original wheels, however I switched from
100:1 gearboxes to
50:1 to get more speed (the 100:1 were really
too much torque, the robot literally could climb a wall).
The robot, as you probably guessed, runs ROS. Specifically I'm using the following packages:
- etherbotix_python -
these are my drivers for the Etherbotix board. In addition to controlling the motors and providing
odometry, this board also acts as a serial->ethernet adapter for the GPS module. The drivers
publish the raw NMEA sentences that are sent by the GPS into ROS.
- um7 - this is the driver for the UM7 IMU.
- nmea_navsat_driver - this is used to
convert NMEA sentences into a sensor_msgs/NavSatFix message.
- imu_transformer - is used to translate the
IMU position into the base_link frame. My IMU is actually mounted "upside down" so this is super
important.
- imu_filter_madgwick - this is used to
track the pose of the IMU. Importantly it fuses the magnetometer information, allowing the IMU
to act like a compass for the global EKF.
- robot_localization - I
use two instances of the EKF filter. The first fuses the IMU with the wheel odometry in order to get a good
local odometry frame. The second fuses the IMU, wheel odometry and GPS (processed by the
navsat_transform_node) into a global odometry.
- rviz_satellite - not used on the robot,
but is an awesome plugin for RVIZ that can download
Global Localization
Setting up the global localization took me a little while to get working. In order to make this process
easier, I setup my
main launch file
so that I have an "offline_mode" argument which doesn't launch the drivers. Then I have a
launch file
for recording bagfiles running only the drivers. I can then change everything in my various pipelines
when re-running the bagfiles locally. This has been quite useful as I've been tweaking the IMU
processing pipeline in parallel with adding the global EKF.
Satellite Imagery in RVIZ
Visualization is always a powerful tool. While RVIZ doesn't have much going for outdoor robots out
of the box, the
rviz_satellite plugin makes it awesome.
rviz_satellite overlay with some odometry tracks
The one challenging part of rviz_satellite is setting the "Object URI". For an off-road robot, the
default OpenStreetMaps don't do much. I ended up using MapBox satellite imagery - but getting the
Object URI right took a bit of digging around. It turns out the correct URI is:
https://api.mapbox.com/styles/v1/mapbox/satellite-v9/tiles/256/{z}/{x}/{y}?access_token=XYZ
Also, free accounts with MapBox are limited to 200k tile requests per month. To avoid using these
up, you might want to think about running a separate roscore so you can keep RVIZ running even
when you restart the robot launch file. That said, I've only used 148 tile requests this month
and have been restarting RVIZ quite a bit.
Next Steps
I just recently got the global localization working - I'm probably going to
continue to tweak things. The D435 drivers are working pretty reliably now,
so the next step is mount the D435 on the robot and start integrating the
data and move onto some basic navigation. I also plan to clean up the IMU
calibration code I created and get it merged into
robot_calibration.