Raspberry pi camera servers

From Rsewiki

This is notes from installation multi-raspberry pi camera (3 or 5 raspberry pi, each with a camera). The camera images is then collected in one single camera server.

This configuration guide is for the raspberry pi cameras and for the single camera server.

Prerequisites

It is assumed that mobotware is compiled for (or on) raspberry, and installed to /usr/local/smr, e.g. from the mobotware directory like this:

cd mobotware
make
sudo make install

Make takes a long time (one hour or so).

The make install installs the needed files into /usr/local/smr.local and links to this from /usr/local/smr. But do not put anything into /etc/rc.local.


Files needed

In mobotware a configuration example is in mobotware/build/config/gogo and mobotware/build/config/gogo/picam.

ls mobotware/build/config/gogo
  picam  rhdconfig.xml  ucamserver.ini
ls mobotware/build/config/gogo/picam
  aukeeper.ini  connectrhd.rule  keepcam.rule  keeplaser.rule  keepnav.rule  rc.local  startKeeper  ucamserver.ini

In mobotware/build/config/gogo/picam is the configuration files for the raspberry pi camera, the mobotware/build/config/gogo/ucamserver.ini is the configuration file for the single image collecting camera server (mostly for image recording).

Raspberry pi camera

The raspberry pi cameras in this example is named rpi6, rpi7, ... rpi10 as their hostname (in /etc/hostname).

Keeper

The keeper is a server that starts the cameraserver when needed (and restarts it if it crashes or gets stuck in other ways).

The Keeper should be started at reboot using the /etc/rc.local, like this line almostat the end of rc.local:

# rc.local
...
# start keeper
su -c "/home/local/startKeeper" local
#
exit 0

This starts a script placed in /home/local named startKeeper as the user local. This is to run as much as possible without root privileges.

Copy the start script to /home/local (beeing the user local:

cd
cp mobotware/build/config/gogo/picam/startKeeper .
chmod +x startKeeper

Make a live directory where the configuration files for keeper and camera server resides, and copy the ini-files:

mkdir live
cd live
cp mobotware/build/config/gogo/picam/*.ini .

Theaukeeper.ini file looks something like this

# open server - port and path
server port=24926
server imagepath="/var/log/keeper"
server datapath="/var/log/keeper"
module load=var
# cam
module load="if" alias=cam
cam connect=localhost:24920
camonconnect cmd="cam push t=3 cmd='var allcopy'"
var global.requiredclients=1
var global.keep.cam=1
var global.caminifile="/home/local/live/ucamserver.ini"
# start rules
module load="aurule.so.0"
rule log=false
rule load="/usr/local/smr/bin/aursconf/keepcam.rule"
rule resume

The keeper uses a rule to monitor and restart the cameraserver, this is assumed to be placed in /usr/local/smr/bin/aursconf, so copy the rules there (they may be there already, so probably not needed)

sudo cp *.rule /usr/local/smr/bin/aursconf/

The rule used for the camera server looks like this:

<?xml version="1.0" ?>
<rule name="keepcam" if="defined('cam.connected')">
 <description>
   * This rule is trying to keep cam server running, when the
   number of clients is more than required in global.requiredclients and
   the variable global.keep.cam is true.
   * This rule starts when the cam interface module is available (and
   has created the variable cam.connected).
   * When number of clients falls below the number of required, then
   the server is closed (if it was started by the keeper).
   * If the cam server is running already then the keeper will monitor the running
   instance and restart it if it crashes. The server is crashed if no "alive" signal
   is received within 3 seconds (value in alive rule below)
 </description>
 <init>
   # define global status variables
   global.alive.cam = false
   global.alive.camStarted = false
   allow = 7  # allow time is allowed time to be dead
   if (not defined('global.keep.cam'))
     global.keep.cam=false
   deadCnt = allow + 1
   <rule name="alive" if="cam.connected[1] and
                         cam.hostalive and
                         not global.alive.cam">
     global.alive.cam = true
     print("cam is alive after " deadCnt " secs to start")
     deadCnt = 0
     allow = 3  # seen alive to dead detect
   </rule>
   <rule name="deadCnt" if="not cam.connected[1] or not cam.hostalive">
     # if (deadCnt < 0.51)
     #   print("cam seems to be dying " deadCnt " : cam.connected=" cam.connected[1] ", cam.alive=" cam.hostalive)
     deadCnt = deadCnt + rulestate.sampletime
   </rule>
   <rule name="noclient" if="cam.connected[1] and
                             global.requiredclients > core.clients and
                             global.alive.camStarted">
     global.alive.camStarted = false
     print("Closing cam server - as there is no clients left on port " core.port)
     core.send("cam quit")
   </rule>
   <rule name="dead" if="deadCnt >= allow and global.alive.cam">
     print("cam assumed dead after " deadCnt " secs with no responce")
     print(" - cam status: cam.connected=" cam.connected[1] ", cam.alive=" cam.hostalive)
     global.alive.cam = false
     global.alive.camStarted = false
   </rule>
   <rule name="start" if="core.clients >= global.requiredclients and
                         not global.alive.cam and
                         not global.alive.camStarted and
                         global.keep.cam">
     <init>
       inistr=" "
       startstr=" "
     </init>
     deadCnt = 0
     if (defined("global.caminifile"))
       inistr = "--script " global.caminifile
     global.alive.camStarted = true
     allow = 30 # allow for start-up time
     # startstr = 'su -c "nice --adjustment=5 /usr/local/smr/bin/startserver ucamserver ' inistr '" local' 
     startstr = 'nice --adjustment=5 /usr/local/smr/bin/startserver ucamserver ' inistr
     core.send("bash " startstr)
   </rule>
 </init>
 # keep monitoring rule active
 wait() : false
</rule>

It is a XML formatted rule script, as defined for the aurule.so.0 plugin.

Camera server on Raspberry camera

In the live directory is the camera configuration file, it looks something like this:

local@rpi7:~/mobotware/build/config/gogo/picam $ cat ucamserver.ini
#!/bin/bash
# camera server scriptfile
server imagepath="."
server datapath="."
server replayPath="foo"
# server port=24920
#
module load=var
module load=odopose
module load=aurule.so.0
module load=aupoly.so.0
# camera parameters
camset device=6 focallength=532 posx=0.45 posy=0.03  posz=0.65 rotphi=0 rotkappa=-1.57  # east -90 deg
camset device=7 focallength=532 posx=0.45 posy=0.00  posz=0.65 rotphi=0 rotkappa=0
camset device=8 focallength=532 posX=0.45 posY=-0.03 posZ=0.65 rotPhi=0 rotkappa=-0.785
camset device=9 focallength=532 posX=0.45 posY=-0.03 posZ=0.65 rotPhi=0 rotkappa=0.785
camset device=10 focallength=532 posX=0.45 posY=-0.03 posZ=0.65 rotPhi=0 rotkappa=1.57
# raspberry pi camera
module load=aupicam.so.0
# use the one fit for this pi-cam
# will then use the corresponding image-pool number
# then much easier when collecting images
var picam.camDeviceNum=6
; var picam.camDeviceNum=7
; var picam.camDeviceNum=8
; var picam.camDeviceNum=9
; var picam.camDeviceNum=10
var picam.framerate=2
var picam.resolution="768 1024"
var picam.white=7
var picam.exposuremode=1
var picam.meter=0
var picam.pyramidlevels=3
picam open
# end of script


The camset commands configure the raspberry camera with different numbers, this is to ease the transfer of images to the combining camera server.

Near the end is the camera number for this raspberry camera defined - the others are commented out.

NB! it seems like a frame rate of 1 (var picam.framerate=1) will not work, so keep it at 2 or higher.

common camera server

A camera server to combine all the images may be needed - at least for logging of images and odometry.

The camera server configuration file could look like this:

local@rpi7:~/mobotware/build/config/gogo $ cat ucamserver.ini 
 #!/bin/bash
 server imagepath="."
 server datapath="."
 server replayPath="log_cam"
 server port=24920
 #
 module load=var
 #
 module load=odoPose
 odopose log
 module load=mappose
 mappose log
 module load=utmpose
 utmpose log
 ;mapPose replay
 module load=ucamif.so.0
 camdata add=img
 camdata add=gmk
 camdata add=path
 camdata add=cam
 ;
 # autostart cameraserver on raspberry pi cameras
 module load=if alias=cam6keep
 module load=if alias=cam7keep
 module load=if alias=cam8keep
 module load=if alias=cam9keep
 module load=if alias=cam10keep
 cam6keep connect=rpi6.local:24926
 cam7keep connect=rpi7.local:24926
 cam8keep connect=rpi8.local:24926
 cam9keep connect=rpi9.local:24926
 cam10keep connect=rpi10.local:24926
 ; ;
 # connect to get images
 module load=if alias=cam6
 module load=if alias=cam7
 module load=if alias=cam8
 module load=if alias=cam9
 module load=if alias=cam10
 ; #
 camset device=6 focallength=455 posx=0.45 posy=0.03  posz=0.65 rotphi=0 rotkappa=0.8
 camset device=7 focallength=455 posx=0.45 posy=0.00  posz=0.65 rotphi=0 rotkappa=0
 camset device=8 focallength=455 posX=0.45 posY=-0.03 posZ=0.65 rotPhi=0 rotkappa=-0.8
 camset device=9 focallength=455 posX=0.45 posY=-0.03 posZ=0.65 rotPhi=0 rotkappa=-1.57
 camset device=10 focallength=455 posX=0.45 posY=-0.03 posZ=0.65 rotPhi=0 rotkappa=1.57
 ; ;
 # load images scaled 2 levels down cam 6 is img=6+2 = 8
 # to get small overview images
 cam6onconnect cmd="cam6 poolpush img=8 cmd='poolget all toimg=26 fmt=rgb'"
 cam7onconnect cmd="cam7 poolpush img=9 cmd='poolget all toimg=27 fmt=rgb'"
 cam8onconnect cmd="cam8 poolpush img=10 cmd='poolget all toimg=28 fmt=rgb'"
 cam9onconnect cmd="cam9 poolpush img=11 cmd='poolget all toimg=29 fmt=rgb'"
 cam10onconnect cmd="cam10 poolpush img=12 cmd='poolget all toimg=30 fmt=rgb'"
 ; ;
 cam6 connect=rpi6.local:24920
 cam7 connect=rpi7.local:24920
 cam8 connect=rpi8.local:24920
 cam9 connect=rpi9.local:24920
 cam10 connect=rpi10.local:24920
 ;
 poolset img=26 log
 poolset img=27 log
 poolset img=28 log
 poolset img=29 log
 poolset img=30 log
 ;

This configuration file connects to 5 camera boards.

This gets an image from each of the raspberry pi cameras and load them to image numbers 26 to 30 in RGB format.