机器谱 发表于 2023-8-24 10:22:16

机器人制作开源方案 | 滑板助力器项目0.1版

本帖最后由 机器谱 于 2023-8-24 10:22 编辑

我们可以用一块废滑板做些什么呢?

如今,越来越多的人选择电动滑板作为代步工具或娱乐方式,市场上也涌现出越来越多的电动滑板产品。
https://28846868.s21i.faiusr.com/4/ABUIABAEGAAg_N3GpgYosPr9zAQw3As46Ac!600x600.png.webphttps://28846868.s21i.faiusr.com/4/ABUIABAEGAAgit7GpgYo5MGc0gQw3As43As!400x400.png.webp(图片来源:https://www.backfireboards.com/products/backfire-zealot-x-belt-drive-electric-skateboard)

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAg897GpgYoqMSArwEw8Qk4yQU!900x900.png.webp
(图片来源:https://boostedusa.com/collections/evolve-skateboards)

那么,这是不是就意味着传统的"原始"滑板会被冷落呢?

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAgj_DGpgYogtOznQYw8Qk4yQc!900x900.png.webp
       我们团队里有几个滑板爱好者,他们认为可以发明一种滑板助力器,让原始滑板焕发新生命,摆脱对人体能量的依赖。安装滑板助力器之后,我们的原始滑板就可以得到电动助力,延长滑行距离,提高速度,并且不再依赖个人体力续航。同时也要保留传统滑板的操作方式,尽量不改变滑板玩家的操作习惯(比如上下板的动作),这样既能享受电动滑板的便利和效率,又能保留一些“原始情结”。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAg0eDGpgYoiPyw5Acw8Qk4tQc!900x900.png.webp
       既然要做电动的助力器,那么选择一个合适的电机是少不了的。经过对比,我们选择了robodyno一体化可编程电机Pro-P12型。该电机采用驱控一体设计,全钢齿轮组1:12.45行星减速器,14位绝对位置磁编码器,IIC总线接口,额定功率35W,最高转速310rpm,堵转扭矩2.79N·m。借助一块小小的通信模块与电脑连接后,即可使用python语言直接编程,非常适合开发。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAgtOHGpgYo1JfBLDDwBziFBA!900x900.png.webp
曾经用于开发机械臂、机器狗、智能车等开发项目。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAg_uHGpgYokrWSoAUw8Qk4ywY!900x900.png.webp
电机选定后,接下来的关键就是轮子,我们从网上挑选了一款搅拌机用的摩擦轮,改造后便可作为助力器的轮子。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAgtuLGpgYoguX73wcwwQY4vQM.png.webp
接下来准备好全套零部件及装配工具。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAglOPGpgYo4NaPnAEw8Qk4xgU!900x900.png.webp
去掉摩擦轮原先的轮毂并安装上Robodyno Pro-P12电机配套的轮毂零件,以及3D打印的轮毂外圈,轮子部分就完成了。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAgxOPGpgYoxL6xlQMw8Qk4xgU!900x900.png.webp
然后给电机装上轮架侧板,再和轮子装配在一起。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAgluTGpgYoqLn-rQIw8Qk4xgU!900x900.png.webphttps://28846868.s21i.faiusr.com/4/ABUIABAEGAAgquTGpgYogNrsuAMw8Qk4xgU!900x900.png.webp
       将轴承、轮架顶板、轮架外板装上(在上图中,轮架顶板和轮架外板已经装成了L型,即图中右侧第一个零件),助力轮便装配完成了。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAg5OXGpgYo2rqdvwMw8Qk4xgU!900x900.png.webp
最后,在滑板板尾和合适位置上打孔,并将助力轮安装在滑板上面。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAgm_bGpgYoqOmR3wIw8Qk4xgU!900x900.png.webp
装配完成。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAgzebGpgYowpe7pQUw8Qk4xgU!900x900.png.webp
      下面是我们选择的控制器,采用ESP32-Pico-D4芯片,安装了一块OLED屏幕(用于显示操作界面)以及一个旋钮按键(用于操作)。这个控制器里已经集成一个电机通信模块,因此不需要额外安装电机通信模块了。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAg7efGpgYovP6-jwEw8Qk4xgU!900x900.png.webp
我们编写了一套固件,让它可以用于控制滑板。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAgn_jGpgYoo4DLsAMw8Qk4xgU!900x900.png.webp
       通过拨动旋钮,可以控制助力轮的输出力矩,数字越大,力矩越大,旋转速度也就越快,使用者可以根据自身体重和期望的起步速度来选择合适的数值。

https://28846868.s21i.faiusr.com/3/ABUIABADGAAg5ejGpgYogIekkwUwsAQ4uwI.gif.webp
点击下方的正方形按钮,就可以控制助力轮启停。

https://28846868.s21i.faiusr.com/3/ABUIABADGAAgnOnGpgYottei0wUwsAQ4uwI.gif.webp
然后接上11.1V动力电池,并将电池和控制器用轧带固定在滑板上。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAgm_rGpgYokOrUtgIw8Qk4xgU!900x900.png.webp
       我们最期待的功能是要让滑板像普通滑板一样,蹬一脚就能走,用脚摩擦地面就能停。换句话说,就是开机后,电机在未受力的情况下静止,受到一定的推力后开始转动,受到一定的阻力后停止转动。Robodyno Pro电机使用python即可编程,非常方便。

https://28846868.s21i.faiusr.com/4/ABUIABAEGAAg6OrGpgYohYCMhQYw8Qk4xgU!600x600.png.webp
程序源代码如下:
main.py
Python
import lvgl as lv
from ili9XXX import ili9488, LANDSCAPE
SCR_WIDTH = 480
SCR_HEIGHT = 320
disp = ili9488(mosi=4, miso=18, clk=5, cs=14, dc=13, rst=12, \
               power=-1, backlight=15, backlight_on=1, factor=32, \
               width=SCR_WIDTH, height=SCR_HEIGHT, rot=LANDSCAPE)
from ft6x36 import ft6x36
touch_rst = Pin(2, Pin.OUT)
touch_rst.on()
touch = ft6x36(sda=27, scl=26, width=SCR_HEIGHT, height=SCR_WIDTH, inv_x=True, swap_xy=True)

style = lv.style_t()
style.init()
style.set_radius(0)
style.set_bg_color(lv.color_hex(0x2E3234))
style.set_text_color(lv.color_hex(0xC2C1C1))
style.set_border_width(0)

screen = lv.scr_act()
screen.add_style(style, 0)

from motor_controller import MotorController
         
controller = MotorController(screen)

import time
while True:
    controller.update()
    time.sleep(0.01)
boot.py
Python
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
#import webrepl
#webrepl.start()
from machine import Pin
import time
power = Pin(32, Pin.OUT)
_roller_btn = Pin(36, Pin.IN)
_enc_a = Pin(37, Pin.IN)
_enc_b = Pin(38, Pin.IN)
_a0 = 1
_b0 = 1
_ab0 = 1
enc_pos = 0
def on_enc_change(pin):
    global _a0, _b0, _ab0, enc_pos
    a = _enc_a.value()
    b = _enc_b.value()
    if not a == _a0:
      _a0 = a
      if not b == _b0:
            _b0 = b
            enc_pos += 1 if a == b else -1
            if not (a == b) == _ab0:
                enc_pos += 1 if a == b else -1
            _ab0 = (a == b)
         
_enc_a.irq(handler = on_enc_change)
time.sleep(2)
power.on()
encoder.py
Python
from machine import Pin
class Encoder:
    def __init__(self):
      self.btn_pin = Pin(36, Pin.IN)
      self.enc_a = Pin(37, Pin.IN)
      self.enc_b = Pin(38, Pin.IN)
      self.a0 = 1
      self.b0 = 1
      self.ab0 = 1
      self.pos = 0
      self.enc_a.irq(handler = self.on_change)
   
    def on_change(self, pin):
      a = self.enc_a.value()
      b = self.enc_b.value()
      if not a == self.a0:
            self.a0 = a
            if not b == self.b0:
                self.b0 = b
                self.pos += 1 if a == b else -1
                if not (a == b) == self.ab0:
                  self.pos += 1 if a == b else -1
                self.ab0 = (a == b)
motor_controller.py
Python
from encoder import Encoder
import lvgl as lv
from machine import Pin
import time
from robodyno import *

class MotorController:
    def __init__(self, container):
      self.running_flag = False
      self.encoder = Encoder()
      self.container = container
      self.power = Pin(32, Pin.OUT)
      self.can = CanBus()
      self.motor = Motor(self.can, 0x10, ROBODYNO_PRO_12)
      self.motor.torque_mode()
      self.max_torque = 3
      self.draw_power_btn()
      self.draw_bar()
      self.draw_btn()
   
    def draw_bar(self):
      self.label = lv.label(self.container)
      self.label.set_text(str(self.encoder.pos))
      self.label.set_style_text_font(lv.font_montserrat_24, 0)
      self.label.align(lv.ALIGN.TOP_MID, 0, 50)

      self.bar = lv.bar(self.container)
      self.bar.set_size(200, 20)
      self.bar.align(lv.ALIGN.TOP_MID, 0, 90)
      self.bar.set_value(self.encoder.pos, lv.ANIM.OFF)
   
    def draw_btn(self):
      self.btn = lv.btn(self.container)
      self.btn.align(lv.ALIGN.TOP_MID, 0, 150)
      self.btn.set_size(100,100)
      self.btn.set_style_bg_color(lv.color_hex(0x4EB181), 0)
      self.btn_label = lv.label(self.btn)
      self.btn_label.set_style_text_font(lv.font_montserrat_24, 0)
      self.btn_label.set_text(lv.SYMBOL.PLAY)
      self.btn_label.center()
      self.btn.add_event_cb(self.on_btn_click, lv.EVENT.CLICKED, None)
   
    def on_btn_click(self, e):
      btn = e.get_target()
      label = btn.get_child(0)
      if self.running_flag:
            self.motor.set_torque(0)
            self.motor.disable()
            self.running_flag = False
            btn.set_style_bg_color(lv.color_hex(0x4EB181), 0)
            label.set_text(lv.SYMBOL.PLAY)
      else:
            self.motor.set_torque(0)
            self.motor.enable()
            self.running_flag = True
            btn.set_style_bg_color(lv.color_hex(0xF44336), 0)
            label.set_text(lv.SYMBOL.STOP)
   
    def draw_power_btn(self):
      self.power_btn = lv.btn(self.container)
      self.power_btn.align(lv.ALIGN.TOP_RIGHT, -40, 40)
      self.power_btn.set_size(40,40)
      self.power_btn.set_style_bg_color(lv.color_hex(0xF44336), 0)
      self.power_btn_label = lv.label(self.power_btn)
      self.power_btn_label.set_style_text_font(lv.font_montserrat_24, 0)
      self.power_btn_label.set_text(lv.SYMBOL.POWER)
      self.power_btn_label.center()
      self.power_btn.add_event_cb(self.on_power_off, lv.EVENT.CLICKED, None)
   
    def on_power_off(self, e):
      self.power.off()
   
    def update(self):
      if self.encoder.pos > 100:
            self.encoder.pos = 100
      if self.encoder.pos < 0:
            self.encoder.pos = 0
      self.bar.set_value(self.encoder.pos, lv.ANIM.OFF)
      self.label.set_text(str(self.encoder.pos))
      if self.running_flag:
            b = 0.002 + self.encoder.pos / 100 * 0.3
            vel = self.motor.get_vel()
            if vel is not None:
                vel = -vel
                torque = 0
                if vel > 3:
                  torque = max(-0.107 - b * vel, -self.max_torque)
                elif vel < -3:
                  torque = min(0.1 - b * vel, self.max_torque)
                self.motor.set_torque(torque)

将以上4个程序文件拷贝到控制器的内存中,即可被固件调用。
接下来测试一下起步。

https://28846868.s21i.faiusr.com/3/ABUIABADGAAgoe-GpgYoxofNqQYwsAQ4uwI.gif.webp
然后再试一下转弯,表现效果还不错。

https://28846868.s21i.faiusr.com/3/ABUIABADGAAgy_-GpgYopOCgWjCwBDi7Ag.gif.webp
最后试一下刹车。

https://28846868.s21i.faiusr.com/3/ABUIABADGAAgi-DGpgYo3a7XqwQwsAQ4uwI.gif.webp
这些稳如狗的表现自然是通过一次又一次的调试和数不清的翻车换来的。

https://28846868.s21i.faiusr.com/3/ABUIABADGAAg7LrLpgYo3qeHswQwsAQ4uwI!450x450.gifhttps://28846868.s21i.faiusr.com/3/ABUIABADGAAg97rLpgYooKPAoAYwsAQ4uwI!450x450.gifhttps://28846868.s21i.faiusr.com/3/ABUIABADGAAghbvLpgYouMegWjCwBDi7Ag!450x450.gifhttps://28846868.s21i.faiusr.com/3/ABUIABADGAAgj7vLpgYo3MPL-QEwsAQ4uwI!450x450.gif
现在,我们的报废滑板又获得赛博新生啦~(可以踩着它出去浪了)

https://28846868.s21i.faiusr.com/3/ABUIABADGAAgmvLGpgYo26LKdDCwBDi7Ag.gif.webp
本项目已经开源,资料可在下方链接处下载。如果您对本项目有任何建议,欢迎留言。

附:Robodyno Pro一体化可编程电机技术文档 https://www.robotway.com/h-col-132.html
附:Robodyno Pro一体化可编程电机购买链接
https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-23266032403.11.29eb23e1kDRcgP&id=685203618488&mt=

附:主要零件清单

序号零件名称数量备注
1普通滑板1

2Robodyno Pro-P12一体化可编程电机1

3搅拌机摩擦轮轮皮1

4Robodyno Pro轮毂零件1

53D打印轮毂外圈1PLA
6Robodyno Pro轮架外板1铝
7Robodyno Pro轮架侧板1铝
8Robodyno Pro轮架顶板1铝
9M2.5*15螺丝9不锈钢
10Robodyno Pro电机线1

11ESP32控制器1ESP32-Pico-D4芯片
12TFT3.5通信转接板
1安装在ESP32控制器外壳中

附:开源资料

资料清单
序号内容
1【A004】-程序源代码
2【A004】-电路原理图

3【A004】-控制器电路板生产文件

4【A004】-控制器外壳3D打印文件

5控制器固件


更多资料下载详见 https://www.robotway.com/h-col-258.html

页: [1]
查看完整版本: 机器人制作开源方案 | 滑板助力器项目0.1版