More than code

More Than Code
The efficiency of your iteration of reading, practicing and thinking decides your understanding of the world.
  1. 首页
  2. 未分类
  3. 正文

opencat 1 init

2024年6月22日 375点热度 0人点赞 0条评论

最近搞了个petoi的机器狗,是基于opencat这个框架做的,这里来读一读opencat的代码,顺便入一下嵌入式开发的门

顺便体验一下gpt,看看在目前大模型的帮助下自己入门一个新领域需要多长时间。

这里的代码仓库是opencat的esp32版本:https://github.com/PetoiCamp/OpenCatEsp32

板子


电源目前还看不太懂,就不看了,这里就是看看一些功能性的模块

  • ESP32,SoC,有WiFi,蓝牙,16M的flash
  • CP2102 UART to USB,是用来做串口通信的
  • MPU6050,是陀螺仪,用来获知当前的动作状态,通过I2C和ESP32连接
  • EEPROM,持久化存储,通过I2C和ESP32连接,有64k的空间
  • 12个PWM舵机接口:一般是3个接口,电源,地(电源负极),控制信号,用来控制舵机的转角
  • 还有一些其他的,暂时不关注

代码

然后看看代码,ESP32支持使用Arduino IDE开发,代码读起来也比较简单。和普遍的C程序类似,Arduino的程序预定义了setup()和loop()两个函数

  1. setup() 函数:
    • setup() 函数只在整个程序的开始时调用一次。它用于初始化变量、配置引脚模式、启动外围设备、初始化串行通信等。
    • 在setup()函数中,你会设置你的Arduino板需要的所有初始配置。一旦setup()函数完成执行,Arduino将永远不会再次进入这个函数。
  2. loop() 函数:
    • loop() 函数在setup()函数之后被调用,并且会不断地重复执行,直到Arduino板断电或重置。
    • 在loop()函数中,你编写需要重复执行的任务,比如读取传感器数据、处理输入、控制输出设备等。
    • loop()函数的执行频率取决于在其中执行的代码的复杂性和Arduino板的处理能力。理论上,loop()函数的执行速度尽可能快,但实际上,它受到代码执行时间和板载硬件的限制。

这里的loop就是每执行完一轮就立刻执行下一轮,所以loop的逻辑要足够简单才能执行的够快

setup


Arduino中有一些预定义的库,这个Serial就是一个,负责进行串口通信,一般是debug用。

这里的具体含义就是初始化波特率为115200,即115200 bit/s,然后把缓冲区中的数据都清空,再调用initRobot

initRobot中,先Wire.begin(),这里表示的是初始化I2C总线。

I2C(Inter-Integrated Circuit)是一种用于连接低速外围设备的串行通信协议,常用于传感器、EEPROM、RTC(实时时钟)等设备的通信。
Wire 库是Arduino IDE中内置的库,它提供了与I2C设备通信的接口。Wire.begin() 函数需要在setup()函数中调用,以启动I2C总线,并使Arduino作为I2C设备进行通信准备。

然后打了一些debug日志,如Start等等

printToAllPorts(),这里会把当前板子的名字,这里是"bitte"发给所有的端口。比如发送蓝牙等,具体细节回头再看


* 读取eeprom中的数据
* Wire.beginTransmission()是开启I2C传输会话,DEVICE_ADDRESS是对应的EEPROM设备的I2C地址
* Wire.Wire()写入eeaddress的高8位和低8位。MSB和LSB代表Most Significant Byte和Lease Significant Byte
* Wire.endTransmission(),停止会话,这里就是把上面的地址发给了EEPROM
* Wire.requestFrom(),向EEPROM发送读取请求,请求一个byte的数据
* Wire.read()读取数据


* i2cDetect,扫描I2C网络,并打印出所有连接的I2C的设备的地址
* I2C的地址范围是1~126,,这里是扫描所有的端口
* Wire.beginTransmission(), Wire.endTransmission(),如果返回err为0,说明这个设备有响应,则打印对应的地址
* 如果错误码为4,说明传输超时,设备没有响应(可能是有设备,但是设备坏了什么的),会打印Unknown error

  • 然后是i2cEepromSetup,这里应该是读取现有的数据,来看程序是否是第一次初始化
    • 先读一下birth mark,看看是否已经初始化了。如果是新的板子的话,会执行初始化的函数
    • 这里会把sortware version持久化进去
    • 写入sound state和buzzer volume
    • 将moduleActivatedQ数据中的内容写入到eeprom中,这里moduleActivatedQ还不清楚是什么含义
    • playMelody,这里会播个音乐
    • 生成一个新的id,作为robot的名字
    • 这里会询问用户,是否做一些数据的初始化,校准的offset什么的
    • 如果不是第一次执行的话,这里会判断一下代码的版本,如果有升级的话,这里会做resetAsNewBoard,重新初始化
    • 读取moduleActivatedQ数组
    • 这里有一个小疑问是,如果我更改了代码的版本的话,他是否会初始化EEPROM呢?
  • 然后是一些其他设备的初始化:
    • imuSetup,陀螺仪的初始化,inertial measurement unit,即惯性测量单元
    • bleSetup,bluetooth low energy
    • blueSspSetup,bluetooth secure simple pairing
  • servoSetup,初始化伺服系统
    • 伺服系统的介绍:
      • 伺服系统(Servo System)是一种用于精确控制位置、速度和力的系统。它广泛应用于机器人、自动化设备、数控机床、遥控模型等领域。伺服系统通常包括一个伺服驱动器(Servo Driver)和一个伺服电机(Servo Motor)。
        1. 伺服驱动器:伺服驱动器是伺服系统的控制部分,它接收来自控制器的信号,并将其转换为伺服电机可以理解的电信号。伺服驱动器通常具有电流、速度和位置控制功能,可以精确控制伺服电机的运动。
        2. 伺服电机:伺服电机是伺服系统的执行部分,它将电信号转换为机械运动。伺服电机通常具有高扭矩、高精度、快速响应的特点,可以快速准确地执行指令。
      • 伺服系统的工作原理通常包括以下步骤:
        • 指令输入:控制器发送一个指令给伺服驱动器,指示伺服电机需要达到的位置、速度或力。
        • 信号处理:伺服驱动器接收到指令后,进行信号处理,生成一个可以驱动伺服电机的电信号。
        • 电机驱动:伺服驱动器将处理后的电信号发送给伺服电机,伺服电机根据电信号驱动机械部件运动。
        • 反馈调整:伺服系统通常包括一个反馈机制,如编码器或霍尔效应传感器,用于检测伺服电机的实际位置或速度。反馈信号发送回伺服驱动器,驱动器根据反馈信号调整电信号,以实现精确控制。
    • 先读取EEPROM中的校准值到servoCalib中
    • allocateTimer,还不清楚是干什么的
    • attachAllESPServos
      • 遍历所有的PWM,一共12个,然后这里的DOF是8(DOF是自由度的意思,degrees of freedom。具体计算还不是很清楚,这里他一共有8个关节,可能就是8个DOF的意思)
      • 获取servoModule的类型,这个计算方式很诡异,对于前8个是regular,然后后面4个是knee
      • 代码提前定义了ServoModel,分别是servoG41, servoP1S和servoP1L,定义了伺服系统的angel range, frequency, minPulse, maxPulse
        • 等下看看具体都代表什么
      • Servo.attach(PWM_pin, modelObj),给对应的伺服系统添加pin,以及model obj。
      • zeroPosition,记录对应关节的offset
      • calibratedZeroPosition,修正关节的offset
  • 如果定义了VOLTAGE的话,这里会检测,如果是电量过低,会死循环,不会继续推进了

  • QA,quality assurance program,应该是一些测试之类的玩意,保证硬件正常

  • 写入birth mark,标记板子初始化正确。让我联想到一些原子初始化相关的事情。如果初始化到一半,写入内存失败怎么半?感觉应该记录一个initializing的状态,如果是初始化未完成,会放弃读取EEPROM中的所有数据。或者单纯在没有初始化的时候就不读取EEPROM

  • initModuleManager(),下来再看看

Servo

伺服系统可以通过脉冲宽度调整(Pulse Width Modulation, PWM)和角度调整来实现精确控制,这主要是因为伺服电机的设计和控制方式。伺服电机通常包括一个旋转编码器,它可以测量电机轴的旋转角度。这种设计使得伺服系统能够通过两种不同的方式进行控制:
1. 脉冲宽度调整(PWM):
- 伺服系统使用PWM信号来控制电机的转速。PWM信号的频率和占空比决定了电机的转速和方向。
- 频率(F)是指PWM信号的周期数,而占空比(D)是指每个周期中信号为高电平的时间与整个周期的比例。
- 通过改变PWM信号的频率和占空比,可以精确控制电机的转速和方向。
2. 角度调整:
- 伺服电机通常配备了旋转编码器,它可以测量电机的旋转角度。
- 控制器通过发送一系列的脉冲信号来控制电机的旋转角度。每个脉冲信号都会导致电机旋转一定角度,这个角度通常由控制器内部的精确计数器来测量。
- 通过发送特定数量的脉冲信号,可以精确控制电机的旋转角度。例如,发送100个脉冲信号可能会导致电机旋转90度。

  • 看一看Servo的逻辑,这个貌似是一个开源的库,ESP32的Servo library
  • attach,传入GPIO的引脚号,以及ServoModel,即上面说的angleRange什么的
    • 这里就是记录一些参数,然后调用pwm.attachPin(),每一个Servo都有一个ESP32PWM的成员
  • write(),设置Servo的角度,单位是度。这里的语意是:
    • 如果传入的value是0~180,则认为是degree
    • 如果传入的value是min~max,这里直接设置的是500 ~ 2500,则会认为是microseconds。这里是设置脉冲宽度
  • writeMicroseconds(),以脉冲宽度写入Servo
  • read(),读取上次设置的脉冲宽度,以angle来读
  • readMicroseconds,读取上次写入的脉冲宽度,以microsecond来读

这里主要的逻辑都在ESP32PWM中,用来发送PWM信号。问了下GPT,发送ESP32中发送PWM信号有几种:
* 可以直接用Arduino的analogWrite
* 使用GPIO直接发送PWM信号
* ledc库
* 使用定时器生成PWM信号
* 外部PWM发生器

ledcWrite()本来用来控制led灯带的亮度,原理是通过发送PWM信号,来控制电流的通断时间,从而控制电流的大小,来调节亮度。
ledcWrite()的参数是接受一个通道号和占空比,ledcWrite将占空比转化为PWM信号发送给对应的LED灯带通道
https://docs.petoi.com/v/chinese/li-cheng/8.-pwm
ledc:
* ledcSetup,设置通道的频率,resolution。ESP32中有16个通道,8个高速通道和8个低速通道。这里的精度有上限,如果频率很高,那么占空比就不能很细致。大概感觉就是他的timer是有精度上限的。设置timer生成信号的频率就可以得到PWM信号了。
* ledcAttachPin,设置ledc的输出引脚,就是把对应的通道的PWM信号发给那个设备来控制
* ledcWrite,选择通道,设置他的占空比进行输出

先写到这里吧,感觉舵机这块需要单独看看他的module test

标签: 暂无
最后更新:2024年6月22日

sheep

think again

点赞
< 上一篇
下一篇 >

文章评论

取消回复

COPYRIGHT © 2021 heavensheep.xyz. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS