分类目录Ubuntu

PCA9685舵机控制板

https://github.com/adafruit/Adafruit_Python_PCA9685

PCA9685 c++ WiringPi https://www.raspberrypi.org/forums/viewtopic.php?t=67618

https://www.jianshu.com/p/782058c93a7a
https://www.jianshu.com/p/b2a6306e583d?from=timeline&isappinstalled=0
https://zhuanlan.zhihu.com/p/22805173

https://www.cnblogs.com/yjphhw/p/10649940.html
https://blog.csdn.net/qq_41559171/article/details/87950642
http://www.cirmall.com/bbs/thread-97290-1-1.html


假设舵机为50HZ的频率,脉宽为0.5ms~2.5ms,12位分辨率(4096级),相关计算如下:

PWM周期:1/50=0.02s=20ms=20000us

时间分辨率:200000/(2^{12})=4.88us

最大脉宽时间:2.5ms-0.5ms=2ms=2000us

最大脉宽时间可分成的份数:2000us/4.88us=410

0-180度的舵机,角度分辨率:180^{\circ}/410=0.439^{\circ}


如果在舵机控制中,采用内置晶振,取osc_clock=25000000,update_rate=50(舵机控制频率50Hz)

设置角度
60度对应的脉宽=0.5ms+(60/180)*(2.5ms-0.5ms)=1.1666ms
利用占空比=1.1666ms/20ms=off/4096,off=239,50hz对应周期20ms
setPWM(num,0,239);;;;当然也可以利用SERVO000和SERVO180计算

C++

发展

C 面像过程 ->1979 C 带类的C->1983 C++面像对象OOP->C++98 STL标准库-> 2011 C++11 Boost库 -> 14->17->20 每三年一版
三大特性:封装 继承 多态
强大的抽象封装能力
高性能
低功耗
语法相对复杂,学习曲线较陡
注意开发规范,否则代码难维护
开发成本高

.cpp编译 -> .asm -> 链接.obj -> .exe
qt自带gcc windows
xocde glang
g++
源码.c -E预处理.i -S编译.s -c汇编.o gcc 链接 -o 指定输出文件名
一步完成 gcc -o a.exe a.c

教程

  1. https://www.runoob.com/cplusplus/cpp-tutorial.html
    char 1 -128~127|0~255
    shor int 2 -32768~32767|0-65535
    long int 4 -21247483648~2147483647|0~4294967295
    int 4 -21247483648~2147483647|0~4294967295
    float 4 3.4e+/-38
    double 8 1.7e+/-308
    long double 8 1.7e+/-308
    boolr 1 true/false
    wchart_t 2

C++ keywords: https://en.cppreference.com/w/cpp/keyword
定义常量 #define(宏替换) const(建议)
前缀0x十六进制 0八进制 L’x’宽字符 后缀U符号 L long
\转义字符 \ \’ \” \? \a 警报铃声 \b 退格键 \f 换页符 \n 换行符 \r 回车 \t 水平制表符 \v 垂直制表符 \ooo 一到三位的八进制数 \xhh… 一个或多个数字十六进制数

逻辑运算&& || ! 位运算 & ^ | ~ << >>(对有符号的数,尽可能不要使用右移运算)
sizeof(), Condition ? X:Y, , . , ->, Cast(不建议使用),&(取变量地址),*(指向变量)

补码(两种计算方式) 大小端

注释: // /* */

typedef struct{
short Sunday=0;
….
}Week;

默认成员权限公开的
struct A{
}
默认成员权限私有的
class Trace{
public:
virtual double Area() const=0; //多态,子类方法实现不一致时加virtual,const=0表示简单实现,虚的
}
typedef enum __COLOR{
RED,
}color;

union Score{
}
定义常量 #define const
声明 enum color{red,gree}; color color1;

注意结构体数据对齐问题32位CPU4字节
连续分配修改编译选项:
V C++: #pragma pack(n)
g++: attribute(aligned(n)) attribute(packed)

int arr[2] = {1,2}; arr[2] =5;int * p =arr;*(p+2)=5;
int len = sizeof(arr)/sizeof(arr[0]);
差一错误, (]左闭右开,
循环时尽可能满足”空间局部性”
新型数组vector(面向对象方式的动态数组)
字符串是以空字符(‘\0′)结束的字符数组,在声明时应预留一个额外的元素
双引号是字符串,单引号是一个字
char c1=0; ->0x00 char c2=’\0′; ->0x00 char c3=’0’;->0x30
UTF-8:1byte,兼容ASCII码,效率高,变长(不方便内部随机访问),无字节序问题
UTF-16:BE/LE 2bytes 定长(不方便内部随机访问),有字节序问题
UTF-32:BE/LE 4bytes 定长(不方便内部随机访问),有字节序问题
windows的文件可能有BOM,在其它平台使用可去掉

指针表示方法T,存贮地址,间接访问值的操作t,指针自身也有地址t,&t
char ch=’a’; char* cp=&ch;
表达式 左值 右值
&ch 非法 ch的地址
*cp ch的地址 ch的值=a
*cp+1 非法 ch的值+1=b
*(cp+1) ch的地址+1 ch的地址+1的值 注意内存溢出

char s[] = ‘hello’; //数组s不可变,s[index]的值可以变
char* P1 = s;
char* p= “hello”; //指针p可变,p[index]的值是否可变取决于指区间的存储区域是否可变
float* P=&F;
指针指向的内容P = 内容F <- 内容地址&F
int
a[4];批针的数组,数组中的每个内容是指针
int(b)[4]; 数组的指针,指针指向的是一个数组
指向指针的指针 int** c=&P1;
指针确保初始化并赋值,小心意外发生
int
a = NULL;不指向任何东西时的状态,指针不用时置NULL,使用前先判断是否为NULL,nullptr(新)
定义函数指针: typedef void(*FuncPtr)(); FuncPtr funcPtr;

const 常量,修饰不充许修改,先看左侧最近的部分,没有则看右侧
char const* s; const char *; 内容不可变
char* const s; 指针不可变
char const* const s; 指什及内容都不可变

全局初始化区GVAR
全局末初始化区bss
代码区text
堆 heap-队列 先进先出 ,整个程序范围内new,malloc建立,注意delete,free释放,
栈 stack 先进后出,函数体内,语句块{},windows默认大小1M,linux8/10M,ulimit -s查看

内存分配 地址低–>高
代码–>数据(GVAR–>常量–>BSS–> 堆cd )–> 空闲内存 <–栈cc <–系统保留

资源管理方案RAII,依托栈和析构函数管理所有资源,智能指针代表std:auto_ptr(C++11废弃,17删除),销毁时管理的对象也自动deldete,会发生所有权转移
unique_ptr(局限较大),专属所有权,不支持复制和赋值,可以使用std::move()进行控制权的转移
auto w=std:make_unique< int >(10); *(w.get());
auto i = unique_ptr< int>(new int(10));
boost:shared_ptr,引用计数共享一个对象,引用计数为0时没有被使用,可以进行析构销毁,防止循环引用
weak_ptr,观察者模式与shared_ptr共同工作,只对shared_ptr进行引用,不计数,shared_ptr销毁时也一同销毁.

引用是一种特殊的指针,不充许修改的指针,int x=1;int& rx=x;相当于小名,别名

string.h 字符串常用的操作,尽量使用安全的API
底层操作有风险时在预处理器定义#define _CRT_SECURE_NO_WARNINGS,关闭安全警告C4996
strlen(s) 返回字符串的长度,不包含’\0′
strlen_s()
strcmp(s1,s2) 相同返回0,s1<s2返回小于0,否则大于0,自左向右以ASCII值比较
strcpy(s1,s2) 字符串s2拷贝复制到s1,注意s1空间长度
strcpy_s(s1,STR_LEN_NUM,s2)
strnpy(s1,s2,n)字符串s2前n个字符拷贝复制到s1,注意s1空间长度
strnpy_s(s1,STR_LEN_NUM,s2,n)
strcat(s1,s2) 字符串s2拼接到s1,注意s1空间长度
strcat_s(s1,STR_LEN_NUM,s2)
strchr(s1,ch) 查找s1中字符ch的第一次出现位置
strstr(s1,s2) 查找s1中字符串s2的第一次出现位置

string(新)namespace std 对性能要求不是特别高的可以使用
string s; string s=”hello”; string s (“hello”); string s= string(“hello”);
s.length() s.size() s.capacity() == != > >= < <=(比较原则同老版)
转为C风格的字符串const char* c_str = s.c_str();
随机访问某个字符 s[0] == “h”;
字符拷贝 string s2 = s;
连接 +, += string s3 = s+s2; s += s2;

if(p !=NULL){
….
}else if(…){
….
}else{
….
}

switch(){
case: xxx:
{…
break;}
default:
break;
}
while(..){…}
do{…}while(..);
for (…;..;..){…}

重载时函数另一种指针方式调用,每个函数都有一个入口地址
int(p)(int); 定义指针函数,与test函数相对应,数据类型(指针变量名)(参数表)
p=test; 指向test函数
int result = p(1); 调用test函数
注意:int
p(int); //是函数,返回的是一个指针

命名空间
namespace coust{
int test(int);
}
using namespace coust
coust::test(1)

VC++ 调作约定设置影响传参(默认/Gd)
优化-内联函数的扩展->/Ob1
inline int Xxxx(){} 内联过于复杂可能失效

传统的 printf,scanf,getch,gets..坑多可移植性差,不可编程
C++I/O流,可编程,简化编程iostream.h,fstream.h
ios<--istream<--cin
                    <--ifstream
                    |<--iostream <--fstream
    <--ostream<--ofstream
                    <--cout,cerr,clog

streambuf<--strstreambuf
                <--filebuf

IO标准缓存模式:按块(文件),按行(\n),不缓存
#include <iostream> //流
#include <string.h> //使用字符串
#include <string> //使用新字符串
#include <assert> //断言
#include<vector> //动态数组
#include<list> //列表
#include<queue> //队列
#include<stack> //栈
#include<map> //map
#include<algorithm> //算法
#include<memory> //内存
#include <math.h> //数学
vector<int> vec={1,2};
vec.push_back(3);vec.capacity/size(容量/已存贮的个数)
vec.insert(--vec.end(),4);
vec.pop_back();vec.erase(vec.end()-1);

避免同一个文件被include多次
#ifndef _SOMEFILE_H
#define _SOMEFILE_H
…通过宏来防止,可移植性好,无法防止宏名重复,不易排错
#endif //_SOMEFILE_H

#pragma once 通过编译器来防止,易排错,可移植性不好

浅拷贝:只拷贝指针地址,易引发多次释放
深拷贝:拷贝指针指向重新分配堆内存,浪费空间,但不会导致多次释放
写时复制优化策略
引用计数
C++新标准的移动语义

void* NULL nullptr

//C定义
#define NULL ((void *) 0)
//C++定义
#ifndef NULL
    #ifdef _cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif
//C++11定义
nullptr->(void *)0 ,NULL ->0;

类型转换
隐式转换: double f = 1.0/2;
显式转换: (类型说明符)(表达式)
C类型转换时任意类型之间都可以转换,编译器无法判断其正确性,难于错误定位
C++:const_cast用于转换指针或引用,去掉类型的const属性
reinterpret_cast重新解释类型,很危险操作,不检查指向内容,也不检查指针类型本身,但要转换前后的类型所占用内存大小一致,否则引发编译错误
static_cast用于基本转换,有继承关系对象和类指针之间的转换,由程序员确保转换是安全的
dynamic_cast 只能用于含有虚函数的类,必须用在多态体系中,向下转换时如有非法情况对于指针返回NULL


编程思想

设计模式:面向对象可复用的常见23种,反模式,企业应用构架模式…
创建型:结构型:行为型
单列模式-思路有一个私有构造函数,无法通过new直接实例化,包含一个静态私有成员变量instance和静态公有方法instance(),将构造与析构私有化
观察者模式-订阅,解耦
适配器模式(Adapter)-1使用多重继承,接口继承方式.2组合的方式

泛型编程思想
更加直接抽象,静态期多态,将算法 与特定类型,结构剥离,尽可能代码复用 template


STL
空间配制器allocator
容器 序列式string vector list deque 关联式map set multimap multiset
适配器stack queue priority_queue
仿函数greater less …
算法find swap reverse sort merge…
迭代器iterator const_iterator reverse_iterator const_reverse_iterator

for_each()
map<string,double> sts;
sts[“lim”]=99.9; //可修改值
sts.insert(pair<string,double>(“zs”,100.0));
sts.insert(map<string,double>::value_type(“lx”,96.8));
iter =sts.begin();
注意迭代器erase时失效问题,sts.erase(iter++);iter =sts.erase(iter);iter=sts.find(“zs”);sts.erase(iter); int n= sts.erase(‘”zs”);
sts.erase(sts.begin(),sts.end());


引入C
extern “C”{
#include “libavformat/avformat.h”
}

显性包函
#pragma comment(lib,”avformat.lib”)
附加目录
属性-常规:输入-附加依赖项


UI

  1. Duilib
  2. clayui
  3. libuiDK windows
  4. QT IDE->Qt Creator
    https://www.qt.io/
  5. wxWidgets库

网络
1. Linux下的Muduo

  1. BOOST的ASIO库和非boost库版的asio
    http://think-async.com/
    是一个异步IO库,封装了对Socket的常用操作,简化了基于socket程序的开发。支持跨平台。 注:要配置Boost库,还要熟悉Boost,还是麻烦;

  2. libev
    是一个C语言写的,只支持linux系统的库

  3. libuv
    linux下用libev实现,Windows下用IOCP实现;
  4. Libevent
    是一个C语言写的网络库, 官方主要支持的是类linux 操作系统, 最新的版本添加了对windows的IOCP的支持。由于IOCP是异步IO,与linux下的POLL模型,EPOLL模型,还有freebsd的KQUEUE等这些同步模型在用法上完全不一致,所以使用方法也不一样,就好比ACE中的Reactor和Proactor模式一样, 使用起来需要转变思路。如果对性能没有特别的要求, 那么使用libevent中的select模型来实现跨平台的操作, select模型可以横跨windows, linux, unix,solaris等系统。
  5. ACE库
    http://www.cs.wustl.edu/~schmidt/ACE.html
    是一个大型的中间件产品,代码20万行左右,过于宏大,一堆的设计模式,架构了一层又一层,使用的时候, 要根据情况,看你从那一层来进行使用。支持跨平台。注:除非特别需要,否则不建议;
  6. Qt有nerwork

  7. POCO
    POCO C++ Libraries 提供一套 C++ 的类库用以开发基于网络的可移植的应用程序,功能涉及线程、线程同步、文件系统访问、流操作、共享库和类加载、套接字以及网络协议包括:HTTP、 FTP、SMTP 等;其本身还包含一个 HTTP 服务器,提供 XML 的解析和 SQL 数据库的访问接口。POCO库的模块化、高效的设计及实现使得POCO特别适合嵌入式开发。在嵌入式开发领域,由于C++既适合底层(设备I/O、中断处理等)和高层面向对象开发,越来越流行。
    http://pocoproject.org/

  8. libcurl
    libcurl是免费的轻量级的客户端网络库,支持DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet, TFTP. 支持SSL, HTTP POST, HTTP PUT, FTP上传, HTTP form上传,代理,cookies, 用户名与密码认证。
    如果你开发的是客户端,libcurl是一个不错的选择。
    http://curl.haxx.se/libcurl/

  9. c++ sockets library
    封装了sockets C API的C++类库。支持SSL, IPv6, tcp 和 udp sockets, sctp sockets, http协议, 高度可定制的错误处理。
    http://www.alhem.net/Sockets/

  10. C++ Socket Class for Windows ,http://www.adp-gmbh.ch/win/misc/sockets.html;
    注:这个文章的文件不可以下载自己复制 保存 h 和 cpp文件;
    觉得很不错,也很简单,例子很明了; 不过有一个小小的缺点,只收发字符串,而且接收函数中的判断也有点不好;如果要用这个库传输文件等数据,需要 自己重载 收发函数,不过也很简单;

  11. SimpleSockets,
    http://sockets.carrierlabs.com/index.html
    跨平台,支持 TCP ,UDP;注:这个编译简单而且用起来也简单

Halcon

Halcon:机器视觉行业里知名的商业视觉库,非开源的,在国内市场份额处于第一,其提供了1500个多个API算子供开发人员使用,有些编程基础的都可以轻松的入门,其调试也是很方便的,断点单步运行,图像变化随时可以看到。Halcon算子可以被C++,VB,C#,Delphi等开发语言调用,同时算子也提供COM版,原则上,支持COM的所有语言都是可以调用Halcon算子的,所以其开发灵活性和调试方便性深受广大开发者喜欢。

相机镜头

定焦镜头

  1. 广角镜头:一般低于35mm的镜头为广角镜头,低于28mm的为超广角镜头。广角镜头视角广,纵深感强,景物会有变形,比较适合拍摄较大场景的照片,如建筑、集会等。
  2. 中焦镜头:一般在36mm到134mm的镜头为中焦镜头。中焦镜头比较接近人正常的视角和透视感,景物变形小,适合拍摄人像、风景、旅游纪念照等。
  3. 长焦镜头:一般高于135mm以上的镜头为长焦镜头,也被称为远摄镜头。其中,大于300mm以上的为超长焦镜头。长焦镜头视角小,透视感弱,景物变形小,适合拍摄无法接近的事物,如野生动物、舞台等,也可以利用长焦镜头虚化背景的作用,拍摄人像。

变焦镜头


增倍镜

增倍镜是能够增大相机光学变焦倍数的镜头。如果光学变焦倍数不够,我们可以在镜头前加一增倍镜,其计算方法是这样的,一个2倍的增倍镜,套在一个原来有4倍光学变焦的数码相机上,那么这台数码相机的光学变焦倍数由原来4倍变为8倍,即以增距镜的倍数和光学变焦倍数相乘所得。但是会降低相对应的有效光圈,如2倍增倍镜就会降低相当于2级的有效光圈。但多数对应的镜头仍能完成自动对焦下的拍摄。


传感器

CCD尺寸一般用英寸来表示,1/2就是二分之一英寸,是对角线尺寸。靶面尺寸就是CCD尺寸,数码传感器CCD的尺寸,一英寸换算成毫米是16mm,而不是通常的25.4mm。一般长宽比为4:3

1/2英寸对角线就是8mm,按4:3的比率,长和宽就是6.4X4.8。数码相机的传感器也一样可以这样换算。理论上靶面尺寸越大越好,但是尺寸大跟配合的镜头也有很大关系,所以考虑到性价比,

现在主流的是1/3和1/4,1/3主要用在固定摄像机,1/4主要用在变倍摄像机上。还有1/2主要是用在工业上的,1/2靶面的用镜头都是非球面镜头。要不然看到的图像是有弧度的。

数码相机制造业界通用的规范是:
1 英寸大小=长12.8×宽9.6mm 对角线为16mm 的4:3 比例传感器所对应的面积。
1/2 英寸传感器的对角线就是1 英寸的一半,为8mm,
1/4 英寸就是1 英寸的1/4,对角线长度即为4mm。

CCD/CMOS尺寸越大,感光面积越大,成像效果越好。而相同尺寸的CCD/CMOS像素增加固然是件好事,但这也会导致单个像素的感光面积缩小,有曝光不足的可能。


选型

视野范围FOV
检测精度
工作距离WD
工业相机的分辨率
假设检测一个物体的表面划痕,要求拍摄的物体大小为108mm,要求的检测精度是0.01mm。首先假设我们要拍摄的视野范围在1210mm,那么相机的最低分辨率应该选择在:(12/0.01)(10/0.01)=12001000,约为120万像素的相机,也就是说一个像素对应一个检测的缺陷的话,那么最低分辨率必须不少于120万像素,但市面上常见的是130万像素的相机,因此一般而言是选用130万像素的相机。但实际问题是,如果一个像素对应一个缺陷的话,那么这样的系统一定会极不稳定,因为随便的一个干扰像素点都可能被误认为缺陷,所以我们为了提高系统的精准度和稳定性,最好取缺陷的面积在3到4个像素以上,这样我们选择的相机也就在130万乘3以上,即最低不能少于300万像素,通常采用300万像素的相机为最佳,还应该注意的是,H.264格式非常大。

通信接口:USB 2.0、USB 3.0、GigE、Camera Link、PoE以及VGA
–USB 2.0 :这种较便宜的系统需要通过电缆连接到摄像机(最长可以达到5米)。在理论传输量高达480mbps的情况下,该系统对于数据传输来说可能是不可靠的。现在已被USB 3.0取代
–USB 3.0:此接口与USB 2.0具有相似的功能,但具有更高和更可靠的传输量。
–CameraLink:此通信接口具有非常高的传输量(高达6 Gbits/s),非常适合高分辨率。价格较高。 电缆长度可以达到10米。
–GigE(或Giga以太网):这种比较便宜的通信接口的传输量高达1 Gbit/s,如果您需要较长的电缆长度(它可以达到100米),就可以选择这种接口。
–PoE(或以太网供电):它的传输量可达1 Gbit/s,其主要优点是可以在进行数据通信时为摄像机供电。
–VGA(视频图形阵列):VGA卡考虑到640×480的分辨率。它们可以传送多达256种颜色。这个接口开始消失了。

像传感器类型:
CCD
拍运动物体,首先CCD
CCD传感器价格较低。它们的优点是性价比非常高。但是,CCD传感器的检测光谱只对可见元素敏感。此外,它们的缺点是对炫光非常敏感,并且有许多寄生电子,这些电子随温度急剧增加。因此需要冷却CCD传感器以避免产生热噪音。
CMOS
在拍运动物体, CMOS采用帧爆光的可以代替CCD
CMOS传感器比CCD传感器的价格更昂贵,但具有很多优点。首先,与CCD传感器相比,它们的质量更高(尽管最新一代CCD传感器的技术使其更接近CMOS传感器)。更重要的是,它们适用于微光视觉。因此,弱光照水平不会造成任何问题。CMOS传感器的读取速度也比大多数CCD传感器高,而且更节能。
CMOS传感器与CCD传感器一样,并用于相同的应用(扫描仪、实验室、包装线监测等)。
FPA
FPA传感器的主要优点是其非常高的灵敏度和出色的图像质量。但是,图像分辨率不是很高(通常为320 x 240像素)。这种图像传感器非常大而且很昂贵,可以用于武器制导和检查摄像机,以及空间和医学成像。
微辐射热测量计
微辐射热计传感器仅用于热像仪,主要使用红外辐射。微辐射热计摄像机机重量轻,节能,可以实现非常快速的输出通信。也就是说,它们非常昂贵。此外,它们比冷却温度传感器敏感度低,因为像CCD传感器一样,寄生电子的数量随着温度的升高而增加。这可能会导致分辨率问题。但是,当前的技术倾向于解决这些问题,从而使微辐射热计传感器更加高效。
得益于其重量轻、体积小和低功耗,可以将微辐射热计传感器安装在施工头盔或便携式设备上。

色彩:
黑白:一般黑白
彩色:与图像颜色有关

帧率
根据要检测的速度,选择相机的帧率一定要大于或等于检测速度,就是你处理图像的时间一定要快,一定要在相机的曝光和传输的时间内完成。

面阵,线阵
在速度快,精度高,面阵达不到的情况下用线阵

光源

  1. 条形光源特点:
    1. LED均成直线或其组合排列,照度高,光源指向性强,照明效果也符合直线型规律或其叠加。
    2. 采用特殊光学透镜,有效改变LED照射角度。
    3. 采用标准模具成型、拼接安装,结构稳定
    4. 可选漫射版导光,光线均匀。
    5. 尺寸、颜色可以根据实际需求定制。
    6. 条光照射宽度最好大于检测的距离,否则可能会照射距离远造成亮度差,或者是距离近而幅射面积不够;
    7. 条光长度能够照明所需打亮的位置即可,无须太长造成安装不便,同时也增加成本,一般情况下,光源的安装高度会影响到所选用条光的长度,高度越高,光源长度要求越长,否则图像两侧亮度传经比中间暗;
    8. 如果照明目标是高反光物体,最好加上漫射板,如果是黑色等暗色不反光产品,也可以拆掉漫射板以提高亮度。
    9. 应用场合:可用于电子元件识别与缺陷检测;文字、形状识别等;

环形光源分为垂直照射环形光源,角度照射环形光源,低角度环形光源,无影环形光源。
1. 了解光源安装距离,过滤掉某些角度光源;例如要求光源安装尺寸高,就可以过滤掉大角度光源,选择用小角度光源,同样,安装高度越高,要求光源的直径越大;
1. 目标面积小,且主要特性在表面中间,可选择小尺寸0角度或小角度光源;
1. 目标需要表现的特征如果在边缘,可选择90度角环光,或大尺寸高角度环形光;
1. 检测表面划伤,可选择90度角环光,尽量选择波长短的光源。

  1. 垂直照射环形光源的特点:
    1. 用高亮度LED高密度安装,提供红、绿、蓝、白、红外、紫外等颜色;
    2. 照明面积较大,光照均匀性好,适用于较大面积照明;
    3. 可用于底基和线路板定位、晶片部件的检查等。

角度照射环形光源的特点:
1. 用超高亮度LED高密度安装,提供红、绿、蓝、白、红外、紫外等颜色;
1. 在一定工作距离下,光束集中亮度高,均匀性好,照射面积相对较小;
1. 用于塑胶容器检查、工件螺孔定位、标签检测、管脚、集成电路印子检测等类似用途。
1. 用于文字形状识别和缺陷检测。

低角度环形光源的特点:
1. 用超高亮度LED进行水平照射,提供红、绿、蓝、白、红外、紫外等颜色;
1. 低角度照射光照明对表面凹凸表现力强,特别适用于晶片或者玻璃底基上的伤痕检测,刻印文字的读取、工件边缘轮廓抽取、划痕检测等。

无影环形光源的特点:
1. 使用超高亮度LED进行光线垂直照射,提供红、绿、蓝、白、红外、紫外等颜色;
1. 漫反射二次光源,光照均匀,无影效果,对镜面反射体如晶片或玻璃基底上的伤痕检测,金属表面检测效果突出。

背光源的特点:
1. 选择背光源时,根据物体的大小选择合适大小的背光源,以免增加成本造成浪费
1. 背光源四周一条由于的外壳遮挡,因此其亮度会低于中间部位,因此,选择背光源时,尽量不要使目标正好位于背光源边缘;
1. 背光源一般在检测轮廓时,可以尽量使用波长短的光源,波长短的光源其衍射性弱,图像边缘不容易产生重影,对比度更高;
1. 背光源与目标之间的距离可以通过调整来达到最佳的效果,并非离得越近效果越好,也非越远越好;
1. 检测液位可以将背光源侧立使用;
1. 圆轴类的产品,螺旋状的产品尽量使用平行背光源。
1. 使用超高亮度LED进行正面垂直照射,提供红、绿、蓝、白、红外、紫外等颜色;
1. 经过特殊导光材料优化光路,平行性好;
1. 适合外观检测,或者尺寸测量;
1. 可安装于底部背光源和侧部背光源表面;

同轴光源的特点:
1. 采用特殊照明设计,达到高均匀性;
1. 特殊导热材料,散热好,稳定性高;
1. 使用分镜头,提供优质光学效果;
1. 选择同轴光时主要看其发光面积,根据目标的大小来选择合适发光面积的同轴光;
1. 同轴光的发光面积最好比目标尺寸大1.5~2倍左右,因为同轴光的光路设计是让光路通过一片45度半反半透镜改变,光源靠近灯板的地方会比远离灯板的亮度高,因此,尽量选择大一点的发光面避免光线左右不均匀;
1. 同轴光在安装时尽量不要离目标太高,越高,要求选用的同轴光越大,才能保证才均匀性。
1. 广泛用于半导体、PCB板、以及金属零件的表面成像检测,具有很好的均匀性,对光洁表面上的异常特征成像突出,表现力好。

平行同轴光
1. 平行同轴光光路设计独特,主要适用于检测各种划痕;
1. 平行同轴光与同轴光表现的牲点不一样,不能替代同轴光使用;
1. 平行同轴光检测划伤之类的产品,尽量不要选择波长长的光源。

点光源的特点:
1. 使用大功率LED,颜色可选,体积小,发光强度高;
1. 可组合使用做重点照明或者补光照明;
1. 光钎卤素灯的替代品,可配合同轴镜头代替同轴光使用。


各种颜色的光源适用场景介绍:

1. 白色光源(W)
白色光源通常用色温来界定,色温高的颜色偏蓝色(冷色,色温>5000K),色温低的颜色偏红(暖色,色温<3300K),界于3300与5000K之间称之为中间色,白色光源适用性广,亮度高,特别是拍摄彩色图像时使用更多。

  1. 蓝色光源(B)
    蓝色光源波光为430-480之间,适用产品:银色背景产品(如钣金,车加工件等)、薄膜上金属印刷品。

  2. 红色光源(R)
    红色光源的波长通常在600-720之间,其波长比较长,可以透过一些比较暗的物体,例如底材黑色的透明软板孔位定位、绿色线路板线路线路检测,透光膜厚度检测等,采用红色光源更能提高对比度。

  3. 绿色光源(G)
    绿色光源波长510-530,界于红色与蓝色之间,主要针对产品:红色背景产品,银色背景产品(如钣金,车加工件等)。

  4. 红外光(IR)
    红外光的波长一般为780-1400,大多采用940波长的红外光,红外光属于不可见光,其透过力强。一般LCD屏检测、视频监控行业应用比较普遍。

  5. 紫外光(UV)
    紫外光的波长一般为190-400,我司主要采用385波长的紫外光,其波长短,穿透力强,主要应用于证件检测、触摸屏ITO检测、布料表面破损、点胶溢胶检测等方面,金属表面划痕检测等。

常见的打光方式有以下几种:前面打光法;背面打光法;结构光打光法;混合多方式照明;特殊式打光法。
1. 光源垂直照射
特点:照射面积大、光照均匀性好、适用于较大面积照明。可用于基底和线路板定位、晶片部件检查等(0角度环光、面光源)。
1. 角度照射
特点:在一定工作距离下,光束集中、亮度高、均匀性好、照射面积相对较小。常用于液晶校正、塑胶容器检查、工件螺孔定位、标签检查、管脚检查、集成电路印字检查等(30、45、60、75等角度环光)。注意观察光源的角度。
1. 低角度照射
特点:对表面凹凸表现力强。适用于晶片或玻璃基片上的伤痕检查(90度环光
)。
1. 背光照射
特点:发光面是一个漫射面,均匀性好。可用于镜面反射材料,如晶片或玻璃基底上的伤痕检测;LCD检测;微小电子元件尺寸、形状,靶标测试。(背光源、平行背光源)。
1. 多角度照射
特点:RGB三种不同颜色不同角度光照,可以实现焊点的三维信息的提取。适用于组装机板的焊锡部份、球形或半圆形物体、其它奇怪形状物体、接脚头(AOI光源)。
1. 同轴光照明
特点:类似于平行光的应用,光源前面带漫反射板,形成二次光源,光线主要趋于平行。用于半导体、PCB板、以及金属零件的表面成像检测,微小元件的外形、尺寸测量。(同轴光源,平行同轴光源)

pysimplegui

https://pysimplegui.readthedocs.io/en/latest/

pip install pysimplegui
pip3 install pysimplegui

sudo apt-get install python3-tk


import PySimpleGUI as sg
print(sg)
print(sg.version)

Popup(args=*<1 or N object>,
    title=None,
    button_color=None,
    background_color=None,
    text_color=None,
    button_type=0,
    auto_close=False,
    auto_close_duration=None,
    custom_text=(None, None),
    non_blocking=False,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=False,
    location=(None, None),
    any_key_closes=False,
    image=None,
    modal=True)

sg.popup('popup')  # Shows OK button
sg.popup_ok('popup_ok')  # Shows OK button
sg.popup_yes_no('popup_yes_no')  # Shows Yes and No buttons
sg.popup_cancel('popup_cancel')  # Shows Cancelled button
sg.popup_ok_cancel('popup_ok_cancel')  # Shows OK and Cancel buttons
sg.popup_error('popup_error')  # Shows red error button
sg.popup_timed('popup_timed')  # Automatically closes
sg.popup_auto_close('popup_auto_close')  # Same as PopupTimed

popup_scrolled(args=*<1 or N object>,
    title=None,
    button_color=None,
    background_color=None,
    text_color=None,
    yes_no=False,
    auto_close=False,
    auto_close_duration=None,
    size=(None, None),
    location=(None, None),
    non_blocking=False,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=False,
    font=None,
    image=None,
    modal=True)
sg.popup_scrolled(my_text)
sg.popup_scrolled(my_text, size=(80, None))

popup_no_wait(args=*<1 or N object>,
    title=None,
    button_type=0,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=False,
    auto_close_duration=None,
    non_blocking=True,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=False,
    location=(None, None),
    image=None,
    modal=False)

popup_get_text(message,
    title=None,
    default_text="",
    password_char="",
    size=(None, None),
    button_color=None,
    background_color=None,
    text_color=None,
    icon=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=False,
    location=(None, None),
    image=None,
    modal=True)
text = sg.popup_get_text('Title', 'Please input something')
sg.popup('Results', 'The value returned from PopupGetText', text)

popup_get_file(message,
    title=None,
    default_path="",
    default_extension="",
    save_as=False,
    multiple_files=False,
    file_types=(('ALL Files', '*.*'),),
    no_window=False,
    size=(None, None),
    button_color=None,
    background_color=None,
    text_color=None,
    icon=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=False,
    location=(None, None),
    initial_folder=None,
    image=None,
    modal=True)

popup_get_folder(message,
    title=None,
    default_path="",
    no_window=False,
    size=(None, None),
    button_color=None,
    background_color=None,
    text_color=None,
    icon=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=False,
    location=(None, None),
    initial_folder=None,
    image=None,
    modal=True)

popup_animated(image_source,
    message=None,
    background_color=None,
    text_color=None,
    font=None,
    no_titlebar=True,
    grab_anywhere=True,
    keep_on_top=True,
    location=(None, None),
    alpha_channel=None,
    time_between_frames=0,
    transparent_color=None,
    title="",
    icon=None)

one_line_progress_meter(title,
    current_value,
    max_value,
    key="OK for 1 meter",
    args=*<1 or N object>,
    orientation="v",
    bar_color=(None, None),
    button_color=None,
    size=(20, 20),
    border_width=None,
    grab_anywhere=False,
    no_titlebar=False)
for i in range(1,10000):
    sg.one_line_progress_meter('My Meter', i+1, 10000, 'key','Optional message')

Debug Output(easy_print = Print = eprint)
for i in range(100):
    sg.Print(i)

sg.theme('Dark Blue 3')  # please make your windows colorful
layout = [ [sg.Text('Enter a Number')],
           [sg.Input()],
           [sg.OK()] ],
           [sg.InputText(), sg.FileBrowse()],
           [sg.Submit(), sg.Cancel()]]

window = sg.Window('Enter a number example', layout)
event, values = window.read()

使用菜单
# ------ Menu Definition ------ #
menu_def = [['&File', ['&Open', '&Save', 'E&xit', 'Properties']],
            ['&Edit', ['Paste', ['Special', 'Normal', ], 'Undo'], ],
            ['&Help', '&About...'], ]
layout = [
    [sg.Menu(menu_def, tearoff=True)],

    # 定义右键菜单
    right_click_menu = ['Unused', ['Right::_Right_', '!&Click', '&Menu', 'E&xit', 'Properties']]
    # 定义布局
    layout = [[sg.Menu(menu_def, tearoff=False, pad=(20,1))],
               # 底部菜单
              [sg.ButtonMenu('ButtonMenu',key='-BMENU-', menu_def=menu_def[0])],]
    # 定义 Window
    window = sg.Window("Windows-like program",layout,
                       default_element_size=(12, 1),
                       grab_anywhere=True, # 非阻塞
                       right_click_menu=right_click_menu, # 添加右键菜单
                       default_button_element_size=(12, 1)
                      )
'Right::_Right_' 是指定该菜单的 Key 的方式。!禁用

Theme Name
There are 130 themes available. You can preview these themes by calling theme_previewer() which will create a LARGE window displaying all of the color themes available. Default is Dark Blue 3
Dark Color #
or
Light Color #
DarkAmber
    # print(sg.ListOfLookAndFeelValues())
    # ['SystemDefault', 'Reddit', 'Topanga', 'GreenTan', 'Dark', 'LightGreen', 'Dark2', 'Black', 'Tan', 'TanBlue',
    # 'DarkTanBlue', 'DarkAmber', 'DarkBlue', 'Reds', 'Green', 'BluePurple', 'Purple', 'BlueMono', 'GreenMono',
    # 'BrownBlue', 'BrightColors', 'NeutralBlue', 'Kayak', 'SandyBeach', 'TealMono']
sg.theme()窗口样式
sg.theme('BluePurple')
要查看您的PySimpleGUI版本的上述预览,请进行以下调用以生成所有可用主题的预览:
sg.theme_previewer()
如果您想在系统上看到一个窗口,如上面的主题预览屏幕截图,那么进行此调用,您将看到相同的窗口:
import PySimpleGUI as sg
sg.preview_all_look_and_feel_themes()
您还可以通过致电获取主题名称列表 theme_list
import PySimpleGUI as sg
theme_name_list = sg.theme_list()


Making your window modal
To make a Modal Wio=ndow you have 2 options.
    Set the moodel=True parameter in your Window calls.
    Call the method Window.make_modal() to chance a window from non-modal to modal. There is no modal to non-modal. Don't see the need for one. If one comes up, sure!
MPORTANT - Many of the Window methods require you to either call Window.read or Window.Finalize (or set finalize=True in your Window call) before you call the method. This is because these 2 calls are what actually creates the window using the underlying GUI Framework. Prior to one of those calls, the methods are likely to crash as they will not yet have their underlying widgets created.

"Elements" are the building blocks used to create windows. Some GUI APIs use the term "Widget" to describe these graphic elements.
    Text
    Single Line Input
    Buttons including these types:
        File Browse
        Folder Browse
        Calendar picker
        Date Chooser
        Read window
        Close window ("Button" & all shortcut buttons)
        Realtime
    Checkboxes
    Radio Buttons
    Listbox
    Slider
    Multi-line Text Input/Output
    Multi-line Text Output (not on tkinter version)
    Scroll-able Output
    Vertical Separator
    Progress Bar
    Option Menu
    Menu
    ButtonMenu
    Frame
    Column
    Graph
    Image
    Table
    Tree
    Tab, TabGroup
    StatusBar
    Pane
    Stretch (Qt only)
    Sizer (plain PySimpleGUI only)

Text Element | T == Txt == Text
Window.FindElement(key) shortened to Window[key]
window['-MULTILINE KEY-'].print('My variables are', a, b, c)     # Routed to your multiline element
Text Input Element | InputText == Input == In
Combo Element | Combo == InputCombo == DropDown == Drop
layout = [[sg.Listbox(values=['Listbox 1', 'Listbox 2', 'Listbox 3'], size=(30, 6))]]
layout = [[sg.Slider(range=(1,500),default_value=222,size=(20,15),orientation='horizontal',font=('Helvetica', 12))]]
layout =  [[sg.Radio('My first Radio!', "RADIO1", default=True),sg.Radio('My second radio!', "RADIO1")]]
Checkbox Element | CBox == CB == Check
layout =  [[sg.Checkbox('My first Checkbox!', default=True), sg.Checkbox('My second Checkbox!')]]
layout =  [[sg.Spin([i for i in range(1,11)], initial_value=1), sg.Text('Volume level')]]
layout = [[sg.Image(r'C:\PySimpleGUI\Logos\PySimpleGUI_Logo_320.png')],]
Button Element
    Button= ReadButton = RButton = ReadFormButton (Use Button, others are old methods)
    CloseButton = CButton
    RealtimeButton
    DummyButton
    layout =  [[sg.Button('Ok'), sg.Button('Cancel')]] layout =  [[sg.Ok(), sg.Cancel()]]
Button Element Shortcuts
    OK
    Ok
    Submit
    Cancel
    Yes
    No
    Exit
    Quit
    Help
    Save
    SaveAs
    Open
Chooser" Buttons
These buttons are used to show dialog boxes that choose something like a filename, date, color, etc.. that are filled into an InputText Element (or some other "target".... see below regarding targets)
    CalendarButton
    ColorChooserButton
    FileBrowse
    FilesBrowse
    FileSaveAs
    FolderBrowse
he code for the entire window could be:
layout = [[sg.T('Source Folder')],
              [sg.In()],
              [sg.FolderBrowse(target=(-1, 0)), sg.OK()]]
or if using keys, then the code would be:
layout = [[sg.T('Source Folder')],
              [sg.In(key='input')],
              [sg.FolderBrowse(target='input'), sg.OK()]]
sg.Button('Restart Song', button_color=(sg.theme_background_color(), sg.theme_background_color()),
               image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0)
VerticalSeparator(pad=None)
sg.OneLineProgressMeter('My Meter', i+1, 1000,  'key', 'Optional message')
Output(size=(80,20))


Qt Designer

https://github.com/nngogol/PySimpleGUIDesigner
pip install PySimpleGUIDesigner

Use GUI(by default):
PySimpleGUIDesigner

Use CLI:
PySimpleGUIDesigner -xml “~/folder1/test.ui” -ob “somegroupBox”

#removing (if installed) PySimpleGUIDesigner: 源码安装
pip uninstall -y PySimpleGUIDesigner

mkdir psgdesigner
cd psgdesigner
git clone https://github.com/nngogol/PySimpleGUIDesigner
python3 -m PySimpleGUIDesigner

python3 main.py –xmlfile=”~/folder1/test.ui” –objname=”somegroupBox”
#a bit shorter command:
python3 main.py -xml “~/folder1/test.ui” -ob “somegroupBox”

pypSide2

PySide2是QT官方出的Python的QT封装
1. pip安装
pip3/pip install PySide2
如果安装太慢,可以尝试:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyside2
通过http下载安装 注意版本号根据自己的需要选择相应的版本
pip install –index-url=http://download.qt.io/snapshots/ci/pyside/5.13.0/latest pyside2 –trusted-host download.qt.io

  1. conda安装
    添加清华源,注意是 http:
    conda config –add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
    conda config –add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
    conda config –add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
    conda config –set show_channel_urls yes

如果没有解决的话,在C:\Users\用户名 目录下修改 Anaconda 配置文件 .condarc,将 -defaults 这一行删掉即可

ssl_verify: true
channels:
  - http://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
  - http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
  - http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
show_channel_urls: true

conda install PySide2

Designer位于 /Users/sweet/opt/anaconda3/bin
打开使用 open -a Designer
转换 pyside2-uic d1.ui > ui_mainwindow.py
pyside2-uic d1.ui -o d1_ui.py
pyuic5 -o d1.ui d1_ui.py

import sys
from PySide2.QtWidgets import QApplication, QMainWindow
from PySide2.QtCore import QFile
from ui_mainwindow import Ui_MainWindow

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    window.show()

    sys.exit(app.exec_())
或者直接使用原UI文件
import sys
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QFile

if __name__ == "__main__":
    app = QApplication(sys.argv)

    ui_file = QFile("mainwindow.ui")
    ui_file.open(QFile.ReadOnly)

    loader = QUiLoader()
    window = loader.load(ui_file)
    ui_file.close()
    window.show()

    sys.exit(app.exec_())

支持的模块资料
pyside2 https://doc.qt.io/qtforpython/modules.html
qt5 https://doc.qt.io/qt-5/qtmodules.html

建立.pro文件
translation_en_to_zh_CN.pro
SOURCES = ui_mainwindow.py
TRANSLATIONS = translation_en_to_zh_CN.ts
CODECFORTR = UTF-8
CODECFORSRC = UTF-8

生成语言转换文件.ts
pyside2-lupdate translation_en_to_zh_CN.pro

使用 qt linguist 打开 .ts 文件来翻译成各语言版本
TRANSLATOR = QtCore.QTranslator()
app.installTranslator(TRANSLATOR) # 非常重要的一步,为 app 安装 TRANSLATOR,如果不安装,是没有效果的

pyinstaller -F main.py
-F表示生成一个独立的可执行的exe文件。-m表示生成的文件不带cmd
python pyinstaller.py -F -w test.py
这里-w是不生成console窗口,仅显示UI。
pip install pywin32
pip install pyinstaller


问题故障

  1. qt.qpa.plugin: Could not load the Qt platform plugin “windows” in “” even though it was found.
    This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
    Available platform plugins are: direct2d, minimal, offscreen, windows.
    解决:
    import os
    envpath = r’D:\anaconda3\Lib\site-packages\PySide2\plugins\platforms’
    os.environ[“QT_QPA_PLATFORM_PLUGIN_PATH”] = envpath

  2. You might be loading two sets of Qt binaries into the same process. Check that all plugins are compiled against the right Qt binaries. Export DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded.
    qt.qpa.plugin: Could not load the Qt platform plugin “cocoa” in “” even though it was found.
    This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
    解决:多个版本,保留一个

Kurento

主页: http://www.kurento.org/
github: https://github.com/kurento
服务端采用C++实现: https://github.com/Kurento/kurento-media-server
Docker https://hub.docker.com/r/kurento/kurento-media-server
说明文档 https://doc-kurento.readthedocs.io/en/stable/

Kurento模块分为三类:
主要模块与Kurento Media Server开箱即用合并:
kms-core:Kurento Media Server的主要组件。
kms-elements:Kurento Media Elements的实现(WebRtcEndpoint,PlayerEndpoint等)
kms-filters:
Kurento过滤器的实现(FaceOverlayFilter,ZBarFilter等)
内置模块Kurento团队开发的额外模块,用于增强Kurento Media Server的基本功能。到目前为止,有四个内置模块,分别是:
kms-platedetector:用于检测视频流中的车牌的过滤器。
kms-pointerdetector:基于颜色跟踪检测视频流中指针的过滤器。
kms-chroma:过滤器,它在顶层使用颜色范围并使之透明,从而在后面显示另一个图像。
kms-crowddetector:用于检测视频流中人聚集的过滤器。
定制模块

 docker run --name kms -d -p 8888:8888 \
  -e KMS_STUN_IP= \
  -e KMS_STUN_PORT=3478 \
  -e KMS_TURN_URL=user@password:urlorIp:3478 \
   kurento/kurento-media-server:latest

  -e KMS_NETWORK_INTERFACES= \
  -e KMS_EXTERNAL_ADDRESS= \
  -e KMS_MIN_PORT= \
  -e KMS_MAX_PORT= \
  -e KMS_MTU=1200 \
    kurento/kurento-media-server:latest
配制文件位置
/etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini
/etc/kurento/modules/kurento/BaseRtpEndpoint.conf.ini
#查看KMS日志
docker logs kms
#实时查看:
docker logs -f kms
日志输出
    docker logs --follow kms >"kms-$(date '+%Y%m%dT%H%M%S').log" 2>&1

    检查KMS是否已启动并正在侦听连接,请使用以下命令:
curl \
    --include \
    --header "Connection: Upgrade" \
    --header "Upgrade: websocket" \
    --header "Host: 127.0.0.1:8888" \
    --header "Origin: 127.0.0.1" \
    http://127.0.0.1:8888/kurento
应该得到类似于以下内容的响应:
HTTP/1.1 500 Internal Server Error
Server: WebSocket++/0.7.0

#进入镜像
docker exec -it kms /bin/bash
#安装vim
apt-get update
apt-get install vim
#进入配置文件夹
cd /etc/kurento/modules/kurento/
#编辑配置文件
vim WebRtcEndpoint.conf.ini
stunServerAddress=xx.xx.xx.xx
stunServerPort=pp
turnURL=username:userpwd@xx.xx.xx.xx:pp?transport=tcp
or
修改docker运行的kms配置文件方法
1.复制容器中的 配置文件到 宿主机中
docker cp  kms:/etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini  ~/WebRtcEndpoint.conf.ini
2.修改配置文件
vim ~/WebRtcEndpoint.conf.ini
3.宿主机配置文件 复制到 kms容器中
 docker cp ~/WebRtcEndpoint.conf.ini kms:/etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini

#查看当前启动的容器
docker ps
docker restart  {kurento容器ID}

kurento相关配置
1、Kurento Media Server 日志
Kurento Media Server日志文件存储在 /var/log/kurento-media-server/文件夹中。 此文件夹的内容如下:
    media-server_.<log_number>.<kms_pid>.log: Kurento Media Server的当前日志
    media-server_error.log: 第三方错误
    logs: 包含KMS已旋转日志的文件夹
2、Kurento Media Server 配置
    /etc/default/kurento-media-server 默认配置
    /etc/kurento/kurento.conf.json 通用配置
    /etc/kurento/modules/kurento/MediaElement.conf.ini Media Elements 通用参数。
    /etc/kurento/modules/kurento/SdpEndpoint.conf.ini SdpEndpoints的音频/视频参数( 例如: WebRtcEndpoint and RtpEndpoint)。
    /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini WebRtcEndpoint 特定参数。
    /etc/kurento/modules/kurento/HttpEndpoint.conf.ini HttpEndpoint 特定参数。

#### kurento-hello-world
““
git clone https://github.com/Kurento/kurento-tutorial-java.git
cd kurento-tutorial-java/kurento-hello-world
vim src/main/resources/static/js/index.js
在函数function uiStart()里,增加一个叫iceservers的变量,格式如下:
var iceservers={
“iceServers”:[
{
urls:”stun:xx.xx.xx.xx:3478″
},
{
urls:[“turn:xx.xx.xx.xx:3478″]
username:”xxxx”,
credential: “xxxx”
}
]
}
再修改底下的options变量:
const options = {
localVideo: uiLocalVideo,
remoteVideo: uiRemoteVideo,
mediaConstraints: { audio: true, video: true },
onicecandidate: (candidate) => sendMessage({
id: ‘ADD_ICE_CANDIDATE’,
candidate: candidate,
}),
configuration: iceservers //修改在这里,增加了一个configuration的key
};

启动项目
mvn -U clean spring-boot:run -Dkms.url=ws://xx.xx.xx.xx:8888/kurento

用 idea 打开 kurento-hello-world 项目,下载 maven 依赖完成后,在 run configurations 中添加 new configurations。
选择 maven , command line 中输入:
spring-boot:run -Dkms.url=ws://:8888/kurento # 这里的 public-ip 添你安装 kms 的服务器公网 ip 地址

启动完之后用谷歌或者火狐浏览器打开demo页面https://localhost:8443/
点击start启动

运行 hello world 示例时,会遇到 使用websocket传输内容过长的问题
//项目中创建 package org.kurento.tutorial.helloworld.config
//config 包下创建类
package org.kurento.tutorial.helloworld.config;

@Configuration
//@ComponentScan
//@EnableAutoConfiguration
public class WebAppRootContext implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println(“org.apache.tomcat.websocket.textBufferSize”);
servletContext.addListener(WebAppRootListener.class);
servletContext.setInitParameter(“org.apache.tomcat.websocket.textBufferSize”,”1024000″);
}

}
““

API Java SDK for Kurento Media Server

https://github.com/Kurento/kurento-java

Kurento tutorials for Node JS

https://github.com/Kurento/kurento-tutorial-node

信令服务器与web客户端 kurento-room

https://github.com/Kurento/kurento-room

NUBOMEDIA

NUBOMEDIA Autonomous Installer
https://nubomedia.readthedocs.io/en/latest/tools/autonomous-installer/

Android客户端开源库

https://github.com/nubomedia-vtt/nubo-test

iOS客户端开源库

https://github.com/nubomediaTI/Kurento-iOS

ffmpeg转发模块

利用ffmpeg可执行文件将本地RTP流封装转发成RTMP推送出去,具体可以参考这个开源工程:
kurento-rtmp https://github.com/godka/kurento-rtmp

gstreamer转发模块

RTP解包 —— 音视频转换 —— FLV封包 —— RTMP推流