2905浏览
查看: 2905|回复: 6

Mind+Python+Mediapipe项目——AI健身之跳绳

[复制链接]
本帖最后由 云天 于 2021-12-1 15:49 编辑

【项目背景】
跳绳是一个很好的健身项目,为了获知所跳个数,有的跳绳上会有计数器。但这也只能跳完这后看到,能不能在跳的过程中就能看到,这样能让我们坚持跳的更多,更有趣味性。
【项目设计】
2021_0109_d728a471g00qmngmq0592d200ia00a7g00ia00a7.gif

通过Mind+Python模式下加载Google的开源Mediapipe人工智能算法库,识别人体姿态,来判断跳绳次数,并通过Pinpong库控制LED灯实时显示次数。
【测试程序】
测试程序中,使用人体姿态23,24两坐标点中点与标准点的比较来确认跳绳完成程度。

pose_tracking_full_body_landmarks.png
  1. import numpy as np
  2. import time
  3. import cv2
  4. import PoseModule as pm
  5. cap = cv2.VideoCapture("tiaosheng.mp4")
  6. detector = pm.poseDetector()
  7. count = 0
  8. dir = 0
  9. pTime = 0
  10. success=True
  11. point_sd=0
  12. while success:
  13.   success, img = cap.read()
  14.   if success:
  15.     img = cv2.resize(img, (640, 480))
  16.     img = detector.findPose(img, False)
  17.     lmList = detector.findPosition(img, False)
  18.    
  19.     if len(lmList) != 0:
  20.         
  21.         point = detector.midpoint(img, 24, 23)
  22.         if point_sd==0:
  23.             point_sd=point
  24.             print(point_sd["y"])
  25.         # 计算个数
  26.         print(point["y"])
  27.         if point["y"]> point_sd["y"]+15:
  28.          
  29.             if dir == 0:
  30.                 count += 0.5
  31.                 dir = 1
  32.         if point["y"]<point_sd["y"]+5:
  33.         
  34.             if dir == 1:
  35.                 count += 0.5
  36.                 dir = 0
  37.         #print(count)
  38.         cv2.putText(img, str(int(count)), (45, 460), cv2.FONT_HERSHEY_PLAIN, 7,(255, 0, 0), 8)
  39.     cTime = time.time()
  40.     fps = 1 / (cTime - pTime)
  41.     pTime = cTime
  42.     cv2.putText(img, str(int(fps)), (50, 100), cv2.FONT_HERSHEY_PLAIN, 5,(255, 0, 0), 5)
  43.     cv2.imshow("Image", img)
  44.     cv2.waitKey(1)
复制代码

【PoseModule.py】

上面程序用到的“PoseModule.py”文件中,在”poseDetector“类中增加了“midpoint”函数,用于求两点的中点坐标。
  1. import math
  2. import mediapipe as mp
  3. import cv2
  4. class poseDetector():
  5.     def __init__(self, mode=False, upBody=False, smooth=True,
  6.                  detectionCon=0.5, trackCon=0.5):
  7.         self.mode = mode
  8.         self.upBody = upBody
  9.         self.smooth = smooth
  10.         self.detectionCon = detectionCon
  11.         self.trackCon = trackCon
  12.         self.mpDraw = mp.solutions.drawing_utils
  13.         self.mpPose = mp.solutions.pose
  14.         self.pose = self.mpPose.Pose(self.mode, self.upBody, self.smooth,
  15.                                      self.detectionCon, self.trackCon)
  16.     def findPose(self, img, draw=True):
  17.         imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  18.         self.results = self.pose.process(imgRGB)
  19.         if self.results.pose_landmarks:
  20.             if draw:
  21.                 self.mpDraw.draw_landmarks(img, self.results.pose_landmarks,
  22.                                            self.mpPose.POSE_CONNECTIONS)
  23.         return img
  24.     def findPosition(self, img, draw=True):
  25.         self.lmList = []
  26.         if self.results.pose_landmarks:
  27.             for id, lm in enumerate(self.results.pose_landmarks.landmark):
  28.                 h, w, c = img.shape
  29.                 # print(id, lm)
  30.                 cx, cy = int(lm.x * w), int(lm.y * h)
  31.                 self.lmList.append([id, cx, cy])
  32.                 if draw:
  33.                     cv2.circle(img, (cx, cy), 5, (255, 0, 0), cv2.FILLED)
  34.         return self.lmList
  35.     def midpoint(self,img,p1,p2,draw=True):
  36.         x1, y1 = self.lmList[p1][1:]
  37.         x2, y2 = self.lmList[p2][1:]
  38.         x3=int((x1+x2)/2)
  39.         y3=int((y1+y2)/2)
  40.         if draw:
  41.          cv2.circle(img, (x3, y3), 10, (0, 0, 255), cv2.FILLED)
  42.          cv2.circle(img, (x3, y3), 15, (0, 0, 255), 2)
  43.         point={"x":x3,"y":y3}
  44.         return point
  45.     def findAngle(self, img, p1, p2, p3, draw=True):
  46.         # Get the landmarks
  47.         x1, y1 = self.lmList[p1][1:]
  48.         x2, y2 = self.lmList[p2][1:]
  49.         x3, y3 = self.lmList[p3][1:]
  50.         # Calculate the Angle
  51.         angle = math.degrees(math.atan2(y3 - y2, x3 - x2) -
  52.                              math.atan2(y1 - y2, x1 - x2))
  53.         if angle < 0:
  54.             angle += 360
  55.         # print(angle)
  56.         # Draw
  57.         if draw:
  58.             cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255), 3)
  59.             cv2.line(img, (x3, y3), (x2, y2), (255, 255, 255), 3)
  60.             cv2.circle(img, (x1, y1), 10, (0, 0, 255), cv2.FILLED)
  61.             cv2.circle(img, (x1, y1), 15, (0, 0, 255), 2)
  62.             cv2.circle(img, (x2, y2), 10, (0, 0, 255), cv2.FILLED)
  63.             cv2.circle(img, (x2, y2), 15, (0, 0, 255), 2)
  64.             cv2.circle(img, (x3, y3), 10, (0, 0, 255), cv2.FILLED)
  65.             cv2.circle(img, (x3, y3), 15, (0, 0, 255), 2)
  66.             cv2.putText(img, str(int(angle)), (x2 - 50, y2 + 50),
  67.                         cv2.FONT_HERSHEY_PLAIN, 2, (0, 0, 255), 2)
  68.         return angle
复制代码

【测试网络视频】

【存在的问题】
测试结果令人比较满意,但这里存在这样两个问题:1、标准点point_sd这个坐标是以视频开始第一帧画面是站在原地未起跳为前提。
2、标准点纵坐标的判定区间(point_sd["y"]+5与 point_sd["y"]+15)是根据运行后的数据人为分析出来的,只对这一段视频有效,不具有通用性。
【解决问题思路】
1、在正式跳绳计数前,先试跳,通过数据分析出标准点、判定区间(防止数据在判定点抖动,出现错误计数)。在上个程序中判定点为:point_sd["y"]+10。
2、以手势控制屏幕上的虚拟按钮来分析初始化数据,并启动跳绳计数及终止计数。
【解决问题步骤】
QQ截图20211201152932.png

第一步:实现手势控制屏幕按钮。
程序中使用了计时器,以防止连续触发问题。
  1. import cv2
  2. import numpy as np
  3. import time
  4. import os
  5. import HandTrackingModule as htm
  6. #######################
  7. brushThickness = 25
  8. eraserThickness = 100
  9. ########################
  10. drawColor = (255, 0, 255)
  11. cap = cv2.VideoCapture(0)
  12. cap.set(3, 640)
  13. cap.set(4, 480)
  14. detector = htm.handDetector(detectionCon=0.65,maxHands=1)
  15. imgCanvas = np.zeros((480, 640, 3), np.uint8)
  16. rect=[(20, 20), (120, 120)]
  17. font = cv2.FONT_HERSHEY_SIMPLEX
  18. cv2.rectangle(imgCanvas, rect[0], rect[1],(0, 255, 0), 2)
  19. cv2.putText(imgCanvas, "SET", (45,85), font, 1, drawColor, 2)
  20. bs=0
  21. bs2=0
  22. while True:
  23. # 1. Import image
  24. success, img = cap.read()
  25. if success:
  26.   img = cv2.flip(img, 1)
  27. # 2. Find Hand Landmarks
  28.   img = detector.findHands(img)
  29.   lmList = detector.findPosition(img, draw=False)
  30.   
  31.   if len(lmList) !=0:
  32.   
  33. # tip of index and middle fingers
  34.    x1, y1 = lmList[8][1:]
  35.    x2, y2 = lmList[12][1:]
  36. # 3. Check which fingers are up
  37.    fingers = detector.fingersUp()
  38. # print(fingers)
  39. # 5.  Index finger is up
  40.    if fingers[1] and fingers[2] == False:
  41.     cv2.circle(img, (x1, y1), 15, drawColor, cv2.FILLED)
  42.     if bs2==1:
  43.       if time.time()-time_start>3:
  44.          bs2=0
  45.     else:      
  46.      if x1>rect[0][0] and x1<rect[1][0] and y1>rect[0][1] and y1<rect[1][1]:
  47.       if bs==0:
  48.        print("OK")
  49.        imgCanvas = np.zeros((480, 640, 3), np.uint8)
  50.        cv2.rectangle(imgCanvas, rect[0], rect[1],(0, 255, 0), 2)
  51.        cv2.putText(imgCanvas, "STOP", (30,85), font, 1, drawColor, 2)
  52.        bs=1
  53.        bs2=1
  54.        time_start=time.time()
  55.       else:
  56.        imgCanvas = np.zeros((480, 640, 3), np.uint8)
  57.    
  58.   imgGray = cv2.cvtColor(imgCanvas, cv2.COLOR_BGR2GRAY)
  59.   
  60.   img = cv2.bitwise_or(img,imgCanvas)
  61. # img = cv2.addWeighted(img,0.5,imgCanvas,0.5,0)
  62.   cv2.imshow("Image", img)
  63.   cv2.waitKey(1)
复制代码

上面程序引用的“HandTrackingModule.py”文件。
  1. import cv2
  2. import mediapipe as mp
  3. import time
  4. import math
  5. import numpy as np
  6. class handDetector():
  7.     def __init__(self, mode=False, maxHands=2, detectionCon=0.8, trackCon=0.5):
  8.         self.mode = mode
  9.         self.maxHands = maxHands
  10.         self.detectionCon = detectionCon
  11.         self.trackCon = trackCon
  12.         self.mpHands = mp.solutions.hands
  13.         self.hands = self.mpHands.Hands(self.mode, self.maxHands,
  14.         self.detectionCon, self.trackCon)
  15.         self.mpDraw = mp.solutions.drawing_utils
  16.         self.tipIds = [4, 8, 12, 16, 20]
  17.     def findHands(self, img, draw=True):
  18.         imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  19.         self.results = self.hands.process(imgRGB)
  20.     # print(results.multi_hand_landmarks)
  21.         if self.results.multi_hand_landmarks:
  22.             for handLms in self.results.multi_hand_landmarks:
  23.                 if draw:
  24.                     self.mpDraw.draw_landmarks(img, handLms,
  25.                     self.mpHands.HAND_CONNECTIONS)
  26.         return img
  27.     def findPosition(self, img, handNo=0, draw=True):
  28.         xList = []
  29.         yList = []
  30.         bbox = []
  31.         self.lmList = []
  32.         if self.results.multi_hand_landmarks:
  33.             myHand = self.results.multi_hand_landmarks[handNo]
  34.             for id, lm in enumerate(myHand.landmark):
  35.             # print(id, lm)
  36.                 h, w, c = img.shape
  37.                 cx, cy = int(lm.x * w), int(lm.y * h)
  38.                 xList.append(cx)
  39.                 yList.append(cy)
  40.             # print(id, cx, cy)
  41.                 self.lmList.append([id, cx, cy])
  42.                 if draw:
  43.                     cv2.circle(img, (cx, cy), 5, (255, 0, 255), cv2.FILLED)
  44.             xmin, xmax = min(xList), max(xList)
  45.             ymin, ymax = min(yList), max(yList)
  46.             bbox = xmin, ymin, xmax, ymax
  47.             if draw:
  48.                 cv2.rectangle(img, (xmin - 20, ymin - 20), (xmax + 20, ymax + 20),
  49.         (0, 255, 0), 2)
  50.         return self.lmList
  51.     def fingersUp(self):
  52.         fingers = []
  53.     # Thumb
  54.         if self.lmList[self.tipIds[0]][1] > self.lmList[self.tipIds[0] - 1][1]:
  55.             fingers.append(1)
  56.         else:
  57.             fingers.append(0)
  58.     # Fingers
  59.         for id in range(1, 5):
  60.             if self.lmList[self.tipIds[id]][2] < self.lmList[self.tipIds[id] - 2][2]:
  61.                 fingers.append(1)
  62.             else:
  63.                 fingers.append(0)
  64.         # totalFingers = fingers.count(1)
  65.         return fingers
  66.     def findDistance(self, p1, p2, img, draw=True,r=15, t=3):
  67.         x1, y1 = self.lmList[p1][1:]
  68.         x2, y2 = self.lmList[p2][1:]
  69.         cx, cy = (x1 + x2) // 2, (y1 + y2) // 2
  70.         if draw:
  71.             cv2.line(img, (x1, y1), (x2, y2), (255, 0, 255), t)
  72.             cv2.circle(img, (x1, y1), r, (255, 0, 255), cv2.FILLED)
  73.             cv2.circle(img, (x2, y2), r, (255, 0, 255), cv2.FILLED)
  74.             cv2.circle(img, (cx, cy), r, (0, 0, 255), cv2.FILLED)
  75.             length = math.hypot(x2 - x1, y2 - y1)
  76.         return length, img, [x1, y1, x2, y2, cx, cy]
复制代码
第二步,分析数据,得到判定点纵坐标。思路是,坐标数据是上下波动,将数据中的波峰和波谷分别提取出来计算均值,然后取中值,和差值。中值为判定点,差值用来确定判定区域。波峰和波谷的判定采用的是两边数据与当前数据做差值看差值方向,如果方向相反,即为峰值。但这里就存在,Mediapipe识别准确度的问题,可能在上升或下降的过程中数据不平滑,出现数据波动。可能在分析时,出现误判,采集到错误的峰值。后期可采用滤波算法处理此问题。现在看效果,还不错。
  1. import numpy as np
  2. import time
  3. import cv2
  4. import PoseModule as pm
  5. import math
  6. def max_min(a):
  7. h = []
  8. l = []
  9. for i in range(1, len(a)-1):
  10.     if(a[i-1] < a[i] and a[i+1] < a[i]):
  11.         h.append(a[i])
  12.     elif(a[i-1] > a[i] and a[i+1] > a[i]):
  13.         l.append(a[i])
  14. if(len(h) == 0):
  15.     h.append(max(a))
  16. if(len(l) == 0):
  17.     l.append(min(a[a.index(max(a)):]))
  18. mid=(np.mean(h)+np.mean(l))/2
  19. print(int(mid),int(np.mean(h)-np.mean(l)))
  20. return(int(mid),int(np.mean(h)-np.mean(l)))
  21. cap = cv2.VideoCapture("tiaosheng.mp4")
  22. detector = pm.poseDetector()
  23. count = 0
  24. dir = 0
  25. pTime = 0
  26. success=True
  27. point=[]
  28. while success:
  29.   success, img = cap.read()
  30.   if success:
  31.     img = cv2.resize(img, (640, 480))
  32.     img = detector.findPose(img, False)
  33.     lmList = detector.findPosition(img, False)
  34.    
  35.     if len(lmList) != 0:
  36.         point_tem=detector.midpoint(img, 24, 23)
  37.         point.append(point_tem['y'])
  38.         cv2.putText(img, str(point_tem['y']), (45, 460), cv2.FONT_HERSHEY_PLAIN, 7,(255, 0, 0), 8)
  39.     cTime = time.time()
  40.     fps = 1 / (cTime - pTime)
  41.     pTime = cTime
  42.     cv2.putText(img, str(int(fps)), (50, 100), cv2.FONT_HERSHEY_PLAIN, 5,(255, 0, 0), 5)
  43.     cv2.imshow("Image", img)
  44.     cv2.waitKey(1)
  45. max_min(point)
  46. cap.release()
  47. cv2.destroyAllWindows()
复制代码



最终得到“304 26”为“中值 差值”


【完整程序】

将以上分段程序进行整合,得到完整程序,并进行实地测试。(纯手工敲码)
  1. import cv2
  2. import numpy as np
  3. import time
  4. import os
  5. import HandTrackingModule as htm
  6. import PoseModule as pm
  7. #计算判定点
  8. def max_min(a):
  9. h = []
  10. l = []
  11. for i in range(1, len(a)-1):
  12.     if(a[i-1] < a[i] and a[i+1] < a[i]):
  13.         h.append(a[i])
  14.     elif(a[i-1] > a[i] and a[i+1] > a[i]):
  15.         l.append(a[i])
  16. if(len(h) == 0):
  17.     h.append(max(a))
  18. if(len(l) == 0):
  19.     l.append(min(a[a.index(max(a)):]))
  20. mid=(np.mean(h)+np.mean(l))/2
  21. print(int(mid),int(np.mean(h)-np.mean(l)))
  22. return(int(mid),int(np.mean(h)-np.mean(l)))
  23. #######################
  24. brushThickness = 25
  25. eraserThickness = 100
  26. ########################
  27. drawColor = (255, 0, 255)
  28. cap = cv2.VideoCapture(0)
  29. cap.set(3, 640)
  30. cap.set(4, 480)
  31. detector_hand = htm.handDetector(detectionCon=0.65,maxHands=1)
  32. detector_pose = pm.poseDetector()
  33. imgCanvas = np.zeros((480, 640, 3), np.uint8)
  34. rect=[(20, 20), (120, 120)]
  35. font = cv2.FONT_HERSHEY_SIMPLEX
  36. cv2.rectangle(imgCanvas, rect[0], rect[1],(0, 255, 0), 2)
  37. cv2.putText(imgCanvas, "SET", (45,85), font, 1, drawColor, 2)
  38. bs=0
  39. bs2=0
  40. bs3=0
  41. point=[]
  42. count=0
  43. pTime = 0
  44. dire=0
  45. while True:
  46. success, img = cap.read()
  47. if success:
  48.       img = cv2.flip(img, 1)
  49.       if bs==1 and bs2==0:
  50.        if bs3==1:
  51.          if time.time()-time_start<4:
  52.           cv2.putText(img, str(3-int(time.time()-time_start)), (300, 240), cv2.FONT_HERSHEY_PLAIN, 10,(255, 255, 0), 5)
  53.          else:
  54.           bs3=0
  55.           time_start=time.time()
  56.        else:
  57.           if time.time()-time_start<11:
  58.             img = detector_pose.findPose(img, False)
  59.             lmList = detector_pose.findPosition(img, False)
  60.    
  61.             if len(lmList) != 0:
  62.                 point_tem=detector_pose.midpoint(img, 24, 23)
  63.                 point.append(point_tem['y'])
  64.                 cv2.putText(img, str(point_tem['y']), (45, 460), cv2.FONT_HERSHEY_PLAIN, 7,(255, 0, 0), 8)
  65.             cv2.putText(img, str(10-int(time.time()-time_start)), (500, 460), cv2.FONT_HERSHEY_PLAIN, 10,(255, 255, 0), 5)
  66.           else:
  67.               point_sd,l=max_min(point)
  68.               bs=2
  69.               cv2.rectangle(imgCanvas, rect[0], rect[1],(0, 255, 0), 2)
  70.               cv2.putText(imgCanvas, "START", (30,85), font, 1, drawColor, 2)
  71.          
  72.       if bs==3 and bs2==0:  
  73.        if bs3==1:
  74.          if time.time()-time_start<4:
  75.           cv2.putText(img, str(3-int(time.time()-time_start)), (300, 240), cv2.FONT_HERSHEY_PLAIN, 10,(255, 255, 0), 5)
  76.          else:
  77.           bs3=0
  78.           time_start=time.time()
  79.        else:
  80.           img = detector_pose.findPose(img, False)
  81.           lmList = detector_pose.findPosition(img, False)
  82.    
  83.           if len(lmList) != 0:
  84.             point = detector_pose.midpoint(img, 24, 23)
  85.             if point["y"]> point_sd+l/4:
  86.          
  87.               if dire == 0:
  88.                 count += 0.5
  89.                 dire = 1
  90.             if point["y"]<point_sd-l/4:
  91.         
  92.               if dire == 1:
  93.                 count += 0.5
  94.                 dire = 0
  95.       
  96.             cv2.putText(img, str(int(count)), (45, 460), cv2.FONT_HERSHEY_PLAIN, 7,(255, 0, 0), 8)  
  97.       if bs2==1:#等待三秒
  98.          if time.time()-time_start>4:
  99.             bs2=0
  100.             time_start=time.time()
  101.                      
  102.       else:
  103.         #手势操作
  104.         img = detector_hand.findHands(img)
  105.         lmList = detector_hand.findPosition(img, draw=False)
  106.         if len(lmList) !=0:
  107.          x1, y1 = lmList[8][1:]
  108.          x2, y2 = lmList[12][1:]
  109.          fingers = detector_hand.fingersUp()
  110.          #出示食指
  111.          if fingers[1] and fingers[2] == False:
  112.           cv2.circle(img, (x1, y1), 15, drawColor, cv2.FILLED)     
  113.           if x1>rect[0][0] and x1<rect[1][0] and y1>rect[0][1] and y1<rect[1][1]:#食指进入按钮区域
  114.            if bs==0:
  115.             print("OK")
  116.             imgCanvas = np.zeros((480, 640, 3), np.uint8)
  117.             bs=1
  118.             bs2=1
  119.             bs3=1
  120.             time_start=time.time()
  121.            elif bs==1:
  122.             imgCanvas = np.zeros((480, 640, 3), np.uint8)
  123.             bs2=1
  124.             bs3=1
  125.             time_start=time.time()
  126.            elif bs==2:
  127.             imgCanvas = np.zeros((480, 640, 3), np.uint8)
  128.             cv2.rectangle(imgCanvas, rect[0], rect[1],(0, 255, 0), 2)
  129.             cv2.putText(imgCanvas, "STOP", (30,85), font, 1, drawColor, 2)
  130.             bs=3  
  131.             bs2=1
  132.             bs3=1
  133.             time_start=time.time()
  134.            elif bs==3:
  135.             imgCanvas = np.zeros((480, 640, 3), np.uint8)
  136.             cv2.rectangle(imgCanvas, rect[0], rect[1],(0, 255, 0), 2)
  137.             cv2.putText(imgCanvas, "START", (30,85), font, 1, drawColor, 2)
  138.             bs=2  
  139.             bs2=1
  140.             bs3=1
  141.             time_start=time.time()
  142.       cTime = time.time()
  143.       fps = 1 / (cTime - pTime)
  144.       pTime = cTime
  145.       cv2.putText(img, str(int(fps)), (500, 100), cv2.FONT_HERSHEY_PLAIN, 5,(255, 0, 0), 5)
  146.       imgGray = cv2.cvtColor(imgCanvas, cv2.COLOR_BGR2GRAY)
  147.       img = cv2.bitwise_or(img,imgCanvas)
  148.       cv2.imshow("Image", img)
  149.       cv2.waitKey(1)
复制代码

【计数炫灯】
使用Pinpong库,连接Micro:bit,控制LED灯随跳绳次数增加亮灯数。



glwz007  高级技师

发表于 2021-12-2 08:24:15

太高大上了
回复

使用道具 举报

glwz007  高级技师

发表于 2021-12-2 08:29:02

是通过判断腰部那个点上下移动来判断跳绳次数吗?
回复

使用道具 举报

云天  高级技匠
 楼主|

发表于 2021-12-2 11:44:56

glwz007 发表于 2021-12-2 08:29
是通过判断腰部那个点上下移动来判断跳绳次数吗?

利用的就是这个点,一个简单有效的方法
回复

使用道具 举报

EVE  中级技匠

发表于 2021-12-2 15:28:35

这个厉害了!
回复

使用道具 举报

glwz007  高级技师

发表于 2021-12-3 07:41:55

云天 发表于 2021-12-2 11:44
利用的就是这个点,一个简单有效的方法

但是这样的话,如果知道了测量模式,就可以作弊了,主要不停的上下跳动,就可以记为跳绳次数了。
回复

使用道具 举报

云天  高级技匠
 楼主|

发表于 2021-12-3 08:55:15

glwz007 发表于 2021-12-3 07:41
但是这样的话,如果知道了测量模式,就可以作弊了,主要不停的上下跳动,就可以记为跳绳次数了。 ...

如果只是为自己跳绳时,实时显示次数、增加趣味性,这个问题就不是问题了。

还有就是当跳绳速度快到一定程度,这个项目是测不准的。所以这个项目是为普通健身者设计的。
回复

使用道具 举报

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

本版积分规则

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

硬件清单

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

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

mail