Open JamesNewton opened 3 years ago
Supporting a single OpenMV cam isn't too hard, given the above. However, supporting multiple cameras at the same time is a bit more difficult. The port on which the device appears is assigned by the OS and we have no control over it. There is no serial number for the device in the port information.
A great way to know which device is sending information is to assign each device an ID number. You can do this by programming it with a custom program, or by simply adding this to your program:
idno = "0"
try:
idno = open('/id.txt').read()
except:
print("[{\"error\":\"id not set in id.txt file\"}]")
And place a file named id.txt in the cameras flash drive with whatever id you like as the contents of the file. Then the ID numbers can easily be changed in the field.
Then the device can be asked to identify itself, or start off by sending it's ID over and over on power up until it's told to send something else.
But what if you don't want to send commands to the device, and just want it to return the device ID along with the data coming back? e.g. if you do blob detection or read a barcode, and you are already printing that object out as JSON to Dexter, wouldn't it be great to just add the ID number of the device into that string? Sadly, these are built in objects (blob, barcode, april_tag, etc...) and it is totally impossible to:
blobs[0][8] = ido
"TypeError: 'blob' object doesn't support item assignment"A way to "get 'er done" is to just unpack the data and print your own JSON:
x = blobs[0].x()
y = blobs[0].y()
w = blobs[0].w()
h = blobs[0].h()
pixels = blobs[0].pixels()
print("[{\"id\":"+str(idno)
+", \"x\":"+str(x)
+", \"y\":"+str(y)
+", \"w\":"+str(w)
+", \"h\":"+str(h)
+"}]")
It is possible to make your own object, and you can extract from the main object the items you want and place them in your object. However, this will print as e.g. <mutobj object at 30006920>
You must provide your own method for printing your object.
class mutobj(object):
#def __init__(self, ):
# setattr(self, a, v)
def __str__(self):
o = []
for a,v in self.__dict__.items():
o.append( "\""+a+"\":"+str(v) )
return "[{" + ", ".join(o) + "}]"
pass
....
x = blobs[0].x()
y = blobs[0].y()
w = blobs[0].w()
h = blobs[0].h()
b = mutobj()
b.idno = idno; b.x = x; b.y = y; b.w=w; b.h=h
print(b)
Wouldn't it be nice if the constructor for mutobj could take an object to copy, and iterate it's attributes, copying them into itself? Sadly,
.__dict__
object which appears to be how object attribute iteration is done in Python.Perhaps we could pass in an array of the attributes we want to the mutobj constructor? Nope.
So to get an attribute by NAME you would have to send an object with names and indexes. E.g. send me the [0] item and call it "X" e.g.
class mutobj(object):
def __init__(self, obj, attr):
for n, i in attr.items():
setattr(self,n,obj[i])
def __str__(self):
o = []
for a,v in self.__dict__.items():
o.append( "\""+a+"\":"+str(v) )
return "[{" + ", ".join(o) + "}]"
pass
blobattr = {"x":0,"y":1,"w":2,"h":3} # etc.. pick the things you want and what to call them.
...
b = mutobj(blobs[0], blobattr)
b.idno = idno
print(b)
which, frankly, is a stupid amount of work to jump through just to get an id back with the data. It probably takes less ram and fewer cycles to just edit the damn string. e.g.
print("[{\"idno\":"+str(idno)+", "+str(blobs)[2:])
and that will work with any of the built in objects. It only adds the id to the first returned blob, but the other methods had the same limitation.
To interpret the base64 data returned via the lines:
import ubinascii
...
shrink = 20 #desired compression. Use smaller values if out of memory
img_data = str(ubinascii.b2a_base64(img.compress(shrink)))[1:]
print("[{\"image_length\":"+str(img.size())+", \"data\":"+img_data+"}]")
in the above camera program when the "!" command is sent, use this javascript routine:
function b64toimg(str) {
return Buffer.from(str, 'base64').toString('binary')
}
but be sure to write the file out with the "ascii" encoding.
write_file("test.jpg", b64toimg(b64img), "ascii")
or it won't be a valid image.
Kamino cloned this issue to HaddingtonDynamics/OCADO
A few notes:
When connected to Dexter, it shows up as a
/dev/ttyACM#
where number starts a 0, then 1, etc... depending on the number of devices connected. SSH into Dexter, do anls /dev/tty*
with the device disconnected, then connect, do the command again, and notice the difference.Connect at 115200 N 8 1 This works to test connectivity:
enter Ctrl+C to stop, then
fg 1
and Ctrl+C again. For more on serial interface, see: https://github.com/HaddingtonDynamics/Dexter/wiki/Dexter-Serial-PeripheralsWhen connected, press Ctrl+C to stop any running program ( works nicely from PuTTY, but you can't do this from the cat trick shown above need to find a work around, probably
echo $\cc>/dev/ttyACM0
), Ctrl+D to reload / sync the files from the file folder, andimport main
to run the main.py file from the folder.To find and mount the flash drive to re-program the OpenMV cam, you can use
blkid
to show available block devices without the device connected, then connect it and use the command again. The new device is the camera. It will probably appear as/dev/sda1: SEC_TYPE="msdos" UUID="4621-0000" TYPE="vfat"
. To access the contents, make a folder:mkdir /mnt/openmvcam
then mount the device:sudo mount /dev/sda1 /mnt/openmvcam/
and the files will appear at/mnt/openmvcam
. Editmain.py
to change the cameras code. When finished, be sure toumount /mnt/openmvcam
Again, Ctrl+D causes the camera to reset and reload the file from the drive if you are connected via the serial terminal. Then you can type
import main
to run it. Ctrl+C to break it.The following program allows you to ask the Camera to look for a number of things. For a Job Engine program to interface with this see: https://github.com/HaddingtonDynamics/Dexter/issues/60#issuecomment-605376411