树莓派摄像头-追踪

今天实现了对物体检测并控制舵机运动的应用。
舵机使用了pca9685控制,因此备份一下pac9685舵机控制板的驱动:

PCA9685.py

import time
import math

class PWM:
    _mode_adr      = 0x00
    _base_adr_low  = 0x08
    _base_adr_high = 0x09
    _prescale_adr  = 0xFE

    def __init__(self, bus, address = 0x40):
        '''
        Creates an instance of the PWM chip at given i2c address.
        @param bus: the SMBus instance to access the i2c port(0 or 1).
        @param address: the address of the i2c chip (default: 0x40).
        '''
        self.bus = bus
        self.address = address
        self._writeByte(self._mode_adr, 0x00)

    def setFreq(self, freq):
        '''
        Sets the PWM frequency. The value is stored in the device.
        @param freq: the frequency in Hz (approx.)
        '''
        prescaleValue = 25000000.0  # 25MHz
        prescaleValue /= 4096.0     # 12-bit
        prescaleValue /= float(freq)
        prescaleValue -= 1.0  
        prescale = math.floor(prescaleValue + 0.5)
        oldmode = self._readByte(self._mode_adr)
        if oldmode == None:
            return
        newmode = (oldmode & 0x7F) | 0x10
        self._writeByte(self._mode_adr, newmode)
        self._writeByte(self._prescale_adr, int(math.floor(prescale)))
        self._writeByte(self._mode_adr, oldmode)
        time.sleep(0.005)
        self._writeByte(self._mode_adr, oldmode | 0x80)

    def setDuty(self, channel, duty):
        '''
        Sets a single PWM channel. The value is stored in the device.
        @param channel: one of the channels 0..15
        @param duty: the duty cycle 0..100
        '''
        data = int(duty * 4096 /100)  # 0..4096 (included)
        self._writeByte(self._base_adr_low + 4 * channel, data & 0xFF)
        self._writeByte(self._base_adr_high + 4 * channel, data >> 8)

    def _writeByte(self, reg, value):
        try:
            self.bus.write_byte_data(self.address, reg, value)
        except:
            print("Error while writing to I2C device")

    def _readByte(self, reg):
        try:
            result = self.bus.read_byte_data(self.address, reg)
            return result
        except:
            print("Error while Reading from I2C device")
            return None

调用方法简单:

import time
from smbus2 import SMBus
from pca9685 import PWM

freq = 50
addr = 0x40
channels = [0, 1, 2] 
a = 12.5
b = 2

bus = SMBus(1)
pwm = PWM(bus, addr)
pwm.setFreq(freq)

def setPos(channel, pos):
    duty = a / 180 * pos + b 
    pwm.setDuty(channel, duty)
    time.sleep(0.1)

while True:
    try:
        for pos in range(60, 120, 2):
            setPos(channels[0], pos)
            time.sleep(0.1)

        for pos in range(60, 120, 3):
            setPos(channels[1], pos)
            time.sleep(0.5)

        for pos in range(0, 90, 3):
            setPos(channels[2], pos)
            time.sleep(0.01)

    except KeyboardInterrupt:
        for i in channels:
            setPos(i, 0)
        break

OpenCV 通过颜色检测并控制:

import cv2
import time
import numpy as np
from pca9685 import PWM
from smbus2 import SMBus

freq = 50
addr = 0x40
ch1  = 0
ch2  = 1

bus = SMBus(1)
pwm = PWM(bus, addr)
pwm.setFreq(freq)

def setPos(channel, position):
    pwm.setDuty(channel, position)
    time.sleep(0.01)

def posMap(x, in_min, in_max, out_min, out_max):
    return int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)

cap = cv2.VideoCapture(0)
cap.set(3, 320)
cap.set(4, 160)
ret, frame = cap.read()

cx = int(frame.shape[1] / 2)
center = int(frame.shape[1] / 2)
pos = 90

while True:
    ret, frame = cap.read()
    frame = cv2.flip(frame, 1)
    hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    ly = np.array([51, 0, 0])
    hy = np.array([180, 255, 255])

    mask = cv2.inRange(hsv_frame, ly, hy)
    contours, hir = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    contours = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)
    # cv2.drawContours(frame, contours[1], -1, (0, 0, 255), 2)
    for cnt in contours[1:]:
        (x, y, w, h) = cv2.boundingRect(cnt)
        cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 255,0), 2)
        cx = int((x + x + w)/ 2)
        # cv2.drawContours(frame, cnt, -1, (0, 0, 255), 2)
        cv2.line(frame, (cx, 0), (cx, frame.shape[0]), (0, 0, 255), 1)
        if cx < center:
            pos -= 1.5
        elif cx > center:
            pos += 1.5  
        # pos = posMap(pos, 1, 480, 60, 120)
        setPos(ch1, pos)

        break
    # mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
    # hstack = np.hstack([mask, frame])
    cv2.imshow('frame', frame)
    if cv2.waitKey(1) & 0xFF == 27:
        break

cap.release()
cv2.destroyAllWindows()
setPos(ch1, 0)

总结

就是控制dutyCycle,频率肯定要50hz,一般的多级可能需要a, b函数作为微调。根据自己的舵机微调a和b的值。

可以尝试 a 设置8.5, b设置2

树莓派安装windows11 预览版遇到的问题总结

在最近发现windows11 预览版arm64位系统发布后,可以通过一个cmd的脚本生成需要的镜像,但是也会产生一个非常大容量的目录: MountUUP,应该是通过UUPtoISO生成的临时文件,非常巨大,包含了很多cab的包还有一些wim的东西,在执行删除的时候总是提示你需要trustinstaller的权限,无法删除,会占用大量磁盘,实际上是因为它挂载了一个镜像文件install.wim, 我想要删除它清空磁盘空间,找了一圈,发现可以通过先取消挂载然后再删除就顺利干掉了。
下面是一段网上查到的资料原文。
The following DISM command will look for stale mountpoints on all drives and (attempt to) unmount them:

DISM.exe /cleanup-wim

Or, you can do it manually:

DISM.exe /Unmount-WIM /mountdir=C:\MountUUP /discard

管理员打开cmd窗口,执行上述命令后删除相应目录即可。问题解决。

Ubuntu 上安装 Cacti 现在这么简单了么?

前言

最近讲监控, 又将很久以前的东西重新拾起来, 尝试在我的小 mac 上的虚拟机里面进行各种安装测试.

安装 SNMP

我以前的印象中,安装 snmp 需要用的软件包的名字是: net-snmp,但是在 ubuntu 上我发现,就直接安装snmp 和 snmpd 好像就行了.

   sudo apt-get -y install snmp snmpd 

安装 cacti

我之前的经验是需要安装一套 LAMP 架构.先.
但是发现 ubuntu 上面已经让我可以变得非常懒了, 直接一条命令就都给你安装上来了.

sudo apt-get -y install cacti

顺手更新了一把系统.

疑惑

官方网站上说 cacti 账户和密码默认是 admin,但是好像不行.真是折腾.
后来用了账户 admin,密码用的数据库的密码才正常登陆.

Mac OS 上虚拟机无法联网的临时修复方法记录

事情的起因

由于要上课,因此安装 centos8.3.2011一个短命的系统,但是网卡不好用.

实际的电脑信息

我电脑是:

然后我的 parallel 的虚拟机网卡不好用, 尝试联网各种失败,开机无法联网,CentOS 也不好用.

解决方法:

于是搜了半天, 尝试了半天,终于找到一个看似能用的方法:
->关闭所有VMS和Parallels Desktop-

打开终端并键入:sudo -b /Applications/Parallels\Desktop.app/Contents/MacOS/prl_client_app

总结:

Parallels将打开,也许您需要打开您的VM再次...网络将正常工作...
这是一个临时解决方案,而官方的并行支持不起作用..

Linux内核编译如果出问题了怎么办?

如果出问题了

如果您遇到的问题似乎是由于内核错误引起的,请检查文件维护者以查看是否有特定人员与您遇到麻烦的内核部分相关联。如果没有列出有任何人,那么第二个最好的办法是将它们邮寄给(torvalds@linux-foundation.org),以及可能的其他任何有关的邮件列表或新闻组。

在所有错误报告中,请告诉您您在操作的是什么内核,如何复制问题以及您的设置是什么(使用常识)。如果问题是新问题,请告诉他,如果问题是旧问题,请在首次发现问题时告诉他,他才是Linux内核最大的BOSS。

例如:
如果该错误导致显示如下消息:

unable to handle kernel paging request at address C0000010
Oops: 0002
EIP:   0010:XXXXXXXX
eax: xxxxxxxx   ebx: xxxxxxxx   ecx: xxxxxxxx   edx: xxxxxxxx
esi: xxxxxxxx   edi: xxxxxxxx   ebp: xxxxxxxx
ds: xxxx  es: xxxx  fs: xxxx  gs: xxxx
Pid: xx, process nr: xx
xx xx xx xx xx xx xx xx xx xx

或屏幕上或系统日志中的类似内核调试信息,请准确复制。转储对您来说似乎令人难以理解,但是它确实包含有助于调试问题的信息。转储上方的文本也很重要:它说明了为什么内核转储代码的原因(在上面的示例中,这是由于内核指针错误引起的)。有关寻找转储的更多信息,请参见“错误查找”

如果使用CONFIG_KALLSYMS编译内核,则可以按原样发送转储,否则,您将不得不使用该ksymoops程序来理解转储(但通常首选使用CONFIG_KALLSYMS进行编译)。可以从https://www.kernel.org/pub/linux/utils/kernel/ksymoops/下载该实用程序 。另外,您可以手动进行转储查找:

在上面的调试转储中,如果您可以查找EIP值的含义,那么它将大有帮助。这样的十六进制值对我或其他人没有太大帮助:它将取决于您的特定内核设置。您应该做的是从EIP行中获取十六进制值(忽略0010:),然后在内核名称列表中查找该值,以查看哪个内核函数包含有问题的地址。

要找出内核函数名称,您需要找到与表现出该症状的内核相关的系统二进制文件。这是文件“ linux/vmlinux”。要提取名称列表并将其与内核崩溃时的EIP匹配,请执行以下操作:

nm vmlinux | sort | less

这将为您提供按升序排序的内核地址列表,从中可以轻松找到包含违规地址的函数。请注意,内核调试消息给出的地址不一定与功能地址完全匹配(实际上,这不太可能),因此您不能只是“ grep”列表:但是,列表将为您提供每个内核函数的起始点,因此,通过查找起始地址比您要查找的起始地址低的函数,然后查找具有较高地址的函数,可以找到所需的起始地址。实际上,在问题报告中包括一些“上下文”可能是个好主意,在有趣的地方加上几行即可。

如果由于某种原因您不能执行上述操作(您有预编译的内核映像或类似文件),请尽可能多地告诉他有关您的设置的信息。有关详细信息,请阅读“报告问题”。

另外,您可以在运行的内核上使用gdb。(只读;即,您不能更改值或设置断点。)为此,首先使用-g编译内核;适当地编辑arch/x86/Makefile,然后执行。您还需要启用CONFIG_PROC_FS(通过)。

make cleanmake config

使用新内核重新引导后,请执行。现在,您可以使用所有常用的gdb命令。查找系统崩溃点的命令是。(用EIP值替换XXXes。)

gdb vmlinux /proc/kcorel *0xXXXXXXXX

当前,对非运行内核进行gdb操作失败,因为gdb(错误地)无视了为其编译内核的起始偏移量。

MySQL 小结

1. 备份数据库中的表。

1.1 选择备份路径,执行备份。

退出mysql客户端并切换到D盘,d:

mysqldump -u root -p employee > myemployee.sql

注: employee 是数据库名, myemployee.sql 是备份文件名
确认成功后,尝试删除数据库

2. 删除数据库

mysql -u root -p 
mysql> drop database 数据库名;
mysql> drop database employee;

3. 尝试恢复数据库。

3.1 先创建数据库

mysql -u root -p 
passwd: xxxxxx
mysql> create database employee;
mysql> exit
d:\> mysql -u root -p employee < myemployee.sql   还原数据库中的表
password: xxxxxx
d:\> mysql -u root -p 
password: xxxxxx
mysql> show databases;     显示数据库
mysql> use employee;     打开数据库
mysql> desc employee_tbl;   描述表结构
mysql> show tables;  显示表名

3.2 一条命令执行查询的方法:

D:\>mysql -u root -pzypxxx -e "use employee; select coalesce(name,'总数:'), SUM(signin) as sigin_total from employee_tbl group by name with rollup;"

4. 额外创建一个表user_into

mysql> use employee;
mysql> create table user_info (
-> `id`  int(11) NOT NULL, 
-> `name` varchar(10) NOT NULL DEFAULT '',
-> `age`  int(3) NOT NULL,
-> `sex`  varchar(2) NOT NULL,
-> `salary`  float(9,2) NOT NULL,
-> PRIMARY KEY(`id`));
mysql> insert into user_info values 
-> ('1', '小明', 23, '男', 5200.22),
-> ('2', '小王', 24, '男', 5300.22),
-> ('3', '小美', 26, '女', 6200.32);

5. 更新表结构

mysql>alter table user_info add column `Addr`  varchar(100) NOT NULL after `salary`;
mysql>update  user_info set Addr='奉贤紫屿培训学校' where name='小明';

6. 联合查询连接功能

mysql> select a.name, a.age, a.salary, b.signin from user_info a inner join employee_tbl b  on a.name=b.name;

7. 总结

  • 库操作: 增删改查
    增:create database 数据库名字;
    删:drop database 数据库名字;
    改: use 数据库名字; 打开数据库
    查:show databases;
  • 表操作: 增删改查
    增:create table 表名(字段名 字段类型 字段属性, 字段名2.....);

    create table aa (`id` int(10)  NOT NULL Auto_increment, `name` varchar(20) NOT NULL);

    删:drop table aa;
    改:alter table aa ADD COLUMN addr varchar(10) NOT NULL AFTER name;

    alter table  aa MODIFY COLUMN `salary` float(9,2);
    alter table  aa DEL COLUMN `addr`;
    改记录
    update  aa set salary=5300.33 where name='小明';

    查:desc aa; 查表结构
    查记录: select 字段1, 字段2 .... 字段N FROM 表名;

               select * from aa; 
               select * from aa where  id=1; 
               select * from aa order by id desc; 
               select * from aa order by id asc;
               select * from aa group by name;
               select name,  count(*) from aa group by name;
               select coalesce(name, "总数:"),  SUM(signin) from aa group by name with rollup;

    8.数据表的备份和还原

    8.1 备份数据库:

    mysqldump -u root -p 数据库名字  >  备份文件.sql

    8.2 还原数据表:

    mysql -u root -p -e "create database bbs;"
    mysql -u root -p bbs < 备份文件.sql

mysql -u root -p -e "create database bbs"
mysql -u root -p bbs < 备份文件.sql

ESP32测试脚本

ESP32 测试脚本

Micropython

from machine import Pin
from time import sleep

leds = [13, 12, 14, 27, 26, 25, 33, 32, 23, 22, 21, 19, 18, 5, 16, 17, 4, 0, 2, 15 ]
try:
    while True:
        for i in range(len(leds)):
            print(leds[i])
            i = Pin(leds[i], Pin.OUT)
            i.on()
except KeyboardInterrupt:
    print("\nCtrl-C pressed.")

KiCAD 第一弹: 小白入门级

KiCAD

缘起

自从安装了 KiCAD 以后,突然发现这是一个非常棒的开源软件,好多的资源可以用,内心激动的迫不及待的想做出一个 PCB 板.手头还有一片 ATmega328P-AU 的 arduino 芯片,赶紧搜索了一下 arduino 的最小系统原理图,照猫画虎来了一遍.其实还是有多小细节要注意的.

其实发现还是有 arduino Uno 的一个器件的.还需要慢慢摸索.这次的硬核马拉松我觉得我要败了..

发现

树莓派上竟然也可以跑 kicad, 而且安装极其简单,简单的不像话....

sudo apt-get update 
sudo apt-get -y install kicad
kicad &

就好了.就打开了,第一次可能有点儿卡顿,但是一旦跑起来那种丝般柔顺的感觉真的很舒服~
突然就爱上了这个软件.虽然相比 AD 可能还有很大差距,但是毕竟是开源软件,还有那么多用户,github 也是人气满满.强烈推荐大家尝试一下..
我发现我需要学习的东西太多了,我一直感觉自己是一个小白一样的存在, 看了很多资料还是前面看完后面就不记得了....
哎,人到中年不如狗系列啊!
好了,不吐槽了.还是要有满满的正能量才可以, 继续啃啃技术材料吧~
我是骑驴玩儿漂移,一个胖胖的爱玩儿树莓派或者是喜欢嘚瑟的胖纸!

抗击疫情宅家充电系列-树莓派编程基础

抗击疫情宅家充电系列

今天受邀在电子森林,哦,应该是硬禾学堂做了树莓派的入门级讲解,昨天也做了一个硬件讲解的直播,使用的软件是小鹅通,感觉还不错,有兴趣的朋友可以尝试一下.

下面是直播的地址:

第一天内容

国庆节学习小计

今天下班开始就进入了十一小长假的状态,但是内心里面忐忑不安的是,太多事情要处理了,没有一天可以闲着.真的很累.
明天要去嘉定,非常远,昨天 3 点多才睡觉,生活不规律,但也没办法,就像现在我还在查资料一样.

搭建在阿里云的个人博客也应为 mysql 数据库服务器的日志塞满导致了宕机,由于开启了秘钥登陆,在公司还无法操作,眼睁着看着也无计可施.
刚才维护了一下,其实阿里云的平台也还不错,最近也试了试 azure iot 的平台,感觉更加成熟和稳定. 未来的物联网 Demo 还是要在这个平台上多做做测试.

明天一天忙碌完,就可以好好休息了.
假期安排:

  1. 继续写书
  2. 整理家里的东西,该扔的扔了,该卖的卖了.保持轻装前行
  3. 假期可以好好看看书,打打篮球
  4. 减肥减肥,继续减肥
  5. 陪老婆做做计划,干点儿有意义的事情.
  6. 把构想的电路图画完.
  7. 优化雷达小车的代码.

各种应用代理的配置

缘起

昨天因为要弄jetson nano的环境,好多资源要扶墙,所以正好也整理了一下,在不同的应用上添加代理的方法:

APT

全局代理

在Ubuntu系统中,使用代理有一种通用方式:系统设置 –> 网络 –> 网络代理 –> 应用到整个系统,这里设置的代理是全局代理,整个系统都会走这个代理设置。但一般我们不会这样使用,我们需要对我们指定的工具或软件设置代理。

APT代理

apt-get工具可以使用-o参数来使用配置字符串,或使用-c参数使用指定配置文件。

APT工具集使用的默认配置文件是/etc/apt/apt.conf,打开后发现文件默认是空文件。但是当我们设置了全局代理后,文件的内容变为:

Acquire::http::proxy "http://192.168.3.3:1080/";
Acquire::https::proxy "https://192.168.3.3:1080/";

这里稍微解释一下,这里的代理服务器是我跑了shadowsocks的一台主机,1080是这台主机开放的端口。记得在shadowsocks上开允许局域网接入。
如果只是偶尔用一次:

sudo apt-get -o Acquire::http::proxy="http://192.168.3.3:1080/" update

当然,如果你用man查看过还会发现-c选项

使用-c选项

创建个人配置文件~/apt_proxy.conf,

Acquire::http::proxy "http://192.168.3.3:1080/";
Acquire::https::proxy "https://192.168.3.3:1080/";

使用命令:

sudo apt-get -c ~/apt_proxy.conf update
  1. 如果我们设置了环境变量APT_CONFIG, 那么apt工具也将使用APT_CONFIG指向的配置文件。
    export APT_CONFIG=~/apt_proxy.conf
    sudo apt-get update

    这是软件包安装使用代理,那么还有使用wget下载有时候也要用。

    wget使用代理

    为wget使用代理,直接修改/etc/wgetrc,也可以在主目录下创建.wgetrc,并编辑相应内容。
    将/etc/wgetrc中与proxy有关的几行复制到~/.wgetrc,然后填写代理的地址和端口

    https_proxy = http://192.168.3.3:1080/
    http_proxy = http://192.168.3.3:1080/
    use_proxy = on

    这里 use_proxy = on 开启了代理,如果不想使用代理,每次都修改此文件未免麻烦,我们可以在命令中使用-Y参数来临时设置:

-Y, --proxy=on/off           打开或关闭代理

使用-e参数

wget本身没有专门设置代理的命令行参数,但是有一个"-e"参数,可以在命令行上指定一个原本出现在".wgetrc"中的设置。于是可以变相在命令行上指定代理:

-e, --execute=COMMAND   执行`.wgetrc'格式的命令

例如:

wget -c -r -np -k -L -p -e "http_proxy=http://192.168.3.3:1080" http://www.kernel.org/latest_version

临时使用代理下载比较方便。

git 使用代理

设置代理

git config --global https.proxy http://192.168.3.3:1080
git config --global https.proxy https://192.168.3.3:1080

取消设置

git config --global --unset http.proxy
git config --global --unset https.proxy

socks5

git config --global http.proxy 'socks5://192.168.3.3:1080'
git config --global https.proxy 'socks5://192.168.3.3:1080'

单一定向

只对github.com

git config --global http.https://github.com.proxy socks5://192.168.3.3:1080

取消代理

git config --global --unset http.https://github.com.proxy

目前用到这些,先记录下来,以后有别的再继续补充。
另外,还有个taskset比较好用,可以将任务绑定到CPU执行。
例如: omxplayer -o local xxx.mp4 播放一个影片,产生了一个进程,用ps aux |grep omxplayer发现其pid为40945
那么可以让单个cpu专门处理视频的解码和播放或者让1核,2核,3核工作,留一个核干别的。

taskset -cp 1,2,3 40945

意思就是让这个播放的进程可以使用1,2,3核, 通过htop就可以看到工作效果了。