Spotmicroai机械狗初上手——组装测试踩坑
你问我为什么还是不写组装?
当然是组装完了出bug没解决狗子动不了呗(乐)
说明
这里我们主要还是通过读代码来解决我(们)在组装的时候碰到的bug。
我遇到的问题是运行run.sh,摇动摇杆之后终端输出按下START或者OPTION键来启动motion_controller(运动控制);但是当我在PS4上按下了之后终端又会有报错。
——————
先把这个问题解决了再出bug之后再说。
以及虽然按照正常的阅读顺序我们要从我们直接运行的run.sh文件或者直接找到输出报错开始阅读,但是为了避免重复说明以及阅读清晰,我这里会先列出几个作者(指SpotmicroAI的作者)用的很多的包;这些包很好理解而且并不是实现狗子的核心文件,所以目前我们只要知道它们是干什么的就行了。
spotmicroai/utilities文件夹
log.py
用于将内容写入日志文件。
config.py
一个配置文件;里面定义了一堆IO口对应的名称,以及一些读取配置文件内容的函数。
queues.py
定义了一堆名称。
run.sh
启动狗子的时候是直接运行这个shell文件的,当然就是从这开始了。
这里面除开环境配置以外只有一行干了事情:
venv/bin/python3 spotmicroai/main.py
那我们接下来就到报错的那一行去。
spotmicroai/motion_controller/motion_controller.py
通过文档的查找工具,我很快找到了摇动摇杆时输出错误的那一行代码:
# 219行
if not self.is_activated:
log.info('Press START/OPTIONS to enable the servos')
continue
跳转到293行,一个名为activate_pca9685_boards(self)的函数将is_activated赋值为True;
对PCA9685熟悉的各位就可以不用接着往下看了;PCA9685舵机模块上面只有一个OE口控制模块使能。
AbortController
从这个类的字面意思上来看,这东西叫做中止控制器;从它的两个函数中也可以看出它的主要功能为使能或者中止两个PCA9685模块:
def activate_servos(self):
self._lcd_screen_queue.put(queues.LCD_SCREEN_SHOW_ABORT_CONTROLLER_OK_ON)
GPIO.output(self.gpio_port, GPIO.LOW)
def abort(self):
self._lcd_screen_queue.put(queues.LCD_SCREEN_SHOW_ABORT_CONTROLLER_OK_OFF)
GPIO.output(self.gpio_port, GPIO.HIGH)
一个函数的功能是将self.gpio_port这个IO口输出低电平,另一个是输出高电平;根据接线图可以看出rpi上只有一个通用IO口,接到了两个PCA9685的OE引脚。
(图里面有点小错误)
OE是PCA9685的使能引脚,低电平时使能芯片。模块中已经下拉保持低电平,因此使用时可不连接。
然后看到__init__()中:
# 20行处
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
关于signal库的使用可以参考一下这篇文章:https://blog.csdn.net/weixin_42350212/article/details/80588973
这两行的的作用就是当树莓派通过键盘收到ctrl + c或者kill sigterm pid指令时,调用exit_gracefully()函数,也就是将self.gpio_port这个连接着PCA9685 OE口的IO口拉高;
让我迷惑的就是下一行对于self.gpio_port的定义:
self.gpio_port = Config().get(Config.ABORT_CONTROLLER_GPIO_PORT)
在spotmicroai/utilities/config.py文件中可以看到 ABORT_CONTROLLER_GPIO_PORT 的值是一个内容为 ‘abort_controller[0].gpio_port’ 的字符串,不过我死活找不到哪里有名为abort_controller的数组的定义…
也就是我一开始在代码中没找到两个PCA9685的OE接口应该接树莓派的哪个IO口(虽然在接线图中OE接的是GPIO.25)。
———分界线———
直到我想起来一个问题。
Config.get()这个函数不是字段的访问器函数!
这个取名的误导性有点强…
马上定位到config.py的166行:
def get(self, search_pattern):
log.debug(search_pattern + ': ' + str(jmespath.search(search_pattern, self.values)))
return jmespath.search(search_pattern, self.values)
第一行日志不用管;第二行return中的jmespath是一个可以在json文件中通过键寻找值的库;search_pattern是传入的键,ABORT_CONTROLLER_GPIO_PORT 也就是 ‘abort_controller[0].gpio_port’ 会传入进来;self.values在上面的load_config(self)函数中被导入了spotmicroai.json文件中的内容,而这个文件是通过spotmicroai.default直接改后缀名得到的。
我们直接把spotmicroai.default后缀改成json然后打开:
可以看到第一行就是我们的abort_controller,里面就一个值:17;
然后我们随便找个rpi引脚图:
看到BCM一列,17对应的是GPIO.0。
同时我们回到spotmicroai.json文件中,还可以看到两个PCA9685的地址:一个是默认的0x40编号为1,另一个是0x42编号为2;再往下看可以看到左侧舵机是1号PCA9685控制,右侧舵机是2号控制,所以我们还需要将连接右侧舵机的PCA9685的A1这一位给焊上。
第二个坑
在解决完接口的问题之后,发现依旧报错,无法开启运动控制;而且运行 sudo i2cdetect -y 1 的时候检测不到任何的i2c设备,并且是每过一秒钟才检测一个地址的设备。
这个i2cdetect命令比较玄学;我在少数情况下是可以看到正常扫描设备的,但是大多数情况下是无法检测到设备的
进过各种网站的bug寻找之后,发现在运行raspi-config使能i2c之后有这么一行报错:
/usr/bin/raspi-config: 910: dtparam: not found
而dtparam是Raspberry Pi OS的一个配置文件。
好家伙折腾了半天还是不能用Ubuntu吗…不过实际上和这个没什么关系;各位可以将I2C分线板取下用单个I2C设备连接树莓派的I2C接口,运行i2cdetect命令在多数情况下是可以检测到的。
出现I2C读取十分缓慢或者所有的设备地址都被读取到这两种情况都是因为硬件接线的问题,和软件或者系统无关;通常原因是I2C设备和树莓派供电用的是同一个电源。
解决方法:
用树莓派给模块供电。
第三个坑:电源
一开始我使用12V,6000mAh的聚合物锂电池给整只狗供电,但实际上所有设备同时运转的功率远超电池功率——每次开启运行./run.sh并使能运动时,电池就会自动保护断电导致树莓派重启;将树莓派单独用一个电池供电也会出现这种情况;如果使用更大功率的电池的话,机械狗装不下。(实在不知道作者是怎么供电的…
我采用的方案是用两个12V,3000mAh的电池分别给机械狗身体左右两组六个舵机供电,并用一个7.4V的电池给树莓派供电,这样功率就可以达到要求。