goo blog サービス終了のお知らせ 

-・-・ --・-  -・-・ --・-  -・-・ --・- 

不定期更新。電子工作オタクのメモなので、おもしろくないですよ。

darknetから物体検出情報をsocket通信で取得する

2022-06-09 15:02:27 | ソフトウェア
darknetからの物体検出情報をsocket通信で取得する方法のメモ
Jetson nanoでのカメラ追尾とダブリますが、ソースがうまく貼れそうなので、アップしてみます。
あくまで、素人なので間違えている可能性があります。

1. image_opencv.cppの修正
(1) 80行目当たりに/* added source by sakachan */から/**************************/までを追加しました。

#ifndef CV_AA
#define CV_AA cv::LINE_AA
#endif

/* added source by sakachan */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//#define SERVER_PATH "/var/tmp/tpf_unix_sock.server"
#define SERVER_PATH "/tmp/foo.sock"

/**************************/

extern "C" {

//struct mat_cv : cv::Mat { };

(2) 820行当たりのdraw_detections_cv_v3関数を以下のように修正
socket通信のclientとして、物体検出情報を出力(フレームの区切りとして、SとEの文字を出力)します。
// ====================================================================
// Draw Detection
// ====================================================================
extern "C" void draw_detections_cv_v3(mat_cv* mat, detection *dets, int num, float thresh, char **names, image **alphabet, int classes, int ext_output)
{
try {
cv::Mat *show_img = (cv::Mat*)mat;
int i, j;

/*********** added by sakachan ***********/
int err;
int client_socket, rc;
struct sockaddr_un remote;
char start[] = "S\n";
char end[] = "E\n";
char my_buff[256];
char my_str[256];

if (ext_output) {

memset(&remote, 0, sizeof(struct sockaddr_un));

/****************************************/
/* Create a UNIX domain datagram socket */
/****************************************/
client_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
if (client_socket == -1) {
err = errno;
printf("SOCKET ERROR create = %d : %s", err, strerror(err));
// exit(1);
}

/***************************************/
/* Set up the UNIX sockaddr structure */
/* by using AF_UNIX for the family and */
/* giving it a filepath to send to. */
/***************************************/
remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, SERVER_PATH);
rc = sendto(client_socket, start, strlen(start), 0, (struct sockaddr *) &remote,
sizeof(remote)); // send "S"
if (rc == -1) {
err = errno;
printf("SENDTO ERROR start = %d : %s", err, strerror(err));
close(client_socket);
// exit(1);
}
sprintf(my_buff, "num:%d\n", num);
rc = sendto(client_socket, my_buff, strlen(my_buff), 0, (struct sockaddr *) &remote,
sizeof(remote)); // send "num:x"
if (rc == -1) {
err = errno;
printf("SENDTO ERROR num = %d : %s", err, strerror(err));
close(client_socket);
// exit(1);
}

}
/***************************************/

if (!show_img) return;
static int frame_id = 0;
frame_id++;

for (i = 0; i < num; ++i) {

/*********** added by sakachan ***********/
char out_buff[4096] = { 0 };
/***************************************/

char labelstr[4096] = { 0 };
int class_id = -1;
for (j = 0; j < classes; ++j) {
int show = strncmp(names[j], "dont_show", 9);
if (dets[i].prob[j] > thresh && show) {
if (class_id < 0) {
strcat(labelstr, names[j]);
class_id = j;
char buff[20];
if (dets[i].track_id) {
sprintf(buff, " (id: %d)", dets[i].track_id);
strcat(labelstr, buff);
}
sprintf(buff, " (%2.0f%%)", dets[i].prob[j] * 100);
strcat(labelstr, buff);
printf("%s: %.0f%% ", names[j], dets[i].prob[j] * 100);

/*********** added by sakachan ***********/
sprintf(my_str,"%s: %.0f%%", names[j], dets[i].prob[j] * 100);

strcat(out_buff, my_str);
/***************************************/

if (dets[i].track_id)
printf("(track = %d, sim = %f) ", dets[i].track_id, dets[i].sim);
}
else {
strcat(labelstr, ", ");
strcat(labelstr, names[j]);
printf(", %s: %.0f%% ", names[j], dets[i].prob[j] * 100);

/*********** added by sakachan ***********/
sprintf(my_str,",%s: %.0f%%", names[j], dets[i].prob[j] * 100);

strcat(out_buff, my_str);
/****************************/

}
}
}
if (class_id >= 0) {
int width = std::max(1.0f, show_img->rows * .002f);

//if(0){
//width = pow(prob, 1./2.)*10+1;
//alphabet = 0;
//}

//printf("%d %s: %.0f%%\n", i, names[class_id], prob*100);
int offset = class_id * 123457 % classes;
float red = get_color(2, offset, classes);
float green = get_color(1, offset, classes);
float blue = get_color(0, offset, classes);
float rgb[3];

//width = prob*20+2;

rgb[0] = red;
rgb[1] = green;
rgb[2] = blue;
box b = dets[i].bbox;
if (std::isnan(b.w) || std::isinf(b.w)) b.w = 0.5;
if (std::isnan(b.h) || std::isinf(b.h)) b.h = 0.5;
if (std::isnan(b.x) || std::isinf(b.x)) b.x = 0.5;
if (std::isnan(b.y) || std::isinf(b.y)) b.y = 0.5;
b.w = (b.w < 1) ? b.w : 1;
b.h = (b.h < 1) ? b.h : 1;
b.x = (b.x < 1) ? b.x : 1;
b.y = (b.y < 1) ? b.y : 1;
//printf("%f %f %f %f\n", b.x, b.y, b.w, b.h);

int left = (b.x - b.w / 2.)*show_img->cols;
int right = (b.x + b.w / 2.)*show_img->cols;
int top = (b.y - b.h / 2.)*show_img->rows;
int bot = (b.y + b.h / 2.)*show_img->rows;

if (left < 0) left = 0;
if (right > show_img->cols - 1) right = show_img->cols - 1;
if (top < 0) top = 0;
if (bot > show_img->rows - 1) bot = show_img->rows - 1;

int b_x_center = (left + right) / 2; /* uncomment by sakachan */
int b_y_center = (top + bot) / 2;
int b_width = right - left;
int b_height = bot - top;
//sprintf(labelstr, "%d x %d - w: %d, h: %d", b_x_center, b_y_center, b_width, b_height);
// sprintf(labelstr, "x:%d y:%d w:%d h:%d", b_x_center, b_y_center, b_width, b_height); // modified by sakachan

float const font_size = show_img->rows / 1000.F;
cv::Size const text_size = cv::getTextSize(labelstr, cv::FONT_HERSHEY_COMPLEX_SMALL, font_size, 1, 0);
cv::Point pt1, pt2, pt_text, pt_text_bg1, pt_text_bg2;
pt1.x = left;
pt1.y = top;
pt2.x = right;
pt2.y = bot;
pt_text.x = left;
pt_text.y = top - 4;// 12;
pt_text_bg1.x = left;
pt_text_bg1.y = top - (3 + 18 * font_size);
pt_text_bg2.x = right;
if ((right - left) < text_size.width) pt_text_bg2.x = left + text_size.width;
pt_text_bg2.y = top;
cv::Scalar color;
color.val[0] = red * 256;
color.val[1] = green * 256;
color.val[2] = blue * 256;

// you should create directory: result_img
//static int copied_frame_id = -1;
//static IplImage* copy_img = NULL;
//if (copied_frame_id != frame_id) {
// copied_frame_id = frame_id;
// if(copy_img == NULL) copy_img = cvCreateImage(cvSize(show_img->width, show_img->height), show_img->depth, show_img->nChannels);
// cvCopy(show_img, copy_img, 0);
//}
//static int img_id = 0;
//img_id++;
//char image_name[1024];
//sprintf(image_name, "result_img/img_%d_%d_%d_%s.jpg", frame_id, img_id, class_id, names[class_id]);
//CvRect rect = cvRect(pt1.x, pt1.y, pt2.x - pt1.x, pt2.y - pt1.y);
//cvSetImageROI(copy_img, rect);
//cvSaveImage(image_name, copy_img, 0);
//cvResetImageROI(copy_img);

cv::rectangle(*show_img, pt1, pt2, color, width, 8, 0);
if (ext_output) {
printf("\t(left_x: %4.0f top_y: %4.0f width: %4.0f height: %4.0f)\n",
(float)left, (float)top, b.w*show_img->cols, b.h*show_img->rows);

/****** added by sakachan ******/
sprintf(my_buff, ",c_x: %4.0f,c_y: %4.0f,w: %4.0f,h: %4.0f\n",
(float)b_x_center, (float)b_y_center, (float)b_width, (float)b_height);
strcat(out_buff, my_buff);
rc = sendto(client_socket, out_buff, strlen(out_buff), 0, (struct sockaddr *) &remote, sizeof(remote));
if (rc == -1) {
err = errno;
printf("SENDTO ERROR out = %d : %s", err, strerror(err));
// close(client_socket);
// exit(1);
}


} else {
printf("\n");
}

cv::rectangle(*show_img, pt_text_bg1, pt_text_bg2, color, width, 8, 0);
cv::rectangle(*show_img, pt_text_bg1, pt_text_bg2, color, CV_FILLED, 8, 0); // filled
cv::Scalar black_color = CV_RGB(0, 0, 0);
cv::putText(*show_img, labelstr, pt_text, cv::FONT_HERSHEY_COMPLEX_SMALL, font_size, black_color, 2 * font_size, CV_AA);
// cv::FONT_HERSHEY_COMPLEX_SMALL, cv::FONT_HERSHEY_SIMPLEX
}
}
if (ext_output) {

/*****************************/
/* Close the socket and exit */
/*****************************/
rc = sendto(client_socket, end, strlen(end), 0, (struct sockaddr *) &remote,
sizeof(remote)); // send "E"
if (rc == -1) {
err = errno;
printf("SENDTO ERROR end = %d : %s", err, strerror(err));
// close(client_socket);
// exit(1);
}
rc = close(client_socket);
/*****************************/

fflush(stdout);
}
}
catch (...) {
cerr << "OpenCV exception: draw_detections_cv_v3() \n";
}
}
// ----------------------------------------

2. socket通信のサーバ側のプログラム(C#)
単純にdarknetからのsocket通信を受信するだけです。
あとは、受信したメッセージを加工して、利用することになります。
Server.cs

using System;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Text;
using System.Collections.Generic;
using System.Net;

class Program
{
private static void Main(string[] args)
{
var unixSocket = "/tmp/foo.sock";

// delete file if it exists
if (System.IO.File.Exists(unixSocket))
System.IO.File.Delete(unixSocket);

// 接続ソケットの準備
Socket s = new Socket(AddressFamily.Unix, SocketType.Dgram, ProtocolType.Unspecified);
var unixEp = new UnixDomainSocketEndPoint(unixSocket);
System.IO.File.Delete(unixSocket);
s.Bind(unixEp);

Console.WriteLine("Server started waiting for client to connect...");

string message = "";
byte[] buffer = new byte[1024];

while (true)
{
int numberOfBytesReceived = s.Receive(buffer, 0, buffer.Length, SocketFlags.None);
if (numberOfBytesReceived <= 0)
{
break;
}

Console.WriteLine(numberOfBytesReceived);
message = System.Text.Encoding.UTF8.GetString(buffer, 0, numberOfBytesReceived);
Console.WriteLine(message);
}
s.Close();
}
}

Jetson nanoでカメラ追尾実験(つづき 3)

2021-01-23 18:32:53 | ソフトウェア
(2) PIC1509 (main.c)
ドローンへデータを送信するために、Jetson nanoのシリアルポートにIM920cを接続してデータを送信するようにserver.cを変更しています。
PICでserver.cからのデータを受信する場合は、server.cのmy_printf文の終端文字を\rにするか、本プログラムの受信判定文字を\nに変更する必要があります。
MPLAB X IDEのMCCを使って初期設定をしています。CLCモジュールの設定は、電子工作の実験室の設定が参考になりました。
ADCは当初雲台の動作確認のため、秋月電子のジョイスティックをつないで実験したときに使ったもので、Jetson nanoとつなぐときは不要です

#include "mcc_generated_files/mcc.h"

#include stdlib.h
#include string.h
/*
                         Main application
 */

/** グローバル定数変数定義 **/
unsigned int HWidth, VWidth;
int XPos, YPos;
char temp;

#define CENTER_X    320     // X center (Jetson nano)
#define CENTER_Y    240     // Y center (Jetson nano)
#define MAX_X       639     // X max (Jetson nano)
#define MAX_Y       479     // Y max (Jetson nano)
#define CENTER_H    680     // H cener 1.36ms
#define CENTER_V    265     // V center 0.53ms
#define WIDTH_X     640.0F  // width X (Jetson nano)
#define WIDTH_Y     480.0F  // width Y (Jetson nano)
#define WIDTH_H     460.0F  // width H
#define WIDTH_V     70.0F   // width V
#define MAX_VWIDTH  300     // max VWidth
#define MIN_VWIDTH  230     // min VWidth
#define MAX_HWIDTH  910     // max HWidth
#define MIN_HWIDTH  450     // min HWidth

//, DFlag, Mode, i;
//unsigned int Interval, AutoFlag, AutoInt, Level, NewLvl;
//unsigned int PeakL, PeakH, PeakV;

//void UART_Demo_Initialize(void); 
//void UART_Demo_Command(void);
//void UART_Demo_Command_INT(void);

/***************************************
*  A/D変換入力関数
****************************************/
unsigned int ADConv(unsigned char ch){
    int i, j;
    unsigned int value;

    value = 0;
    for(j=0; j<5; j++){                         // 5回の平均
        ADCON0 = (unsigned char)((ch << 2) | 0x01);              // チャネル選択
        for(i=0; i<40; i++);                    // アクイジションタイム待ち
        GO = 1;                                 // 変換開始
        while(GO);                              // 変換終了待ち
        value += (unsigned int)((ADRESH & 0x0F)*256 + ADRESL);  // 変換結果計算
    }
    return(value/5);                            // 変換結果を返す
}

void main(void)
{
    // ★☆★☆最後にADCを外すこと

    // initialize the device
    SYSTEM_Initialize();

    int cnt = 0;  //文字数カウント用
    int i;  //for文用
//    char temp;  //一時保存用
    unsigned char string[50];  //最終的につくる文字列。長さはテキトーです。
    char *ptr;

    // When using interrupts, you need to set the Global and Peripheral Interrupt Enable bits
    // Use the following macros to:

    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();

    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();

    // Disable the Global Interrupts
    //INTERRUPT_GlobalInterruptDisable();

    // Disable the Peripheral Interrupts
    //INTERRUPT_PeripheralInterruptDisable();

    /* PWM1,4の初期設定 2.048msec周期*/
    /* Duty値設定範囲  450 to 1023 (0.9ms to 2.05ms width) */
    VWidth = CENTER_V;
    HWidth = CENTER_H;
    PWM1_LoadDutyValue(HWidth);
    PWM4_LoadDutyValue(VWidth);

    while (1)
    {
//        UART_Demo_Command_INT();
        if(eusartRxCount!=0) 
        {   
//            temp=EUSART_Read();  // read a byte for RX
            temp=getch();  // read a byte for RX
//            EUSART_Write(temp);  // send a byte to TX  (from Rx)
            putch(temp);  // send a byte to TX  (from Rx)
            //Enterキーか判別
            if(temp != '\r')
            {
                string[cnt] = (unsigned char)temp;
                cnt++;
            }
            else
            {
                //null文字挿入
                string[cnt]=0;
                //文字列として表示
                WriteString("\r\n");
                cnt=0;
                // カンマを区切りに文字列を分割
                // 1回目
                ptr = strtok(string, ",");
                XPos = (unsigned int)atoi(ptr);     // 文字→数値変換
                if(XPos < 0)                        // たぶん意味ない判定
                    XPos = 0;
                else if(XPos > MAX_X)
                    XPos = MAX_X;
                XPos = XPos - CENTER_X;             // centerとの差に変換
                XPos = (int)(XPos * WIDTH_H / WIDTH_X); // 画像の座標からサーボの座標へ変換
                if(XPos < 0){
                    XPos = abs(XPos);               // 負から絶対値へ変換
                    HWidth = (unsigned int)XPos + CENTER_H; // 負の場合は右方向
                } else {
                    HWidth = CENTER_H - (unsigned int)XPos; // 正の場合は左方向
                }
                printf("%d\r\n", HWidth);
                // 2回目以降
                while(ptr != NULL)
                {
                    // strtok関数により変更されたNULLのポインタが先頭
                    ptr = strtok(NULL, ",");
                    // ptrがNULLの場合エラーが発生するので対処
                    if(ptr != NULL) {
                        YPos = (unsigned int)atoi(ptr);     // 文字→数値変換
                        if(YPos < 0)
                            YPos = 0;
                        else if(YPos > MAX_Y)
                            YPos = MAX_Y;
                        YPos = YPos - CENTER_Y;
                        YPos = (int)(YPos * WIDTH_V / WIDTH_Y);
                        if(YPos < 0){
                            YPos = abs(YPos);
                            VWidth = (unsigned int)YPos + CENTER_V;
                        } else {
                            VWidth = CENTER_V - (unsigned int)YPos;
                        }
                    }
                }
                printf("%d\r\n", VWidth);

        /* Joy X input */
//        HWidth = (unsigned int)(ADConv(6)+225); // AN6変換 中央値補正
                if(VWidth < MIN_VWIDTH)
                    VWidth = MIN_VWIDTH;                       // 最小側制限   約0.46msec
                else if(VWidth > MAX_VWIDTH)
                    VWidth = MAX_VWIDTH;                      // 最大側制限 約2.05msec
        /* Joy Y input */
//        VWidth = (unsigned int)(ADConv(5)+175); // AN5変換 中央値補正
                if(HWidth < MIN_HWIDTH)
                    HWidth = MIN_HWIDTH;                       // 最小側制限 約0.9msec
                else if(HWidth > MAX_HWIDTH)
                    HWidth = MAX_HWIDTH;                      // 最大側制限 約2.05msec
                /* デューティにセット */
                PWM1_LoadDutyValue(HWidth);
                PWM4_LoadDutyValue(VWidth);
            }
        }
    }
}
/**
 End of File
*/

Jetson nanoでカメラ追尾実験(つづき 2)

2021-01-22 16:35:09 | ソフトウェア
includeファイルを&lt;と&gt;で囲って投稿しても表示されない。
編集画面に戻ると&lt;ではなく、<になっている。何でだろう?つづき1では大丈夫だったのにな。困ったもんだ。
(追記) <と>を外しました。

2) serverプログラム(server.c)

/************************************************************/
/* This is a datagram socket server sample program for UNIX */
/* domain sockets. This program creates a socket and        */
/* receives data from a client.                             */
/************************************************************/

#include stdio.h
#include stdlib.h
#include errno.h
#include string.h
#include sys/types.h
#include sys/socket.h
#include sys/un.h
#include unistd.h
#include fcntl.h
#include termios.h
#include unistd.h
#include time.h
#include stdbool.h
#include assert.h

#include "my_printf.h"

//#define SOCK_PATH "tpf_unix_sock.server"
#define SOCK_PATH	"/var/tmp/ram/tpf_unix_sock.server"

#define MAXITEM		6			// 1行内のアイテム(object, 認識率, x, y, 面積)
#define MAXFRAME	20			// 目的のオブジェクトが連続して、このフレーム数ないと一旦クリア
#define OBJECT		"person"	// 目的のオブジェクト
#define AVERAGE		20			// 移動平均のフレーム数
#define N			200			// 1行の最大文字数(バイト数)
#define NAME_SIZE	20

int microsecond = 0.5 * 1000000;
int ptr = 0;
int Buff_x[AVERAGE];
int Buff_y[AVERAGE];
int sum_x = 0;
int sum_y = 0;

typedef struct object{
	char name[NAME_SIZE];
	char center_x[5];
	char center_y[5];
	int area;
	bool obj_check;
	bool max_flag;
}st_obj;

int my_split( char *str, const char *delim, char *outlist[] ) {
    char    *tk;
    int     cnt = 0;

    tk = strtok( str, delim );
//    while( tk != NULL && cnt < MAXITEM ) {
    while( tk != NULL ) {
        outlist[cnt++] = tk;
        tk = strtok( NULL, delim );
    }
    return cnt;
}

void Calc(int dat_x, int dat_y, int *ret_x, int *ret_y)
{
	int i;

	if(ptr <= AVERAGE - 1){
		Buff_x[ptr] = dat_x;
		Buff_y[ptr] = dat_y;
		sum_x += dat_x;
		sum_y += dat_y;
		ptr++;
		*ret_x = 0;
		*ret_y = 0;
	} else {
		// AVERAGE + 1個目から先のデータ
		sum_x += dat_x - Buff_x[0];
		sum_y += dat_y - Buff_y[0];

		for( i=0; i <= AVERAGE - 2; i++ ) {
			Buff_x[i] = Buff_x[i + 1];
			Buff_y[i] = Buff_y[i + 1];
		}
		Buff_x[AVERAGE - 1] = dat_x; //最新データ
		Buff_y[AVERAGE - 1] = dat_y; //最新データ
		*ret_x = sum_x/AVERAGE;
		*ret_y = sum_y/AVERAGE;
	}
}	

// mystrcat: t を s の終わりに連結する;s が指す領域は十分大きいこと
void *my_strcat(char *s, char *t)
{
	int	i, j;
	char *space = " ";

	i = j = 0;
	while( s[i] != '\0')	// s の終わりを探す
		i++;
	s[i] = *space;			// 文字列の間にスペースを入れる
	i++;
	while( (s[i++] = t[j++]) != '\0')	// t をコピーする
		;
	s[i] = '\0';
}

int main(void){
	int err;
	int server_sock, len, rc;
	int bytes_rec = 0;
	struct sockaddr_un server_sockaddr, peer_sock;
	char buf[200];
	int i, j, cnt;
	char str[N];
	int data_x, data_y;
	int ave_x, ave_y;

	char *splitlist[MAXITEM + 1];
 	int frame_cnt = 0;		// obj未検出カウンタ
	int no_obj_ck = 1;
	bool frame_check = false;
	int obj_cnt = 0;
	const char *start = "S";
	const char *end = "E";
	char *num[2];
	char *temp_str[2];
	int val_num;
	int num_cnt;
	int temp_w;
	int temp_h;
	char (*temp_name)[NAME_SIZE];
	size_t char_size = sizeof(char);
	size_t int_size = sizeof(int);
	st_obj *s;
	size_t elm_size = sizeof(st_obj);
	int *temp_obj;

	fd = open(DEV_NAME, O_RDWR | O_NOCTTY);
	if(fd < 0)
	{
		printf("\n Error! in Opening ttyTHS1\n");
		exit(1);
	}
	else
		printf("ttyTHS1 Opend Successfully\n");

	serial_init(fd);

    memset(&server_sockaddr, 0, sizeof(struct sockaddr_un));
    memset(buf, 0, 200);
    
    /****************************************/
    /* Create a UNIX domain datagram socket */
    /****************************************/
    server_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (server_sock == -1){
		err = errno;
		printf("SOCKET ERROR = %d : %s",err, strerror(err));
//        printf("SOCKET ERROR = %d", sock_errno());
        exit(1);
    }
    
    /***************************************/
    /* Set up the UNIX sockaddr structure  */
    /* by using AF_UNIX for the family and */
    /* giving it a filepath to bind to.    */
    /*                                     */
    /* Unlink the file so the bind will    */
    /* succeed, then bind to that file.    */
    /***************************************/
    server_sockaddr.sun_family = AF_UNIX;
    strcpy(server_sockaddr.sun_path, SOCK_PATH); 
    len = sizeof(server_sockaddr);
    unlink(SOCK_PATH);
    rc = bind(server_sock, (struct sockaddr *) &server_sockaddr, len);
    if (rc == -1){
		err = errno;
		printf("BIND ERROR = %d : %s",err, strerror(err));
//        printf("BIND ERROR = %d", sock_errno());
        close(server_sock);
        exit(1);
    }
    
    /****************************************/
    /* Read data on the server from clients */
    /* and print the data that was read.    */
    /****************************************/
	while(1){
    	bytes_rec = recvfrom(server_sock, buf, sizeof(buf), 0, (struct sockaddr *) &peer_sock, &len);
    	if (bytes_rec == -1){
			printf("RECVFROM ERROR = %d : %s",err, strerror(err));
        	close(server_sock);
        	exit(1);
		} else {
			strcpy(str, buf);		// ★☆★☆

		if(strlen(str) == 1) {	// "S"受信から"E"受信までの処理
			printf("%s\r", str);
			if(strcmp(str, start) == 0 && frame_check == false) {	// "S" ? フレーム開始
				no_obj_ck = 1;
				printf("%s\r", str);
				frame_check = true;	// frame start
			} else if(strcmp(str, end) == 0) {						// "E" ? フレーム終了後の処理
				if(obj_cnt > 0) {	// フレーム内各objの面積最大objをチェックする
					for(j = 0; j < obj_cnt; j++) {
						for(i = 0; i < obj_cnt; i++) {
							if(s[i].obj_check == false){
								s[i].obj_check = true;		// チェック済にする
								if(i == 0) {
									s[i].max_flag = true;	// 仮にmaxにしておく
									temp_obj[j] = i;		// 最大のobj番号を記憶
									continue;
								}
								if(strcmp(s[i].name, s[temp_obj[j]].name) == 0) {
									if(s[i].area > s[temp_obj[j]].area) {	// 面積比較
										s[temp_obj[j]].max_flag = false;	// 面積が大きければ更新
										s[i].max_flag = true;
										temp_obj[j] = i;
									}
								} else {
									s[i].max_flag = true;	// 新しいobjは仮にmaxとする
									temp_obj[j] = i;
								}
							}
						}
					}
					for(i = 0; i < obj_cnt; i++) {	// 上のループに入れてもいいかも

						// 指定したオブジェクトの判定
						if(strcmp(s[i].name, OBJECT) == 0 && s[i].max_flag == true) {
							no_obj_ck = 0;		// チェックフラグオフ(目的のオブジェクト)
							frame_cnt = 0;		// 目的のobjがあったのでクリア
							data_x = atoi(s[i].center_x);	// x
							data_y = atoi(s[i].center_y);	// y
							Calc(data_x, data_y, &ave_x, &ave_y);
							if(ave_x > 0 && ave_y > 0){
								printf("TXDATGDAT %s %d %d\r\n", s[i].name, ave_x, ave_y);
								my_printf("TXDATGDAT %s %d %d\r\n", s[i].name, ave_x, ave_y);  // 追記: IM920cへ送るコマンドでした PICへ送るのはave_xとaveyだけです
							}
						}
					}
				}	/* if(obj_cnt > 0) */
				if(no_obj_ck == 1) {		// 検出objなし
					frame_cnt++;
					if(frame_cnt >= MAXFRAME) {
						frame_cnt = 0;
						ptr = 0;
						sum_x = 0;
						sum_y = 0;
					}
				}

				free(s);				// (フレーム終了処理) メモリを解放
				free(temp_obj);			// メモリを解放
				obj_cnt = 0;			// objカウンタ初期化
				frame_check = false;	// フレーム終了
			}	/* else if(strcmp(str, end) == 0) */
		} else {	// "S"受信後のフレーム受信処理
			if(frame_check == true) {
				cnt = my_split( str, " " , splitlist );
				if(cnt == 1) {	// Jestsonからのnum数に応じて動的配列を確保
					num_cnt = my_split( splitlist[0], ":" , num );
					val_num = atoi(num[1]);
					if(val_num == 0) {	// num=0でも1個は確保
						val_num = 1;
					}
					// 動的配列を確保する
					temp_name = malloc(char_size * val_num * NAME_SIZE);
					temp_obj = malloc(int_size * val_num);
					if (temp_obj == NULL) {		// メモリ割当てエラーの処理
						perror("malloc temp_obj");
						exit(1);
					}
					s = malloc(elm_size * val_num);
					if (s == NULL) {			// メモリ割当てエラーの処理
						perror("malloc s");
						exit(1);
					}
				} else if(cnt < MAXITEM) {
					printf("Item value = %d Error!\n", cnt);
					continue;
				} else {
					if(cnt > MAXITEM) {							// objectが2つに別れている場合
						my_strcat(splitlist[0], splitlist[1]);
						splitlist[2] = NULL;					// 要素を移動する
						splitlist[2] = splitlist[3];
						splitlist[3] = NULL;
						splitlist[3] = splitlist[4];
						splitlist[4] = NULL;
						splitlist[4] = splitlist[5];
						splitlist[5] = NULL;
						splitlist[5] = splitlist[6];
//						printf("[0]=%s [2]=%s [3]=%s [4]=%s [5]=%s\n", splitlist[0],
//						 splitlist[2], splitlist[3], splitlist[4], splitlist[5]);
					}
					strcpy(s[obj_cnt].name, splitlist[0]);				// name
					num_cnt = my_split( splitlist[2], ":" , temp_str );	// center_x
					strcpy(s[obj_cnt].center_x, temp_str[1]);
					num_cnt = my_split( splitlist[3], ":" , temp_str );	// center_y
					strcpy(s[obj_cnt].center_y, temp_str[1]);
					num_cnt = my_split( splitlist[4], ":" , temp_str );	// width
					temp_w = atoi(temp_str[1]);
					num_cnt = my_split( splitlist[5], ":" , temp_str );	// height
					temp_h = atoi(temp_str[1]);
					s[obj_cnt].area = temp_w * temp_h;					// area
					s[obj_cnt].obj_check = false;						// obj_check
					s[obj_cnt].max_flag = false;						// max_flag

					obj_cnt++;

				}	/* else cnt == 1 */
			}	/* frame_check == true */
		}	/* else strlen(str) == 2 */

			memset(buf, 0, 200);
		}	// end else bytes_rec == -1
//		usleep(microsecond);
	}	// end while
    
    /*****************************/
    /* Close the socket and exit */
    /*****************************/
    close(server_sock);

	close(fd);

    return 0;
}

3) UARTからの出力プログラム(my_print.c)
シリアル通信でオリジナルprintf関数を作ろうを参考にさせていただきました。(感謝)

/*==============================================================================
 +
 + 書式つきターミナル出力
 +
 +============================================================================*/
#include stdio.h
#include fcntl.h
#include termios.h
#include unistd.h
#include stdlib.h
#include string.h
#include time.h

#include stdarg.h
#include "my_printf.h"

#define C_MAX 12	/* ロングデータを8進数で表示するときの最大桁数 */

static void outs(char *, short);
static void outc(char, short);
static void outn(char, unsigned long, short);
static void output(char ch);
void serial_init(int);

// init serial port
void serial_init(int fd)
{
    struct termios tio;
    memset(&tio,0,sizeof(tio));
    tio.c_cflag = CS8 | CLOCAL | CREAD;
    tio.c_cc[VTIME] = 100;
    // set baud rate
    cfsetispeed(&tio,BAUD_RATE);
    cfsetospeed(&tio,BAUD_RATE);
    // set device
    tcsetattr(fd,TCSANOW,&tio);
}

/*------------------------------------------------------------------------------
 + 書式つきターミナル出力。
 +----------------------------------------------------------------------------*/
void my_printf(char *fmt, ...)
{
	va_list args;
	char *p;
	unsigned long v;
	short c;

	va_start(args, fmt);
	for (p = fmt; *p != '\0'; p++) {
		if (*p == '%') {
			p++;
			if ((*p >= '1') && (*p <= '9')) {
				c = *p++ - '0';
				while ((*p >= '0') && (*p <= '9')) c = c * 10 + (*p++ - '0');
			}
			else
				c = -1;

			v = (unsigned long)va_arg(args, long);

			switch (*p) {
			case 's':
				outs((char*)v, c);
				break;
			case 'c':
				outc((char)v, c);
				break;
			case 'd': case 'o': case 'x': case 'b':
				outn(*p, v, c);
			}
			if (*p == '\0') goto fin;
		}
		else if (*p == '\n') {
			output('\n');
		}
		else
			output(*p);
	}
fin:
	va_end(args);
}

/*------------------------------------------------------------------------------
 + 文字列を出力する。
 +----------------------------------------------------------------------------*/
static void outs(char *s, short c)
{
	char *p;
	for (p = s; *p != '\0'; p++) {
		if (c-- == 0) return;
		output(*p);
	}
	while (c-- > 0) output(' ');
}

/*------------------------------------------------------------------------------
 + 文字を出力する。
 +----------------------------------------------------------------------------*/
static void outc(char ch, short c)
{
	output(ch);
	while (--c > 0) output(' ');
}

/*------------------------------------------------------------------------------
 + 1文字を出力する。
 +----------------------------------------------------------------------------*/
static void output(char ch)
{
	write(fd, &ch, sizeof(ch));
}

/*------------------------------------------------------------------------------
 + 整数を出力する。
 +----------------------------------------------------------------------------*/
static void outn(char fm, unsigned long v, short c)
{
	char s[C_MAX], *p, z;
	unsigned short b;

	switch (fm) {
	case 'd':
		b = 10; z = ' '; break;
	case 'o':
		b = 8; z = '0'; break;
	case 'x':
		b = 16; z = '0'; break;
	case 'b':
		b = 2; z = '0'; break;

	default:
		return;
	}
	p = s;
	do {
		c--;
		*p++ = "0123456789abcdef"[v % b];
		v /= b;
	} while (v != 0);

	for (; c > 0; c--) output(z);

	while (p > s) output(*--p);
}


4) my_print.cのヘッダーファイル(my_print.h)

#define DEV_NAME	"/dev/ttyTHS1"
#define BAUD_RATE	B19200
#define BUFF_SIZE	255

extern void my_printf(char *fmt, ...);
extern void serial_init(int);
int fd;

Jetson nanoでカメラ追尾実験(つづき 1)

2021-01-22 12:26:21 | ソフトウェア
そもそもJetson nanoでのカメラ追尾は、ドローンに載せたカメラ映像を地上で受けて、Jetson nanoで物体検出を行い、その物体の座標情報をドローンに返して、ドローンが物体を追いかけることを実現するための事前準備として行った実験です。
とりあえずは人物で追尾していますが、ゆくゆくはドローンで別の物体を追尾させる予定です。

1. 実験システムの構成
(1) Jetson nano
darknetにより、物体の検出を行います。検出結果をシリアルポート(/dev/ttyTHS1)から、カメラの雲台を制御するPICへ送ります。
(2) PIC16F1509
Jetson nanoから送られてきた物体の座標情報をカメラを移動させる座標情報へ変換し、直接サーボモータを駆動します。
回路図、プログラムとも電子工作の実験室を参考にさせていただきました。(感謝)
(3) webカメラ
USBインターフェースの軽いカメラです。
(4) 雲台
アマゾン当たりで売っている、サーボモータを2つ組み合わせたものです。

2. プログラムの構成
(1)Jetson nano
Jetson nanoへのAlexeyAB DarknetおよびYOLOのインストールについては、ネットに沢山の情報があります。
エラー無くbuildが終了し、 ./darknet detector demo cfg/coco.data cfg/yolov4-tiny.cfg yolov4-tiny.weights -ext_output -c 0
で物体検出ができている必要があります。

darknetからフレームの開始と終了に"S"、"E"の文字を入れて、検出結果を外部のプログラムにsocket通信で送ります。
socket通信ではdarknet側をclient、外部のプログラム側をserverにしています。socket通信の部分は、IBMのサンプルコードを利用させていただきました。(感謝)
serverプログラムでは同一の物体のうち、検出面積の一番大きいものを特定します。また、複数種類の物体から指定した物体(今回はperson)の座標の移動平均をとり、ふらつきを抑えた結果をシリアルポートから出力します。

ソースをそのままアップロードできないので、貼り付けます。
あくまで素人の作ったものなので、間違いがあります。もし参考にされる場合は、自己責任でお願いします。

1) darknet側の修正部分
srcフォルダ内image_opencv.cppの追加修正
・80行当たりにinclude fileを追加(不要なものがあるかもしれない)
・define行は事前に作っていたram fileのパスを指定(SDカードでの頻繁な書き換えを避けるため・・不要かも)

/* add source by sakachan */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#define SERVER_PATH "/var/tmp/ram/tpf_unix_sock.server"
/**************************/
  ・887行当たりのDraw Detectionを修正します
// ====================================================================
// Draw Detection
// ====================================================================
extern "C" void draw_detections_cv_v3(mat_cv* mat, detection *dets, int num, float thresh, char **names, image **alphabet, int classes, int ext_output)
{
    try {
        cv::Mat *show_img = (cv::Mat*)mat;
        int i, j;

		/*********** add by sakachan ***********/
		int err;
		int client_socket, rc;
		struct sockaddr_un remote; 
		char start[] = "S";
		char end[] = "E";
		char my_buff[10];

		if (ext_output) {

			memset(&remote, 0, sizeof(struct sockaddr_un));
    
			/****************************************/
			/* Create a UNIX domain datagram socket */
			/****************************************/
			client_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
			if (client_socket == -1) {
				err = errno;
				printf("SOCKET ERROR = %d : %s",err, strerror(err));
//				exit(1);
			}

			/***************************************/
			/* Set up the UNIX sockaddr structure  */
			/* by using AF_UNIX for the family and */
			/* giving it a filepath to send to.    */
			/***************************************/
			remote.sun_family = AF_UNIX;        
			strcpy(remote.sun_path, SERVER_PATH); 
			rc = sendto(client_socket, start, strlen(start), 0, (struct sockaddr *) &remote,
				 sizeof(remote));
			if (rc == -1) {
				err = errno;
				printf("SENDTO ERROR = %d : %s",err, strerror(err));
				close(client_socket);
//				exit(1);
			}
			sprintf(my_buff, "num:%d", num);
			rc = sendto(client_socket, my_buff, strlen(my_buff), 0, (struct sockaddr *) &remote,
				 sizeof(remote));
			if (rc == -1) {
				err = errno;
				printf("SENDTO ERROR = %d : %s",err, strerror(err));
				close(client_socket);
//				exit(1);
			}

		}
		/***************************************/

        if (!show_img) return;
        static int frame_id = 0;
        frame_id++;

        for (i = 0; i < num; ++i) {

			/*********** add by sakachan ***********/
			char out_buf[4096] = { 0 };
			/***************************************/

            char labelstr[4096] = { 0 };
            int class_id = -1;
            for (j = 0; j < classes; ++j) {
                int show = strncmp(names[j], "dont_show", 9);
                if (dets[i].prob[j] > thresh && show) {
                    if (class_id < 0) {
                        strcat(labelstr, names[j]);
                        class_id = j;
                        char buff[20];
                        if (dets[i].track_id) {
                            sprintf(buff, " (id: %d)", dets[i].track_id);
                            strcat(labelstr, buff);
                        }
//                        sprintf(buff, " (%2.0f%%)", dets[i].prob[j] * 100);
						sprintf(buff, " %2.0f%% ", dets[i].prob[j] * 100);	// modified by sakachan
                        strcat(labelstr, buff);
                        printf("%s: %.0f%% ", names[j], dets[i].prob[j] * 100);

						/*********** add by sakachan ***********/
						if (ext_output) {
							strcat(out_buf, labelstr);
						}
						/***************************************/

                        if (dets[i].track_id)
							printf("(track = %d, sim = %f) ", dets[i].track_id, dets[i].sim);
                    }
                    else {
                        strcat(labelstr, ", ");
                        strcat(labelstr, names[j]);
                        printf(", %s: %.0f%% ", names[j], dets[i].prob[j] * 100);

						/*********** add by sakachan ***********/
//						if (ext_output) {
//							strcat(out_buf, labelstr);
//						}
						/****************************/

                    }
                }
            }
            if (class_id >= 0) {
                int width = std::max(1.0f, show_img->rows * .002f);

                //if(0){
                //width = pow(prob, 1./2.)*10+1;
                //alphabet = 0;
                //}

                //printf("%d %s: %.0f%%\n", i, names[class_id], prob*100);
                int offset = class_id * 123457 % classes;
                float red = get_color(2, offset, classes);
                float green = get_color(1, offset, classes);
                float blue = get_color(0, offset, classes);
                float rgb[3];

                //width = prob*20+2;

                rgb[0] = red;
                rgb[1] = green;
                rgb[2] = blue;
                box b = dets[i].bbox;
                if (std::isnan(b.w) || std::isinf(b.w)) b.w = 0.5;
                if (std::isnan(b.h) || std::isinf(b.h)) b.h = 0.5;
                if (std::isnan(b.x) || std::isinf(b.x)) b.x = 0.5;
                if (std::isnan(b.y) || std::isinf(b.y)) b.y = 0.5;
                b.w = (b.w < 1) ? b.w : 1;
                b.h = (b.h < 1) ? b.h : 1;
                b.x = (b.x < 1) ? b.x : 1;
                b.y = (b.y < 1) ? b.y : 1;
                //printf("%f %f %f %f\n", b.x, b.y, b.w, b.h);

                int left = (b.x - b.w / 2.)*show_img->cols;
                int right = (b.x + b.w / 2.)*show_img->cols;
                int top = (b.y - b.h / 2.)*show_img->rows;
                int bot = (b.y + b.h / 2.)*show_img->rows;

                if (left < 0) left = 0;
                if (right > show_img->cols - 1) right = show_img->cols - 1;
                if (top < 0) top = 0;
                if (bot > show_img->rows - 1) bot = show_img->rows - 1;

                int b_x_center = (left + right) / 2;	/* uncomment by sakachan */
                int b_y_center = (top + bot) / 2;
                int b_width = right - left;
                int b_height = bot - top;
                //sprintf(labelstr, "%d x %d - w: %d, h: %d", b_x_center, b_y_center, b_width, b_height);
				sprintf(labelstr, "x:%d y:%d w:%d h:%d", b_x_center, b_y_center, b_width, b_height);	// modified by sakachan

					/****** add by sakachan ******/
					strcat(out_buf, labelstr);
					rc = sendto(client_socket, out_buf, strlen(out_buf), 0, (struct sockaddr *) &remote, sizeof(remote));
					if (rc == -1) {
						err = errno;
						printf("SENDTO ERROR = %d : %s",err, strerror(err));
//						close(client_socket);
//						exit(1);
					}
					/****************************/

                float const font_size = show_img->rows / 1000.F;
                cv::Size const text_size = cv::getTextSize(labelstr, cv::FONT_HERSHEY_COMPLEX_SMALL, font_size, 1, 0);
                cv::Point pt1, pt2, pt_text, pt_text_bg1, pt_text_bg2;
                pt1.x = left;
                pt1.y = top;
                pt2.x = right;
                pt2.y = bot;
                pt_text.x = left;
                pt_text.y = top - 4;// 12;
                pt_text_bg1.x = left;
                pt_text_bg1.y = top - (3 + 18 * font_size);
                pt_text_bg2.x = right;
                if ((right - left) < text_size.width) pt_text_bg2.x = left + text_size.width;
                pt_text_bg2.y = top;
                cv::Scalar color;
                color.val[0] = red * 256;
                color.val[1] = green * 256;
                color.val[2] = blue * 256;

                // you should create directory: result_img
                //static int copied_frame_id = -1;
                //static IplImage* copy_img = NULL;
                //if (copied_frame_id != frame_id) {
                //    copied_frame_id = frame_id;
                //    if(copy_img == NULL) copy_img = cvCreateImage(cvSize(show_img->width, show_img->height), show_img->depth, show_img->nChannels);
                //    cvCopy(show_img, copy_img, 0);
                //}
                //static int img_id = 0;
                //img_id++;
                //char image_name[1024];
                //sprintf(image_name, "result_img/img_%d_%d_%d_%s.jpg", frame_id, img_id, class_id, names[class_id]);
                //CvRect rect = cvRect(pt1.x, pt1.y, pt2.x - pt1.x, pt2.y - pt1.y);
                //cvSetImageROI(copy_img, rect);
                //cvSaveImage(image_name, copy_img, 0);
                //cvResetImageROI(copy_img);

                cv::rectangle(*show_img, pt1, pt2, color, width, 8, 0);
                if (ext_output) {
                    printf("\t(left_x: %4.0f   top_y: %4.0f   width: %4.0f   height: %4.0f)\n",
                    (float)left, (float)top, b.w*show_img->cols, b.h*show_img->rows);
                } else {
                    printf("\n");
                }

                cv::rectangle(*show_img, pt_text_bg1, pt_text_bg2, color, width, 8, 0);
                cv::rectangle(*show_img, pt_text_bg1, pt_text_bg2, color, CV_FILLED, 8, 0);    // filled
                cv::Scalar black_color = CV_RGB(0, 0, 0);
                cv::putText(*show_img, labelstr, pt_text, cv::FONT_HERSHEY_COMPLEX_SMALL, font_size, black_color, 2 * font_size, CV_AA);
                // cv::FONT_HERSHEY_COMPLEX_SMALL, cv::FONT_HERSHEY_SIMPLEX
            }
        }
        if (ext_output) {

			/*****************************/
			/* Close the socket and exit */
			/*****************************/
			rc = sendto(client_socket, end, strlen(end), 0, (struct sockaddr *) &remote,
				 sizeof(remote));
			if (rc == -1) {
				err = errno;
				printf("SENDTO ERROR = %d : %s",err, strerror(err));
//				close(client_socket);
//				exit(1);
			}
			rc = close(client_socket);
			/*****************************/

            fflush(stdout);
        }
    }
    catch (...) {
        cerr << "OpenCV exception: draw_detections_cv_v3() \n";
    }
}
// ----------------------------------------

Jetson nanoでカメラ追尾実験

2021-01-12 18:24:12 | ソフトウェア
久しぶりの投稿となりましたが、何とかログインできました。
いろいろ細々とやっていましたが、Jetson nanoで物体検出+カメラ追尾ができたので、メモを残しておきます。
まずは、人物(person)を認識してwebカメラが動いている様子です。

椅子に座って、ディスプレイをスマホで映していますが、良く見ると少し遅れて追尾しているのがわかります。

全体の構成です。


物体検出はAlexeyAB Darknet YOLOv4で動かしています。今のところ、オリジナルのYOLOv4を使っています。(追記:yolov4-tinyです)
検出した複数のオブジェクトごとに、検出面積の一番大きいオブジェクトをDarknet内で計算して別プログラムに送り、そこで指定したオブジェクト(person)の座標をUARTから出力します。

Webカメラです。白い台座はサーボの雲台に取り付けるための治具です。3Dプリンターで作りました。


Jetson nanoです。UARTと+3.3Vを引き出しています。


Jetson nanoからのX、Yの座標情報を受けて、サーボを動かすPICです。右側の緑の基板はJetson nanoからのUART 3.3VをPIC、サーボ側の5Vに変換する秋月電子のレベル変換基板です。


メモのために、プログラムをアップロードしたかったのですが、久しぶりの投稿なのでアップロードの仕方がわかりません。
アップロード方法がわかりましたら、別途投稿します。(追記:goo blogではテキストファイルなどのアップロードはできないようです)