wanweiwei07 / wrs Goto Github PK
View Code? Open in Web Editor NEWThe WRS Robot Planning & Control System
License: MIT License
The WRS Robot Planning & Control System
License: MIT License
Line 380 in .CPP file
is mistakenly commented.
wrs/drivers/devices/phoxi/phoxicontrol.cpp
Line 380 in d24d42d
The parameter rgba_list
for function data_adaptor.pandageom_from_points
(
Lines 319 to 320 in 8c91537
NumPy array
. Otherwise, it takes time to adapt a NumPy array to a list. In addition, rgba_list
is finally converted to a NumPy array (Lines 322 to 324 in 8c91537
The code solves the bug:
def delete_cdobj(self, cdobj_info):
"""
:param cdobj_info: an lnk-like object generated by self.add_objinhnd
:param objcm:
:return:
"""
self.all_cdelements.remove(cdobj_info)
cdnp_to_delete = self.np.getChild(cdobj_info['cdprimit_childid'])
self.ctrav.removeCollider(cdnp_to_delete)
for cdlnk in cdobj_info['intolist']:
cdnp = self.np.getChild(cdlnk['cdprimit_childid'])
current_into_cdmask = cdnp.node().getIntoCollideMask()
new_into_cdmask = current_into_cdmask & ~cdnp_to_delete.node().getFromCollideMask()
cdnp.node().setIntoCollideMask(new_into_cdmask)
cdnp_to_delete.removeNode()
In the function robot_sim.robots.yumi.yumi.py
,
line 229, in the sub-function update_oih
, a in
keyword is missing.
def fk(self, component_name, jnt_values):
"""
:param jnt_values: nparray 1x6 or 1x14 depending on component_names
:hnd_name 'lft_arm', 'rgt_arm', 'both_arm'
:param component_name:
:return:
author: weiwei
date: 20201208toyonaka
"""
def update_oih(component_name='rgt_arm'):
# inline function for update objects in hand
if component_name in ['rgt_arm', 'rgt_hnd']:
oih_info_list = self.rgt_oih_infos
elif component_name ['lft_arm', 'lft_hnd']: <- forget a *in*
oih_info_list = self.lft_oih_infos
for obj_info in oih_info_list:
gl_pos, gl_rotmat = self.cvt_loc_tcp_to_gl(component_name, obj_info['rel_pos'], obj_info['rel_rotmat'])
obj_info['gl_pos'] = gl_pos
obj_info['gl_rotmat'] = gl_rotmat
In the function robot_math.py/quaternion_average
, line 339:
anglelist.append([quaternion_from_axangle(quaternion)[0]])
I get the error TypeError: quaternion_from_axangle() missing 1 required positional argument: 'axis'
In old version, the code is:
anglelist.append([quaternion_axangle(quaternion)[0]])
It seems that quaternion_from_axangle
and quaternion_axangle
are two different functions.
I found a case that the yumi robot collided with itself but is_collided
function reports false
. The photo shows the situation.
I thought something wrong happening to self collision detection.
The code is following:
base = wd.World(cam_pos=[2, 0, 1.5], lookat_pos=[0, 0, .2])
robot_s = ym.Yumi() # simulation robot
jnts= np.array([-1.95503661, - 1.50237315, 1.15998945, 1.02486284 , 3.42901897,- 0.30705591,
1.83156597])
robot_s.fk("rgt_arm",jnts)
robot_s.gen_meshmodel().attach_to(base)
robot_s.show_cdprimit()
print(robot_s.is_collided())
base.run()
wrsを更新したところ,utils.pyのmain以下のプログラムが動作しなくなりました.
以下のようなエラー文が出たので,
jaw_width, gl_jaw_center, pos, rotmat = grasp_info
ValueError: too many values to unpack (expected 4)
こちらに変更したほうが,良いと思われます.
jaw_width, gl_jaw_center_pos, gl_jaw_center_rotmat, hnd_pos, hnd_rotmat = grasp_info
また,上記のように変更して実行してみたところ,ハンドの中心位置がずれて表示されました.原因はわからないのですが,grip_atなどの変更が関係しているかもしれません.修正をお願いできないでしょうか.
In function StaticGeometricModel.unshow_localframe
:
def unshow_localframe(self):
if self._localframe is not None:
self._localframe.removeNode() <- this sentence has a bug
self._localframe = None
self._localframe.removeNode()
should be remove
instead of removeNode
万先生
プログラムを作成中にWRSの更新を行ったところ,処理がかなり重たくなって描画のフレームレートが落ちてしまいました.
git上でbisectを行って原因を探したところ,12/16に行われた"collision detection update"というcommitが原因のようです.
ご都合がよろしい時に,一回見てもらえれば幸いです.
念の為,今回作成していたプログラムのコードを添付させて頂きます.
WまたはSキーでxarmの台車部分がシミュレータ上で回転動作を行うものです.
よろしくお願い致します.
import math
import time
import keyboard
import numpy as np
import threading
import basis.robot_math as rm
import visualization.panda.world as wd
import modeling.geometric_model as gm
import robot_sim.robots.xarm7_shuidi_mobile.xarm7_shuidi_mobile as rbs
# import robot_con.xarm_shuidi.xarm_shuidi_client as rbx
from direct.task.TaskManagerGlobal import taskMgr
base = wd.World(cam_pos=[3, 1, 1.5], lookat_pos=[0, 0, 0.5])
rbt_s = rbs.XArm7YunjiMobile()
# rbt_s.gen_meshmodel().attach_to(base)
# base.run()
# rbt_x.agv_move(agv_linear_speed=-.1, agv_angular_speed=.1, time_intervals=5)
onscreen = []
onscreen_agv = []
operation_count = 0
pre_pos = np.array([0, 0, math.radians(0)])
pre_angle = math.radians(0)
def agv_move(task):
global onscreen, onscreen_agv, operation_count, pre_pos, pre_angle
# agv_linear_speed = .002
agv_linear_speed = 0
agv_angular_speed = .5
arm_linear_speed = .03
arm_angular_speed = .1
for item in onscreen:
item.detach()
onscreen.clear()
for item in onscreen_agv:
item.detach()
onscreen_agv.clear()
agv_pos = rbt_s.get_jnt_values("agv")
agv_loc_rotmat = rm.rotmat_from_axangle(axis=[0, 0, 1], angle=-1*agv_pos[2])
print(agv_pos)
agv_direction = np.dot(np.array([1, 0, 0]), agv_loc_rotmat)
# print(agv_direction)
# onscreen_agv.append(gm.gen_dasharrow(spos=np.array([agv_pos[0], agv_pos[1], 0]), epos=np.array([agv_pos[0], agv_pos[1], 0]))+np.array(agv_direction))
onscreen_agv.append(gm.gen_arrow(epos=np.array([agv_direction[0], agv_direction[1], agv_direction[2]]), rgba=[1,0,1,1]))
# onscreen_agv.append(gm.gen_frame(pos=np.array([agv_pos[0], agv_pos[1], 0]), rotmat=agv_loc_rotmat, length=3))
onscreen_agv[-1].attach_to(base)
# print(agv_pos)
pressed_keys = {'w': keyboard.is_pressed('w'),
'a': keyboard.is_pressed('a'),
's': keyboard.is_pressed('s'),
'd': keyboard.is_pressed('d'),
'r': keyboard.is_pressed('r'), # x+ global
't': keyboard.is_pressed('t'), # x- global
'f': keyboard.is_pressed('f'), # y+ global
'g': keyboard.is_pressed('g'), # y- global
'v': keyboard.is_pressed('v'), # z+ global
'b': keyboard.is_pressed('b'), # z- gglobal
'y': keyboard.is_pressed('y'), # r+ global
'u': keyboard.is_pressed('u'), # r- global
'h': keyboard.is_pressed('h'), # p+ global
'j': keyboard.is_pressed('j'), # p- global
'n': keyboard.is_pressed('n'), # yaw+ global
'm': keyboard.is_pressed('m'), # yaw- global
'o': keyboard.is_pressed('o'), # gripper open
'p': keyboard.is_pressed('p')} # gripper close
values_list = list(pressed_keys.values())
if pressed_keys["w"] and pressed_keys["a"]:
print("Invalid Operation!!")
# rbt_x.agv_move(linear_speed=agv_linear_speed, angular_speed=agv_angular_speed, time_interval=.5)
elif pressed_keys["w"] and pressed_keys["d"]:
print("Invalid Operation!!")
# rbt_x.agv_move(linear_speed=agv_linear_speed, angular_speed=-agv_angular_speed, time_interval=.5)
elif pressed_keys["s"] and pressed_keys["a"]:
print("Invalid Operation!!")
# rbt_x.agv_move(linear_speed=-agv_linear_speed, angular_speed=-agv_angular_speed, time_interval=.5)
elif pressed_keys["s"] and pressed_keys["d"]:
print("Invalid Operation!!")
# rbt_x.agv_move(linear_speed=-agv_linear_speed, angular_speed=agv_angular_speed, time_interval=.5)
elif pressed_keys["w"] and sum(values_list) == 1: # if key 'q' is pressed
rbt_s.fk(component_name='agv', jnt_values=np.array(pre_pos + [agv_linear_speed, 0, math.radians(0.5)]))
pre_pos = np.array(pre_pos + [agv_linear_speed, 0, math.radians(0.5)])
# rbt_x.agv_move(linear_speed=agv_linear_speed, angular_speed=0, time_interval=.5)
elif pressed_keys["s"] and sum(values_list) == 1: # if key 'q' is pressed
rbt_s.fk(component_name='agv', jnt_values=np.array(pre_pos + [-1*agv_linear_speed, 0, math.radians(-0.5)]))
pre_pos = np.array(pre_pos + [-1*agv_linear_speed, 0, math.radians(-0.5)])
# rbt_x.agv_move(linear_speed=-agv_linear_speed, angular_speed=0, time_interval=.5)
elif pressed_keys["a"] and sum(values_list) == 1: # if key 'q' is pressed
rbt_s.fk(component_name='agv', jnt_values=np.array(pre_pos + [0, agv_linear_speed, math.radians(0)]))
pre_pos = np.array(pre_pos + [0, agv_linear_speed, math.radians(0)])
# rbt_x.agv_move(linear_speed=0, angular_speed=agv_angular_speed, time_interval=.5)
elif pressed_keys["d"] and sum(values_list) == 1: # if key 'q' is pressed
rbt_s.fk(component_name='agv', jnt_values=np.array(pre_pos + [0, -1*agv_linear_speed, math.radians(0)]))
pre_pos = np.array(pre_pos + [0, -1*agv_linear_speed, math.radians(0)])
# rbt_x.agv_move(linear_speed=0, angular_speed=-agv_angular_speed, time_interval=.5)
elif pressed_keys["o"] and sum(values_list) == 1: # if key 'q' is pressed
print("Invalid Operation!!")
# rbt_x.arm_jaw_to(jawwidth=100)
elif pressed_keys["p"] and sum(values_list) == 1: # if key 'q' is pressed
print("Invalid Operation!!")
# rbt_x.arm_jaw_to(jawwidth=0)
elif any(pressed_keys[item] for item in ['r', 't', 'f', 'g', 'v', 'b', 'y', 'u', 'h', 'j', 'n', 'm']) and\
sum(values_list) == 1: # global
tic = time.time()
current_jnt_values = rbt_s.get_jnt_values()
current_arm_tcp_pos, current_arm_tcp_rotmat = rbt_s.get_gl_tcp()
rel_pos = np.zeros(3)
rel_rotmat = np.eye(3)
if pressed_keys['r']:
rel_pos = np.array([arm_linear_speed * .5, 0, 0])
elif pressed_keys['t']:
rel_pos = np.array([-arm_linear_speed * .5, 0, 0])
elif pressed_keys['f']:
rel_pos = np.array([0, arm_linear_speed * .5, 0])
elif pressed_keys['g']:
rel_pos = np.array([0, -arm_linear_speed * .5, 0])
elif pressed_keys['v']:
rel_pos = np.array([0, 0, arm_linear_speed * .5])
elif pressed_keys['b']:
rel_pos = np.array([0, 0, -arm_linear_speed * .5])
elif pressed_keys['y']:
rel_rotmat = rm.rotmat_from_euler(arm_angular_speed*.5, 0, 0)
elif pressed_keys['u']:
rel_rotmat = rm.rotmat_from_euler(-arm_angular_speed*.5, 0, 0)
elif pressed_keys['h']:
rel_rotmat = rm.rotmat_from_euler(0, arm_angular_speed*.5, 0)
elif pressed_keys['j']:
rel_rotmat = rm.rotmat_from_euler(0, -arm_angular_speed * .5, 0)
elif pressed_keys['n']:
rel_rotmat = rm.rotmat_from_euler(0, 0, arm_angular_speed*.5)
elif pressed_keys['m']:
rel_rotmat = rm.rotmat_from_euler(0, 0, -arm_angular_speed*.5)
new_arm_tcp_pos = current_arm_tcp_pos+rel_pos
new_arm_tcp_rotmat = rel_rotmat.dot(current_arm_tcp_rotmat)
last_jnt_values = rbt_s.get_jnt_values()
new_jnt_values = rbt_s.ik(tgt_pos=new_arm_tcp_pos, tgt_rotmat=new_arm_tcp_rotmat, seed_jnt_values=current_jnt_values)
# if new_jnt_values is None:
# continue
rbt_s.fk(jnt_values=new_jnt_values)
toc = time.time()
start_frame_id = math.ceil((toc - tic) / .01)
# rbt_x.arm_move_jspace_path([last_jnt_values, new_jnt_values], time_interval=.1, start_frame_id=start_frame_id)
onscreen.append(rbt_s.gen_meshmodel())
onscreen[-1].attach_to(base)
# print(pre_pos)
# print(onscreen)
operation_count += 1
# time.sleep(1/30)
return task.cont
if __name__ == '__main__':
gm.gen_frame(length=3, thickness=0.01).attach_to(base)
# threading.Thread(target=agv_move, args=()).start()
taskMgr.doMethodLater(1/60, agv_move, "agv_move",
extraArgs=None,
appendTask=True)
base.run()
Return value error of forward Kinematics
wrs/robot_sim/kinematics/jlchain.py
Lines 156 to 160 in 2848f95
tcp_gl_rotmat, tcp_gl_rotmat, j_mat
and tcp_gl_rotmat, tcp_gl_rotmat
should be tcp_gl_pos, tcp_gl_rotmat, j_mat
and tcp_gl_pos, tcp_gl_rotmat
When I execute multiple pick and place motions, only the first pick and place motion can be executed. From the second pick and place motion, the holding object sub motion of the pick-and-place motion always be in a collision. If I use the old version of collision_check.py
, it can execute successfully. I guess some problem may happen in collision_check.py
.
robot_math
のrotmat_from_quaternion
の結果が同次変換行列になっていました.
The function add_cdobj
in robot_sim/_kinematics/collision_checker.py
directly use the reference of the objcm
. During fk
, the position of the objcm
will be updated. I am not sure this is a feature or a potential bug.
In module manipulation.pick_place_planner.py
, the instance method self.gen_holding_moveto
in function gen_pick_and_place_motion
works incorrectly when there is only one mid point.
piecewisepoly
のinterpolate_by_max_spdacc
で補間する際にpathに重複があるとエラーになるため,重複を除去するコードです.
def interpolate_by_max_spdacc(self, path, control_frequency=.005, max_jnts_spd=None, max_jnts_acc=None):
...
new_path = []
for i, pose in enumerate(path):
if i < len(path)-1 and np.allclose(pose, path[i+1]):
continue
new_path.append(pose)
path = new_path
...
'RobotiqHE' object has no attribute 'jaw_center_loc_pos'のエラーが出ました
ur3e_duarの77行目jaw_center_loc_posがないと思われます
NameError: name 'rm' is not defined
このようなエラーが出るのですが,以下が足りないと思います.
import basis.robot_math as rm
In robot_con/nxt/nxtrobot_server.py, function closeHandToolRgt
line 121,
self._robot.gripper_r_close()
should be
self._robot._hands.gripper_r_close()
Same problem in function closeHandToolLft
line 129, openHandToolRgt
line 137, openHandToolLft
line 145
When using multiple clients, the grpc client without a "namespace" will result a name conflict problems. For example, message
"Empty" is defined in both A.proto
and B.proto
. If I use two clients using the A.proto
and B.proto
respectively. The name conflict error will occur since the message "Empty" is defined in both A.proto
and B.proto
.
I thought adding a "namespace" can solve the problem.
Here is a link. It may helpful.
https://developers.google.com/protocol-buffers/docs/proto3#packages
color
, there is a bug that makes the length ofcontiguous buffer object expected
bug. (change numpy array into continous buffer: ascontiguousarray
)In gen_pick_and_place_motion
function, the argument obstacle_list
of gen_holding_moveto
used to generate middle trajectory is given as empty list.
When calculating the jaw_center_pos_rel
, it uses the global coordinate of the lft_outer
. It should use the local coordinate. Otherwise, when the lft_outer
changes, the following code is incorrect.
ロボットの関節可動範囲を取得しようとrobot_sのget_jnt_ranges(component_name)
を呼び出したところエラーが出ました.
確認したところ,manipulator_interface.py
のget_jnt_ranges(self)
でself.jlc.get_jnt_ranges()
がリターンされていたのですが,JLChainにそのようなメソッドは存在せず,代わりにjnt_ranges
というプロパティがありました.
そのため,self.jlc.get_jnt_ranges()
ではなくself.jlc.jnt_ranges
をリターンすべきかと思います.
enable_cc
function should not be public
nbitmask
should be deducted at the end of delete_cdobj()
function,
def delete_cdobj(self, cdobj_info):
"""
:param cdobj_info: an lnk-like object generated by self.add_objinhnd
:param objcm:
:return:
"""
self.all_cdelements.remove(cdobj_info)
cdnp_to_delete = self.np.getChild(cdobj_info['cdprimit_childid'])
self.ctrav.removeCollider(cdnp_to_delete)
for cdlnk in cdobj_info['intolist']:
cdnp = self.np.getChild(cdlnk['cdprimit_childid'])
current_into_cdmask = cdnp.node().getIntoCollideMask()
new_into_cdmask = current_into_cdmask & ~cdnp_to_delete.node().getFromCollideMask()
cdnp.node().setIntoCollideMask(new_into_cdmask)
cdnp_to_delete.detachNode()
self.nbitmask -= 1
robobsim/robots/ur3_dual内のur3_dual.pyと、robotcon/ur/ur3_dual_x.pyについてです。
ur3_dual.py内の関数fkでは、398行目と399行目で
'lft_arm' -> jnt_values[0:6]
'rgt_arm' -> jnt_values[6:12]
のように左右の関節角度を取り出していますが、
ur3_dual_x.py内の関数move_jntsでは、65行目と66行目で
self._rgt_arm_hnd.move_jnts -> jnt_values[0:6]
self._rgt_arm_hnd.move_jnts -> jnt_values[6:12]
のように先程と左右が逆の取り出し方をしており、シミュレーションと実機を同時に使いたい場合に少し不便に感じます。
どちらかに統一していただけると嬉しいです。
get_gl_tcpの引数であるmanipulator_nameにはどのような値を入れれば良いのでしょうか?
"arm","agv_arm"などは試したのですが,わかりませんでした.
robot_math中の関数rotmat_from_eulerの引数についてです.
角度の単位を度(degree)と表記されていますが,この関数の元となる_euler_matrixの処理でmath.sin()などに渡すことを考えると角度の単位はラジアン(radian)が正しいのではないかと思います.
ご確認よろしくお願い致します.
Line 321 in 2a165ac
It should be np.ndarray
instead of np.array
After using the cvt_gl_to_loc_tcp
to convert to local pose and using the cvt_loc_tcp_to_gl
to convert to the global pose, the global pose after converting is different from the global pose inputted.
rrt 应该检测初始角度是否超出各个轴运动范围
pick_place_planner.py内のインポート部分において、下側の__main__内でしか使われていないモジュールが一番上側でインポートされていたり、PickPlacePlannerでも__main__内でも用いられているモジュールについて__main__内でインポートされていないことから、__main__内のコードを流用しようとしてもすぐに用いれなかったため、以下のように変更を加えました。
こちらの方が可読性が高く、流用時のエラーも無くなりますので、お手数ですが変更についてご検討いただければと思います。
--git a/manipulation/pick_place_planner.py b/manipulation/pick_place_planner.py
index 5ba6e70..a6777cf 100644
--- a/manipulation/pick_place_planner.py
+++ b/manipulation/pick_place_planner.py
@@ -4,11 +4,11 @@ import copy
import pickle
import numpy as np
import basis.data_adapter as da
-import modeling.collision_model as cm
import motion.optimization_based.incremental_nik as inik
import motion.probabilistic.rrt_connect as rrtc
import manipulation.approach_depart_planner as adp
+import basis.robot_math as rm
class PickPlacePlanner(adp.ADPlanner):
@@ -584,11 +584,13 @@ class PickPlacePlanner(adp.ADPlanner):
if __name__ == '__main__':
import time
- import basis.robot_math as rm
+ import modeling.collision_model as cm
import robot_sim.robots.yumi.yumi as ym
import visualization.panda.world as wd
import modeling.geometric_model as gm
import grasping.annotation.utils as gutil
+ import numpy as np
+ import basis.robot_math as rm
base = wd.World(cam_pos=[2, 0, 1.5], lookat_pos=[0, 0, .2])
gm.gen_frame().attach_to(base)
robobsim/robots/ur3_dual内のur3_dual.pyと、robotcon/ur/ur3_dual_x.pyについてです。
ur3_dual.py内の関数fkにjoint_valuesを渡す時は、joint_valuesの各値はradianに変換されたものを代入する必要がありますが、
ur3_dual_x.py内の関数move_jntsについては、150行目でjoint_valuesの各値がradianに変換されるので、move_jntsにjoint_valuesを渡す際はdegreeで代入する必要があります。
そのため、シミュレーションと実機で引数の渡し方が違い、少し不便に感じます。
可能であれば、どちらかに統一していただけると嬉しいです。
When finding the common_grasp_id_list in function manipulation.pick_place_planner.gen_pick_and_place_motion
, the case that common_grasp_id_list is empty is not considered.
def gen_pick_and_place_motion(self,
manipulator_name,
hand_name,
objcm,
grasp_info_list,
start_conf,
goal_homomat_list):
"""
:param manipulator_name:
:param hand_name:
:param grasp_info_list: a list like [[jaw_width, gl_jaw_center, pos, rotmat], ...]
:param start_conf:
:param goal_homomat_list: a list of tcp goals like [homomat0, homomat1, ...]
:return:
author: weiwei
date: 20191122, 20200105
"""
common_grasp_id_list, _ = self.find_common_graspids(manipulator_name,
hand_name,
grasp_info_list,
goal_homomat_list)
grasp_info = grasp_info_list[common_grasp_id_list[0]] <- **this sentence has bug when the common_grasp_id_list is empty**
gripperのプログラムがなくなったので追加をしていただけないでしょうか
Line 119: if len(conf_list) == 0
should be if conf_list is None
Line 121: should return None, None
robot_sim.robots.ur3_dual.ur3_dual.py
内の gen_meshmodel
関数の定義で呼び出されている self.lft_hnd.gen_meshmodel
とself.rgt_hnd.gen_meshmodel
の引数が間違っているようです.原因はrobot_sim.end_effectors.grippers.robotiq85.robotiq85.py
のgen_meshmodel
の引数が変更されたことのようで,以下のように引数tcp_loc_pos
とtcp_loc_rotmat
を削除するとうまく動きました.
self.lft_hnd.gen_meshmodel(toggle_tcpcs=False,
toggle_jntscs=toggle_jntscs,
rgba=rgba).attach_to(meshmodel)
self.rgt_hnd.gen_meshmodel(toggle_tcpcs=False,
toggle_jntscs=toggle_jntscs,
rgba=rgba).attach_to(meshmodel)
File "/home/kento/PycharmProjects/wrs/robotsim/_kinematics/collisionchecker.py", line 156, in is_collided
obstacle.objpdnp.reparentTo(base.render)
NameError: name 'base' is not defined
上記のようなエラーが出ます.
geometricmodel.pyの中にある.set_rotmatが反映されないです.
万先生
xarmのリモートコントロールのプログラム中で,スペルミスが存在しますのでご都合がよろしい時に修正をお願い致します.
arm_get_jnt_vlaues --> arm_get_jnt_values
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.