Skip to main content

· 17 min read
Allen
此内容根据文章生成,仅用于文章内容的解释与总结

如果你想要实现这样一个功能:当窗外开始下雨,窗户自动关闭

如果你想知道解决方案,可以直接跳到最后一部分。

我们仅看前半部分,那么你需要:检测窗外是否有雨水,并把这个信息传递给窗户控制器

这个过程中,你需要:

  • 传感器:检测窗外是否有雨水/或者获取网络上的天气信息
  • 单片机主控板:可以接收传感器的数据,并收发网络请求
  • 执行器:窗户控制器,可以接收单片机的指令,控制窗户的开关
  • 供电:保证传感器和主控板的正常工作

这个过程你需要知道以下内容:单片机如何烧录程序、传感器如何连接、如何获取传感器数据、如何发送网络数据、如何制作外壳(例如 3D 打印)。

传感器基础知识

  • 负极表示符号: - / G / Gnd / 黑色
  • 正极表示符号:+ / V / Vcc / 红色
  • 信号管脚: S 可以表示信号,根据传感器的不同,参数范围是 0-1023 或 0 1
  • 模拟量信号管脚表示符号: A 参数范围在 0-1023
  • 数字量信号管脚表示符号:D 参数为 0 或 1
tip

如果标识与颜色发生冲突,一般以标识为准:例如接口上写着 V,但连接线颜色为黑,一般当作正极处理。

当单片机通电时,所有的引脚都带电,但是只有信号管脚的电压会随着传感器的变化而变化。

因此传感器的正负极理论上可以任意连接,只需保证信号管脚连接指定的即可。

A 口的功能比 D 口更加强大,因为 A 口可以接收模拟信号,而 D 口只能接收数字信号。因此部分传感器 D 接 A 也可以正常读数。

两管脚

常见的有扬声器、电机(俗称马达)。

这类设备因为较为特殊,一般有专门的接口,或者占用 2 个信号端口,通过信号的变化来工作。

马达往往需要更大的工作电压,如果没有专门的连接口,有可能需要在板上使用跳针切换工作电压。

三管脚

这类传感器数量最多,往往由 GVA 或者 GVD 组成。

使用时,正负极与单片机正负极连接,信号线与板上标注的 A 或 D 进行连接。

四管脚

四管脚传感器分为很多类

特殊接口的,譬如:人体温度传感器

正负极与信号口一般都专门对应的位置供连接。

双信号接口的,譬如:超声波

一般有四个接口:GVTE,其中 GV 正常连接,T 和 E 都接在信号管脚上。

同时接收 AD 的,譬如:烟雾传感器

一般有四个接口:GVAD,其中 GV 正常连接,D 表示有没有烟雾,A 表示烟雾浓度。分别接在对应的信号管脚即可。

五管脚

譬如:摇杆传感器

一般五个接口为:GVXYB,其中 GV 正常连接,X 表示 X 轴(是模拟量接 A)、Y 表示 Y 轴(是模拟量接 A)、B 表示按钮(是数字量接 D)

其他特殊类

其他特殊的传感器一般有特殊接口,譬如:摄像头、屏幕等。

根据说明接入即可。

与单片机通信

单片机(Microcontroller Unit,简称 MCU)是指一个微型计算机集成在一个单独的微型芯片中,它包括处理器(CPU)、内存(通常包括 RAM 和 ROM)、以及各种输入/输出(I/O)接口等在内的完整计算设备。

单片机设计用于嵌入式应用,通常在硬件设备中执行特定任务。例如,你的电视遥控器可能就是由一个单片机控制的,它可以接收你的输入,然后发送相应的信号到电视上。其他常见的单片机应用包括玩具、家用电器、医疗设备、汽车等。

有的单片机可以使用完整的 Python,譬如华硕的 thinker edge R、部分树莓派,有的 Arduino 板子、ESP32 等只能使用简化的 MicroPython。

tip

当我们希望通过 windows 计算机的 USB 接口和单片机设备进行串口通信时,需要将 USB 接口转换为标准的串行接口,这个过程需要一个介于 USB 和串口之间的翻译,我下面的驱动就是这个翻译。

在搜索引擎中搜 CH341SER 驱动

过程中所有弹窗有下一步点下一步,有确认点确认

在编程软件中识别单片机

常用的编程软件有:Scratch、Mixly、Mixly2、MaixPy 等。

有的支持图形化编程与代码编程,有的需要仅支持代码编程。

下载对应的编程软件后,打开软件。

选择主控这个环节,不同软件的选择方式不同。

tip
  • Vegeta 这样基于 Scratch 的编程软件,需要从左下角选择添加对应的主控型号。

  • Mixly 从右下角,串口旁的下拉菜单选择对应的主控型号。

  • Mixly2 从登录菜单中主控型号后,进入代码编辑页,右上角选择串口旁可以选择更加详细的主控型号。

  • MaixPy 从上方的工具页面中选择开发板型号。

通过连接线连接电脑与单片机。此时可能会有多种情况:

  1. 会提示:有串口连接,并弹出且仅弹出 1 个串口。
  2. 识别计算机上的所有串口,需要自己选择(可以通过反复插拔确认新增的端口号)。
  3. 不弹出任何串口,需要主控通电启动后才识别串口。
  4. 也有的串口时有时无,此时可以考虑:连接线接触不良(更换连接线),或者是主控/USB 电压不稳定——常见于学校机房(主控或电脑独立供电)
  5. 还有的默认的波特率需要调整,否则无法识别传输信号。

连接成功后记得初始化固件,使其恢复到软件对应的固件版本。类似 Android 手机的刷机/恢复出厂设置。

单片机编程

这里的传感器特指狭义的通过半导体检测物理量的传感器,如温度传感器、湿度传感器、光敏传感器等。这些传感器的特点是:输出信号是数字/模拟信号。

数字量传感器的输出信号是数字信号,他的特点是只 返回/发出 两种状态:高电平和低电平。对应在代码中是 1 和 0 。

  • 如声音传感器如果是数字量传感器,当检测到声音时输出高电平,否则输出低电平。

  • 如小灯,输出高电平表示亮,输出低电平表示灭。

模拟量传感器的输出信号是模拟信号,他的特点是输出的电压值是连续变化的。对应在代码中是 0-1023(通常如此,并非绝对) 。

  • 还是以声音传感器为例,如果是模拟量传感器,当检测到声音时输出的电压值会随着声音的大小而变化。

  • 还是以小灯为例,输出最大值表示最亮,输出最小值表示最暗,亮度会随输出的电压值变化。

有的传感器同时支持数字量和模拟量输出,有的不是。

因此,对于不确认的传感器,我们一般先假设传感器是模拟量传感器,如果不是,再当作数据量处理。

模拟量传感器读取

下面以 32 接口为例

import machine
adc32 = machine.ADC(machine.Pin(32))
while True:
print(adc32.read_u16())

模拟量传感器输出

下面以 0 接口为例

import machine
pwm0 = machine.PWM(machine.Pin(0))
pwm0.duty_u16(0)
pwm0.duty_u16(255)

然而,有些动力类传感器需要设置占空比:占空比主要与脉冲宽度调制(Pulse Width Modulation,PWM)相关,它是一种模拟信号的数字化表示方法。在 PWM 中,一个周期内的高电平时间占总周期时间的比例就是占空比。

传感器的输出类型可以有多种,包括模拟电压、模拟电流、数字信号(如 I2C、SPI、UART 等)、频率、PWM 等。只有在使用 PWM 输出的传感器时,才需要设置占空比。例如,一些伺服电机会使用 PWM 信号来控制其位置,这时就需要设置占空比。

对于其他类型的传感器,如模拟电压输出的传感器、数字信号输出的传感器等,就不需要设置占空比。这些传感器的输出通常是连续的或者是特定的数字信号,不涉及到占空比的概念。

from machine import Pin, PWM
import time

# 创建一个PWM对象
pwm = PWM(Pin(2))

# 设置PWM信号的频率为50Hz
# 每秒50个周期,所以每个周期的时间是1秒/50,即20ms。
pwm.freq(50)

# 一般来说,当PWM信号的高电平时间为1ms时,舵机转到0度;
# 当高电平时间为2ms时,舵机转到最大角度。
# 这个范围内的其他高电平时间对应的是0到180度之间的其他角度。

# 转到0度()
pwm.duty(52) # 1ms / 20ms * 1024 = 51.2 取不低于最小值的整:52
time.sleep(1) # 等待一段时间让舵机转到指定位置

# 转到180度
pwm.duty(102) # 2ms / 20ms * 1024 = 102.4 取不高于最大值的整:102
time.sleep(1) # 等待一段时间让舵机转到指定位置

# 关闭PWM
pwm.deinit()

数字量传感器读取

import machine

pin0 = machine.Pin(0, machine.Pin.IN)
while True:
print(pin0.value())

数字量传感器输出

import machine
import time

pin13 = machine.Pin(13, machine.Pin.OUT)
while True:
pin13.value(0)
time.sleep_ms(50)
pin13.value(1)
time.sleep_ms(50)

单片机网络通信

获取天气

心知天气 API 分为免费版、付费版等多个坂本,不同的版本返回的数据数量有所不同。

免费版仅返回三种基本数据,付费版可以返回多种数据。mixly 中默认的 KEY 为高级付费版,可返回全部数据。

数据返回的格式为字典,因此可以通过如下方式进行解包,下面的代码提供了部分数据解包的方法。

需要注意的是,该功能为联网功能,需要在联网环境下使用,确保 wifi 名和密码正确。

import mixiot
import machine
import seniverse_api


mixiot.wlan_connect('wifiname','wifipassword')
print(seniverse_api.weather_now('SGJl0ExVN-4j27msR','北京'))

onenet 物联网传输数据至云端

onenet 物联网是中国移动推出的物联网交互平台,主要面向一般开发者,因此 AIbox 这款设备可以使用 onenet 物联网平台进行数据传输。

相比于 mixio 这样专注于单片机的物联网平台来说,onenet 的文档与接口可能会频繁变动,如有出入以官网教程为准。

onenet 物联网平台网址:https://open.iot.10086.cn/doc/

文档中提供了传输文本与文件 2 种方式

import json
import asyncio
import websockets
from uuid import uuid4

# 音频文件测试路径。
audioFile = "test.mp3"
# 使用自己产品Id和apikey替换下列参数。
productId = "x"
apikey = "x"

#发送文本请求
async def textRequest(ws):
content = {
"aiType":"dm",
"topic": 'nlu.input.text',
"recordId": uuid4().hex,
"refText": "测试" #修改文本请求的输入
}
try:
await ws.send(json.dumps(content))
resp = await ws.recv()
print(resp)
except websockets.exceptions.ConnectionClosed as exp:
print(exp)

#发送音频请求
async def audioRequest(ws):
content = {
"aiType": "dm", #可选dm/asr, dm获取对话结果,asr只获取asr结果
"topic": "recorder.stream.start",
"recordId": uuid4().hex,
"audio": {
"audioType": "mp3", #修改为测试文件的类型
"sampleRate": 16000, #修改为测试文件的sampleRate
"channel": 1, #修改为测试文件的channel
"sampleBytes": 2 #修改为测试文件的sampleBytes
},
"asrParams": {
"realBack": True, #实时返回asr结果
"enableVAD": True, #启动VAD
"enablePunctuation": True, #返回结果是否带拼音
"enableTone": True, #返回结果是否带声调
"enableConfidence": True, #返回结果是否带置信度
"enableNumberConvert": True, #返回结果是否进行数字转换
},
}
try:
#发送文本消息
await ws.send(json.dumps(content))
# 发送音频消息
with open(audioFile, 'rb') as f:
while True:
chunk = f.read(400) #wav buffsize=3200 其他的400
if not chunk:
await ws.send(bytes("", encoding="utf-8"))
break
print(len(chunk))
await ws.send(chunk)
async for message in ws:
print(message)
resp = json.loads(message)
if 'dm' in resp:
break
except websockets.exceptions.ConnectionClosed as exp:
print(exp)
ws.close()

async def dds_demo():
url = f"ws://botai-dsg.and-home.cn:4443/dsg/v1/prod?productId={productId}&apikey={apikey}"
print(url)
async with websockets.connect(url) as websocket:
#await textRequest(websocket) #发送文本请求
await audioRequest(websocket) #发送音频请求
asyncio.get_event_loop().run_until_complete(dds_demo())

后话

最后,通过大量的学习和试错打样,你发现米家雨水传感器,淘宝 46 包邮,搞活动更便宜,这大概是你最后的选择。

· 14 min read
Allen
此内容根据文章生成,仅用于文章内容的解释与总结

内容为最近一年的个人硬件产品的结构设计的对比分析,作为DIY爱好者,必然会有不足之处,欢迎指正。

假设你想自己制作一个桥梁模型送给朋友,手工制作一个当然很好,使用木头、或者捏一个土坯然后烧制、又或者直接使用车床加工。但是这需要一定的技艺,以及各种器具。

但是除了手工制作之外,你依然有很多种方式可以选择:

  • 乐高搭建,你只需要知道乐高拼搭基础,设计出图纸后导出并购买零件(正版与非正版价格差距极大)。

  • 3D打印,3D打印有多种不同的原理,材料可选类型也比较多,设计出模型之后切片打印即可。

  • 激光切割,使用激光切割机切割亚克力板或者木板,之后拼搭。

简短对比 :

技术乐高3D打印(FDM)激光切割
特点模块化设计,易于组装和拆解可以打印出复杂的三维结构可以精确地切割出复杂的二维结构
结构强度玩具级工具级工具级
设计难度小时级小时级小时级
调试难度儿童中等
设备价格千元级千元级
材料价格十元级十元级百元级
制作速度小时级小时级分钟级
气味有(根据材料不同,可能有塑料熔化的气味)有(切割过程可能产生烧焦的气味)
噪音有(打印过程可能会产生噪音)有(切割过程可能会产生噪音)
优点总结1. 适合所有年龄层,易于上手
2. 可重复使用,具有高度的灵活性
3. 无需特殊工具或设备
1. 可以制作出复杂的三维结构
2. 可以打印出定制的零件,适合个性化设计
1. 可以精确地切割出复杂的二维结构
2. 结构稳固,适合制作承受重负的结构
缺点总结1. 结构可能不够稳固,不适合制作大型或承受重负的结构
2. 设计和功能可能受到乐高模块种类和数量的限制
1. 打印速度较慢,大型结构可能需要很长时间
2. 需要一定的设计和操作技能,学习曲线较陡峭
1. 设备价格高昂,运行和维护成本也较高
2. 需要一定的设计和操作技能,学习曲线较陡峭

乐高类

优点

  • 设计快速、简单,易于拆卸,非常适合快速验证创新想法。

  • 乐高模型可以随时调整,拆解后的部件可再次使用,避免了材料的直接损耗。相比之下,3D打印和激光切割等技术一旦出现错误,调整难度较大,材料利用率相对较低。

缺点

  • 乐高结构的强度有限,不适合承受较大的力,因此更适合轻量级的应用。

  • 乐高零件种类有限,对复杂结构的设计造成一定限制。

  • 乐高零件的精度也有限,主要的结构单位和半个单位,这意味着你的模型尺寸需要是0.5个单位长度的倍数。

学习心得

搜索一些搭建图纸,可以帮助你更好地理解乐高的设计原理和技巧。不用管书籍的语言是德语还是英语。

软件

乐高结构设计软件:https://studiohelp.bricklink.com/hc/en-us

优点:软件可以导出零件名称,一键跳转采购。

总结

乐高设计快,但是采购之后自己拼搭比较耗时,遇到问题时可以先思考我之前有没有见过类似的图纸,没有见过而且比较复杂的话,一般选择3D打印。

3D打印

3D打印是个人制造的一种重要方式,它可以将数字模型转化为实体物体,广泛应用于工业设计、医疗、教育等领域。

3D打印分为多种技术,常见的有FDM(熔融沉积成型)、SLA(光固化成型)、SLS(激光烧结成型)等。其中FDM是最常见的3D打印技术,也是我使用的技术。

在3D打印过程中,材料的选择对打印效果和性能有重要影响。我使用的是创想三维K1C,相比同价位拓竹优势一些,下面是它支持的材料及其特点。

  1. ABS(丙烯腈-丁二烯-苯乙烯共聚物):ABS是一种常用的热塑性塑料,具有强度高、耐热性好、易加工等特点,适用于一般性的3D打印项目。

  2. PLA(聚乳酸):PLA是一种生物降解塑料,通常采用玉米等植物原料制成,具有环保、易于打印、无毒等特点,适用于一般性的3D打印项目。

  3. PETC(PETG,改性聚对苯二甲酸乙二醇酯):PETC是一种耐热、透明度高的塑料,具有良好的耐化学性和强度,适用于需要透明或耐热性能的打印项目。

  4. PET(聚对苯二甲酸乙二醇酯):PET是一种透明度高、耐热性好的塑料,常用于食品包装等领域,适用于需要透明性能的打印项目。

  5. TPU(热塑性聚氨酯):TPU具有弹性好、耐磨性强的特点,通常用于制作柔软的零件或弹性组件。

  6. PAASA(聚酰胺酸酯):PAASA是一种工程塑料,具有优异的耐热性和机械性能,适用于要求高强度和耐热性的打印项目。

  7. PC(聚碳酸酯):PC具有优异的抗冲击性和透明度,常用于制作耐用的零件或透明的构件。

  8. PLA-CF(碳纤维增强聚乳酸):PLA-CF是碳纤维增强的聚乳酸材料,具有更高的强度和刚性,适用于要求更高机械性能的打印项目。

  9. PA-CF(碳纤维增强聚酰胺):PA-CF是碳纤维增强的聚酰胺材料,具有更高的强度和耐热性,适用于要求更高机械性能的打印项目。

  10. PET-CF(碳纤维增强聚对苯二甲酸乙二醇酯):PET-CF是碳纤维增强的PET材料,具有更高的强度和刚性,适用于要求更高机械性能的打印项目。

建模软件

建模软件是3D打印的基础,可选的非常多,譬如3D oneTinkercadFusion 360

模型可以从0开始建,也在别人的基础上修改。

成品模型库:

优点

  • 只需要一个晚上就可以验证你的复杂创意。

  • 提升空间大,可以选择一体成型,或者碳纤维材料,也可以使用暂停打印嵌入螺丝、螺母、磁石、金属棒等来创作一些强度极高的混合材质的工具。

缺点

  • 连接处的强度不够需要使用人工介入使用特殊手段提升。

  • 打印时间长而且偶尔成品有瑕疵。

学习心得

3D打印的知识相对来说比较碎片化,需要持续学习与实战验证。可以关注一些3D打印博主、逛逛3D打印社区,可以学习到很多建模、切片、机器的调试、嵌入等技巧。

软件

3D打印需要用到2种软件:3D建模与切片

建模软件就是构建出你要打印的物品的模型的软件。

切片软件则是把这个模型切割为一层一层,控制3D打印机的喷嘴运动路径等参数的软件。目前我的3D打印机只认切片后的文件。

这里建模软件推荐找个教程,教程上用什么跟着学也用什么。

切片软件一般厂家有提供。

激光切割

激光切割机主要用于切割和雕刻材料,如亚克力板、金属片、木板等。激光切割机通过激光束对材料进行加热,使其熔化或气化,然后通过气流将熔化或气化的材料吹走,从而实现切割。

可以通过调整激光头的运行速度和激光强度来实现穿透切割和表面雕刻。

激光强度大,激光头移动速度慢,可以实现穿透切割,即将材料完全切断(速度:10分钟级别)。

激光强度小,激光头移动速度快,可以实现表面雕刻,即在材料表面刻出图案或文字(速度:秒级别)。

优点

  • 刻字速度极快,立等可取。

  • 支持材料多样。

缺点

  • 设备占地较大,气味明显需要单独通风,水冷型的需要偶尔换水。

  • 部分材料切割容易边缘不光滑、发黑。

学习心得

激光切割主要是调试激光能量强度与材料的关系。常见的材料有以下几种:

亚克力板,比较常见的像一块30*30cm 5mm厚的透明板材

个人很喜欢的材料,透明的材质用来装日用摆件类的单片机很有“探索版”的感觉。做工具用比较脆,有一定韧性,但保存不当会产生划痕。

金属片:强度比木板要好不少,相比于亚克力板的硬、脆,金属片可以在切割后折弯,用来制作需要弯曲且强度要求高的结构。

无可替代的优势,你永远可以相信金属。

木板:木板具有良好的硬度和强度,面积较大的区域有一定韧性,但细的地方极其容易断裂。另外木板在潮湿的环境下可能会变形,因此需要在适当的环境中存储和使用。

用来制作各种家居装饰、艺术品和模型。木板的颜色和纹理使得切割出来的产品具有自然的美感和温馨的氛围。此外,木板也可以通过砂纸或者涂料进行后期处理,以改变其颜色和质感,增加产品的美观性和耐用性。

有些材料理论上可行,但是我实际工作中没有使用,不便评价:玻璃和陶瓷、织物和皮革、塑料和橡胶

相比前面两项个人级别的制作,激光切割家用较少,其一是气味较大,需要单独通风,有些激光切割机还需要用水桶接冷凝水。其二是占地面积较大,3D打印机可以放在桌子上,激光切割机需要放在地上,且需要有一定的安全距离。

在学校、工作室等场所,通常会有激光切割机,可以提供激光切割服务。

软件

厂家附赠非开源软件。

· 7 min read
Allen
此内容根据文章生成,仅用于文章内容的解释与总结

群晖是一款非常优秀的 NAS 产品,它可以提供文件存储、多媒体服务、远程访问等功能。一般来说,一台群晖的寿命 4-6 年,折合下来比服务器便宜一点点。因此,我决定做云时代的逆行者,将服务器上的一些服务迁移到群晖上。

注意:云服务器的优点有很多,包括:更加安全、更加灵活、更加便宜等等,迁移需谨慎。

原环境分析

租用的服务器是阿里云产品,选择了宝塔面板进行管理。

网站使用了前后端分离的设计模式,前端基于 Vue 生成的静态页面,后端则基于 Java。后端允许上传一些文件,使用的是 3000 端口进行通信,文件传输则在 8000 端口,路径设置为/upload/。通过 Nginx 进行了反向代理,将/upload/路径的请求转发到 8080 端口,将其他请求(80 端口)转发到 3000 端口。因此,对于这个网站,通信主要通过 80 端口和 8080 端口进行。

tip

如果你遇到了类似的问题,只需要梳理出我们最终通过宝塔面板的哪些端口访问就可以了。

迁移策略

服务器迁移

创建一个 Docker 容器(如果原本用的是宝塔面板,那就继续使用宝塔面板),并按照原来的部署文档在容器内部重新运行服务。

将相关的命令设置为开机自启动。

另外,重要的文件等资料需要单独挂载,并可以设置为只读模式,以防止数据丢失同时更加安全。

端口映射

Docker 的镜像可以通过端口映射的方式,将容器内的端口映射到宿主机(群晖)的端口上。

路由器可以将外网流量转发到宿主机(群晖)上,因此只需要在路由器上设置端口转发即可。

tip

需要注意的是,群晖的部分端口可能已被群晖自身使用,可以通过群晖官网查询。外网的部分端口(如 80 端口)可能被运营商封掉,或者路由器自身需要使用。因此,我们需要选择一些不常用的端口,以避免出现服务异常。

因此有了以下的端口映射规则(你可以先停止容器再设置):

容器内端口(宝塔)宿主机端口(群晖)外网端口
8040804080
808080804880

域名解析

现在我们需要将域名解析到服务器上,由于 80 端口不能直接访问,并且小区和域名供应商都会要求备案,这就需要我们使用 Cloudflare 的 DNS 解析服务。Cloudflare 可以充当中间人,将流量转发到服务器上。

即无法通过 http://www.xxx.com 直接访问到服务器,只能通过域名+端口号如 http://www.xxx.com:8080 访问。

在 cloudflare 中添加域名,全程按照提示操作添加即可。

添加完成后,选择规则->回源(Origin Rules)。因为 Java 用到了两个端口,所以需要添加两个端口转发规则:

  • 当满足条件时(访问域名且路径不以/upload/开头),将流量转发到服务器的 4080 端口。

  • 当满足条件时(访问域名且路径以/upload/开头),将流量转发到服务器的 4880 端口。

结果

通过以上步骤,我们成功地将网站迁移到了新服务器上。用户访问网站时,流程如下:

  • 通过 DNS 解析,找到 cloudflare 的服务器。
  • cloudflare 根据规则,把 80 流量转发到路由器的 4080 上。
  • 服务器接收到流量,把流量转发到群晖的 4080 端口。
  • 群晖接收到流量,把流量转发到容器的 80 端口。
  • 容器接收到流量,通过 Nginx 分配数据。
  • Nginx 根据规则,把流量转发到容器内的 8080 端口。
  • 数据按照原本的路径逐一返回至 cloudflare。
  • cloudflare 把数据返回给访问者。

如法炮制可以继续迁移其他站点。

这种迁移方式不仅保证了原有服务的连续性,也确保了服务器不会被外网直接访问,从而提高了网络安全性。即使网站被黑,也不会影响到其他服务,只需重启这个容器即可恢复原状。

后话

这次的迁移过程体现了分层思想的重要性,这主要来自《白帽子讲 Web 安全》这本书。整个过程没有遇到什么问题,只需要对容器化和 Cloudflare 有一定的了解。我希望我的经验能给读者带来一些启示。

· 8 min read
Allen
此内容根据文章生成,仅用于文章内容的解释与总结

前段时间偶然间看到了一些生成式 AI 文本摘要项目,觉得很有意思。个人不太信任第三方服务,于是就加到待办里,想着自己也实现一个,最近终于有空了。

逻辑上的核心功能是:自动生成,无需人工干预,一次生成,再次生成消耗 key

样式上的核心功能是:逐字显示,好像是个机器人真的在实时生成。

本篇文章将记录如何实现这个功能。

原型

博客是基于 Docusaurus 搭建的,而 Docusaurus 是基于 React 的,文章内容是通过 markdown 文件写的,所以需要设计一个 React 组件,传入 markdown 文件内的文本内容,每次有请求时,将文章内容转换为文本摘要。

但是这样做一些问题,主要的是重复的每次请求都会消耗 key,因此需要储存已请求内容。

判断条件可以设为如果内容不存在,则直接调用,否则就重新生成,然后存储。

由此可知我们至少需要:内容(用来判断是否重复)、摘要(用来显示)

{
"This is the text to summarize": "This is the summary",
"This is the text to summarize 2": "This is the summary 2",
}

如果储存是需要成本的,我们可以使用hash值来判断内容是否相同,如果hash值相同,那么就不需要重新生成摘要了。这样不要存储一篇文章,只需要存储hash值和摘要就可以了。

{
"248ae1890a0084b3bbc30bd3c0c2e17e": "summary"
}

如果有多个文章如何每次请求只请求指定的文章呢?

我们可以使用路径来区分不同的文章,在服务器上我们的方法就太多了。

但是静态的话我使用文件名来区分不同的文章。将文章路径中的/替换为_,然后加上.json后缀,就可以了。

blog_1.json
{
"248ae1890a0084b3bbc30bd3c0c2e17e": "summary"
}

把这个代码逻辑插入到 React 组件中就可以实现了,根据你调用的API不同,你也许可以设置返回的摘要长度等参数。

记得别直接把key写在代码里,而是通过环境变量传入。如果你的项目通过github pages部署,那么可以在项目的setting中设置环境变量REACT_APP_API_KEY,然后在代码中通过process.env.REACT_APP_API_KEY来获取。

实现

当然,这只是一个比较粗糙的想法,接下来让我们完善下代码细节,让它优雅的同时,可以在博客中使用。

逻辑功能

我在reflex-chat#20里提交了关于百度API的实现,在这个仓库里你应该能找到其他API的操作方式。

main.py
import os
import json
import time
import hashlib
import pathlib
import requests
import feedparser
from parsel import Selector
from datetime import datetime
from jinja2 import Environment, FileSystemLoader
class BaiduAI:
def __init__(self):
self.BAIDU_API_KEY = os.getenv("BAIDU_API_KEY")
self.BAIDU_SECRET_KEY = os.getenv("BAIDU_SECRET_KEY")
self.token = self.get_access_token()

def get_access_token(self):
"""
:return: access_token
"""
url = "https://aip.baidubce.com/oauth/2.0/token"
params = {
"grant_type": "client_credentials",
"client_id": self.BAIDU_API_KEY,
"client_secret": self.BAIDU_SECRET_KEY,
}
return str(requests.post(url, params=params).json().get("access_token"))

def get_result(self, text: str):
messages = json.dumps(
{
"messages": [
{
"role": "user",
"content": "阅读下面的博文,然后尽可能接近50个词的范围内,提供一个总结。只需要回复总结后的文本:{}".format(
text
),
}
]
}
)
session = requests.request(
"POST",
"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro?access_token="
+ self.token,
headers={"Content-Type": "application/json"},
data=messages,
)
json_data = json.loads(session.text)
if "result" in json_data.keys():
answer_text = json_data["result"]
return answer_text


class Jsonsummary:
def __init__(self):
root = pathlib.Path(__file__).parent.resolve()
self.json_file_path = os.path.join(root,"summary")
self.url = "https://jiangmiemie.com/"
self.pages = []

def load_json(self):
# 加载JSON文件
loaded_dict = {}
for file in os.listdir(self.json_file_path):
with open(os.path.join(self.json_file_path, file), "r", encoding="utf-8") as json_file:
loaded_dict[self.url + file.replace("_", "/").replace(".json", "")] = json.load(json_file)
return loaded_dict

def save_json(self,loaded_dict):
# 将字典存入JSON文件
for key in loaded_dict:
key_path = key.replace(self.url, "").replace("/", "_") + ".json"
save_path = os.path.join(self.json_file_path, key_path)
with open(save_path, "w", encoding="utf-8") as json_file:
json.dump(loaded_dict[key], json_file, indent=4)

def clean_json(self):
# 根据RSS结果清理JSON文件
for file in os.listdir(self.json_file_path):
if file not in self.pages:
os.remove(os.path.join(self.json_file_path, file))

def blog_summary(feed_content):
jsdata = Jsonsummary()
loaded_dict = jsdata.load_json()

for page in feed_content:
url = page["link"].split("#")[0]
jsdata.pages.append(url.replace(jsdata.url, "").replace("/", "_") + ".json")
# 剪切掉摘要部分,仅保留正文
content = page["content"][0]["value"]
selector = Selector(
text=content.split("此内容根据文章生成,仅用于文章内容的解释与总结")[1]
)
content_format = "".join(selector.xpath(".//text()").getall())
content_hash = hashlib.md5(content_format.encode()).hexdigest()
if (
loaded_dict.get(url)
and loaded_dict.get(url).get("content_hash") == content_hash
):
continue
else:
ai = BaiduAI()
summary = ai.get_result(content_format)
loaded_dict.update(
{url: {"content_hash": content_hash, "summary": summary}}
)
jsdata.save_json(loaded_dict)
jsdata.clean_json()

def fetch_blog():
content = feedparser.parse("https://jiangmiemie.com/blog/rss.xml")["entries"]
blog_summary(content)


if __name__ == "__main__":
fetch_blog()

BAIDU_API_KEYBAIDU_SECRET_KEY传入git action的环境中的示例:

- name: Update
run: python build_readme.py
env:
BAIDU_API_KEY: ${{ secrets.BAIDU_API_KEY }}
BAIDU_SECRET_KEY: ${{ secrets.BAIDU_SECRET_KEY }}

完整代码参考我的github仓库

这样我访问部署网址/summary/博客路径就可以精准得到对应的摘要了,接下来就是在博客中使用了。

样式功能

样式上的核心功能是:逐字显示,好像是个机器人真的在实时生成。可以更详细的拆为:获取摘要、逐字显示、放入框架。

//逐字显示
const TypingComponent = ({ text, speed = 100 }) => {
const [displayedText, setDisplayedText] = useState('');

useEffect(() => {
let index = 0;

const typingInterval = setInterval(() => {
setDisplayedText((prevText) => {
if (index < text.length) {
return prevText + text[index++];
} else {
clearInterval(typingInterval);
return prevText;
}
});
}, speed);

return () => clearInterval(typingInterval);
}, [text, speed]);

return <>{displayedText}</>;
};
// 获取摘要
const JsonReader = ({
fieldToMatch,
}) => {
// 替换url与/
const path = fieldToMatch.replace(/https:\/\/jiangmiemie.com\//, "").replace(/\//g, "_");
const url = `https://jiangmiemie.com/jiangyangcreate/summary/${path}.json`;
const [jsonData, setJsonData] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const data = await response.json();
setJsonData(data);
} catch (error) {
console.error("Error fetching JSON:", error);
}
};

fetchData();
}, [url]);

const getFieldData = () => {
if (!jsonData) {
return <TypingComponent text='摘要生成中...' speed={100} />;
}
// 根据字段进行匹配
const matchingField = jsonData["summary"];
return (
<>
<TypingComponent text={matchingField} speed={100} />
</>
);
};

return <>{getFieldData()}</>;
};

// 放入框架
const Aisummary = ({ children }) => (
<div class="post-ai">
<div class="ai-title">
<a
class="ai-title-left"
href="/blog/2024/1/31/"
title="查看详情"
data-pjax-state=""
>
<div class="ai-title-text">文章摘要</div>
</a>
</div>
<div class="ai-explanation" style={{ display: "block" }}>
<JsonReader fieldToMatch = {children}/>
</div>
<div class="ai-suggestions"></div>
<div class="ai-bottom">
<div class="ai-tips">此内容根据文章生成,仅用于文章内容的解释与总结</div>
</div>
</div>
);

以上所有代码构成了你现在在本篇文章中看到的效果。

· 6 min read
Allen
此内容根据文章生成,仅用于文章内容的解释与总结

写博客对我而言,是一种爱好,可以追溯到 2009 年,这篇文章记录了一些博客写作过程之中的实践。

设计博客

广泛的查看别人的博客

设计博客好比画画,从零开始画出一幅好画比较困难,但是如果临摹大师的作品就会相对容易一些。你可以搜索一些博客聚合类站点,查看成员的博客配置,对博客站点的设计有个大概的印象。这类站点通常有比较好的可迁移性。

不需要买域名和服务器

我建议个人博客使用 markdown 编写,存在 GitHub 并绑定自己默认是个非常好的选择。如果你从服务器开始搭建,不光会耗尽初始的热情,也会由于更新不便,服务器异常而法专注于内容。

博客美化切记过度

起初,写技术博客对我来说是一件容易的事,因为我无时无刻都有很多想法。我添加许多炫酷的特效在我的博客上,包括但不限于鼠标特效、点击特效、全局画布、一言、看板娘、音乐播放器、随机背景图、各种悬浮点击渐变特效。但这些美化难以做到不同设备上的兼容。此时我开始删减博客中我曾经认为“增色”的部分:内容不是越多越好。

更新方式

周更

周更的使用者是阮一峰老师,他从 2018 年开始每周都会定期更新,周更压力在于:不知道这周写什么。

双周更

双周更理论上能够很好的保持足够的输入,但实际操作中更容易遇到一整周都很忙的情况。

月更

月更是我坚持最久的更新方式,一个月足以输入足够的知识和内容。

载体选择

纯文字

纯文字的内容往往更能加载更快、获得国际流量的青睐、非常易于检索。

多媒体

只在必要的地方加入多媒体。注意:我并不是在否定文字以外的媒介,越来越多的知识不局限于通过书籍的方式传播:视频、音频、图片、动态网页、互动游戏。

整理博客

好的博客离不开定期整理,包括:

  • 清除无法访问的链接
  • 汇总合并类似的章节
  • 将碎片的知识串联成体系

标签分类

我个人建议:表头的栏目推荐为 4-5 个,如有折叠展开:展开内容为 3-5 个。我们信息加工能力的局限1

风格化

这一步是要将你的站点与其他站点区分开来,风格化过程中会涉及到一些编程相关的知识,但主要是审美。

Live Editor
// 一个足够简单的单元,配上无数次的重复即可呈现一个有趣的画面
// 一张小巧无缝矢量图即可实现用极小的内存平铺满整个背景。
function example(props) {
  // 使用 XPath 查询选择输出框
  const xpathSelector =
    "/html/body/div/div[2]/div/div/main/article/div/div[2]/div[4]";
  const myElement = document.evaluate(
    xpathSelector,
    document,
    null,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
  ).singleNodeValue;
  // 你可以在这里查看或修改这个SVG图片
  // 譬如 https://jiangmiemie.com/img/logo-192.svg
  myElement.style.backgroundImage =
    'url("https://jiangmiemie.com/img/protruding-squares.svg")';
  myElement.style.backgroundColor = "ee5522"; // 使用 backgroundColor,而不是 background-color
  // 添加一个时钟
  const [date, setDate] = useState(new Date());
  useEffect(() => {
    const timerID = setInterval(() => tick(), 1000);

    return function cleanup() {
      clearInterval(timerID);
    };
  });

  function tick() {
    setDate(new Date());
  }
  return (
    <div
      style={{
        color: 'white',
        height: "200px", // 适当调整高度
      }}>
    <h1>{date.toLocaleTimeString()}</h1>
    
    </div>
  );
}
Result
Loading...

放平心态

由于各种问题都会发生,譬如国内忽然不能访问 Github 了,那么容灾和冗余就决定了你是否能够快速恢复站点(如果不能的话,对你的打击会非常大)

博客的流量和短视频相比差的太多了,数年无人问津更是常态。不要急于求成,否则只会适得其反。这里推荐几个真正在玩博客的前辈:

  • 苏洋博客 —— 一个 real man 一个乐于分享的前辈。
  • 阮一峰的网络日志 —— 科技爱好者周刊已经成了我每周必看的内容,阮老师是真正的布道者。

Footnotes

  1. Miller, G. A. (1956). 神奇的数字:7±2;我们信息加工能力的局限(The magical number seven, plus or minus two: Some limits on our capacity for processing information)

· 30 min read
Allen
此内容根据文章生成,仅用于文章内容的解释与总结

做个现实的人

一切精神源于物质:一份镇定剂可以改变人的情绪,一杯咖啡能让人振作。精神世界与虚拟网络世界都是基于现实的物质存在的,或许它让你感到挫折和压抑,但是事实就是如此。

因此:直面现实,用行动代替祈祷才能真正的改变世界。

记录下所有

现实生活不比游戏里角色,人的知识一定会走向遗忘,你常用的那一部分会被记住,你不常用的另一部分最终会被遗忘,所以在你大规模的学习知识的时候,应该成体系地把它记录下来,衡量笔记质量的指标:能否快速复现知识。

保持开放心态

我们这代人身处漩涡的中心,有着独特的使命与难以想象的机遇,当然也需要应对前所未有的挑战。在接下来加速自己的学习计划,探索世界真相。

每个人都有自己的目标和世界观,请尤其注意群体标签不等于个体实际。

我看到因癌症离世的开源社区工作者写下:“Even if I keep on failing, as long as hope is still there, I will try and try again and try again. Because when I give up, that's it.”

来自世界各地的开源爱好者为开源社区无私奉献,非宗教的跨地域的共同体的雏形让我相信人类必能团结一心。

工业革命使得人类的生产力大大提高,大部分简单劳动被机器代替,由此催生了资本。人工智能革命或许没有那么强的划时代性,但是它也逐步使得简单脑力(驾驶、决策)得到替代。

因此相信平等与自由,所以我相信社会主义,一切都预示着我们正走向一个命运共同体。

在生活上,明确并公示自己的目标和世界观可以帮助自己更快达成目的:找到志同道合的朋友,获得他人的帮助、避免不必要的误会。

在软件上,初学者开源可以增强表达能力,获得指点。成熟者开源可以补充细节,完善项目,获得簇拥者。‌

我们越是成长,越是积累。越是需要保持开放心态,向他人学习。

平等自由的第一阶段是古中国:只有皇帝是是自由的,其他人都必须依附皇帝的意志。 平等自由的第二阶段是古希腊:被认可的阶级是平等自由的,奴隶主阶级彼此同等的享有投票权且都只能投一票。 平等自由的第三阶段是新教改革:在精神上用上帝面前人人平等肯定了人和人都是平等的,包括奴隶,也包括皇帝。 平等自由的第四阶段是社会主义:人们彼此即在精神上平等,也在物质上共有。

我相信平等自由,也相信社会主义 。

一切皆要独立

个人的美好生活只有自己争取:子女不应寄希望于父母,父母亦不应寄希望于子女。伴侣不应当寄希望于对方,公民不应当寄希望于政府。亲属可能突然离世,政府也会换届倒台。 自己的美好生活应当做好独立实现的准备,能让自己赢得美好生活的只有自己。

不光是人需要独立,软件也是如此。在电子信息如此广博的当下,社会分工越发细致。譬如微信公众号,QQ 空间,但你没有办法拒绝其中的广告服务,你也无法阻止他们做出变更,譬如发表的内容和次数做限制。因此要永远相信自己的系统,做好随时脱钩的准备。

理性交流

  • 交流可以代指讨论、辩论等深入的意见交流。区别于情绪交流(如:I think my boss is a bad guy)
  • 所有的会议的目的应当是指向一个明确的结果,譬如:明确责任、具体的战略或方案执行。(高层探讨战略执行,一线探讨方案执行)

脱离明确的结果的讨论会导致胜负欲的出现。

最大规模的探讨冲突地为:娱乐与社科,这里对想要情绪交流的人使用了意见交流的一套方案,而对想要意见交流的人,使用的情绪交流的方法。因此从一开始,额外的冲突就是注定的。

  • 所有我的建议是:在混乱未被规范之前,避免在未明的冲突区讨论。

客观认识权威

权威的人

在成长过程之中,人自然会遇到一些权威的人,他们在同样的道路上比你走的更远。但是要时刻记住,辩证的对待权威。因为权威在专业领域之外,往往表现的与普通人一致。

所以当权威的人就一个领域外的问题提出并不那么深刻的见解时,不要因此盲从,也不要否认他在本领域的专业性。

养成自己的辨别能力很重要,探寻深层次的问题,例如五次追问,现场还原等等。

权威的规则

法律是一种权威的规则。

规则的最终约束力不取决于人性的高贵,而是违约后果。

明文的规则是国家法律条款,违约后果的执行有国家公信力保证。如果国家公信力不能保证,譬如一个强大的军阀借了政府巨额资产不还,政府往往是没有办法的。再譬如农民借了地主的债务导致要被逼死选择起义。地主如果没有足够的武装力量,也是会被推翻。并且胜利的农民会写下:这是正义的起义。

客观的认识国家法律需要意识到 2 点:

  • 同个国家的法律是不停在变化的
  • 不同国家的法律对同个行为的规定是不同的

常见的法律的变化譬如:防疫政策的改变。 常见的法律的不同譬如:禁毒、禁酒。有的国家禁,有的国家不禁。

刑法威慑存在三个维度:严厉性、确定性、及时性 刑法惩罚的三种类型:财产刑、自由刑、生命刑

  • 严厉性 人民朴素的认为:只要提高刑法的严厉性就能预防犯罪,然而实际上二者并非一次函数那样线性相关。高强度的恐惧下往往会激起对立情绪。

“严打”之后的八年(1984 年至 1991 年)中,凶杀案平均每年递增 30%,强奸案每年递增 20%,伤害案每年递增 35%,抢劫案每年递增 80%。

而相反,在死刑复核权收回最高法之后,恶性的案件有所下降。这也能理解,假设打架是死刑,那么可想而知每一次打架的暴力程度都会急剧上升。

  • 确定性 确定性 = 实际受到惩罚的犯罪/应该受到惩罚的犯罪 我国的破案率在 33%--40%之间,相信在看到这个数字之后,会有一些人萌发了犯罪的想法。当旁观者看见大量的犯罪者没有受到惩罚的时候,对旁观者而言,刑法就会失去威慑作用,进而催化犯罪。也是违法犯罪呈现区域化的原因。而相反,如果一个区域的犯罪都会被及时的处罚,那么大家更容易遵守。
  • 及时性 及时性:惩罚距离犯罪发生的间隔有多长 譬如吸毒人员回家后的一年每天都有概率被随机抽查是否复吸,在这样随机且高频度的检查下,吸毒人员的犯罪行为与惩罚间隔非常近,因此能够取得一定的成效。

因此在现代社会下,如果想要参与预防犯罪的发生这一过程里,可以多多从身边小事做起,勿以恶小而为之。

放大到国与国之间,明文的规则是国际法律条款,而只有拥有追偿能力的国家,才能成为世界经济的中心。如 A 国没有追偿能力,拥有 A 债的国家选择违约不偿还则很容易。常见的追偿能力包括:全世界第一的军费开支/各种国际组织的主导地位。二战之后主战国重新制定世界规则,所以法律的本质是一种当下的临时的规范,规范的前提是在这个框架下所有人的生活会更好。一旦这个前提不存在了,衍生品自然也不复存在。譬如 A 国失败的禁酒令。

因此到这里,要思考规则为什么此时得以存在和得以维持,才能在某些可以打破规则的契机出现时,通过打破规则来创造新的规则。而在未搞清楚规则得以维持的条件之前,打破规则的出头鸟下场不言而喻。

体系化学习

体系的知识才能创造价值

如果你仅仅知道胡萝卜长出根须需要多长时间,那么这是一条有用的知识。

但是,单独的这条知识并没有什么价值。

但如果你同时还知道关于胡萝卜生长叶片所需营养的所有信息,那么这条知识就有了价值。甚至可能有胡萝卜企业愿意请你去做顾问。

当你不知道怎么做的时候,应该去看一看别人是怎么做的。那些已经做出来的人,他们的文档与目录,不应该是俄罗斯方块式学习,而是拼图式学习,你知道一共多少块拼图,你现在在哪儿。

客观看待失败

失败只是意味着:这个方案行不通。所以你需要换个方案,仅此而已。 再次尝试的额外成本是在方案内的,因此我并不会过度在意。

置身与规划之中

总结与规划是一种习惯,也是一种能力。需要坚持,也需要学习。同时它并不是个阶段性的技能:总结与规划是一件伴随一身的技能,不论是在校、在职、从政还是创业,甚至可以说你走的越远,此项能力便越重要。我建议你找到人生目标之后,可以每天都可以思考自己今天做了什么,明天要做什么。它是否和你人生目标一致。如此一来,你可以避免冲动的抉择,享受坚持的快乐,脱离低级趣味。定期的归纳和总结的磨练过程会带来许多衍生收益,譬如:定时的作息、冷静的性格、结构化思考。

在我自我成长的过程中之后踩了许多坑,真实的成长:获得学历认证、掌握领域通用技能;虚假的成长:学习使用公司独有系统、盘点资产。总结的规律是

  • 可迁移、可考核、可量化的能力才能提升称之为成长

我看到一事无成者花了大把时间担忧自己的健康,我看到毫无资本者小心翼翼的避开未知的挑战。不由得开始思考起人生的层级关系来,我想这个世界重要的事情并无太多,患得患失是陋习。人生的成长过程中,一些我曾经付出资源的爱好没有继续陪伴我走下去,或者说没有达到我自己预定目标,这样的事情如果放在一家公司里,可以称之为产投研失败。

随着我不断的成长,这样的产投研失败是逐渐增多的,当然产投研成功也在增多。我回看这些事迹,如果我同时关注的项目很多,那么可能这些项目都会失败,因此我需要适当的集中精力,保持广泛的认知的同时,警惕雨露均沾。

  • 人生有一些事情是重要的,这些事情是明确的无需过多探索的。

一旦清楚了这一点,我们便可以有意识的介入自己的行为,把它从自动挡切换为手动挡。就像瑜伽练习者有意识的呼吸一样。人的一生是有限的,如果不能依据层级分配精力,日后只会成为他人口中才华横溢却一事无成的小镇青年。我一直相信:有意识的规划大于临时的选择。

从大的方向来说,如果你有一个大学 4 年的详细规划,那么一定好过没有大学 4 年的规划。从小的细节上来说,如果你在逛超市之前有一个规划,比如购物清单。那么采购的效率有好过漫无目的的闲逛。在或者在打开手机之前思考要用手机做什么事情,比漫无目的的打开手机有效率。有规划者,心无杂念。无规划者,情绪被他人晕染。

明确目标

我父亲的目标是开一家属于自己的大超市。

我前任公司老板的目标就是把智能教育做到 L5。

多年前工作室的老板的目标就是在 45 岁前挣到 500 万退休。

一位高中同学的目标就是住上大别墅,种花养草,已经他在老家基本实现。

买衣服也是,花点时间一次系统的学习:色彩、衣服材质、尺寸选择、和版式。当你明确要买什么颜色、材质、尺寸、版式的时候,整个购物过程就已经完成了一大半。

  • 当你决定要出发,最困难的部分其实已经完成了。

明确目标的益处:

  • 帮助做决策:与目标契合的决策都应该做,反之不能做。
  • 便于他人帮助你:吸引志同道合的人,提前避免观念不同的合伙人

如何选择目标?

到了 18 岁之后,过去我没得选,现在我有很多选择,因此机会成本便如影随形(选择去 A 店打工便意味着放弃了 B 店)。

但是如果你迟迟不做出选择,每个选择都会变得更差。

  • 如果你能果断做出选择并坚持,每个选择都比开始更好。

如何养成习惯

生活是一个复杂的系统,养成的习惯是我们各自的存量。当你的行为不符合习惯时,需要花较长的时间来改变,这意味着你想变得更好或者更差都需要一定的时间。同一个行为被不停的强化会形成增量反馈,譬如:运动、早起、坚守原则。当然也包括:不运动、晚睡、放弃底线。我们的行为很多,完整分析容易导致精力极度分散,因此给自己行为区分系统,闹钟响了就起床,时间到了就看书,就已经能超过大部分成年人了。

我司有一位公认的技术专家,他精通各类技术。代码写的层次清晰,性能卓越。但我仍然希望自己能够在某个细分领域超越他。这里的”他“是哲学意义上我对自己限制。对我来说,保持某个领域的卓越是一种习惯。好比每天去跑步的习惯,跑步过程中一旦暂停,再次起跑就会格外困难。

  • 按系统规划目的,像管理军队一样管理自己的生活和言行,可以更容易实现自己的个人目标。
  • 要每天不间断做有重大意义的事,保持卓越
  • 立即去做,做错也比不做好
  • 连续的执行才能看见效果 习惯像刷牙,每天都要刷。 一天不刷牙,三天就白刷。
  • 重复,再重复。用潜移默化的方式改变的语言系统或是生活习惯

效率预估

我时常低估自己的效率,也时常高估自己的效率。

譬如我可能坐在椅子前写策划案写一天只能写一半,也可能原本要两天的框架浸提一口气就补完了细节。令人遗憾的是,我曾经准备多场考试,最终有部分仅差几分未能通过,这符合侯世达定律。因此在后续的行动中,如果我预估这个项目需要花费我 4 周,我会预留出 6 周的时间以确保此事的顺利完成。对于预估失败的项目(预估与最终结果的耗时相差过多或过少)记录下反思。

我明明比任何人都了解我自己,为何无法做出准确的预估呢?

前段时间我找到了一部分答案,主要有四点原因。

  • 第一点对我而言影响最小:主观能动性。或许大部分人有过不想做某事导致效率低下的情况,但是这样的事情往往不多。
  • 第二点对我而言影响较大:熟练程度。当然没有任何一件事情和过去一模一样,但是总归有相似的部分,相似的越多我越容易再次完成它。但是这依然不能解决大部分问题,因为业务调整期遇到的都是新需求,而且很多需要你出圈解决。
  • 第三点较为重要:详细的做事安排。我如果要做这件事需要预估出每天不受打扰的时间,然后乘以 1.5 进行安排,它能搞定大部分冗长项目拖延问题,当然营造不受打扰的时间也是有技巧的,我是习惯 25 分钟专注,25 分钟杂事(看邮件看钉钉看微信处理各种生活同事房东朋友的问题),然后下一个 25 分钟开始专注,这样别人也不会一直找不到你,你也能更容易进入心流的状态。但是依然解决不了我一坐一整天的情形。
  • 最后一点是关键,明确这个事情怎么做,并且自己写到示例并确认方向是否正确。 譬如我写了一个策划案,写了第一个小活动,如果给到对应的批语便能很好的继续开展下去。

时间规划

时间规划最难的在于两端,即长远规划与细项规划,我的细线规划的逻辑顺序

  • 我有什么我的时间只有 24 小时,这是固定值.
  • 我做什么相同的事情在不同的时间段执行,效果不同. 对上文的解释:效果差的规划表不一定是细项不合理,可能是分配的时间段不合理. 举例:我们读书时有忘我的高效状态,解题时有思如泉涌的状态,我们要找到这种状态出现的原因.可能原因包括:时间段/光线/心情/主观意愿
  • 常见提效工具的选择分为三个级别 1.被工具掣肘 2.带着需求找/做工具 3.被工具赋能,超出你的期望
  • 超车提效沟通是把双刃剑 1.当你找到了正确的沟通对象时,你的效率提升极为巨大 2.当你找到错误的沟通对象,talk is cheap. 诀窍:可靠数据源要存档
  • 框架比细项重要 1.在规定时间内,五件事情都做了 80 分,比只做了 4 件要好. 2.我们可以不做到 100 分,但是需要具备做到 100 分的能力,有的工作只会选择那些能做到 100 的人.这是出于我们的选择,而不是受制于能力.
  • 定期整理 1.获取有效信息后,收藏至待整理 2.每周清理一次待整理,把特定代码,优化为通用代码 3.我有纸质本,名为待整理,作用为收藏自己的有效信息,定期放在电子设备上

合理的时间安排表:吃饭 30 分钟;不合理的时间安排表:12:00-12:30 吃饭

另外也需要注意:不要把压力留给未来的自己,未来的自己会有属于未来的压力。未来市场是无法预测的,我相信我会在未来活着,但也相信我会在未来猝死。

  • 保持最好的状态的目的就是:让明天能有更大的概率存活。
  • 对具体事情的规划好过对时间的规划。事件在前,时间在后。

使用技术进行信息过滤

公司昨天有人没关窗户,结果现在开始安排值日关窗了。

他说:“你看,这个就是相信管理,如果要是相信技术的公司,就会安装一个自动闭门器,相信什么是刻在公司基因里的。”

我是一个相信技术的人。

信息过滤是一件长久的事情,因为不论是自己还是媒体,都会投己所好。国内与国外的媒体面对外交友邦或者敌对国家的报道可能截然不同,信息源的选取要包含正反两面。

关于信息过滤我有三条原则: 1.超过 2 次虚假或极端片面的媒体会被永久过滤,互联网没有记忆,但数据库有。

2.过滤无法核实的推测的信息源:警惕一天发 10 条新闻的媒体,他们可能不会对信息进行严格的核实。

3.过滤所有的信息讨论,讨论区无法上传证据图片、完整的有结构的论述(字数限制)因此讨论区是一个忽略过程而追求结论的地方,这里的信息不具备参考价值,因为有价值的是一体化的论证过程与结论。

过滤信息是为了利用过滤后的信息来解释并预测这个世界,因此我的纠偏逻辑是:避免无法证伪的思想,积极的世界做出预测并反思自己的逻辑。

这个过程我会经历许多的失败,这些失败是我停步不前的原因,对于一个程序员来说,找到问题时,问题就已经解决了一半。

正视不安的情绪

当面对难以解决的困难之时,正视不安与焦虑会促使我处理这一难题以摆脱这种焦虑。

哪些我回头看来成长最快的阶段,大多来源于从不安与焦虑症中挣扎的想出了一个我此前从未想到的绝妙方案。

· 8 min read
Allen
此内容根据文章生成,仅用于文章内容的解释与总结

完美的家庭自动化需要解决哪些问题?

  • 不必适应技术

应用程序不能成为生活的遥控器,如果你的手机没电了怎么办?

  • 你不是家庭自动化的唯一用户

常见自动化是当在客厅看电影或连续剧时让灯光变暗。但它只有在每个人都在观看电影的情况下才有效。

  • 限制误报和漏报的影响

如果它不起作用会有什么影响?

  • 系统应该在家里运行,而不是在云端运行

互联网可能会停止工作,更新可能出错或运行云的服务器崩溃。发生这种情况时,智能家居应该能够继续运转。云应该被视为智能家居的扩展,而不是在云上运行。

  • 完美的应用程序不是应用程序

大多数时候,最好的应用就是没有应用。以 Apple 为例:控制 HomeKit 设备的唯一方法是使用 Siri。语音界面也不完美。发出命令的速度很慢,因为必须等待响应。命令的可发现性、口音的识别以及对云处理语音的依赖性也存在问题。

  • 全屋智能需要考虑合并同类项

房间如果太多,那么语音控制和全屋智能对应的成本也会上升,转换空间也会产生不适感。合并具有相同或类似功能的物品。不影响质量的情况下减少数量,空间感是最值钱的。

从折叠家具到租房选择

毕业之后,租房不便,因为工资不高,只能选择租一个很小很小的小单间,如果你想做点什么,得把多余的物品收起来,之后再放回去。

我买了很多折叠和收纳的物品,但是发现所有的折叠和收纳物品都在浪费自己时间,一个正常的桌子可以直接被使用,一个折叠的桌子需要 2 分钟的展开与收回;正常我洗漱用品放在洗手台上直接取用,合租时放在床底每次取用耗时增加 2 分钟。

因此我得出一个简单的结论,日常生活的耗时与空间呈相关性:空间为 1 时,日常行动耗时增加。空间增加时,日常行动耗时减少。

所以我开始换个方式去计算自己的租房成本:人的一天有 24 小时

A 房间的实际房租为:租金+水电物维网+通勤费用+通勤时间+空间折算时间

时间最终会换算为: 可折现时间(加班获得报酬)

不可折现时间(加班费用固定,花费更多的时间也不会获得更多的报酬,且你无其他变现方式,则多余的加班时间为不可折现时间)

如果你刚毕业有的是时间,可以选择一个地方远空间小有地铁的地方

如果你已经步入中层时间已经不够用的,可以思考自己每天还需要多少时间。或许在原地换个大房子也能解决问题,因为大空间会让你的日常生活的速度变快。

举个具体的例子: A 住在公司附近,每天加班 1 小时,每个月可以拿到 1000 的加班费,房租 2500+交通 0 元+空间折算时间 300 元。

B 住在隔壁区,每天不加班,房租 1500+交通 300+空间折算时间 0 元。

实际算下来:每月花费相当,而 A 获得了项目经验+可能的职位晋升,B 获得了通勤感悟。

这个例子并不是鼓励大家加班,只是给出一个公式化的方法,让我自己能够客观理性的在租房上班这件事上获取最大收益:有的公司不给加班费,有的公司加班费给的很多,有的公司加班也获得不了经验,有的公司加班可以获得很多经验,具体问题具体分析。

稳定可靠与南北方天气

在广深呆了久了再来到北上之后,很显然的需要增添衣物以及储物空间

首先就是袜子,在深圳,一年四季一套中筒袜. 到北上之后,10 月底就要开始准备长袜了,一直到次年的 3-4 月才能脱下.

其次就是冬裤,可以选择单条厚实的长裤,也可选择秋裤+长裤 我是一个嫉妒讨厌叠穿裤子的人,我也很讨厌寒冷的冬天. 坦诚来说广州的天气也不如深圳.

最后就是外套,我对外套倒是没有那么排斥,但是很讨厌羽绒服,老棉衣. 这些短小的服装以至于稍微一活动就露出肢体,寒冷便顺着露出的肢体刺入血肉之中.

这给我的感觉就是:棉衣与棉裤很不可靠,也许是我身高较高,所以合适的衣服本就难买,更别提棉衣裤.还是深圳好.

搬家指南

1.知道你有多少物品

  • 盘点物品数量
  • 扔掉过期的消耗品
  • 挂咸鱼:百元以下,一年未用

2.计算你的身家

  • 物品分类(易碎品)
  • 物品估价

3.分类打包

  • 对于高估价且易碎的物品,纸箱+木箱
  • 其他物品分类后打入纸箱
  • 对每个纸箱生成一个二维码贴纸,二维码指向一个你自己的链接,你可以扫码查看并修改这个纸箱内的物品信息,方便查找。

4.快递上门

  • 顺丰中有个快递慢运,价格合适

以上适用于跨省、跨国搬家。同城搬家有驾照还是自租车最划算。

· 8 min read
Allen
此内容根据文章生成,仅用于文章内容的解释与总结

天空是被明码标价的,在一线城市生活多年的我早已知道这条潜规则:能看见天空的房间和看不见天空的房间价格是不同,今年天空的价格大概是 300 块一个月。价格不算便宜,但为了能看见阳光,我还是买了。

饭后,沿着昏暗宝深路隧道散步。深圳的雨季总是潮湿,潮湿的好像鱼儿能在空气中游动,行走在海底。

我回想起几个月前,得知某个同学去世的消息,在得知他死去的消息后,我像往常一样生活着。

此刻,我想打个电话给他,打开通讯录,看到他名字的瞬间,他死去时的容颜便突然出现在脑海里。接着便是无穷尽的记忆片段从不知名的地方涌出来,在空气中弥漫着,把我裹挟。粘稠的空气像是混合了他的灵魂,缺氧的我想要急促的喘息,但越是如此便越是窒息。我忽然明白,人的死亡这四个字,是这个意思。

我抬起头,在明亮的隧道里看到了偌大的出口二字。于是我竭力的向前奔跑,我穿过了出口。可惜的是,什么也没有发生。

随之另一段回忆的片段向我涌来:前几年还是大学生的我调研低保户。在和村干部交谈中,一位中年女性带着哭腔走了过来。正在我思考,到底是发生什么事情让一个中年人哭的如此令人动容,得知是家后的百余元的蚕豆被人摘完了,我先是愣了一下,随后愈发的难过了。也许她的路和我一样,也没有出口。

于是我回到家中准备入睡,邻居房内的明黄的灯光正在和对游戏的咒骂声产生奇妙的化学反应。片刻后像是发生了某种爆炸,实验材料从实验室内扩散到整栋楼里。它大抵是对人有害的。我紧闭门窗,拉起窗帘,耳边终于只剩下自己的心跳声伴着风扇的震颤声。

于是在这样一个没有月亮的夜里,我闭上双眼,逃离城市,开始了一段旅程。

这是一段漫长的旅程。

从一座孤岛上出发。驱使着航船去往另一个孤岛。

依靠着夜晚的北极星指路,我们靠吃生鱼肉度日。后来我的船员逐渐得了败血病,我忽然想起来我们似乎还有一些种子,于是我们开始在船上种植,收集漂流的木板,让船变得更加壮大。

很小的时候我有看过一个故事,叫金色的房子里面提到男主人公,从小的时候眺望远方,他会发现远处有一栋金色的房子。后来他费尽千辛万苦到了那个地方,发现不过是一所普通的房子,而当他再回过头来看自己家的房子的时候,他被美丽的夕阳染成了灿烂的金色。

我把思绪拉回到现实,我并不需要一座金色的岛,我只是想要知道,在这座大海上,是否还有人活着。

我们每死掉一个船员,我就会研究他们是因何而死的,如何让剩下的船员不再为此受苦。

就这样我们整理出了一套非常严谨全面的航海生存手册,可是我的船员从 100 个变成了 80 个,60 个 20 个,最后只剩下我。

就这样我开始在大海上独自航行,因为我有着严密的生存手册,我知道什么时候风暴回来,我知道什么时候冰山回来我知道明天可能会发生什么样的事情,我知道我怎么样才能活下去,可是我的那些船员再也回不来了。

就这样,我遵循着航海宝典的注意事项,过着日复一日的日子。每天看星相,收帆,扬帆,转向,种菜,吃,睡,如果不是我意志够坚强,恐怕已经得了精神分裂。

就在这个时候我遇到了一个落水者,他的后背被漂浮物刺穿,虽然经过处理已经没有大碍但留下了醒目的疤痕。我非常高兴,我告诉他在船上怎么做才能活下来。我告诉他做哪些事情可能会让自己丧命。

我毫无保留的把我毕生所学全部传授给他,我把他当做朋友,把他当做家人,把它当成船员,也把他当成船长。

于是,我们一同航行,闲暇时会描绘彼此心中最理想的岛的样子,我们都听过那个金色的岛的故事,我们有着同样的理想的追求,无话不谈,简直像是同一个人。

后来我们一起经历了风暴,一起度过了极夜,我们路过繁华的港口,穿过危险的海峡,终于,在大海中央,发现了一座孤岛。

这座孤岛上荒无人烟,生态丰富,落水者说,他想一个人住在这里,如果我喜欢,我可以留在这座岛上,他会一个人驾船离开。

"我们生来就是孤岛。"他这么说道.

月亮画出一道美丽的弧度,高高的挂在繁星密布的夜幕里,我下船了,背上的疤痕映在海面,夜空和岛都格外安静.

一阵阵的风从耳边吹过,那种感觉像在风平浪静的太平洋正中,有一艘小船。而我就躺在这艘小船上。它慢慢的带我沉向海底,星空逐渐离我而去,于是我的呼吸也渐渐停止。

或许,我该和这个世界再说些什么。于是我用尽最后的力气,在深海中吐出一串气泡:"晚安,这个世界和你。"

此刻,微风吹动窗帘,无数生活在城市的人在月光下梦呓。