前段时间的,新生工程体验课上,两人一组,靠厂家提供的元器件和烧录代码,焊接了了一台智能小车。

最进,碰巧手头有空闲的一块树莓派,本来打算用树莓派去实现远程控制空调,但发现空调的红外编码带有逻辑控制,只能退而求其次,试试远程控制小车,大体框架结构如图。


2017-07-03更新,已经实现对空调类带逻辑编码设备的简单控制。


烧录系统

几乎所有的新手教程都使用Win32DiskImager作为系统安装工具——中文的、英文的、官方的、eLinux wiki的,不一而足。 但是这个工具不支持中文目录名(文件或目录有中文,会出现123错误),不支持压缩,必须先插好SD卡,再开软件。 而USB Image Tool,就是Win32DiskImager的一个更方便的替代品。

写SD卡:直接读取zip压缩包

USB Image Tool可以直读.zip压缩包。网上下载的zip格式系统镜像,下完直接可以烧录。 点击Restore,选择.zip文件即可。注意打开对话框中默认看不到.zip文件,在“文件类型”处选择“All Files (.)”即可。

SSH无法连接问题

自从2016年11月开始,树莓派官方推荐 Raspbian 系统镜像关闭了默认ssh连接,重新开启也很简单,把SD卡拔下来,进入到根目录,新建一个名为ssh的空白文件(无后缀)就可以。

好了然后再把卡插回树莓派,就可以使用SSH了。

  • 初始用户名:pi
  • 初始密码:raspberry

将红外接受管和发射管连接至树莓派GPIO接口

材料:

红外接受管(3pin),红外接受管(2pin),杜邦线若干。

根据不同树莓派版本,查看GPIO的引线图,该实验采用B+版,具体实物对应图和GPIO与pin对应图如下图:

硬件连接

红外接收器

  • vcc 连 pin1 (3.3v)

  • gnd 连 pin6(ground)

  • data 连 pin12(gpio18)

红外发射器

  • gnd 连 pin25(ground)

  • data 连 pin11(gpio17)

红外接受器规格见图,左引脚为data,中为接地,右为3.3V供电

红外发射器规格见图,长脚为data,短脚为接地

接收器和发射器通过杜邦线跟树莓派相连,最后的连接实物图

预先解析控制码

修改 raspbian 仓库默认源

  1. 修改apt源
1
sudo nano /etc/apt/sources.list

例如使用大连东软信息学院软件源镜像,修改之后的内容如下:

1
deb http://mirrors.aliyun.com/raspbian/raspbian jessie main contrib non-free rpi

其他可用源如下:

  1. 更新软件源和软件
1
2
3
4
# 更新软件源
sudo apt-get update
# 更新软件
sudo apt-get upgrade

更换vi文本编译器为vim

因为vi在insert模式下,方向键会变为ABCD,故用vim进行替换

1
2
3
4
#卸载vi
sudo apt-get remove vi-common
#安装vim
sudo apt-get install -y vim

安装lirc

LIRC (Linux Infrared remote control)是一个linux系统下开源的软件包。这个软件可以让Linux系统接收及发送红外线信号。

1
sudo apt-get install lirc

配置硬件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# sudo vim /boot/config.txt #在文件结尾添加
# 修改一下内容
dtoverlay=lirc-rpi
gpio_in_pin=18
gpio_out_pin=17

# sudo vim /etc/lirc/hardware.conf #编辑LRIC的配置文件
# 修改以下内容
LIRCD_ARGS="--uinput"
DRIVER="default"
DEVICE="/dev/lirc0"
MODULES="lirc_rpi"

# 重启生效
sudo /etc/init.d/lirc restart

注意:配置gpio_in_pin和gpio_out_pin时,编号为GPIO号,并非pin号

启动测试

1
sudo mode2 -d /dev/lirc0

红外接收器已经打开,处于监听状态。这个时候,利用任何红外发射器(可以是电视遥控器或其他遥控器)对红外接收模块按任意按钮,就可以在树莓派终端上看到类似如下的代码

看到这个代码便证明红外接收模块是正常工作的。

如果没有看到,请检查你的接线、电压、以及通过lsusb查看是否加载了相应模块。

1
2
3
4
5
6
pulse 1681
space 4816
pulse 1695
space 4784
pulse 1333
space 3638

录制解析控制码

  1. 开始录制
1
irrecord -d /dev/lirc0 ~/lircd.conf #按照提示操作即可,录制完后会让你输入按键名
  1. 查看可用键名列表
1
irrecord --list-namespace
  1. 将已录制的编码加载进 lirc 配置参数
1
sudo cp ~/lircd.conf /etc/lirc/lircd.conf

通过树莓派发射红外编码

  1. 启动lircd服务
1
sudo lircd -d /dev/lirc0
  1. 查看录制好可以使用的键名
1
irsend LIST /home/pi/lircd.conf ""
  1. 发送红外编码
1
irsend SEND_ONCE /home/pi/lircd.conf KEY_XXX

演示效果


关于录制带逻辑编码的红外编码

一个比较令人兴奋的消息,谢谢博客http://blog.just4fun.site/raspberrypi-lirc.html的帮助,直接发送raw原始码就可以实现简单的控制程序。😘

注意:其只能使用raw原始码,记录是通过mode2命令实现。

  1. 制作模版(不设置按键,初始化玩直接跳过)
1
2
sudo /etc/init.d/lirc restart
irrecord -f -d /dev/lirc0 ~/lircd.conf
  1. 录制需要实现的按键
1
2
3
4
5
6
7
8
9
mode2  -d /dev/lirc0 > /tmp/temp.code  
cat /tmp/temp.code | sed -n '2,$p' | grep -o -E "[0-9]+" | xargs echo # 移除第一行,之后把所有数字取出

# 把上述指令写入 ~/lircd.conf 的 KEY_OPEN里
# 值得注意的是 ~/lircd.conf文件里的空格十分重要

sudo cp ~/lircd.conf /etc/lirc/lircd.conf
sudo /etc/init.d/lirc restart
# irsend LIST /home/pi/lircd.conf "" #列出指令
  1. 最后一个参考格式的lircd.conf文件(保证空格正确)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
begin remote

name /home/pi/lircd.conf
flags RAW_CODES
eps 30
aeps 100

gap 8015

begin raw_codes

name KEY_POWER
8927 4522 531 1711 551 1706 559 598 549 599 551 600 551 598 551 597 552 1719 558 597 549 1715 549 1724 540 614 535 592 559 597 550 599 551 610 551 602 549 598 553 1706 558 598 549 601 549 599 550 601 548 614 551 593 557 1717 545 598 551 598 552 598 552 599 549 598 553 1720 556 597 563 589 549 600 549 601 549 607 545 593 555 599 551 614 548 598 551 598 551 600 550 594 604 548 555 597 551 599 551 597 573 7967 558 597 549 598 548 603 548 601 554 598 550 599 550 600 545 619 549 600 545 599 556 598 551 600 549 611 541 597 551 599 551 609 548 601 555 598 552 596 554 598 550 598 563 587 551 600 543 615 556 598 550 598 551 599 576 574 552 596 552 598 552 613 538 612 551 595 552 597 553 598 552 599 552 596 552 1720 545 596 552 610 551 599 551 599 575 575 551 593 559 595 553 598 552 598 549 612 552 598 551 1705 559 597 574 1689 550 1724 540 598 552 592 559 609 553 599 548 598 552 1711 551 1705 560 1708 553 1711 550 599 553 1716 563 7970 559 597 551 600 573 578 550 599 551 600 575 573 565 589 575 586 552 1710 554 597 556 596 553 597 549 604 552 600 550 599 551 614 551 598 552 599 552 599 548 602 553 598 550 1713 552 599 552 613 593 558 545 604 564 586 552 598 552 598 554 595 554 601 546 617 546 607 550 597 551 611 541 597 553 598 553 598 580 572 548 615 552 599 554 596 552 599 551 598 554 598 547 614 536 604 552 609 554 1714 548 598 556 597 548 599 553 601 553 1711 553 598 553 593 549

end raw_codes

end remote
  1. 启动服务,运行指令
1
2
sudo lircd -d /dev/lirc0
irsend SEND_ONCE /home/pi/lircd.conf KEY_POWER

本人常用Mi5s,伴随着MIUI9(7.0)内测,实在等不急官网放MIUI的包(其实是不想使用MIUI,设计风格跟原生差异太大)。

这几天就靠着以前的所积累的刷机经验,并且刷上刚适配的lineage os (CM14.1),以此为基础,替换AOSP(Android Open-Source Project)编码的应用。

再加 上pixel launcher 谷歌“亲儿子”的美化和Nougat的优化,对原生的安卓顿生好感。

当然,刷机过程也是比较曲折的,但总算没有白费精力,为了以后类似操作便捷,特地记录一下。

Step1: 小米官网解锁fastboot

​ 小米自从mi4后,为了增强安全性(防止大家乱刷机),至底层加入bl锁,导致刷入第一步需解锁,但刷机有风险,一旦解锁,就失去保修机会,且修改不可逆,小米会将设备解锁信息在服务器上保存,谨慎操作!

Step2: adb刷入第三方recovery(TWRP)

  1. https://twrp.me/下载最新的TWRP(我当时最新版本3.1.1.0),为之后刷第三方系统做准备,并用adb(Android Debug Bridge)工具包刷入TWRP

ADB下载地址:http://adbshell.com/downloads 选择ADB Kits

  1. 手机关机进入fastboot模式(音量键下+电源键),USB连接电脑,装上驱动(用官方驱动或者第三方驱动安装软件)

powershell 刷入命令

1
.\fastboot flash recovery XXX.img
  • XXX.img代表TWRP所下载的文件名
  1. 最后输入命令重启,再关机,按住 音量键上+电源键 进入recovery模式
1
.\fastboot reboot

Setp3: 刷入lineage os(with root)

lineage os官网下载rom包和root包,在recovery模式下,用TWRP依次刷入两个包

网址:https://download.lineageos.org/

注意:根据设备cpu类型选择root包(我的是arm64,安卓7.1)

Step4: 刷入openGapps(aroma)

网址:http://opengapps.org/

根据设备类型选择包

  • aroma : 图形化安装版本,可以自定义所需刷入的应用。(但有些机型会由于recovery的原因无法使用)。

  • super :最为完备的版本,该有的和不该有的都有了(比如日语输入法、注音输入法、等大陆用户基本上不会需要的应用)

  • stock :包括 nexus 出厂所具备的所有应用,在安装好 CM 、魔趣等系统后,刷入该包会自动替换掉 Aosp 的应用 ,比如 Google 相机、Gmail、Google Now 桌面、Google 相册分别替换掉 Aosp所带的 相机、 邮件、桌面、相册等,当然 Google全家桶的其他软件如 Gooele Play、Youtube、地图、Gooele keep等也会随之刷入你手机。

  • full :与 stock 唯一区别就在于不会替换掉 Aosp 应用。

  • mini、micro、nano、pico 依次减少应用,但都具备 Google service 和 Play

推荐nano包,但下载时需要一个稳定的梯子,建议用chrome直接下载,最后用TWRP刷入既可。

Step5: 精简部分不需要系统应用+修改hosts

该布操作最简单,但比较繁琐,用root删除掉内置的一些不需要使用的google应用,比如其他语言输入法,删除完后重启手机一次。

go hosts APP替换掉原始hosts,使设备能直接访问谷歌(该操作并不完美,但可以用做备用)

Step6: 刷入Magisk框架并接入viperfx音效

因为手机并不自带音效改善软件,且不带HIFI模块的手机插手机的音效很差,所以这里刷入现在口碑最好的viperfx音效,但直接刷入时会存在I/O报错,root报错等原因,所以这里借助Magisk框架修改系统API接口实现同等功能

Magisk 下载网址:https://forum.xda-developers.com/apps/magisk/official-magisk-v7-universal-systemless-t3473445

  1. 先TWRP刷入Magisk
  2. 再开机安装Magisk Manager apk;
  3. 最后在magisk中安装viperfx即可。

最终效果图

题目描述:

小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。

小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是K的两名用户匹配在一起。如果两人分差小于或大于K,系统都不会将他们匹配。

现在小明知道这个网站总共有N名用户,以及他们的积分分别是A1, A2, … AN。

小明想了解最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来(任意两名用户积分差不等于K)?

输入

第一行包含两个个整数N和K。

第二行包含N个整数A1, A2, … AN。

对于30%的数据,\(1 \le N \le 10​\)

对于100%的数据,\(1 \le N \le 100000, 0 \le Ai \le 100000, 0 \le K \le 100000\)

输出:

一个整数,代表答案。

样例输入: 5 1 2 3 1 2 4 2 5 5 3

样例输出: 6

再比如,

样例输入:

10 1

2 1 1 1 1 4 4 3 4 4

样例输出: 8

资源约定:

峰值内存消耗 < 256M

CPU消耗 < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0

注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。

注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。提交时,注意选择所期望的编译器类型。

问题分析:

​这题就相较于前几题就有难度提升,不在依靠于单一基础算法就能较好的解决问题。

​比赛时,我一开始就认为搜索能解决问题,纸上推演的半个小时才发现有问题,始终是一个NP问题。后来经过分析:这样的策略可能达到解决问题的目的:

  1. 因为当K>0时,若选值为m的点,则应该选上值为m的所有点,依照桶排序的思想,进行优化

  2. 当K=0时,最大选择数=不同值的点的类别数

  3. 依照K值,形成关系链(可以为多条关系链)

  4. 单一关系链中,选择类数和并且不关联的最大一个或几个点,该算法应为动态规划

如图所示:

输入:

1
2
22 2  
2 1 1 1 1 4 4 3 4 4 6 7 7 8 8 8 8 9 11 11 12 13

  • 第一条链最大个数和:4
  • 第二条链最大个数和:8
  • 第三条链最大个数和:2
  • 第四条链最大个数和:2
  • 第五条链最大个数和:1

最大个数和:\(4 + 8 + 2 + 2 + 1 = 17\)

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <iostream>
#include <vector>
#include <algorithm>
#include <array>

const int N = 100000;
int n, k;
std::array<int, N> num{0};
std::vector<int> link[N];
int node[N];
int num_node = 0;
int num_link = 0; // 关系链数
std::array<short, N> vis{0}; // 1代表已访问,0代表未访问,-1代表点不存在
std::array<int, N> F{0}; // dp F[i]=max(F[i-1],F[F-2]+num[i])
long long tot = 0;

void init() {
std::cin >> n >> k;
for (int i = 1; i <= n; i++) {
int t;
std::cin >> t;
num[t]++;
vis[i] = -1;
}

}

void work() {
for (int i = 0; i <= N; i++)
if (num[i] != 0) {
node[num_node++] = i;
vis[i] = 0;
}
for (int i = 0; i < num_node; i++) {
if (vis[node[i]] == 0) {
vis[node[i]] = 1;
link[num_link].push_back(node[i]);
int next = node[i] + k;
while (vis[next] == 0) {
link[num_link].push_back(next);
vis[next] = 1;
next = next + k;
}
num_link++;
}
}
}

void dp() {
for (int i = 0; i < num_link; i++) {
F.fill(0);
for (int j = 0; j < link[i].size(); j++) {
if (j >= 2)
F[j] = std::max(F[j - 1], F[j - 2] + num[link[i][j]]);
else if (j == 0)
F[j] = num[link[i][j]];
else if (j == 1)
F[j] = std::max(F[j - 1], num[link[i][j]]);
}
// std::cout << F[link[i].size() - 1] << std::endl;
tot += F[link[i].size() - 1];
}
}

int main() {
init();
work();
dp();
std::cout << tot;
/*
for (int i = 0; i < num_node; i++)
std::cout << node[i] << "->" << num[node[i]] << " ";
std::cout << std::endl;

for (int i = 0; i < num_link; i++) {
for (int j : link[i]) {
std::cout << j << " ";
}
std::cout << std::endl;
}
*/
return 0;
}

题目描述:

跳蚤国正在大力发展旅游业,每个城市都被打造成了旅游景点。

许多跳蚤想去其他城市旅游,但是由于跳得比较慢,它们的愿望难以实现。这时,小C听说有一种叫做火车的交通工具,在铁路上跑得很快,便抓住了商机,创立了一家铁路公司,向跳蚤国王请示在每两个城市之间都修建铁路。

然而,由于小C不会扳道岔,火车到一个城市以后只能保证不原路返回,而会随机等概率地驶向与这个城市有铁路连接的另外一个城市。跳蚤国王向广大居民征求意见,结果跳蚤们不太满意,因为这样修建铁路以后有可能只游览了3个城市(含出发的城市)以后就回来了,它们希望能多游览几个城市。于是跳蚤国王要求小C提供一个方案,使得每只跳蚤坐上火车后能多游览几个城市才回来。 小C提供了一种方案给跳蚤国王。跳蚤国王想知道这个方案中每个城市的居民旅游的期望时间(设火车经过每段铁路的时间都为1),请你来帮跳蚤国王。

【输入格式】

输入的第一行包含两个正整数n、m,其中n表示城市的数量,m表示方案中的铁路条数。 接下来m行,每行包含两个正整数u、v,表示方案中城市u和城市v之间有一条铁路。 保证方案中无重边无自环,每两个城市之间都能经过铁路直接或间接到达,且火车由任意一条铁路到任意一个城市以后一定有路可走。

【输出格式】

输出\(n\)行,第\(i\)行包含一个实数\(t_i\),表示方案中城市\(i\)的居民旅游的期望时间。你应当输出足够多的小数位数,以保证输出的值和真实值之间的绝对或相对误差不超过\(e^{-9}\)

【样例输入】 4 5 1 2 2 3 3 4 4 1 1 3

【样例输出】 3.333333333333 5.000000000000 3.333333333333 5.000000000000

【样例输入】

10 15 1 2 1 9 1 5 2 3 2 7 3 4 3 10 4 5 4 8 5 6 6 7 6 10 7 8 8 9 9 10

【样例输出】 10.000000000000 10.000000000000 10.000000000000 10.000000000000 10.000000000000 10.000000000000 10.000000000000 10.000000000000 10.000000000000 10.000000000000

【数据规模与约定】 对于10%的测试点,\(n \le 10\); 对于20%的测试点,\(n \le 12\); 对于50%的测试点,\(n \le 16\); 对于70%的测试点,\(n \le 19\); 对于100%的测试点,\(4 \le k \le n \le 21,1 \le u, v \le n\)。数据有梯度。

资源约定:

峰值内存消耗 < 256M

CPU消耗 < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0

注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。

注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。提交时,注意选择所期望的编译器类型。

问题分析:

​这道问题真的很难,当时间也很紧,就剩余20分钟左右,考试直接放弃了。现在并没有明确思路,待网上有题解后,有机会再补充。

0%