Simulated GoPiGo For Remote Code Development

While I don’t need a GoPiGo3 simulator for remote code development, someone mentioned remote code development and got me thinking about it.

I put together the trappings of a simulated GoPiGo3 that allows testing a basic robot with a distance sensor:

Where a program normally has:

import easygopigo3

the developer can add:

# Uncomment import of sim.easygopigo3 or easygopigo3 as appropriate
import sim.easygopigo3 as easygopigo3
# import easygopigo3

run the program, and see any print statements as in:

$ ./myRobot.py

Log: Starting myRobot.py main()

Log: Instantiate EasyGoPiGo3 object

Log: Instantiate distance sensor object

Log: set_robot_constants(66.4)

Log: save_robot_constants()

Log: load_robot_constants()

Log: volt() returns 9.5v

Log: set_speed(150)

Log: get_speed() returns 150 DPS

Log: reset_speed()

Log: get_speed() after reset returns 300 DPS

Log: forward() for 2 seconds

Log: stop()

Log: egpg.ds.read_mm() returned 914.4 mm

Log: drive_cm(10.16)

Log: egpg.ds.read_mm() returned 814.4 mm

Log: egpg.ds.read_inches() returned 31.9 inches

Log: drive_inches(-4.0)

Log: egpg.ds.read_inches() returned 35.8 inches

Log: End myRobot.py main()

and the sim.log contains:

more sim.log
2020-11-22 23:30|[gopigo3.py.<module>]sim.gopigo3 imported
2020-11-22 23:30|[easygopigo3.py.<module>]sim.easygopigo3 imported
2020-11-22 23:30|[gopigo3.py.__init__]Simulated GoPiGo3 object instantiated
2020-11-22 23:30|[easygopigo3.py.load_robot_constants]sim.easygopigo3.load_robot_constants() called
2020-11-22 23:30|[easygopigo3.py.set_robot_constants]sim.easygopigo3.set_robot_constants(66.6,116.6) called
2020-11-22 23:30|[easygopigo3.py.set_speed]sim.easygopigo3.set_speed(300) called
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_limit_left' = 300 updated **
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_limit_right' = 300 updated **
2020-11-22 23:30|[easygopigo3.py.__init__]Simulated EasyGoPiGo3 instatiated
2020-11-22 23:30|[easygopigo3.py.init_distance_sensor]sim.easygopigo3.init_distance_sensor(port=I2C) called
2020-11-22 23:30|[easy_distance_sensor.py.__init__]sim.di_sensors.EasyDistanceSensor object instantiated
2020-11-22 23:30|[distance_sensor.py.__init__]Initializing sim.di_sensor.distance_sensor.DistanceSensor() object
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'rangeSensor_mm' = 914.4 updated **
2020-11-22 23:30|[easygopigo3.py.set_robot_constants]sim.easygopigo3.set_robot_constants(66.6,116.6) called
2020-11-22 23:30|[easygopigo3.py.save_robot_constants]sim.easygopigo3.save_robot_constants() called
2020-11-22 23:30|[easygopigo3.py.load_robot_constants]sim.easygopigo3.load_robot_constants() called
2020-11-22 23:30|[easygopigo3.py.set_robot_constants]sim.easygopigo3.set_robot_constants(66.6,116.6) called
2020-11-22 23:30|[easygopigo3.py.set_speed]sim.easygopigo3.set_speed(150) called
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_limit_left' = 150 updated **
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_limit_right' = 150 updated **
2020-11-22 23:30|[easygopigo3.py.get_speed]sim.easygopigo3.get_speed() called
2020-11-22 23:30|[easygopigo3.py.reset_speed]sim.easygopigo3.reset_speed() called
2020-11-22 23:30|[easygopigo3.py.set_speed]sim.easygopigo3.set_speed(300) called
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_limit_left' = 300 updated **
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_limit_right' = 300 updated **
2020-11-22 23:30|[easygopigo3.py.get_speed]sim.easygopigo3.get_speed() called
2020-11-22 23:30|[easygopigo3.py.forward]sim.easygopigo3.forward() called
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_dps_left' = 1000 updated **
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_dps_right' = 1000 updated **
2020-11-22 23:30|[easygopigo3.py.stop]sim.easygopigo3.stop() called
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_dps_left' = 0 updated **
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'motor_dps_right' = 0 updated **
2020-11-22 23:30|[easygopigo3.py.drive_cm]sim.easygopigo3.drive_cm(10) called
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'rangeSensor_mm' = 814.4 updated **
2020-11-22 23:30|[easygopigo3.py.drive_inches]sim.easygopigo3.drive_cm(-4.0) called
2020-11-22 23:30|[easygopigo3.py.drive_cm]sim.easygopigo3.drive_cm(-10.16) called
2020-11-22 23:30|[simDataJson.py.saveData]** simData 'rangeSensor_mm' = 916.0 updated **

This is the test program:

#!/usr/bin/env python3


# Uncomment import of sim_easygopigo3 or easygopigo3 as appropriate
import sim.easygopigo3 as easygopigo3
# import easygopigo3
from time import sleep
import traceback

def testEachMethod(egpg):

    print("Log: set_robot_constants(66.4)")
    egpg.set_robot_constants(66.6, 116.6)

    print("Log: save_robot_constants()")
    egpg.save_robot_constants()

    print("Log: load_robot_constants()")
    egpg.load_robot_constants()

    print("Log: volt() returns {}v".format(egpg.volt()))

    print("Log: set_speed(150)")
    egpg.set_speed(150)

    print("Log: get_speed() returns {} DPS".format(egpg.get_speed()))

    print("Log: reset_speed()")
    egpg.reset_speed()
    print("Log: get_speed() after reset returns {} DPS".format(egpg.get_speed()))

    print("Log: forward() for 2 seconds")
    egpg.forward()

    sleep(2)

    print("Log: stop()")
    egpg.stop()

    print("Log: egpg.ds.read_mm() returned {} mm".format(egpg.ds.read_mm()))

    print("Log: drive_cm(10.16)")
    egpg.drive_cm(10)

    print("Log: egpg.ds.read_mm() returned {} mm".format(egpg.ds.read_mm()))
    print("Log: egpg.ds.read_inches() returned {} inches".format(egpg.ds.read_inches()))

    print("Log: drive_inches(-4.0)")
    egpg.drive_inches(-4.0)

    print("Log: egpg.ds.read_inches() returned {} inches".format(egpg.ds.read_inches()))


def main():

    print("Log: Starting myRobot.py main()")

    try:
        print("Log: Instantiate EasyGoPiGo3 object")
        egpg = easygopigo3.EasyGoPiGo3(use_mutex=True)
        print("Log: Instantiate distance sensor object")
        egpg.ds = egpg.init_distance_sensor()
    except Exception as e:
        print("Log: {}".format(str(e)))
        exit(1)

    try:
        testEachMethod(egpg)
    except Exception as e:
        print("Log: {}".format(str(e)))
        traceback.print_exc()
        exit(1)

    print("Log: End myRobot.py main()")



if __name__ == "__main__":
    main()

The simulator lives in a folder named ‘sim’ along side the user program to test:

$ls -1
myRobot.py
sim
sim.log

Also there is a program to list the GoPiGo3 simulated data content (in sim/simData.json):


$ sim/listSimData.py

vBatt:9.5
motor_limit_left:300
motor_limit_right:300
motor_dps_left:0
motor_dps_right:0
rangeSensor_mm:916.0

Note: it isn’t simulating movement over time - would have to launch a subprocess for that and I didn’t really want to get that tricky.

Just an idea if someone wanted to flesh out a simulator for remote code testing prior to getting access to a bot.

It is possible - would take a bit of work.

To get it download the gzipped tar file SimGoPiGo3.tgz

1 Like

Probably not what you’re looking for, but @brjapon has published a couple of tutorials on using the GoPiGo3 simulated in Gazebo. You don’t even need to buy his book:
http://therobotacademy.com/512_Getting-started-virtual-GoPiGo3/#more .
/K

2 Likes

Pretty cool that it has simulation of the camera even (along with the distance sensor, imu, and lidar). It doesn’t say that the wheel encoders are simulated.

The most enviable feature of ROS (for me) is the fusion of sensor data to create position and orientation estimates, but alas the most beneficial features of ROS need the power of an off-board processor.

1 Like

I haven’t looked to see if the wheel encoders are simulated. I’ll try to remember to check next time I fire up that laptop. And yeah - the Raspberry Pi doesn’t have quite enough umph to run a full ROS navigation stack by itself.
/K

2 Likes

What about the Pi-4?

There’s an 8gig version now and it’s relatively simple to enable 64bit kernel support.

Great question. I’m still just trying to learn the navigation stack as it is. It would be worth trying. And once there’s a Pi-5 it might be totally do-able :wink:
/K

1 Like

Oh, Pi 5’s are for WIMPS!
Stack a couple of Jetson Xavier boards with a smokin’ hot SSD!

Seriously now, I think the Pi is seriously under-utilised as it is due to the Foundation’s policy of total backwards compatibility. With a low latency 64bit kernel and optimised utilities, the Pi itself would be a Hot Smokin’ Weapon!

Somewhat off topic:
There’s a new drop-in replacement kernel for Debian based systems named “Licorice” (spelled slightly differently) that has been highly optimized to favor latency at the expense of both power and raw throughput. Many of the tests, and much anecdotal evidence suggest that on less-than-bleeding-edge systems there is a significant performance boost.

I have a couple of older lapbook-style systems that I plan to drop Mint Mate on and substitute the low latency kernel for the stock one.

It might be Really Interesting if it could be cross-compiled to the Pi-4. (or 3). Particularly since 99.999990% of the Pi use cases don’t need a LAMP stack and sub nanosecond database access times to petabyte sized databases.

Supposedly this kernel is optimized for things that suffer if latency sucks, like video processing or near-real-time processes like robotics, it should be ideal.

P.S.
Apple has been optimizing for latency since day one, and they have Eaten Everyone’s Lunch™ for near-real-time processes since then.

1 Like

Just checked on this environment and fixed getting a mutex.

=== To Get It On Your Remote Dev Environment ===


wget https://github.com/slowrunner/Deskpi/raw/master/Projects/SimGoPiGo3/SimGoPiGo3.tgz

tar -xzvf SimGoPiGo3.tgz

And if that one looks too complicated here is a very basic approach:

SIMULATED GoPiGo3

To get files

wget https://github.com/slowrunner/Deskpi/raw/master/SimGoPiGo3/SimulatedGoPiGo3.tgz
tar -xzvf SimulatedGoPiGo3.tgz

=== Test ===

./test_sim.py

2 Likes

And?

Having downloaded this and run the tests, what’s next?

Have you tried this on anything other than a shell on your Mac? Or is it supposed to run on the 'bot’s Pi?

What about that “other” operating system that I spend 99% of my time using? (I have a VMware virtual machine running on my PC so that I don’t have to reboot any time I need a Linux feature or utility.)

For just command line/ssh type stuff I use Windows Subsystem for Linux (WSL) running Ubuntu. Runs really well and avoids the overhead of a full VM. Supposedly WSL will support GUI windows in the future, so might be able to use “ssh -X” to run a Linux GUI app remotely.

/K

1 Like

Possibly true, but there are things that I can do more easily with a GUI.

@cyclicalobsessive

Having installed either one, how do I use it?

How similar is it to a real GoPiGp? Will it run a web server? What O/S is it running? Can it be changed?

What about research into multiple boot configurations? Do I have access to the config.txt?