Thursday, 14 March 2019

A Complete Rebuild in Acrylic

It was always my plan to make my competition robot in transparent laser cut acrylic, so that the insides could be seen. Plus it just looks so nice! Having learned the art of designing and building a laser cut robot using 3 mm plywood I was now ready to re-cut the refined design in acrylic. The main modification I made was to shorten the legs by 15 mm. I had realised from driving my prototype that it really did not need such high ground clearance as the small wheels could not climb over anything but small obstacles. It was also a little unstable. I could get it to lift some of the wheels off the ground doing sharp turns at speed!

I added some engraving to various parts and also added holes for threading all the motor and servo wires through the legs. I had drilled these holes by hand on the wooden model. Now I knew where I wanted them I added them to the CAD model so these could be laser cut on the acrylic parts. I had already designed an acrylic mount for my main display and had this mounted on the wooden chassis.

With so many parts to cut I wanted to make optimum use of my acrylic sheet material. So I used an online SVG nesting tool to layout the parts. I prepared an SVG file of all the parts in Inkscape, including a containing rectangle required by the nesting tool to limit the area and shape to fit the parts into. I uploaded this to https://svgnest.com/ which generated a cutting layout for me to download.

An optimised parts layout for laser cutting

The complete set of laser cut acrylic parts ready to assemble

First I assembled the 4 steering legs. One snag I ran into is the inconsistency in thickness of acrylic sheets. A 3 mm sheet can vary in thickness between around 2.8 mm and 3.2 mm. This is due to the surface tension on the sheet when it is being cast, which results in a curved top surface dipping down towards the middle of the sheet. The sheet sizes you typically buy are cut from this larger cast sheet. Depending on which part of the cast sheet your piece was cut from you get different thicknesses across your piece. Some of my sheets exceeded 3 mm thickness towards one end, so some of my parts did not slot together as all my slots are cut at 3 mm wide. I had to re-cut a few parts with slightly wider slots. But I did not want to make all my slots wider by default, particularly the motor mounting plates which wobbled too much when the slots were not a tight fit. This an area which could do with some more refinement so that the rigidity of the motor mounts is not so dependent on tight tolerances. In the process of assembly I accidentally snapped one part in half and cracked another by over tightening the bolts holding the parts together. Acrylic is a much more brittle material than plywood and is easy to crack, especially around rectangular holes with sharp internal corners. I later modified some of my parts to increase the width of material around the outside edges of the slots from 3 mm to 4 mm to increase the strength of some parts. After re-cutting (plus a few spares) I had two completed Rocker Bogie arms.

The Rocker Bogie arms remade in acrylic with cable routing holes

As I assembled my robot using the new parts, I tried to extensively photo-document every step of the process. My plan is to write up a full build guide and possibly release a kit of parts for others to build their own rover from my design.

Some of the many photographs I took to document the assembly process

Another problem which came from switching to acrylic was that the plates did not grip each other like the plywood had. Some of my chassis body side plates were not staying together at the ends furthest from the bolts holding them in place. I had to add some additional T-slots and bolts to hold everything firmly together. I had designed a new front plate to mount a Pixy2 camera on, and this was not held on with anything more than the friction of the tabs in the slots. This would need more refinement for the competition as it was also going to be holding my Pi Noon attachment and I did not want that falling off mid-battle. For for now I could push it on and it looked good enough for me to shoot some photos. I set up my filming lights to provide some nice back lighting and took a few photos to try and capture the beauty of the acrylic design.

My final photo submitted for the Pi Wars 2019 program

Wednesday, 13 March 2019

Another Redesign: Electronics Cartridge

One problem I identified with my robot chassis design was that I could not access any of the electronics without taking the whole thing apart. It was not possible to remove the SD Card from the Raspberry Pi without unscrewing the Pi either. One option for the SD Card access which Brian Corteil suggested was to boot the Pi from a USB thumb drive instead. I had not realised this was possible, but some testing with various tiny sized USB memory sticks showed this worked very well (at least with the Pi 3b). But I was still concerned that if I had any wiring problems on competition day it would be difficult to access the internals to fix things.

The idea I had to solve this was to redesign my chassis so that all the electronics were mounted on a plug-in cartridge which could be pulled out of the chassis to easily access everything. This also allowed me to design a second tier to mount breakout boards on above the Raspberry Pi.

CAD model of my electronics cartridge

Having a 2 tier electronics tray posed some problems with how to route the ribbon cable which connects the touch screen display to the Pi. I went through a few iterations before building a prototype cartridge. If I routed the ribbon cable straight up through the upper tier then it limited the space to arrange the breakout boards on that tier. I ended up with a design where the ribbon cable was folded to change the orientation through 90 degrees from the Pi to the display. The fold was contained on the lower tier, which required the height of the lower tier to be higher than originally planned. Space was tight for the upper tier, and I did not take into account the height of the Du Pont connectors used to attach all the wires to the breakout boards. Another redesign of my entire chassis was required to allow another 5mm of head room. I modelled just one Du Pont connector with a wire bending over coming from it (you can see it in the CAD model above) to check I had the required headroom for the wiring.

Redesigned chassis with the electronics cartridge inserted

It all looked great in CAD, but when I came to assemble it I realised I needed a lot longer cables to provide enough slack to actually slide out the cartridge from the chassis. My ribbon cable was not long enough and 40 way ribbon cables seemed very expensive when I looked online at the usual electronics suppliers. Then I remembered somebody had pointed out that IDE hard disk cables in older PCs were just this type of cable. I checked my box of parts for modding PCs and found I had several. They all had 3 connectors, but I simply cut one off close to the middle connector to give a suitable length ribbon cable. All my other Du Pont connector leads I was making myself from a reel of ribbon cable, so I made these long enough to provide some slack.

The reality was that all this extra cable took up a lot of room, and the cartridge was very tightly packed when assembled. It was very hard to slide out due to the cables all catching on bolts, and in hindsight I could have done with another 10mm of head room in the chassis. But I can just about slide it out with a lot of cable wrangling if required to plug in a new lead. I later extended the i2c breakout to a second bus board near the front of the chassis so that I could easily plug and unplug additional ic2 devices mounted on the front of the robot without needing to remove the cartridge.

The assembled electronics cartridge with the multiple folds in the long ribbon cable. Most of the i2c device cables are missing here, apart from one connecting to the PWM breakout used to drive the servos and motor power. The 4 servo cables are also missing here.

Shown with the chassis front and front-side panels removed. The cables completely pack out the space in the cartridge.



Tuesday, 12 March 2019

Robot Software Design

I went for a modular design for my robot software. Python is very modular in structure, with each module importing the modules (or code libraries) it requires. I designed my robot software along the following lines.



Each module does a specific job, and is accessed through a few simple functions which describe the operation being performed. For example the Sensors module contains a function readDistance(sensor) which returns the distance measured by one of the TOF (time of flight) sensors. The parameter (sensor) specifies which sensor (by number) to read. The module also provides the required methods to configure and initialise the sensors, and the i2c multiplexer board they are connected to. So all the complexity of switching i2c buses, turning on sensors and reading them is wrapped up in a module with a very simple code interface.

Another advantage of a modular approach is that you can put all the code which is specific to a set of hardware (e.g. motor controllers) in a single module. The module then presents an interface with simple methods like setMotorSpeed(motor, speed). If you need to change the motor controllers used in your robot, or want to reuse the code on another robot with a similar arrangement of twin motors then you only need to replace this one module and the rest of the code can be reused without changes.

The hardware interface module is called from a module which represents the movement capabilities of the robot. In this case my robot has steerable wheels, and multiple motors wired up in two groups. So the movement control of the robot is wrapped up and controlled through a few simple functions:

  • setLeftMotorPower(speed)
  • setRightMotorPower(speed)
  • setSteering(angle)
  • setSpotTurn(angle)

The LED Matrices module handles setting up and connecting a pair of rgb 5x5 LED matrix breakouts, and displaying patterns on them. A series of patterns are stored in the module and can be called by name. There is also a frame buffer which allows series of patterns to be queued up to show on the LEDs one after the other. Methods can be added to queue up patterns by name. e.g. showBlueEyes() or eyesBlink(). Animations cycle through the frames in the buffer for each display when a nextFrame() method is called.

The main program module handles output onto the main display screen. It also contains all the event handler functions for controller input. A separate module handles detecting different game controllers and mapping their buttons, sticks, hats and triggers to the same named events. So a range of game controllers can be used on the robot and they will all call the same code when their 'triangle button' is pressed.

The main module also contains the code for reacting to touchscreen input to display and navigate through graphical menus, and the main program loop which takes actions based on the mode the robot is in. In this main loop, sensors reading calls can be made, there is the call to display the next frame on the LED matrices, and trigger calls to any event handlers for the connected game controller.

Developing all the code for menus, modes and linking controller input to the robot can be very time consuming when trying to test on the robot itself. But by having the modular structure we can create a virtual copy of our robot, or Digital Twin. By writing a mock copy of each module which interfaces to the actual hardware we can work on the code without the robot being present at all. I did a lot of the coding on a laptop using Raspbian x86 running in a VirtualBox VM. Coding anytime, anywhere. Only the laptop was needed to write, debug and test much of the code.



Just 3 modules (coloured blue above) needed mock versions to the written for the robot to be virtualised. Each of these mock modules has the same name and functions in it as the real version. But the code inside the functions captures the display object from pygame and renders a representation of itself onto the display.

Digital Twin: A virtual representation of the robot
(Background Mars image from Hubble Space Telescope. Credit: NASA)

The mock hardware interface module renders the shape of the robot, and shows the motor speeds and steering leg angles. The mock sensors module generates randomly varying distance readings and renders yellow triangles to indicate the sensor values. The rgb LED matrix mock module renders a representation of each LED matrix onto the screen. I also added an option to display numerical information to aid debugging and diagnosing problems. You can see the virtual robot running in this video clip I posted on Twitter:



The graphical representation of the digital twin of my robot and the sensor output was so useful for debugging issues that I moved it from the mock module into the main robot module. All the graphics slow down the main loop so that it does impair the responsiveness of the robot. So I made it possible to switch on and off via the controller. In normal running the display shows a static graphic of a solar panel, as you can see in this video clip.


(Post Pi Wars 2019 competition I published the full source code for my digital twin robot on github. You can play with my virtual robot using the code on the 'mock' branch. Full instructions and code here: https://github.com/Footleg/PiWars2019/tree/mock )

Monday, 11 March 2019

First Fully Drive-able Build of my Mars Rover

My first prototype body to mount my rocker bogie arms onto made me realise I needed to better balance the weight of my robot over the main pivot axis. I also needed better clearance between the body and the steering legs. Then there was the challenge of where to put the rest of the electronics and the battery. I decided the battery was the thing to provide the weight to counter balance the body, and being a heavier item should also be located closer to the ground to make the robot more stable. So I drafted a design where the rear of the body was tapered giving less width. Enough to accommodate the battery but narrow enough to give sufficient clearance for the rear steering legs.

Early redesign of main body with narrower rear end

In attempting to wire up all the motors, and with the steering servo cables also needing routing into the chassis body, my robot was suddenly looking rather messy. I had deliberately made my prototypes from 3mm ply so they could be modified easily to iterate on the design. I explored ways to route the motor wires by getting out the drill.

Iterative design: wire routing!

Somebody commented on one of my Twitter posts asking where I planned to mount the Pi Noon attachment required for two of the challenges. This was not something I had considered up to this point. The challenge rules stated the attachment point had to be on the front of the robot, in the middle. My robot design at this point had nothing but thin air in that position! I wondered about making some sort of brace which attached to the two front rocker arms to hold it. This would lock the rocker (but the event would have a flat surface so not a big issue). Then I realised it was also needed for the Spirit of Curiosity challenge and this was expected to have terrain of varying difficulty. This was the challenge my robot was built for, and my robot was based on the namesake Mars rover of this challenge. I needed another plan. Looking at pictures of the real Mars Curiosity rover I realised that the rocker arm linking bar was positioned forwards of the main pivot axis, rather than behind as on my rover. I swapped mine to the same (you can see this difference between the original animated CAD model and the actual plywood first prototype in my earlier post).

Redesigned chassis shape with battery compartment and room for breakout electronics

Looking at my remodelled chassis it suddenly struck me that the back looked awfully like a front now, and what I had always imagined as the front was looking much more like the back (complete with USB and ethernet 'exhaust' ports). This now gave me a front panel to attach the Pi Noon attachment to as well. I extended the new front end to bring it level with the end of the rocker arms, and so my final rover shape was decided.

The new completed chassis built and ready for some road testing

Checking the A4 footprint of my new design

Finally I had something I could road test. The closest thing I had to some martian terrain was a load of rocks and fossils collected on family trips to the Jurassic coast.


Overall I was very pleased with how well my robot handled. It was very easy to control. The grip on steeper slopes was not enough, but I think for the Pi Wars challenges it will be sufficient. It was a little top heavy and prone to toppling over on more challenging obstacles. Here are some of the out-takes.


Monday, 18 February 2019

Pi Wars Robot Electrical Design


The fundamental components I needed for my raspberry Pi based robot were a Raspberry Pi board, a servo driver board and dual motor controllers. On top of this there would be various sensors and a camera. I decided early on I wanted some sort of display and buttons to control a menu system on the display to set the various modes of the robot for different challenges. The display I chose was the HyperPixel 4.0 touch screen display which Pimoroni had just released. This would give me a beautiful full colour display and the touch screen would enable me to use the screen itself to select menu items.

The next decision was how to power the robot. I had learned from other people of the problems with using a power source for the Pi shared with the motors. If the motors draw too much power then the supply to the Pi can dip, causing reboots or crashes. One way to avoid these problems is to provide a completely separate power supplies to the Pi and to the motors/servos. But this is not necessary provided the power supply can provide enough power for all components at their full loads. I decided to use a high discharge current LiPo battery. I planned to use UBECs (Universal Battery Eliminator Circuit) to eliminate the need for different battery voltages. I could supply 5V to the Raspberry Pi using one UBEC, and 6V to my motors and servos using a second UBEC. I already had experience using the cheap Hobbywing 3A 6V/5V switch mode UBEC for robots. These are readily available online (try eBay). I tend to buy 10 at a time from China so I have a stock of them in my parts box ready for projects.

Early prototype wiring to test the servo and motor drivers, powered by a LiPo battery and two UBECs

For the motor drivers, you need to choose some which can supply the voltage your motors require and handle the maximum current which the motors can pull. My robot was using 6V rated micro metal gearmotors with 1:50 ratio gear boxes. According to the listing on the Pimoroni website these have a stall current of 770mA. Stall current is the current the motors will draw if they are fully powered and the shaft is prevented from turning. Typically this can happen if the robot is trying to drive up too steep a slope or into an obstacle which prevents it moving, so all motors could draw this current at the same time. My design had them wired up as 2 pairs of 3 motors in parallel, so I needed a 6V supply to the motors and a maximum stall current of 3 x 770mA = 2.3A. I already had a pair of Adafruit DRV8871 motor driver breakout boards, capable of supplying up to 3.6A per board. The UBEC circuit can supply a steady 3A at 6V so I would need a UBEC per motor driver. I built a test circuit for a single motor and found it did not work. Reading the specifications of the motor driver board again I realised if needed a minimum of 6.5V input voltage. So my UBEC regulating the voltage down to 6V would not work.

Some discussions on Twitter led me to the decision to drive the motors directly off the battery voltage. This could be as high as 8.2V when fully charged, but seeing as the software would limit the motor power using PWM anyway I could set a limit to prevent the motors getting the full power of the battery. They would still be getting peaks of more than 6V during the on-cycle of the PWM signal, but the motors are not such sensitive components that this should be a problem, and I had been advised that these motors could be over-driven safely at these voltages. At a higher voltage I would have more speed, more torque but also a higher stall current. But I would not need a UBEC per motor driver now. The driver boards I had can supply up to 45V to motors, and up to 3.6A so this should work fine.

Next I looked up the HyperPixel touch screen board on the excellent pinout.xyz website to find out which GPIO pins it used on the Raspberry Pi. The answer turned out to be all of them! But it did break out the software i2c bus so I still had the option to control i2c hardware from a Pi using this screen. I was already planning to drive the servos using an Adafruit 16 channel 12 bit PWM servo driver board which is an i2c device, so that should work. But I had no GPIO outputs available on the Pi to drive the motor driver boards. I discussed some options with Brian Corteil of Coretec Robotics at one of the monthly robot club sessions he organises at Cambridge Makespace. These included the following:
  • Use an Arduino to provide some additional PWM outputs
  • Use a second Raspberry Pi to connect sensors and drive the motors
  • Use an i2c GPIO extender breakout board
Then it occurred to me that what I needed to drive my motors were digital PWM outputs, and this is exactly what the 16 channel PWM board I was planning to use to drive my steering servos had in abundance. I built a test circuit on a breadboard to experiment with this. I made a mistake thinking I would drive the servos at 6V, because while the motor driver boards can drive motors at up to 45V, the logic inputs to the board are only rated up to 5.5V. My 6V PWM output damaged one of the boards and I had to replace it. My steering servos were plenty fast enough and strong enough using a 5V supply, so I switched the UBEC supplying power to the servos back to 5V.

The last piece of the puzzle was how to drive multiple laser time of flight distance sensors over i2c when the boards I had chosen (the VL53L1X breakout by Pimoroni) had a fixed address. I read it is possible to switch this address on power up, but this presumably required powering up each sensor board in turn, and that would require more digital outputs which I did not have. An easier solution was to use an i2c board which provides switchable i2c buses, and I chose the TCA9548A I2C MULTIPLEXER from Adafruit.

To connect up all my i2c devices, and supply power to the Raspberry Pi, I made a small power and i2c bus breakout board using strip-board. I built this with 5 pin connectors to match the Adafruit PWM board and Pimoroni range of i2c breakouts which all have 5 pin headers including an interrupt line.

i2c and power bus board

The UBEC supplying power to the Pi and screen was connected to this, and power supplied over the i2c V+ and GND lines to all components. You can see the complete wiring diagram below.


The electrical design for my robot (click to see larger version)

In order to mount the display on top of the robot, I soldered up a right angle GPIO connector using a Pimoroni Pico HAT Hacker board which enabled me to connect the display to the Raspberry Pi using a ribbon cable. I soldered a second 4 wire ribbon cable onto this connector to supply power to both the Pi and the display, and to connect the software i2c bus to my breakout bus board. I was relieved when I connected this all up and saw it working as intended.

Working display and Pi powered via the i2c bus and power breakout board

The next challenge was going to be how to fit all this electrical hardware and connecting wiring into my robot chassis.

Saturday, 16 February 2019

Designing the Main Chassis and Complete Rocker Bogie Assembly

Having built a pair of working prototype rocker bogie wheel assemblies, I turned my attention to the main body of the rover. Initially I just used a simple box shape, keeping the front of the chassis well back from the front steerable wheels to avoid collisions. The rear of the chassis had to overlap the area where the rear steerable wheels servo links came under the body in order to fit the top pivot for the linking bar assembly. But I now had a basic chassis design which enabled me to complete my CAD model and see a working mechanical model of the full rocker bogie suspension.

A rendered animation of the rocker bogie suspension

To avoid the rear wheel steering links colliding with the underside of the body, I modified the box so that the bottom was shorter than the total length of the body. This gave the servo steering links more clearance where they passed under the body. I made the front section of the body just long enough to fit in the Raspberry Pi 3 plus two additional support pillars to provide a second pair of holes for the main pivot bolts to pass through. This gave a more rigid main pivot axis assembly than if the bolts just passed through one piece of 3mm sheet material on each side.


I mounted the Raspberry Pi 3 board in the box, with the USB and Ethernet ports poking through a cut out. This was technically the first Raspberry Pi case I had ever designed. The rear of the box still needed to extend far enough back to support the linking bar assembly on the top. To allow this, the base of the box stepped up to a lesser depth at the rear.

The body design with a step up on the underside at the rear 
to allow the steering links to pass underneath the chassis

At this stage I was still pondering over how to build the linking bar assembly. I had modelled a bracket to hold the horizontal linking bar, which I was going to have to 3D print. I also needed to solve how to attach fittings to the ends of the cylindrical bar to link to the rocker arms. At about this time, somebody posted a picture of the NASA Mars Curiosity rover in a lab in reply to one of my progress posts on Twitter.

 Image Credit: NASA/JPL-Caltech

Up to this point I had not realised how large the real Mars Curiosity rover was! But I also noticed the linking bar was a flat bar, not a metal rod as I had modelled based on the JPL educational scale model project which inspired me to design a Pi Wars sized version of my own. That looked a lot easier to build. The picture also gave me another revelation. I did not need to mount the bar on the back of my rover. It could go in front of the main pivot axis of the body. This gave me more room as the body already extended further out in that direction to accommodate the Raspberry Pi. It also better distributed the weight of the body between the horizontal pivot axis and the top mounting point for the linking bar.

I now had a complete design worth building a prototype of. I laser cut it all from 3mm plywood because this was cheaper than plastic, easier to modify with small tools after cutting the pieces and I could re-purpose all the off-cuts and waste pieces as kindling to light the fire at home.

My first prototype, almost fully assembled (one rocker bogie arm is attached here)

This first mechanical prototype enabled me to verify that the design worked in practice. It also allowed me to test how much motion was possible in the rocker bogie arms before the rear steering legs collided with the underside of the chassis. I was not sure there was enough range of movement, and it also made me realise the weight was not well balanced around the main pivot axis of the chassis. I needed to think about how to solve these problems, and also work out how to fit all the electronics and a power source into the body.

Wednesday, 13 February 2019

Refining My Rocker Bogie Design

Using my newly learned skills in CAD and laser cutting I started to build the rocker arms for my robot. I was pleased with the way my servos fitted into the arm, but concerned that using the servo itself as the only mounting point for the steering leg was not going to be strong enough. That tiny screw attaching the horn to the servo just did not feel like it would handle all the strain put on the robot leg.

My first prototype steering leg assembly

I had only attached one leg to the first part of an arm and already I was needing to redesign. Some browsing of possible bolt styles I might use online led me to shoulder bolts. These have a solid cylindrical section (the shoulder) with a short threaded section on the end. I redesigned my leg and arm ends to attach the leg using a long shoulder bolt as the axis. A nylon spacer provided the required separation of the leg from the arm. Moving the servo along enabled me to attach the servo horn to the leg via a hinged link. The shoulder bolts were also perfect for the other pivots in the rocker bogie arms. So with the entire arm remodelled in my CAD software I was ready to assemble a complete version.



My first attempt to put it all together in laser cut 3mm plywood proved that the design would work, but I could not fit the nuts onto the small bolts which held the servo mounting plates in place. I had not left enough room for the nuts to fit alongside the spacers on the pivot bolts. It was at this point that I discovered the collision detection feature of the CAD software and sure enough it highlighted the problem in my CAD model. Lesson learned I made some more adjustments and was finally able to build and assemble a complete pair of rocker bogie arms.



Monday, 21 January 2019

CAD to Laser Cutter and Kerf

The same week I learned I had got into Pi Wars 2019 I also managed to grab a place on a training session for the laser cutters at MakeSpace. These were the machines I had primarily wanted to use to make my robot when I joined MakeSpace and now I had done the induction I just needed to work out how to convert my CAD model into a file format I could use on the laser cutter. Or so I thought.

The first problem was how to turn a 3D model of a part into a 2D outline for cutting. I was using SolidWorks CAD software and this has a drawings feature where you can generate 2D engineering drawings for different views of the parts and assemblies you have modelled. These drawings automatically update based on any changes you make to the modelled parts, which is very handy. But the file format for SolidWorks drawings is not supported by the software which the laser cutter uses. So the drawings had to be saved in a intermediate format which can be imported into the laser cutter software. Initially I was doing this directly as DXF format. A simple 'Save As' from SolidWorks to create a file I could import into a laser cutter project.

I cut the parts of one of the forward arms of my rocker bogie suspension which needed to hold a servo, and the parts assembled around the servo perfectly. It was all going very well.

 Cutting my first parts on the laser cutter

 My assembled servo mount

The servo fitting snugly in the mount

Next I cut the parts for a steering leg, and while they went together without any difficulty, there was a lot of play in the plate holding the motor. I started to think about the tolerance of the parts I was cutting and I soon had a lot of questions which I needed to learn the answers to. I had modelled my parts in CAD software to the exact dimensions I wanted them. But clearly when you cut them out with any sort of tool some material is removed by the cutter (in my case vapourised by a laser, but the same applies to a saw where some material is removed by the blade). Thanks to the maker community on Twitter I learned that the width of the cut produced by a tool is called the 'kerf'. Once I knew that I was able to search online and learn about offsetting (adjusting for the kerf). If I modelled a part to be 30mm wide then the drawing output from my CAD program defined a part which was 30mm wide. But when I cut this on the laser cutter the resulting part was only around 29.8mm wide due to the kerf. This was not so significant for how the part looked, but my tab and slot fittings were just a little loose fitting due to this.

There appeared to be 3 approaches people suggested:
  1. Specify an offset in the CAD program.
  2. Offset my drawings in a 2D drawing program after exporting them from the CAD drawings.
  3. Specify offsets in the laser cutter software.
I also realised that I needed to consider the sizes of holes for my various bolts and screws. I had used a 3mm hole size for an M3 bolt in the CAD software. But if I wanted a freely rotating M3 bolt without excessive wobble then 3mm was not quite right. The hole needs to be slightly larger than 3mm, but the kerf will mean a 3mm circle will be cut slightly larger. So what size circle does the laser cutter need to be given to get a hole just the right amount larger than 3mm for the bolt to fit freely but not too loosely? I also discovered that the offset features in some CAD software were not present in SolidWorks. I think this may be because while a hobbyist might be happy to apply offsets to their drawings where they are cutting parts themselves, a professional using a 3rd party to do their cutting does not know the kerf settings of the cutter to be used, so they leave it to the cutter operator to apply.

So for me there was no option 1. At the time I did not know whether option 3 was possible in the software on the MakeSpace laser cutters either. So I went for option 2, applying my offsets in a 2D drawing program. Various articles online pointed to using an offset feature in Adobe Illustrator. But I did not want to buy an expensive 2D drawing program when free alternatives were available. My go-to free 2D drawing program is Inkscape. This has a feature on the Path menu to Outset or Inset a shape. But this appears to apply a hard coded offset amount and distorted the shape of my parts adding curves to angled corners.
A part with lines outset/inset using Inkscape
(original outline in black, outset/inset lines shown in red)
(the largest circle on the right side is 6mm diameter)

Inkscape also has a feature called Dynamic Offsets where if you select just a single outline and then choose the Dynamic Offset option on the path menu (or Ctrl+J) then you get a drag handle and can adjust the line offset by hand. By duplicating each line I could manually offset it by approximately the line width (setting the line widths to the amount I wanted to offset them. There was still a little distortion of the shapes, but these were insignificant for the amount of offset I needed.
A part with dynamic offsets applied by hand in Inkscape
(original outline in black, offset lines shown in red)

This enabled me to explore different amounts of offset and cut some parts to see what amount of adjustment gave me the right fit. But having to adjust every shape by hand dynamically was time consuming. It also made me realise that I needed different offset settings for different aspects of my parts. My 6mm shoulder bolts used in various pivots between parts needed a different amount of adjustment of the hole size than the machine screws holding parts in fixed positions, which was different to the slots cut to take tabs on the edges of other parts.

I think perhaps option 3 (specify offsets in the laser cutter software) is preferable in the long run, but would require me to adjust all my hole sizes to include some aspect of hole size adjustment. (If I want a 6mm shoulder bolt to rotate freely then I want a slightly larger hole size than 6mm. I should model that hole size in CAD so that when I apply an offset for kerf in the laser cutting software the resulting hole is cut to exactly the size I modelled in the CAD software). At this point I asked a friend at work how SolidWorks recommend doing this, and he pointed me to the free version of DraftSight, as 2D drawing program from Dassault Systemes (who also own SolidWorks). Draftsight features an offset feature which allows you to specify the offset amount numerically. So I developed a workflow to manually apply offsets of different amounts to my various drawing parts using DraftSight.

I discovered that importing DXF files into DraftSight resulted in curves being converted into short sections of straight lines. So my nice rounded edges became jagged. This was avoided by exporting the drawings from SolidWorks as DWG format files. These can be read by DraftSight without converting the curves. I then had to weld all the line segments back together into single outlines for each part in DraftSight. Then I could apply a single offset to the entire outline of each shape (rather than to each line segment individually). Finally I exported the drawing in DXF format to take the file into Inkscape.

At this stage all my shapes have two lines for each feature of a part. The actual CAD drawing outline and the offset outline I want the laser to cut along. In Inkscape I coloured these cutting lines red, leaving the original lines black. That way I could easily verify which offset I have applied and that it is on the correct side (outside for the outlines of parts, and inside for cut-outs/holes in the part). The final step was to save the coloured drawing with offset lines in DXF format and import this into the laser cutter software, telling it to not cut the black lines, and applying cutting settings to the red lines. I was also able to add text to engrave into the material in Inkscape in a 3rd colour if needed and apply settings in the laser cutting software to engrave the shapes outlined by lines in that colour.

At last I was able to cut out parts on the laser cutter which were exactly what I had designed in my CAD model, and start to build my robot.

A set of parts for a steering leg

An assembled steering leg with details of 
the kerf offsets used engraved onto the parts