2022-2-7 23:41:26 只看该作者
16118浏览
查看: 16118|回复: 14
打印 上一主题 下一主题

[项目] 树莓派+Mediapipe+麦克纳姆轮 制作跟随车

[复制链接]
本帖最后由 云天 于 2022-2-8 12:36 编辑





树莓派中安装Mediapipe,通过测试人脸检测,识别效果很好,帧频比较理想。这次使用人体姿态Mediapipe Pose来实现麦克纳姆轮小车,跟随人体移动。
【树莓派4B/3B+ IO扩展板】使用IO扩展板与L289N电机驱动板相连

IO Expansion HAT是一款专为Raspberry Pi 开发的IO扩展板,扩展板将树莓派IO口均引出,包含数字端口、模拟端口、PWM端口、IIC端口、UART端口、SPI端口、IIS端口,完美兼容DFRobot的Gravity传感器系列产品,为使用树莓派省去繁琐的接线和故障排除,让学生、开发者、科研工作者可以专注实现自己的项目。

【L289N电机驱动板】

L298N,是一款接受高电压的电机驱动器,直流电机和步进电机都可以驱动。一片驱动芯片可同时控制两个直流减速电机做不同动作,在6V到46V的电压范围内,提供2安培的电流,并且具有过热自断和反馈检测功能。


【连接图】
L289N电机驱动板与树莓派IO扩展板电路连接


L289N的使能引脚的跳帽要去掉,接到IO扩展板的PWM接口0,1,2,3引脚上。两个L289N驱动板使用两节3.7V锂电池串联供电。L289N要与IO扩展板共地。L289N驱动引脚分别与IO扩展板数字端口4,5,6,7,8,9,10,11引脚相连。






【树莓派供电】使用“DC-DC快充模块6~32V转5V3A”,电压输入范围是6-32V,输出5V/3A(功率还有些小)。它的输出相当稳定,长时间工作电压无漂移。





PWM端口
PWM端口:IO扩展板提供了四组PWM端口,连接到STM32的PWM端,树莓派通过I2C通讯将数据发送给STM32实现控制。BOARD.py
  1. # -*- coding:utf-8 -*-
  2. '''!
  3.   @file DFRobot_RaspberryPi_Expansion_Board.py
  4.   @brief This RaspberryPi expansion board can communicate with RaspberryPi via I2C. It has 10 GPIOs, 1 SPI, 4 I2Cs and 1 uart.
  5.   @copyright   Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
  6.   @license     The MIT License (MIT)
  7.   @author      Frank(jiehan.guo@dfrobot.com)
  8.   @version     V1.0
  9.   @date        2019-3-28
  10.   @url https://github.com/DFRobot/DFRobot_RaspberryPi_Expansion_Board
  11. '''
  12. import time
  13. _PWM_CHAN_COUNT = 4
  14. _ADC_CHAN_COUNT = 4
  15. class DFRobot_Expansion_Board:
  16.   _REG_SLAVE_ADDR = 0x00
  17.   _REG_PID = 0x01
  18.   _REG_VID = 0x02
  19.   _REG_PWM_CONTROL = 0x03
  20.   _REG_PWM_FREQ = 0x04
  21.   _REG_PWM_DUTY1 = 0x06
  22.   _REG_PWM_DUTY2 = 0x08
  23.   _REG_PWM_DUTY3 = 0x0a
  24.   _REG_PWM_DUTY4 = 0x0c
  25.   _REG_ADC_CTRL = 0x0e
  26.   _REG_ADC_VAL1 = 0x0f
  27.   _REG_ADC_VAL2 = 0x11
  28.   _REG_ADC_VAL3 = 0x13
  29.   _REG_ADC_VAL4 = 0x15
  30.   _REG_DEF_PID = 0xdf
  31.   _REG_DEF_VID = 0x10
  32.   
  33.   ''' Enum board Analog channels '''
  34.   A0 = 0x00
  35.   A1 = 0x01
  36.   A2 = 0x02
  37.   A3 = 0x03
  38.   ''' Board status '''
  39.   STA_OK = 0x00
  40.   STA_ERR = 0x01
  41.   STA_ERR_DEVICE_NOT_DETECTED = 0x02
  42.   STA_ERR_SOFT_VERSION = 0x03
  43.   STA_ERR_PARAMETER = 0x04
  44.   ''' last operate status, users can use this variable to determine the result of a function call. '''
  45.   last_operate_status = STA_OK
  46.   ''' Global variables '''
  47.   ALL = 0xffffffff
  48.   
  49.   def _write_bytes(self, reg, buf):
  50.     pass
  51.   
  52.   def _read_bytes(self, reg, len):
  53.     pass
  54.   def __init__(self, addr):
  55.     self._addr = addr
  56.     self._is_pwm_enable = False
  57.   def begin(self):
  58.     '''
  59.       @brief    Board begin
  60.       @return   Board status
  61.     '''
  62.     pid = self._read_bytes(self._REG_PID, 1)
  63.     vid = self._read_bytes(self._REG_VID, 1)
  64.     if self.last_operate_status == self.STA_OK:
  65.       if pid[0] != self._REG_DEF_PID:
  66.         self.last_operate_status = self.STA_ERR_DEVICE_NOT_DETECTED
  67.       elif vid[0] != self._REG_DEF_VID:
  68.         self.last_operate_status = self.STA_ERR_SOFT_VERSION
  69.       else:
  70.         self.set_pwm_disable()
  71.         self.set_pwm_duty(self.ALL, 0)
  72.         self.set_adc_disable()
  73.     return self.last_operate_status
  74.   def set_addr(self, addr):
  75.     '''
  76.       @brief    Set board controler address, reboot module to make it effective
  77.       @param address: int    Address to set, range in 1 to 127
  78.     '''
  79.     if addr < 1 or addr > 127:
  80.       self.last_operate_status = self.STA_ERR_PARAMETER
  81.       return
  82.     self._write_bytes(self._REG_SLAVE_ADDR, [addr])
  83.   def _parse_id(self, limit, id):
  84.     ld = []
  85.     if isinstance(id, list) == False:
  86.       id = id + 1
  87.       ld.append(id)
  88.     else:
  89.       ld = [i + 1 for i in id]
  90.     if ld == self.ALL:
  91.       return range(1, limit + 1)
  92.     for i in ld:
  93.       if i < 1 or i > limit:
  94.         self.last_operate_status = self.STA_ERR_PARAMETER
  95.         return []
  96.     return ld
  97.   def set_pwm_enable(self):
  98.     '''
  99.       @brief    Set pwm enable, pwm channel need external power
  100.     '''
  101.     self._write_bytes(self._REG_PWM_CONTROL, [0x01])
  102.     if self.last_operate_status == self.STA_OK:
  103.       self._is_pwm_enable = True
  104.     time.sleep(0.01)
  105.   def set_pwm_disable(self):
  106.     '''
  107.       @brief    Set pwm disable
  108.     '''
  109.     self._write_bytes(self._REG_PWM_CONTROL, [0x00])
  110.     if self.last_operate_status == self.STA_OK:
  111.       self._is_pwm_enable = False
  112.     time.sleep(0.01)
  113.   def set_pwm_frequency(self, freq):
  114.     '''
  115.       @brief    Set pwm frequency
  116.       @param freq: int    Frequency to set, in range 1 - 1000
  117.     '''
  118.     if freq < 1 or freq > 1000:
  119.       self.last_operate_status = self.STA_ERR_PARAMETER
  120.       return
  121.     is_pwm_enable = self._is_pwm_enable
  122.     self.set_pwm_disable()
  123.     self._write_bytes(self._REG_PWM_FREQ, [freq >> 8, freq & 0xff])
  124.     time.sleep(0.01)
  125.     if is_pwm_enable:
  126.       self.set_pwm_enable()
  127.   def set_pwm_duty(self, chan, duty):
  128.     '''
  129.       @brief    Set selected channel duty
  130.       @param chan: list     One or more channels to set, items in range 1 to 4, or chan = self.ALL
  131.       @param duty: float    Duty to set, in range 0.0 to 100.0
  132.     '''
  133.     if duty < 0 or duty > 100:
  134.       self.last_operate_status = self.STA_ERR_PARAMETER
  135.       return
  136.     for i in self._parse_id(_PWM_CHAN_COUNT, chan):
  137.       self._write_bytes(self._REG_PWM_DUTY1 + (i - 1) * 2, [int(duty), int((duty * 10) % 10)])
  138.   def set_adc_enable(self):
  139.     '''
  140.       @brief    Set adc enable
  141.     '''
  142.     self._write_bytes(self._REG_ADC_CTRL, [0x01])
  143.   def set_adc_disable(self):
  144.     '''
  145.       @brief    Set adc disable
  146.     '''
  147.     self._write_bytes(self._REG_ADC_CTRL, [0x00])
  148.   def get_adc_value(self, chan):
  149.     '''
  150.       @brief    Get adc value
  151.       @param chan: int    Channel to get, in range 1 to 4, or self.ALL
  152.       @return :list       List of value
  153.     '''
  154.     for i in self._parse_id(_ADC_CHAN_COUNT, chan):
  155.       rslt = self._read_bytes(self._REG_ADC_VAL1 + (i - 1) * 2, 2)
  156.     return ((rslt[0] << 8) | rslt[1])
  157.   def detecte(self):
  158.     '''
  159.       @brief    If you forget address you had set, use this to detecte them, must have class instance
  160.       @return   Board list conformed
  161.     '''
  162.     l = []
  163.     back = self._addr
  164.     for i in range(1, 127):
  165.       self._addr = i
  166.       if self.begin() == self.STA_OK:
  167.         l.append(i)
  168.     for i in range(0, len(l)):
  169.       l[i] = hex(l[i])
  170.     self._addr = back
  171.     self.last_operate_status = self.STA_OK
  172.     return l
  173. class DFRobot_Epansion_Board_Digital_RGB_LED():
  174.   def __init__(self, board):
  175.     '''
  176.       @param board: DFRobot_Expansion_Board   Board instance to operate digital rgb led, test LED: https://www.dfrobot.com/product-1829.html
  177.                                               Warning: LED must connect to pwm channel, otherwise may destory Pi IO
  178.     '''
  179.     self._board = board
  180.     self._chan_r = 0
  181.     self._chan_g = 0
  182.     self._chan_b = 0
  183.   def begin(self, chan_r, chan_g, chan_b):
  184.     '''
  185.       @brief    Set digital rgb led color channel, these parameters not repeat
  186.       @param chan_r: int    Set color red channel id, in range 1 to 4
  187.       @param chan_g: int    Set color green channel id, in range 1 to 4
  188.       @param chan_b: int    Set color blue channel id, in range 1 to 4
  189.     '''
  190.     if chan_r == chan_g or chan_r == chan_b or chan_g == chan_b:
  191.       return
  192.     if chan_r < _PWM_CHAN_COUNT and chan_g < _PWM_CHAN_COUNT and chan_b < _PWM_CHAN_COUNT:
  193.       self._chan_r = chan_r
  194.       self._chan_g = chan_g
  195.       self._chan_b = chan_b
  196.       self._board.set_pwm_enable()
  197.       self._board.set_pwm_frequency(1000)
  198.       self._board.set_pwm_duty(self._board.ALL, 100)
  199.   def color888(self, r, g, b):
  200.     '''
  201.       @brief    Set LED to true-color
  202.       @param r: int   Color components red
  203.       @param g: int   Color components green
  204.       @param b: int   Color components blue
  205.     '''
  206.     self._board.set_pwm_duty([self._chan_r], 100 - (r & 0xff) * 100 // 255)
  207.     self._board.set_pwm_duty([self._chan_g], 100 - (g & 0xff) * 100 // 255)
  208.     self._board.set_pwm_duty([self._chan_b], 100 - (b & 0xff) * 100 // 255)
  209.   def color24(self, color):
  210.     '''
  211.       @brief    Set LED to 24-bits color
  212.       @param color: int   24-bits color
  213.     '''
  214.     color &= 0xffffff
  215.     self.color888(color >> 16, (color >> 8) & 0xff, color & 0xff)
  216.   def color565(self, color):
  217.     '''
  218.       @brief    Set LED to 16-bits color
  219.       @param color: int   16-bits color
  220.     '''
  221.     color &= 0xffff
  222.     self.color888((color & 0xf800) >> 8, (color & 0x7e0) >> 3, (color & 0x1f) << 3)
  223. class DFRobot_Expansion_Board_Servo():
  224.   def __init__(self, board):
  225.     '''
  226.       @param board: DFRobot_Expansion_Board   Board instance to operate servo, test servo: https://www.dfrobot.com/product-255.html
  227.                                               Warning: servo must connect to pwm channel, otherwise may destory Pi IO
  228.     '''
  229.     self._board = board
  230.   def begin(self):
  231.     '''
  232.       @brief    Board servo begin
  233.     '''
  234.     self._board.set_pwm_enable()
  235.     self._board.set_pwm_frequency(50)
  236.     self._board.set_pwm_duty(self._board.ALL, 0)
  237.   def move(self, id, angle):
  238.     '''
  239.       @brief    Servos move
  240.       @param id: list     One or more servos to set, items in range 1 to 4, or chan = self.ALL
  241.       @param angle: int   Angle to move, in range 0 to 180
  242.     '''
  243.     if 0 <= angle <= 180:
  244.       self._board.set_pwm_duty(id, (0.5 + (float(angle) / 90.0)) / 20 * 100)
  245. import smbus
  246. class DFRobot_Expansion_Board_IIC(DFRobot_Expansion_Board):
  247.   def __init__(self, bus_id, addr):
  248.     '''
  249.       @param bus_id: int   Which bus to operate
  250.       @oaram addr: int     Board controler address
  251.     '''
  252.     self._bus = smbus.SMBus(bus_id)
  253.     DFRobot_Expansion_Board.__init__(self, addr)
  254.   def _write_bytes(self, reg, buf):
  255.     self.last_operate_status = self.STA_ERR_DEVICE_NOT_DETECTED
  256.     try:
  257.       self._bus.write_i2c_block_data(self._addr, reg, buf)
  258.       self.last_operate_status = self.STA_OK
  259.     except:
  260.       pass
  261.   def _read_bytes(self, reg, len):
  262.     self.last_operate_status = self.STA_ERR_DEVICE_NOT_DETECTED
  263.     try:
  264.       rslt = self._bus.read_i2c_block_data(self._addr, reg, len)
  265.       self.last_operate_status = self.STA_OK
  266.       return rslt
  267.     except:
  268.       return [0] * len
复制代码
PWM测试程序
  1. import time
  2. import RPi.GPIO as GPIO
  3. GPIO.setmode(GPIO.BCM)
  4. GPIO.setup(4,GPIO.OUT)
  5. GPIO.setup(5,GPIO.OUT)
  6. GPIO.setup(6,GPIO.OUT)
  7. GPIO.setup(7,GPIO.OUT)
  8. GPIO.setup(8,GPIO.OUT)
  9. GPIO.setup(9,GPIO.OUT)
  10. GPIO.setup(10,GPIO.OUT)
  11. GPIO.setup(11,GPIO.OUT)
  12. from BOARD import DFRobot_Expansion_Board_IIC as Board
  13. board = Board(1, 0x10)    # Select i2c bus 1, set address to 0x10
  14. def board_detect():
  15.   l = board.detecte()
  16.   print("Board list conform:")
  17.   print(l)
  18. ''' print last operate status, users can use this variable to determine the result of a function call. '''
  19. def print_board_status():
  20.   if board.last_operate_status == board.STA_OK:
  21.     print("board status: everything ok")
  22.   elif board.last_operate_status == board.STA_ERR:
  23.     print("board status: unexpected error")
  24.   elif board.last_operate_status == board.STA_ERR_DEVICE_NOT_DETECTED:
  25.     print("board status: device not detected")
  26.   elif board.last_operate_status == board.STA_ERR_PARAMETER:
  27.     print("board status: parameter error")
  28.   elif board.last_operate_status == board.STA_ERR_SOFT_VERSION:
  29.     print("board status: unsupport board framware version")
  30. if __name__ == "__main__":
  31. board_detect()    # If you forget address you had set, use this to detected them, must have class instance
  32. # Set board controler address, use it carefully, reboot module to make it effective
  33. while board.begin() != board.STA_OK:    # Board begin and check board status
  34.   print_board_status()
  35.   print("board begin faild")
  36.   time.sleep(2)
  37.   print("board begin success")
  38. board.set_pwm_enable()                # Pwm channel need external power
  39. # board.set_pwm_disable()
  40. board.set_pwm_frequency(1000)         # Set frequency to 1000HZ, Attention: PWM voltage depends on independent power supply
  41.   
  42. print("set part pwm channels duty to 60%")
  43. board.set_pwm_duty(3, 60)   # Set pwm0 channels duty
  44. #board.set_pwm_duty(1, 70)  # Set pwm1 channels duty
  45. #board.set_pwm_duty(2, 80)  # Set pwm2 channels duty
  46. #board.set_pwm_duty(3, 90)  # Set pwm3 channels duty
  47.    
  48. GPIO.output(8,GPIO.HIGH)#left 1()
  49. GPIO.output(9,GPIO.LOW)
  50. GPIO.output(6,GPIO.HIGH)#left 2(1)
  51. GPIO.output(7,GPIO.LOW)
  52. GPIO.output(10,GPIO.HIGH)#right 1(2)
  53. GPIO.output(11,GPIO.LOW)
  54. GPIO.output(4,GPIO.HIGH)#right 2(0)
  55. GPIO.output(5,GPIO.LOW)
  56. time.sleep(1)
  57. GPIO.cleanup()
复制代码
【控制麦克纳姆轮】




麦克纳姆轮与普通轮子的区别在于麦克纳姆轮旋转时,由于存在斜向的从动轮,会同时产生一个斜向的力,当我们控制轮子旋转的速度与方向时,将斜向的力增强或抵消,从而实现小车的全向移动。可以完成横移、斜方向移动等普通小车无法完成的高难度动作,轮子的转动方向与小车的运动方向关系如下图:


引脚控制程序封装成类文件mygpio.py
  1. import RPi.GPIO as GPIO
  2. class mygpio():
  3. GPIO.setmode(GPIO.BCM)
  4. GPIO.setup(4,GPIO.OUT)
  5. GPIO.setup(5,GPIO.OUT)
  6. GPIO.setup(6,GPIO.OUT)
  7. GPIO.setup(7,GPIO.OUT)
  8. GPIO.setup(8,GPIO.OUT)
  9. GPIO.setup(9,GPIO.OUT)
  10. GPIO.setup(10,GPIO.OUT)
  11. GPIO.setup(11,GPIO.OUT)
  12. def forward(self):
  13.    GPIO.output(8,GPIO.HIGH)#left 1
  14.    GPIO.output(9,GPIO.LOW)
  15.    GPIO.output(6,GPIO.HIGH)#left 2
  16.   GPIO.output(7,GPIO.LOW)
  17.   GPIO.output(10,GPIO.HIGH)#right 1
  18.   GPIO.output(11,GPIO.LOW)
  19.   GPIO.output(4,GPIO.HIGH)#right 2
  20.   GPIO.output(5,GPIO.LOW)
  21. def back(self):
  22.   GPIO.output(8,GPIO.LOW)#left 1
  23.   GPIO.output(9,GPIO.HIGH)
  24.   GPIO.output(6,GPIO.LOW)#left 2
  25.   GPIO.output(7,GPIO.HIGH)
  26.   GPIO.output(10,GPIO.LOW)#right 1
  27.   GPIO.output(11,GPIO.HIGH)
  28.   GPIO.output(4,GPIO.LOW)#right 2
  29.   GPIO.output(5,GPIO.HIGH)
  30. def left(self):
  31.   GPIO.output(8,GPIO.LOW)#left 1
  32.   GPIO.output(9,GPIO.HIGH)
  33.   GPIO.output(6,GPIO.HIGH)#left 2
  34.   GPIO.output(7,GPIO.LOW)
  35.   GPIO.output(10,GPIO.HIGH)#right 1
  36.   GPIO.output(11,GPIO.LOW)
  37.   GPIO.output(4,GPIO.LOW)#right 2
  38.   GPIO.output(5,GPIO.HIGH)
  39. def right(self):
  40.   GPIO.output(8,GPIO.HIGH)#left 1
  41.   GPIO.output(9,GPIO.LOW)
  42.   GPIO.output(6,GPIO.LOW)#left 2
  43.   GPIO.output(7,GPIO.HIGH)
  44.   GPIO.output(10,GPIO.LOW)#right 1
  45.   GPIO.output(11,GPIO.HIGH)
  46.   GPIO.output(4,GPIO.HIGH)#right 2
  47.   GPIO.output(5,GPIO.LOW)
  48. def l_rotate(self):
  49.   GPIO.output(8,GPIO.LOW)#left 1
  50.   GPIO.output(9,GPIO.HIGH)
  51.   GPIO.output(6,GPIO.LOW)#left 2
  52.   GPIO.output(7,GPIO.HIGH)
  53.   GPIO.output(10,GPIO.HIGH)#right 1
  54.   GPIO.output(11,GPIO.LOW)
  55.   GPIO.output(4,GPIO.HIGH)#right 2
  56.   GPIO.output(5,GPIO.LOW)
  57. def r_rotate(self):
  58.   GPIO.output(8,GPIO.HIGH)#left 1
  59.   GPIO.output(9,GPIO.LOW)
  60.   GPIO.output(6,GPIO.HIGH)#left 2
  61.   GPIO.output(7,GPIO.LOW)
  62.   GPIO.output(10,GPIO.LOW)#right 1
  63.   GPIO.output(11,GPIO.HIGH)
  64.   GPIO.output(4,GPIO.LOW)#right 2
  65.   GPIO.output(5,GPIO.HIGH)
  66. def stop(self):
  67.   GPIO.output(4,GPIO.LOW)
  68.   GPIO.output(5,GPIO.LOW)
  69.   GPIO.output(6,GPIO.LOW)
  70.   GPIO.output(7,GPIO.LOW)
  71.   GPIO.output(8,GPIO.LOW)
  72.   GPIO.output(9,GPIO.LOW)
  73.   GPIO.output(10,GPIO.LOW)
  74.   GPIO.output(11,GPIO.LOW)
  75. #GPIO.cleanup()
复制代码
【Mediapipe】MediaPipe 是一款由 Google Research 开发并开源的多媒体机器学习模型应用框架。在谷歌,一系列重要产品,如 、Google Lens、ARCore、Google Home 以及 ,都已深度整合了 MediaPipe。


安装mediapipe库,在终端中使用:pip3 install mediapipe-rpi4
人体姿态Mediapipe Pose,测试程序:
  1. import cv2
  2. import time
  3. import mediapipe as mp
  4. mp_drawing = mp.solutions.drawing_utils
  5. mp_drawing_styles = mp.solutions.drawing_styles
  6. mp_pose = mp.solutions.pose
  7. pTime = 0
  8. cTime = 0
  9. # For webcam input:
  10. cap = cv2.VideoCapture(0)
  11. j=0
  12. with mp_pose.Pose(min_detection_confidence=0.5,min_tracking_confidence=0.5) as pose:
  13. while cap.isOpened():
  14.     success, image = cap.read()
  15.     if not success:
  16.       print("Ignoring empty camera frame.")
  17.       # If loading a video, use 'break' instead of 'continue'.
  18.       continue
  19.     j=j+1
  20.     if j<3:#丢帧
  21.        continue
  22.     else:
  23.         j=0
  24.    
  25.     h,w,c=image.shape
  26.     image.flags.writeable = False
  27.     image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
  28.     results = pose.process(image)
  29.     # Draw the pose annotation on the image.
  30.     if results.pose_landmarks:
  31.       list_x=[]
  32.       list_y=[]
  33.       image.flags.writeable = False
  34.       image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
  35.       #print(results.pose_landmarks.landmark)
  36.       for i in results.pose_landmarks.landmark:
  37.         list_x.append(i.x)
  38.         list_y.append(i.y)
  39.       min_x=int(min(list_x)*w)
  40.       min_y=int(min(list_y)*h)
  41.       max_x=int(max(list_x)*w)
  42.       max_y=int(max(list_y)*h)
  43.       #cv2.circle(image,(min_x,min_y),10,(0,255,255),-1)
  44.       #cv2.circle(image,(max_x,max_y),5,(0,0,255),-1)
  45.       #print(min_x," ",min_y," ",max_x," ",max_y," ",w," ",h)
  46.       #cv2.rectangle(img,(min_x*2,min_y*2),(max_x*2,max_y*2),(255,0,0),10)
  47.       cv2.rectangle(image,(min_x,min_y),(max_x,max_y),(255,0,0),10)
  48.       #mp_drawing.draw_landmarks(image,results.pose_landmarks,mp_pose.POSE_CONNECTIONS,landmark_drawing_spec=mp_drawing_styles.get_default_pose_landmarks_style())
  49.    
  50.     cTime = time.time()
  51.     fps = 1 / (cTime - pTime)
  52.     pTime = cTime
  53.     cv2.putText(image, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3,(255, 0, 255), 3)
  54.       
  55.     cv2.imshow('MediaPipe Pose', cv2.flip(image, 1))
  56.     if cv2.waitKey(1) & 0xFF == 27:
  57.       break
  58. cap.release()
复制代码


因为帧频有些低,只有5帧/秒左右,所以进行了丢帧处理。
【完整程序】
  1. import sys
  2. import os
  3. import cv2
  4. import time
  5. import mediapipe as mp
  6. import RPi.GPIO as GPIO
  7. from mygpio import mygpio
  8. sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
  9. from BOARD import DFRobot_Expansion_Board_IIC as Board
  10. board = Board(1, 0x10)    # Select i2c bus 1, set address to 0x10
  11. def board_detect():
  12.   l = board.detecte()
  13. board_detect()
  14. while board.begin() != board.STA_OK:    # Board begin and check board status
  15.     print("board begin faild")
  16.     time.sleep(2)
  17. print("board begin success")
  18. board.set_pwm_enable()                # Pwm channel need external power
  19. # board.set_pwm_disable()
  20. board.set_pwm_frequency(1000)
  21. board.set_pwm_duty(0, 60)
  22. board.set_pwm_duty(1, 60)
  23. board.set_pwm_duty(2, 60)
  24. board.set_pwm_duty(3, 60)
  25. g=mygpio()
  26. mp_drawing = mp.solutions.drawing_utils
  27. mp_drawing_styles = mp.solutions.drawing_styles
  28. mp_pose = mp.solutions.pose
  29. pTime = 0
  30. cTime = 0
  31. # For webcam input:
  32. cap = cv2.VideoCapture(0)
  33. j=0
  34. with mp_pose.Pose(min_detection_confidence=0.5,min_tracking_confidence=0.5) as pose:
  35. while cap.isOpened():
  36.     success, image = cap.read()
  37.     if not success:
  38.       print("Ignoring empty camera frame.")
  39.       # If loading a video, use 'break' instead of 'continue'.
  40.       continue
  41.     j=j+1
  42.     if j<3:
  43.        continue
  44.     else:
  45.         j=0
  46.    
  47.     h,w,c=image.shape
  48.     image.flags.writeable = False
  49.     image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
  50.     results = pose.process(image)
  51.     # Draw the pose annotation on the image.
  52.     if results.pose_landmarks:
  53.       list_x=[]
  54.       list_y=[]
  55.       image.flags.writeable = False
  56.       image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
  57.       #print(results.pose_landmarks.landmark)
  58.       for i in results.pose_landmarks.landmark:
  59.         list_x.append(i.x)
  60.         list_y.append(i.y)
  61.       min_x=int(min(list_x)*w)
  62.       min_y=int(min(list_y)*h)
  63.       max_x=int(max(list_x)*w)
  64.       max_y=int(max(list_y)*h)
  65.       #cv2.circle(image,(min_x,min_y),10,(0,255,255),-1)
  66.       #cv2.circle(image,(max_x,max_y),5,(0,0,255),-1)
  67.       #print(min_x," ",min_y," ",max_x," ",max_y," ",w," ",h)
  68.       #cv2.rectangle(img,(min_x*2,min_y*2),(max_x*2,max_y*2),(255,0,0),10)
  69.       cv2.rectangle(image,(min_x,min_y),(max_x,max_y),(255,0,0),10)
  70.       #mp_drawing.draw_landmarks(image,results.pose_landmarks,mp_pose.POSE_CONNECTIONS,landmark_drawing_spec=mp_drawing_styles.get_default_pose_landmarks_style())
  71.       mid=(min_x+max_x)/2
  72.       dis_x=mid-w/2
  73.       dis_w=max_x-min_x
  74.       print(dis_x,"  ",dis_w)
  75.       
  76.       if dis_x>90:
  77.           print("right")
  78.           board.set_pwm_duty(0, 70)
  79.           board.set_pwm_duty(1, 70)
  80.           board.set_pwm_duty(2, 70)
  81.           board.set_pwm_duty(3, 70)
  82.           g.right()
  83.       elif dis_x<-90:
  84.           print("left")
  85.           board.set_pwm_duty(0, 70)
  86.           board.set_pwm_duty(1, 70)
  87.           board.set_pwm_duty(2, 70)
  88.           board.set_pwm_duty(3, 70)
  89.           g.left()
  90.       elif dis_w>350:
  91.           print("back")
  92.           board.set_pwm_duty(0, 60)
  93.           board.set_pwm_duty(1, 60)
  94.           board.set_pwm_duty(2, 60)
  95.           board.set_pwm_duty(3, 60)
  96.           g.back()
  97.       elif dis_w<200:
  98.           print("forward")
  99.           board.set_pwm_duty(0, 63)
  100.           board.set_pwm_duty(1, 60)
  101.           board.set_pwm_duty(2, 63)
  102.           board.set_pwm_duty(3, 60)
  103.           g.forward()
  104.       else:
  105.           print("stop1")
  106.           g.stop()
  107.     else:
  108.       print("stop2")
  109.       g.stop()
  110.     cTime = time.time()
  111.     fps = 1 / (cTime - pTime)
  112.     pTime = cTime
  113.     cv2.putText(image, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3,(255, 0, 255), 3)
  114.       
  115.     cv2.imshow('MediaPipe Pose', cv2.flip(image, 1))
  116.     if cv2.waitKey(1) & 0xFF == 27:
  117.       break
  118. cap.release()
  119. GPIO.cleanup()
复制代码

【演示视频】




沙发

shzrzxlee  高级技匠

发表于 2022-2-8 13:40:30

树莓派和电机扩展版都是用碱性电池盒供电吗
回复

使用道具 举报

板凳

云天  高级技匠
 楼主|

发表于 2022-2-8 16:10:05

shzrzxlee 发表于 2022-2-8 13:40
树莓派和电机扩展版都是用碱性电池盒供电吗

用的是锂电池,DF商城有锂电池18650电池盒。
回复

使用道具 举报

地板

安卓机器人  中级技神

发表于 2022-2-8 16:58:02

多产老师,厉害
回复

使用道具 举报

5#

gray6666  初级技神

发表于 2022-2-8 18:54:45

树莓派可以接充电宝试试
回复

使用道具 举报

6#

云天  高级技匠
 楼主|

发表于 2022-2-8 22:03:29

gray6666 发表于 2022-2-8 18:54
树莓派可以接充电宝试试

我的充电宝最大5V2A,功率太低。开启摄像头,运行Mediapipe,就提示换电源!
回复

使用道具 举报

7#

赤星三春牛!  中级技匠

发表于 2022-2-9 11:14:27

呵呵呵呵
回复

使用道具 举报

8#

赤星三春牛!  中级技匠

发表于 2022-2-9 11:15:31

跟随车,不错。666
回复

使用道具 举报

9#

@_@  学徒

发表于 2022-2-21 14:25:33

老师,请问应该怎么安装mediapipe包呀,我的错误提示、pip和python版本号如图
回复

使用道具 举报

10#

@_@  学徒

发表于 2022-2-21 15:01:00

@_@ 发表于 2022-2-21 14:25
老师,请问应该怎么安装mediapipe包呀,我的错误提示、pip和python版本号如图 ...

我用pip3 install mediapipe-rpi4的命令下载提示这样的错误:

THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them.
    fonttools>=4.22.0 from https://www.piwheels.org/simple/fonttools/fonttools-4.29.1-cp37-cp37m-linux_armv7l.whl#sha256=4fc0c047f0c1137320483808da817128d41f2f080bd095d61a30c350bf0a1b38 (from matplotlib->mediapipe-rpi4):
        Expected sha256 4fc0c047f0c1137320483808da817128d41f2f080bd095d61a30c350bf0a1b38
             Got        72ee12fb14c14b6606552eadb1b4174c7e992d332a4d4b8a4da60cbebd3df610
回复

使用道具 举报

11#

云天  高级技匠
 楼主|

发表于 2022-2-21 20:19:56

@_@ 发表于 2022-2-21 15:01
我用pip3 install mediapipe-rpi4的命令下载提示这样的错误:

THESE PACKAGES DO NOT MATCH THE HASHES  ...

你是用的是树莓派4B?
回复

使用道具 举报

12#

@_@  学徒

发表于 2022-2-21 23:27:04

云天 发表于 2022-2-21 20:19
你是用的是树莓派4B?

是的老师,我看到别人有同样的问题,说安装mediapipe-rpi4出现上面的错误是我的网速有问题,而且有一些依赖包没有安装好,而且我发现我没有cv2,我就去下载了对应版本的opencv-python再import cv2到python,然后又按百度的说法乱七八糟都搞上去就可以了!
回复

使用道具 举报

13#

yangjumei  学徒

发表于 2022-3-7 14:38:38

这车还可以横向移动呀
回复

使用道具 举报

14#

 初级技匠

发表于 2022-4-13 11:11:55

yangjumei 发表于 2022-3-7 14:38
这车还可以横向移动呀

是的,这种轮子是特制的,麦克纳姆轮,看这个了解一下。
https://mp.weixin.qq.com/s/kF2djn5gNV35k_brhKOMfQ
回复

使用道具 举报

15#

 初级技匠

发表于 2022-4-13 11:14:03

很好,如果把轮子换成四条连杆结构的腿会更好!
回复

使用道具 举报

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

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2022 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail