In this example, we will try to get a vision application running on the PLC itself. We are using open-source frameworks as:
Please note, that this is a fairly advanced topic, and as many steps are involved the basics are not explained in detail.
We need the following equipment:
image
image
/opt/pxc/<version/opt/pxc/currentPlease ensure you have a working Linux workstation + the Phoenix PLCNext SDK installed in /opt/pxc/<current I advise to make a symlink
We need to run the following commands:
vincent@vincent-VirtualBox ~ $ source /opt/pxc/current/environment-setup-cortexa9t2hf-neon-pxc-linux-gnueabi
This configures the crosscompiler provided by the Phoenix SDK.
We created an extra file (/opt/pxc/setupCrossCompiler) which has the content:
export ARMPREFIX=/opt/pxc/install
export PKG_CONFIG_PATH=$ARMPREFIX/lib/pkgconfig:$PKG_CONFIG_PATH
We also need to run source /opt/pxc/setupCrossCompiler, to enable this variable.
Last, we create a folder: /opt/pxc/install wich we use to install our cross-compiled libaries into, compiled for the PLCNext platform
::: {.tip} ::: {.admonition-title} Tip :::
Run the source <filename in every terminal session you are working in, as it is limited to the instance executed in, in this example for both files, so twice! :::
We also will compile ffmpeg which contains various codecs, usefull for OpenCV.
proceed with the following steps:
git clone https://github.com/FFmpeg/FFmpeg.git
vincent@vincent-VirtualBox /opt/pxc/ $ cd ffmpeg
git checkout release/2.6
Now we have checked-out this version.
for FFMpeg I've created a file to ease configuration, due to the extensive options. Create the file config.sh in the root of our ffmpeg directory, and make it executable by issuing chmod +x config.sh.
This config.sh file has to containg following content:
export CFLAGS="-mfpu=neon -mfloat-abi=hard -I${ARMPREFIX}/include"
export LDFLAGS="-L${ARMPREFIX}/lib"
export SYSR=/opt/pxc/current/sysroots/cortexa9t2hf-neon-pxc-linux-gnueabi
export FFMPEG_FLAGS="--target-os=linux \
--arch=arm \
--enable-shared \
--enable-pic \
--disable-static \
--disable-symver \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-avfilter \
--disable-encoders \
--disable-muxers \
--disable-filters \
--disable-devices \
--disable-everything \
--disable-encoder=aac \
--disable-libgme \
--disable-libgsm \
--disable-libiec61883 \
--disable-libilbc \
--disable-libmodplug \
--disable-libmp3lame \
--disable-libopencore-amrwb \
--disable-libopencv \
--disable-libopenh264 \
--disable-libopenjpeg \
--disable-libopus \
--disable-libpulse \
--disable-librtmp \
--disable-libshine \
--disable-libsmbclient \
--disable-libsoxr \
--disable-libspeex \
--disable-libssh \
--disable-libtheora \
--disable-libtwolame \
--disable-libv4l2 \
--disable-libvidstab \
--disable-libvo-amrwbenc \
--disable-libvorbis \
--disable-libvpx \
--disable-libwavpack \
--disable-libwebp \
--disable-libx264 \
--disable-libx265 \
--disable-libxavs \
--disable-libxcb \
--disable-libxcb-shm \
--enable-protocols \
--enable-parsers \
--enable-demuxers \
--enable-decoders \
--enable-muxers \
--enable-bsfs \
--enable-network \
--enable-postproc \
--enable-swscale \
--enable-ffmpeg \
--enable-avfilter \
--enable-avresample \
--enable-pthreads \
--disable-stripping \
--disable-iconv \
--disable-xlib \
--disable-zlib \
--disable-bzlib \
--disable-lzma \
--disable-demuxer=sbg \
--disable-demuxer=dts \
--disable-parser=dca \
--disable-decoder=dca \
--enable-ffmpeg \
--sysroot=${SYSR} \
--prefix=${ARMPREFIX} \
--cross-prefix=arm-pxc-linux-gnueabi-
"
export EXTRA_CFLAGS="-I ${ARMPREFIX}/include"
export LDFLAGS="-L ${ARMPREFIX}/lib"
./configure $FFMPEG_FLAGS --extra-cflags="$CFLAGS $EXTRA_CFLAGS" --extra-ldflags="$LDFLAGS"
With this file saved, it is time to execute it, and proceed with the build:
chmod +x config.sh
./config.sh
make -j2
make install
The make -j2 build the ffmpeg, with 2 concurrent processes. The make install command installes our crosscompiled files into ${ARMPREFIX} directory, used by our other crosscompiled applications. These libraries here, will also be copied to the target later on.
<div note=“note”
The -j2 suffix tells make to use to concurent processes, one for every available CPU core (this will speedup compilation on multi-core systems) </div
to test if ffmpeg works on our PLCNext platform and can extract imagery from the webcam, we can copy the ffmpeg binary to th CURRENTPLC to test if ffmpeg has indeed compiled correctly and can access our webcam:
#navigate to the root folder of ffmpeg
scp ffmpeg root@192.168.1.10:/tmp/
Now ssh to the plc ssh root@192.168.1.10 and execute:
cd /tmp
./ffmpeg -i http://root:root@192.168.1.2/mjpg/video.mjpg -r 10 -f image2 test%03d.jpg
ffmpeg version N-86746-g2f84f40 Copyright (c) 2000-2017 the FFmpeg developers
built with gcc 6.2.0 (GCC)
configuration: --target-os=linux --arch=arm --enable-static --disable-symver --disable-doc --disable-ffplay --disable-ffprobe --disable-ffserver --disable-avfilter --disable-encoders --disable-muxers --disable-filters --disable-devices --disable-everything --enable-protocols --enable-parsers --enable-demuxers --enable-decoders --enable-encoders --enable-muxers --enable-bsfs --enable-network --enable-postproc --enable-swscale --enable-ffmpeg --enable-avfilter --enable-avresample --disable-iconv --disable-xlib --disable-zlib --disable-bzlib --disable-lzma --disable-demuxer=sbg --disable-demuxer=dts --disable-parser=dca --disable-decoder=dca --enable-ffmpeg --enable-version3 --sysroot=/opt/pxc/current/sysroots/cortexa9t2hf-neon-pxc-linux-gnueabi --disable-x86asm --prefix=/opt/pxc/install --cross-prefix=arm-pxc-linux-gnueabi- --extra-cflags='-mfpu=neon -mfloat-abi=hard -I/opt/pxc/install/include ' --extra-ldflags=-L/opt/pxc/install/lib
libavutil 55. 67.100 / 55. 67.100
libavcodec 57.100.104 / 57.100.104
libavformat 57. 75.100 / 57. 75.100
libavdevice 57. 7.100 / 57. 7.100
libavfilter 6. 95.100 / 6. 95.100
libavresample 3. 6. 0 / 3. 6. 0
libswscale 4. 7.101 / 4. 7.101
libswresample 2. 8.100 / 2. 8.100
Input #0, mpjpeg, from 'http://root:root@192.168.1.2/mjpg/video.mjpg':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: mjpeg, yuvj420p(pc, bt470bg/unknown/unknown), 320x240 [SAR 1:1 DAR 4:3], 25 tbr, 25 tbn, 25 tbc
Stream mapping:
Stream #0:0 -#0:0 (mjpeg (native) -mjpeg (native))
Press [q] to stop, [?] for help
Output #0, image2, to 'test%03d.jpg':
Metadata:
encoder : Lavf57.75.100
Stream #0:0: Video: mjpeg, yuvj420p(pc), 320x240 [SAR 1:1 DAR 4:3], q=2-31, 200 kb/s, 10 fps, 10 tbn, 10 tbc
Metadata:
encoder : Lavc57.100.104 mjpeg
Side data:
cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: -1
frame= 30 fps= 13 q=24.8 Lsize=N/A time=00:00:03.00 bitrate=N/A dup=0 drop=41 speed=1.29x
video:188kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
Exiting normally, received signal 2.
after a few seconds we can cancel by pressing ctrl+c and when executing ls we see that we have a lot of new files:
root@axcf2152:/tmp# ls
OpenCv_Cam test006.jpg test015.jpg test024.jpg
ffmpeg test007.jpg test016.jpg test025.jpg
test test008.jpg test017.jpg test026.jpg
test.jpg test009.jpg test018.jpg test027.jpg
test001.jpg test010.jpg test019.jpg test028.jpg
test002.jpg test011.jpg test020.jpg test029.jpg
test003.jpg test012.jpg test021.jpg test030.jpg
test004.jpg test013.jpg test022.jpg
test005.jpg test014.jpg test023.jpg
root@axcf2152:/tmp#
copying the files over to the pc by executing
scp root@192.168.1.10:/tmp/test*.jpg .
Will copy the images to the local directory, where we might view them.
Create the following directory: /opt/pxc/opencv-arm/ and create a subdirectory build
Now we clone the OpenCV Git-repository:
cd /opt/pxc/opencv-arm
git clone https://github.com/Itseez/opencv.git
cd opencv
git checkout 2.4.13.2
We need to create a toolchain.cmake file.
Create the file in /opt/pxc/opencv-arm/
its content:
#!/bin/sh
set(ENV{PKG_CONFIG_PATH} $ENV{ARMPREFIX}/lib/pkgconfig)
set(ENV{LD_LIBRARY_PATH} $ENV{ARMPREFIX}/lib)
set(ENV{C_INCLUDE_PATH} $ENV{ARMPREFIX}/include)
set(ENV{CPLUS_INCLUDE_PATH} $ENV{ARMPREFIX}/include)
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} $ENV{ARMPREFIX}/lib)
set(MY_FLAGS "-I$ENV{ARMPREFIX}/include -L$ENV{ARMPREFIX}/lib -lswscale -lavformat -lavutil -lswresample -lavcodec")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MY_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MY_FLAGS}")
set(CMAKE_INSTALL_PREFIX $ENV{ARMPREFIX})
now we create a start.sh file in /opt/pxc/opencv-arm/build/ to configure cmake:
cmake -DENABLE_PRECOMPILED_HEADERS=OFF -DWITH_FFMPEG=ON -DWITH_JPEG=ON -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake -D CMAKE_BUILD_TYPE=RELEASE ../opencv
When this is completed run:
Now we have our libraries ready, we can put them to good use.
First in Eclipse, we create a new C++ program. And we add an aditional Build Configuration. This enables us to build our application for the desktop and the targetplatform as well, so we can test our code (using the GUI) on the pc, and cross-compile using our Phoenix SDK and cross-compiled libraries. I've called it PNext, and set the toolchain in accordance to the way libraries are build.
image
The compiler (both gcc and g++ ) needs to know where it can find the OpenCV Header files, so we add this.
image
projectsettings -G++ Compiler -*AND*** projectsettings -GCC Compiler -
/opt/pxc/install/include
In order to ensure our program is linked against our precomiled libraries, we need to add this list as linker-libraries:
image
In the Libraries (-l) field, add:
``` {.sourceCode .shell} opencv_objdetect opencv_stitching opencv_videostab opencv_calib3d opencv_features2d opencv_highgui opencv_ml opencv_video opencv_ocl opencv_photo opencv_imgproc opencv_flann opencv_core libprotobuf libjpeg libwebp libpng libtiff libjasper IlmImf tegra_hal swscale avformat avutil swresample avcodec opencv_nonfree opencv_gpu
Library search path (-L)
/opt/pxc/install/lib
/opt/pxc/install/share/OpenCV/3rdparty/lib
Our fairly simple program does:
``` {.sourceCode .c++} //============================================================================
// Name : OpenCv_Cam.cpp // Author : Vincent Gijsen // Version : 0.1 // Copyright : // Description : //============================================================================ /* * main.c * * Created on: Jul 29, 2017 * Author: Vincent Gijsen */
#include <opencv2/core/core.hpp\#include <opencv2/highgui/highgui.hpp\#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"
using namespace cv; using namespace std;
int main(int argc, char** argv) {
Mat image; Mat img_gray, img_blur, img_small; Scalar Color; size_t i;
string cam = "0 <http://root:root@192.168.1.2/mjpg/video.mjpg"; string img = "1 /tmp/image.png";
}
string input = argv[2]; string cmd = argv[1];
VideoCapture vcap;
if (cmd == "0") {
vcap.open(input); if (!vcap.isOpened()) { cout << "Error opening videostream: " << cam << endl; return -1;
}
}
//We are in film mode if (!vcap.read(image)) { cout << "no frame" << endl;
waitKey();
}
cout << "Reading image: " << input << endl; //We are in image mode image = imread(input, CV_LOAD_IMAGE_COLOR);
}
cvtColor(image, img_gray, CV_BGR2GRAY);
vector<Vec3f;
blur(img_gray, img_blur, Size(10, 10)); HoughCircles(img_blur, circles, CV_HOUGH_GRADIENT, 1, img_blur.rows / 4, 150, 25, 0, 0); ;
cout << "Found " << circles.size() << " dots" << endl;
// Draw for fun! for (i = 0; i < circles.size(); ++i) { Point pip(cvRound(circles[i][0]), cvRound(circles[i][1])); circle(image, pip, 3, Scalar(0, 255, 0), -1, 8, 0); }
}
}
}
}
```
We need to to compile our program.
If all is well, the Console in Eclipse should look like:
As our program is called OpenCV_Cam, we need to copy this binary.
For testing purposes, I chose to upload everything to /tmp on the controller. Do keep in mind, this /tmp is mapped to RAM, and therefore:
to copy the program, navigate in our project-directory (ecplise) to the build directory (name of the build-configuration) and copy the file:
scp OpenCv_Cam root@192.168.1.10:/tmp
We also need to copy all of our libraris to this directory (or to say it proper, copy it to direcotry that is either indexed by ldconfig, or instructed to be indext).
navigate to our /opt/pxc/install/lib directory.
scp *.4 root@192.168.1.10:/tmp
scp libav* root@192.168.1.10:/tmp
scp libsw* root@192.168.1.10:/tmp
we no have the libraries copied, but executables are not eable to use them.
When we issue the ldconfig -p on the plc, we see this:
root@axcf2152:/tmp# ldconfig -p
65 libs found in cache `/etc/ld.so.cache`
libz.so.1 (libc6,hard-float) =/lib/libz.so.1
libxml2.so.2 (libc6,hard-float) =/usr/lib/libxml2.so.2
...
we need to execute these commands to add them to the LDCACHE:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/tmp
ldconfig
now our new libraries are also available:
root@axcf2152:/tmp# ldconfig -p
91 libs found in cache `/etc/ld.so.cache`
...
libswscale.so.3 (libc6,hard-float) =/tmp/libswscale.so.3
libswresample.so.1 (libc6,hard-float) =/tmp/libswresample.so.1
libopencv_videostab.so.2.4 (libc6,hard-float) =/tmp/libopencv_videostab.so.2.4
libopencv_video.so.2.4 (libc6,hard-float) =/tmp/libopencv_video.so.2.4
libopencv_superres.so.2.4 (libc6,hard-float) =/tmp/libopencv_superres.so.2.4
libopencv_stitching.so.2.4 (libc6,hard-float) =/tmp/libopencv_stitching.so.2.4
libopencv_photo.so.2.4 (libc6,hard-float) =/tmp/libopencv_photo.so.2.4
libopencv_ocl.so.2.4 (libc6,hard-float) =/tmp/libopencv_ocl.so.2.4
libopencv_objdetect.so.2.4 (libc6,hard-float) =/tmp/libopencv_objdetect.so.2.4
libopencv_nonfree.so.2.4 (libc6,hard-float) =/tmp/libopencv_nonfree.so.2.4
libopencv_ml.so.2.4 (libc6,hard-float) =/tmp/libopencv_ml.so.2.4
libopencv_legacy.so.2.4 (libc6,hard-float) =/tmp/libopencv_legacy.so.2.4
libopencv_imgproc.so.2.4 (libc6,hard-float) =/tmp/libopencv_imgproc.so.2.4
libopencv_highgui.so.2.4 (libc6,hard-float) =/tmp/libopencv_highgui.so.2.4
libopencv_gpu.so.2.4 (libc6,hard-float) =/tmp/libopencv_gpu.so.2.4
libopencv_flann.so.2.4 (libc6,hard-float) =/tmp/libopencv_flann.so.2.4
libopencv_features2d.so.2.4 (libc6,hard-float) =/tmp/libopencv_features2d.so.2.4
libopencv_core.so.2.4 (libc6,hard-float) =/tmp/libopencv_core.so.2.4
libopencv_contrib.so.2.4 (libc6,hard-float) =/tmp/libopencv_contrib.so.2.4
libopencv_calib3d.so.2.4 (libc6,hard-float) =/tmp/libopencv_calib3d.so.2.4
libavutil.so.54 (libc6,hard-float) =/tmp/libavutil.so.54
libavresample.so.2 (libc6,hard-float) =/tmp/libavresample.so.2
libavformat.so.56 (libc6,hard-float) =/tmp/libavformat.so.56
libavfilter.so.5 (libc6,hard-float) =/tmp/libavfilter.so.5
libavdevice.so.56 (libc6,hard-float) =/tmp/libavdevice.so.56
libavcodec.so.56 (libc6,hard-float) =/tmp/libavcodec.so.56
...
Now we are ready to run the program:
cd /tmp
./OpenCv_Cam 0 http://root:root@192.168.1.2/mjpg/video.mjpg
If we present our 'dice' to the camera:
image
If now 'dots are recognized, these will be printed
image
Here you see the program running:
image