极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 1723|回复: 0

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

[复制链接]
发表于 2023-8-24 10:22:16 | 显示全部楼层 |阅读模式
本帖最后由 机器谱 于 2023-8-24 10:22 编辑

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

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



(图片来源:https://boostedusa.com/collections/evolve-skateboards)


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


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


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


曾经用于开发机械臂、机器狗、智能车等开发项目。


电机选定后,接下来的关键就是轮子,我们从网上挑选了一款搅拌机用的摩擦轮,改造后便可作为助力器的轮子。


接下来准备好全套零部件及装配工具。


去掉摩擦轮原先的轮毂并安装上Robodyno Pro-P12电机配套的轮毂零件,以及3D打印的轮毂外圈,轮子部分就完成了。


然后给电机装上轮架侧板,再和轮子装配在一起。


       将轴承、轮架顶板、轮架外板装上(在上图中,轮架顶板和轮架外板已经装成了L型,即图中右侧第一个零件),助力轮便装配完成了。


最后,在滑板板尾和合适位置上打孔,并将助力轮安装在滑板上面。


装配完成。


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


我们编写了一套固件,让它可以用于控制滑板。


       通过拨动旋钮,可以控制助力轮的输出力矩,数字越大,力矩越大,旋转速度也就越快,使用者可以根据自身体重和期望的起步速度来选择合适的数值。


点击下方的正方形按钮,就可以控制助力轮启停。


然后接上11.1V动力电池,并将电池和控制器用轧带固定在滑板上。


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


程序源代码如下:
main.py
  1. Python
  2. import lvgl as lv
  3. from ili9XXX import ili9488, LANDSCAPE
  4. SCR_WIDTH = 480
  5. SCR_HEIGHT = 320
  6. disp = ili9488(mosi=4, miso=18, clk=5, cs=14, dc=13, rst=12, \
  7.                power=-1, backlight=15, backlight_on=1, factor=32, \
  8.                width=SCR_WIDTH, height=SCR_HEIGHT, rot=LANDSCAPE)
  9. from ft6x36 import ft6x36
  10. touch_rst = Pin(2, Pin.OUT)
  11. touch_rst.on()
  12. touch = ft6x36(sda=27, scl=26, width=SCR_HEIGHT, height=SCR_WIDTH, inv_x=True, swap_xy=True)

  13. style = lv.style_t()
  14. style.init()
  15. style.set_radius(0)
  16. style.set_bg_color(lv.color_hex(0x2E3234))
  17. style.set_text_color(lv.color_hex(0xC2C1C1))
  18. style.set_border_width(0)

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

  21. from motor_controller import MotorController
  22.            
  23. controller = MotorController(screen)

  24. import time
  25. while True:
  26.     controller.update()
  27.     time.sleep(0.01)
复制代码

boot.py

  1. Python
  2. # This file is executed on every boot (including wake-boot from deepsleep)
  3. #import esp
  4. #esp.osdebug(None)
  5. #import webrepl
  6. #webrepl.start()
  7. from machine import Pin
  8. import time
  9. power = Pin(32, Pin.OUT)
  10. _roller_btn = Pin(36, Pin.IN)
  11. _enc_a = Pin(37, Pin.IN)
  12. _enc_b = Pin(38, Pin.IN)
  13. _a0 = 1
  14. _b0 = 1
  15. _ab0 = 1
  16. enc_pos = 0
  17. def on_enc_change(pin):
  18.     global _a0, _b0, _ab0, enc_pos
  19.     a = _enc_a.value()
  20.     b = _enc_b.value()
  21.     if not a == _a0:
  22.         _a0 = a
  23.         if not b == _b0:
  24.             _b0 = b
  25.             enc_pos += 1 if a == b else -1
  26.             if not (a == b) == _ab0:
  27.                 enc_pos += 1 if a == b else -1
  28.             _ab0 = (a == b)
  29.            
  30. _enc_a.irq(handler = on_enc_change)
  31. time.sleep(2)
  32. power.on()
复制代码

encoder.py

  1. Python
  2. from machine import Pin
  3. class Encoder:
  4.     def __init__(self):
  5.         self.btn_pin = Pin(36, Pin.IN)
  6.         self.enc_a = Pin(37, Pin.IN)
  7.         self.enc_b = Pin(38, Pin.IN)
  8.         self.a0 = 1
  9.         self.b0 = 1
  10.         self.ab0 = 1
  11.         self.pos = 0
  12.         self.enc_a.irq(handler = self.on_change)
  13.    
  14.     def on_change(self, pin):
  15.         a = self.enc_a.value()
  16.         b = self.enc_b.value()
  17.         if not a == self.a0:
  18.             self.a0 = a
  19.             if not b == self.b0:
  20.                 self.b0 = b
  21.                 self.pos += 1 if a == b else -1
  22.                 if not (a == b) == self.ab0:
  23.                     self.pos += 1 if a == b else -1
  24.                 self.ab0 = (a == b)
复制代码

motor_controller.py

  1. Python
  2. from encoder import Encoder
  3. import lvgl as lv
  4. from machine import Pin
  5. import time
  6. from robodyno import *

  7. class MotorController:
  8.     def __init__(self, container):
  9.         self.running_flag = False
  10.         self.encoder = Encoder()
  11.         self.container = container
  12.         self.power = Pin(32, Pin.OUT)
  13.         self.can = CanBus()
  14.         self.motor = Motor(self.can, 0x10, ROBODYNO_PRO_12)
  15.         self.motor.torque_mode()
  16.         self.max_torque = 3
  17.         self.draw_power_btn()
  18.         self.draw_bar()
  19.         self.draw_btn()
  20.    
  21.     def draw_bar(self):
  22.         self.label = lv.label(self.container)
  23.         self.label.set_text(str(self.encoder.pos))
  24.         self.label.set_style_text_font(lv.font_montserrat_24, 0)
  25.         self.label.align(lv.ALIGN.TOP_MID, 0, 50)

  26.         self.bar = lv.bar(self.container)
  27.         self.bar.set_size(200, 20)
  28.         self.bar.align(lv.ALIGN.TOP_MID, 0, 90)
  29.         self.bar.set_value(self.encoder.pos, lv.ANIM.OFF)
  30.    
  31.     def draw_btn(self):
  32.         self.btn = lv.btn(self.container)
  33.         self.btn.align(lv.ALIGN.TOP_MID, 0, 150)
  34.         self.btn.set_size(100,100)
  35.         self.btn.set_style_bg_color(lv.color_hex(0x4EB181), 0)
  36.         self.btn_label = lv.label(self.btn)
  37.         self.btn_label.set_style_text_font(lv.font_montserrat_24, 0)
  38.         self.btn_label.set_text(lv.SYMBOL.PLAY)
  39.         self.btn_label.center()
  40.         self.btn.add_event_cb(self.on_btn_click, lv.EVENT.CLICKED, None)
  41.    
  42.     def on_btn_click(self, e):
  43.         btn = e.get_target()
  44.         label = btn.get_child(0)
  45.         if self.running_flag:
  46.             self.motor.set_torque(0)
  47.             self.motor.disable()
  48.             self.running_flag = False
  49.             btn.set_style_bg_color(lv.color_hex(0x4EB181), 0)
  50.             label.set_text(lv.SYMBOL.PLAY)
  51.         else:
  52.             self.motor.set_torque(0)
  53.             self.motor.enable()
  54.             self.running_flag = True
  55.             btn.set_style_bg_color(lv.color_hex(0xF44336), 0)
  56.             label.set_text(lv.SYMBOL.STOP)
  57.    
  58.     def draw_power_btn(self):
  59.         self.power_btn = lv.btn(self.container)
  60.         self.power_btn.align(lv.ALIGN.TOP_RIGHT, -40, 40)
  61.         self.power_btn.set_size(40,40)
  62.         self.power_btn.set_style_bg_color(lv.color_hex(0xF44336), 0)
  63.         self.power_btn_label = lv.label(self.power_btn)
  64.         self.power_btn_label.set_style_text_font(lv.font_montserrat_24, 0)
  65.         self.power_btn_label.set_text(lv.SYMBOL.POWER)
  66.         self.power_btn_label.center()
  67.         self.power_btn.add_event_cb(self.on_power_off, lv.EVENT.CLICKED, None)
  68.    
  69.     def on_power_off(self, e):
  70.         self.power.off()
  71.    
  72.     def update(self):
  73.         if self.encoder.pos > 100:
  74.             self.encoder.pos = 100
  75.         if self.encoder.pos < 0:
  76.             self.encoder.pos = 0
  77.         self.bar.set_value(self.encoder.pos, lv.ANIM.OFF)
  78.         self.label.set_text(str(self.encoder.pos))
  79.         if self.running_flag:
  80.             b = 0.002 + self.encoder.pos / 100 * 0.3
  81.             vel = self.motor.get_vel()
  82.             if vel is not None:
  83.                 vel = -vel
  84.                 torque = 0
  85.                 if vel > 3:
  86.                     torque = max(-0.107 - b * vel, -self.max_torque)
  87.                 elif vel < -3:
  88.                     torque = min(0.1 - b * vel, self.max_torque)
  89.                 self.motor.set_torque(torque)
复制代码


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


然后再试一下转弯,表现效果还不错。


最后试一下刹车。


这些稳如狗的表现自然是通过一次又一次的调试和数不清的翻车换来的。


现在,我们的报废滑板又获得赛博新生啦~(可以踩着它出去浪了)


本项目已经开源,资料可在下方链接处下载。如果您对本项目有任何建议,欢迎留言。

附: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

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

3
搅拌机摩擦轮轮皮
1

4
Robodyno Pro轮毂零件
1

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

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

附:开源资料
资料清单
序号
内容
1
【A004】-程序源代码
2
【A004】-电路原理图
3
【A004】-控制器电路板生产文件
4
【A004】-控制器外壳3D打印文件
5
控制器固件

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

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-5-2 01:13 , Processed in 0.065145 second(s), 17 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表