あいまいまいんの生物学

あいまいまいんの生物学

生物学が好き。勉強したり遊んだり。

日頃感じたこと思ったこと、出来事など

勉強して面白かった話

授業で使えそうな生物学の知識・雑談・小ネタ

などなどを紹介していきたいと思います

コメント大歓迎!気軽にコメントして下さい

ゲームサイトはこちら

まいばいお22 ストレスによる白髪

この前「髪の毛がストレスのせいで白くなる仕組みを解明!」みたいな記事を見たんですが、

なんでこうしたんだろとかなんでこうなったんだろとか他にどんなことしたんだろとか色々気になって、元の論文をあたってみました。

そしたら中々面白かったので紹介したいと思います。

 

natureの論文紹介ページはこれ↓

www.nature.com

 

実際のPDFは下から読めるはず↓

www.nature.com

 

 

ストレスと白髪はそもそも色々言われていた

取り敢えず大前提として、なんかストレスって髪の毛が白くなるのと関係ありそうだよねっていうのは皆経験則で知っていたと思います。

「マリーアントワネットの髪の毛が処刑への恐怖で一夜にして白くなった」という伝説もあるし。

自分も「若白髪は苦労や努力の象徴だ」みたいなことを聞いたことがあります。

でも本当かどうかわからないし、どうしてストレスが白髪を引き起こすのかそのメカニズム知りたくない?ってことで、今回の論文の出番です。

 

どのストレスが白髪を引き起こす?

まず研究者たちは黒毛のマウス(C57BL/6J)を用意し、どの種類のストレスが髪の毛の白化を引き起こすのかを調べました。

物理的ストレスとか精神的ストレスとか色々あるからね…

今回は拘束によるストレス(restraint stress)、

慢性的で予測不可能なストレス(chronic unpredictable stress)

そして侵害刺激受容誘発刺激によるストレス(カプサイシン類自体:RTXの注射によるストレス)の3種類が検討されています。

 

というか、そもそも拘束ストレスってなんだよって思って調べてみたら、すごいね。

今回は違うみたいだけど、もっと調べたらコニカルチューブに閉じ込めるのとかもあるらしい。

 

慢性的で予測不可能なストレス(chronic unpredictable stress)っていうのも謎なのでやった方法を読んでみると、

ストレス因子の2つを毎日適用した。

ストレス因子はケージ傾斜、隔離、湿った寝具、急速な明暗の変化、夜間の発光、拘束、空のケージおよびケージの3回の交換を含んだ。

すべてのストレッサーを連続週にランダムに反復した。

だそうです。地味な嫌がらせをずっとやられる感じか。私なら耐えられん。

 

とにかくまぁこういうストレス3種類やってみて、

結果、すべてのストレス刺激において無色素の白髪が生じるのが確認されました。

しかし、拘束ストレスと慢性的予測不可能ストレスでは白髪が3~5回の毛周期後に見られたのに対し、

RTXによるストレスは注射後次にくる毛周期で白髪が見られるという急速で強い応答を引き起こしたとのこと。

 

RTXは通常、侵害刺激受容性感覚ニューロンの活性化によって侵害刺激の感覚を引き起こします。

マウスの痛覚はブプレノルフィン(bup)という薬剤の投与によってオピオイド受容体をブロックすることで妨げることが可能だと知られているので、研究者はRTXをbupと共に注射してみました。

この場合には白髪が発生しないことが分かりました。

ここからRTXが白髪を引き起こすのは痛みのストレスを通じてであることが確かめられました。

 

でまぁ、こういうことした結果RTXが一番強いからこれを使って今後は実験していこうという感じになったみたいです。

でもきっと楽だったっていうのもあるんだろうな、と個人的に思う…

それにしてもトウガラシのカプサイシンを注射で突っ込まれると思うとゾクゾクしますね。改めて自分は嫌だ。

あとストレスって形が違っても心でも身体でもストレスなんだな~と改めて思った。完全に一緒ではないけど白髪を引き起こすという点では一緒だね。


ストレスは毛包のどの細胞をどう変えて白髪にするか?

次に研究は白髪という現象に迫っていきます。

そもそも毛髪は、毛包という構造で作られます。

毛包のバルジと呼ばれる部分にはメラノサイト幹細胞(MeSC)があり、この幹細胞が移動&分化することでメラノサイトという細胞を作ります。

メラノサイトは毛包の成長期に、光を吸収するメラニン色素を様々な組み合わせで産生することで毛髪に色を与えます。

成長期が終わると毛包は、変性(退行期)、休止(休止期)という段階に進み、この際メラノサイトは死んでしまいます。

しかし、バルジに豊富なMeSCが残っていれば、次の成長期のメラノサイトも供給され、有色の毛髪が生え続けます。

 

この仕組みを考慮すると、「白髪が発生する」には色んな原因の可能性が考えられることになります。

メラノサイトメラニン色素を作らないとか、MeSCがメラノサイトに分化しないとか、MeSCが減ってしまうとか…。

そこで、どの可能性が正しいのかを検証するために、MeSCもメラノサイトも両方存在し、かつ別の場所にそれぞれが存在している「成長期」に狙いを定め、RTXの注射を行いました。

すると、メラノサイトの数は不変でしたが、皮膚全体のMeSCの数は著しく減少し、5日間のうちにバルジにあったMeSCは完全になくなってしまいました。

メラノサイトは引き続き色素を作り続け、その間毛髪は有色のまま作られましたが、退行期と休止期に突入しメラノサイトが死んでしまうと、新たなメラノサイトが供給されないせいで次の毛周期では白髪が生えてくるようになりました。

 

ストレス→?→MeSC減少→白髪

研究はどんどん細かいところを埋めていくように進んでいきます。

次は「ストレスがなぜMeSCの減少を引き起こすか」、その間を埋めていくのです。

そもそもストレスはどんな変化を身体に引き起こすかというと、現時点で幾つか知られていて

などが分かっていました。どれが白髪に繋がる経路か見極めたいですよね。わかる。たしかめよう!

 

まずは免疫です。

免疫を介して白髪を引き起こすか確かめるため、T細胞とB細胞を欠損している変異体マウスにRTXを注射しました。

すると、このマウスで白髪は引き起こされました。

つまり…ストレスによる白髪に免疫は関係ない。


次に、ストレスで起こるコルチゾール血中濃度上昇を検討しました。

MeSCにあるコルチゾール受容体を遺伝子操作で欠損させ、そのうえでRTXを注射しました。

するとマウスで白髪は引き起こされました。

加えて、コルチゾールを餌に混ぜてストレスなしにコルチゾール濃度を上げた場合も、やはり毛髪の白化は引き起こされませんでした。

コルチゾールもストレスによる白髪の原因ではなさそう…。


ではノルアドレナリンです。

MeSCの細胞膜上にあるADRB2がノルアドレナリン受容体なので、遺伝子操作によってマウスのMeSCからADRB2を取り除きRTXを注射しました。

すると、白髪は発生しませんでした。

ほかにも、

  • MeSCはADRB2を持ったままで、MeSCと同じ場所にいる他の細胞のADRB2を取り除いた場合はRTX注射で白髪は発生する
  • ストレスがない状況下でノルアドレナリンだけを局所的に注射すると、野生型マウスでは白髪が発生するが、MeSCにADRB2がないマウスでは白髪が発生しない

などの知見から、MeSCの減少の直接の原因はノルアドレナリンだと分かりました。

 

ストレス→?→ノルアドレナリン→MeSC減少→白髪

でもこのノルアドレナリンはどこから来たものなんだ?

ということで次はストレスによってどこの供給源から出るノルアドレナリンが白髪の原因になるかの調査です。

研究者はまず副腎のアドレナリンが供給源だと考えました。副腎は有名。

確かめるのも単純明快。副腎を除いたマウスにRTXを注射しました。

結果、白髪は発生したため、副腎は白髪の発生に必要ではないと分かります。

 

他の候補は交感神経の神経末端から放出されるノルアドレナリンです。

交感神経といえばfight or flight responseですね。

ということで、まず神経活動のレポーターとして機能させられるFOS発現レベルをRTX注射時に調べてみる。すると、顕著なFOS発現レベルの上昇がみられました。

更に、最初の実験でも出てきた痛みブロック用のbupとRTXを同時に注射すると、FOS発現の上昇は見られない…

すなわち交感神経は侵害刺激により誘発されるストレスに応答して活性化していることが分かります。

更に、6-OHDAという交感神経に選択毒性を持つ薬剤で処理したマウスにRTXを注射したところ、白髪は発生しませんでした。

交感神経末端からのノルアドレナリン放出を遮断するグアネチジン29を用いてからのRTX注射も行われましたが、これもやはり白髪が発生しませんでした。

これらのことから、ストレスにより交感神経が活性化し、そこから出るノルアドレナリンがMeSC減少を引き起こすのだと結論づけられます。

 

ちなみにストレス抜きにして交感神経からのノルアドレナリンだけでも白髪になるのか?ということも検証していて、

そのために研究者はまず人工的にデザインした薬剤のみで活性化する受容体を作って、外部からストレスなど引き起こさないはずの薬剤を入れれば活性化する交感神経というのを作っています。

すると、薬剤投与した時に白髪は発生し、広い範囲でランダムに活性化させた場合でも受容体が活性化している神経によって支配されている毛包のみがMeSC消失が見られる…というところまで突き止めています。

え…つまりそれって、交感神経が活性化すればするほど白髪になるってこと…???ちょっと……交感神経異常な私はどうすれば………(絶望)

 

ストレス→交感神経→ノルアドレナリン→?→MeSC減少→白髪

だいぶ攻めた気がしますが、まだまだ勢いは止まりません。

次はMeSCの減少についてフォーカスしていきます。

MeSCが減少するといっても、その理由は沢山可能性が考えられます。

アポトーシスネクローシス、細胞の過剰分化、細胞移動、分裂ストップなどなど…

 

アポトーシスネクローシスの検証は、免疫蛍光法を使ったカスパーゼなどの検出によって試みられましたが、RTXやノルアドレナリンを注射した後も活性型カスパーゼはみられませんでした。

また、壊死のために重要なキナーゼを欠いたRipk3変異マウスも、RTXを注射すれば白髪ができます。これは壊死やアポトーシスが関係ないという証拠です。

 

しかし、MeSCが細胞分裂の間期に入っているマウスにRTXやノルアドレナリンを注射すると、分裂期に入るMeSCが著しく増加し、約50%のMeSCがM期のマーカー陽性になりました。

一方でメラノサイトにおける増殖やアポトーシスなどの変化は観察されなかったため、ストレスはMeSCの過剰増殖を引き起こすことでMeSCの数を減らす可能性が高まります。

そこでより詳細に分化や動きをモニターするために、MeSCであればGFPを発現するような仕組みをマウスに作り、RTXを注射します。

その結果、RTX注射後著しいGFP+細胞の増加(MeSCが増えた)、そして分化後の特徴である樹上分岐の開始、その後下方・真皮・表皮への異所移動とそれに伴うバルジ内のすべてのGFP+細胞の消失という結果になりました。

つまりMeSCの減少は、ストレス後の急増殖と分化と移動のせいで起こっていたのです。

この後間期の消失さえ起こさせなければMeSCは枯渇しないし大丈夫…っていう実験が最後の一押しであるんですが、ちょっと疲れたのでここまでにします…(ごめんなさい)

 

結論!

ということで、

ストレスは、

  1. 交感神経の興奮を引き起こす
  2. 交感神経末端からノルアドレナリンが放出される
  3. ノルアドレナリンがMeSC上のADRB2に受容される
  4. MeSCの間期がなくなって過増殖と分化・異所移動が発生する
  5. MeSCがバルジからいなくなることで新たなメラノサイトの供給が途絶えた結果白髪が発生する

ということが研究から分かったそうです。ね!

案外しっかりつめつめやってった実験だったんだなぁという印象を受けました。

あと執念に近いものを感じるというか…すごいね。こんな綺麗にストーリーがつながったら気持ちいいわよね。

 

自分は今のところ白髪はないんですが、交感神経が常に優位らしいので、やばいなというお気持ちです。生き抜きたい…

(そういえばストレスで禿げるっていうのも本当なんですかね?本当っぽいけど論文あるんだろうか。)

がしゃぽんカメがやってきました!

溯ること2019年12月の最終週。

私たちは行きつけのショッピングモールに出かけていました。

このショッピングモールは私のお気に入りで、ここには沢山のガシャポンが置いてあります。

私はこのガシャポンを眺めるのがとっても大好きで、ショッピングモールに行くたびにチェックしていました。

この時もいつも通りガシャポンをチェックしていたのですが…

突然すごいDPが目に留まったのです!!

一つだけ異質な黒バックの、このDP…

f:id:I_my_mine:20200124103823p:plain

ガシャポンブログから拝借

な、なんだって!?!?!?!

私はバンダイさんが出しているダンゴムシの熱狂的ファンなのですが、

その新シリーズ「かめ」が出ているではないですか!!!

しかもただのフィギュアではなく、「骨格再現」…!!!!!!!

最高です!!!!!!!

早速回そう!と中を覗き込んだところ、なんと、空っぽ…

悲しいかな、売り切れでした。

帰宅した後も、その後何日経ってもカメへの切望は消えることなく、

色んなショッピングモールに繰り出してガシャポンを探す旅をするも、全然見つからず…

15軒以上回ったんですが…どこも箱すらないのです。

最初見つけたお気に入りの場所にも、再入荷を期待して行ったのですが、なんと箱が撤去されていてカメではない何かになっていました。

希望が消えたような気がして、本当につらかったです…

見つからない間も、カメへの羨望はなくならず、

ずっとバンダイの開発ブログを読んでいました。これがすごいのよ。

gashapon.jp

この開発日記、10まであるのですが、めちゃくちゃ面白いです。

カメに対する物凄い情熱というか、こだわりが伝わってきます。

ガシャポンという制約の中で、期日や生産ということも考えつつ、どうやったらより良いものが実現できるか…と淡々とクオリティを向上させていった過程に感動しました。

カメの顔とかもすごいですよ。

 

そんな感じで毎日、生きている心地もしないまま、カメと会えるのを夢見て悲しみに溺れて生きていました。

ダンゴムシの時もそうでした。

発売前から情報をキャッチし、発売を心待ちにして、

発売と同時にその日にショッピングモールに行ったのに、全然見つからなくて、つらくって…

でもそんな時、私を救ってくれたのは実は私の生徒でした。

なんとなしに、授業か休憩の時間で話したダンゴムシガシャポンの話を生徒は聞いて、

私のために(または自分のために…?)探してくれたのです!!!!!!

色んな子が情報を持ってきてくれました。本当に嬉しかった…

中でもとりわけ感動したのが、Tくんの行動です。

Tくんはなんと、全然がしゃぽんが見つからなかったそんな最中でも、見事にガシャポンを見つけ出し、しかも私のために買ってきてプレゼントしてくれたのです!!!!!!!!!!

私はその時からTくんのことをある種ガシャポンの神として捉えていて、

このカメについても相談したら、もしかしたらTくんなら見つけ出せるのではないか…??と思い始めました。

そして始業式と同時にTくんにカメを相談したところ、

 

2日後に!!!!!!!

Tくんが見つけて買ってきてくれたのです!!!!!!!!!!!!!

そうしてTくんの素晴らしい活躍のおかげで手に入ったのがこちら…!!!!

f:id:I_my_mine:20200111202706j:image

インドホシガメギリシャリクガメ(アルビノ)さん!!!!!!

すごい、収まってる・・・・!!!!

本当ならガシャポンから出てくるところも見たかったですが、贅沢は言えません。もう受け取った瞬間は胸がいっぱいになりました。Tくんは神。

f:id:I_my_mine:20200111202715j:image

開封したてのところです。

写真を見ると初めて出会えた時のあの感動が蘇ってきます…。

 

早速甲羅から出していきます。

f:id:I_my_mine:20200111202720j:image

f:id:I_my_mine:20200111202725j:image

かわいすぎないか???????????????????????????

めちゃくちゃかわいいんですが!!!!!!!!

顔!!!!再現度高い!!!!!!!!!すっご!!!!!!!!

手足の雰囲気といい、甲羅の質感といい、もう大満足です。これはカメ。

なんちゃってカメが沢山この世のおもちゃにあふれる中で、これはカメですよ。

ちなみにこの写真を撮ってTwitterにアップしたら本物のカメだと間違えられました。

それくらいカメだよ。

 

インドホシガメギリシャリクガメ、ちゃんと顔も爪も甲羅の様子も何もかも違う…

ギリシャリクガメ原色が手に入れば彩色も見れたので、それが見られないのが残念ですが

アルビノちゃんはアルビノちゃんでとってもかわいい。

Twitterだと割と皆勝手に色塗ってるみたい。そういうもんなの?)

 


かわいい…

特に甲羅の美しさとカメの顔がいい。口。口がかわいいよ。最高。

無限に口を動かしてしまいます。

骨格のこだわりも、動かしていてすごく感じます。自然なんです。

首の動きは特に感動します。えっ、こんなにゅって動くの!?伸びすぎじゃない!?というあの感動がある。

本当に素晴らしい作品です。

バンダイさん作ってくれてありがとう…探すのに付き合ってくれた旦那さんありがとう…見つけてくれたTくんありがとう…

カメさんたちのおかげで心が潤いました。一生大事にします。

 

カメさん一匹で見ている限りはとっても大きく感じるのに、ダンゴムシさんたちと並べると縮尺の不安を感じますね。なんなんだこのカオスは。

 

いつも大事なものは学習机の上に全部置いてあるのですが、

そろそろ大事なものが沢山になってきたので、ダイソーにディスプレイケースないかなと探しに行ったところ、

完全にクリアで強度もあるシューズボックス(300円)があったのでそれを購入してみました。

ダンゴムシ、まんまるこがね、かめとバクダンウニの標本を入れてみました。

他にも宝物は沢山あるんだけど(生徒から貰ったカエルの置物とかメンダコキーチェーンとか)それらは箱に入ると大変そうだったので机の上に置いておくままにしました。

なんかみっちみちになっちゃいました。これから増えたら困っちゃうね。増やすけど。

でも幸せいっぱいの箱です。

ルンルンで机に飾ったら旦那さんに苦笑いされていたのは内緒です。

 

次は、3月頃に丸くなるカニさんであるトラフカラッパが出るらしいですね!

ゲットしたいところです。

本当はフチゾリさんもほしいんだけどね…欲が尽きないぜ…

まいばいお21 エボラ

びっくりするほど更新しないまま20日近く経っていました。

なんでや…

理由は忙しかったからなんですが、幾ら何でも放置しすぎですね。反省しています。

色々報告したいこともあるけれど取り敢えずまいばいおです。

今回は感染症をテーマにしました。

最近中国で新たなコロナウイルスが怪しい動きを見せていますね。

本当はその情報をまとめたりできればよかったですが、中々まだ信ぴょう性もなく、変に行動してはダメだと思い、

一方で危機感を持たないでぼーっとしている生徒たちを見ると不安にもなってくるので、

エボラをモデルケースとしてちょっと教訓的な紹介をしたいと思いました。

 

 

✿伝染病は突然現れる

1976 年 6 月末、南スーダンにあるヌザラの綿工場。

突如倉庫番の男性が出血熱の症状を示し、続いてほかの部署の男性 2 人も同じ症状で倒れました。

誰も見たことのない病気でした。

更にこの3名の家族、そして彼らがかかった病院に元いた患者そして医療関係者・・・彼らの周囲でどんどん同じ症状を呈す人が現れ、合計284人が発症、151人が死亡しました。

ほとんど時期を同じくして、約1000km離れたコンゴ民主共和国北部のヤンブクでも、唐突に同じ症状の病気が出現しました。そして318例中280名が死亡しました。

 

両国の流行地域の住民がこの未知の病気出現に対しパニックに陥ったことは言うまでもありません。

このようなセンセーショナルな登場をしたのが「エボラ出血熱」でした。

 

エボラ出血熱とは

エボラ出血熱RNAウイルスの一種、エボラウイルスによる病気です。

エボラウイルスはコウモリが元々持っていたウイルスであると考えられています。

エボラウイルスの形は非常に特徴的で、写真のように「糸状」をしています。

f:id:I_my_mine:20220222141155p:plain

パブリックドメイン

エボラウイルスに感染した際の一般的な症状は、突然の発熱、強い脱力感、筋肉痛、頭痛、喉の痛みなどに始まり、これはインフルエンザの症状に非常に似ていると言われます。

その後、嘔吐、下痢、発疹、肝機能および腎機能が低下し、多臓器不全が主な死因となります。

エボラ出血熱」という名前の所以は勿論、感染すると出血傾向が出現するからです。

エボラ出血熱の死因のもう一つは、出血によるショック症状です。

 

エボラウイルスが出血傾向を引き起こすメカニズムを紹介しましょう。

エボラウイルスは細胞内で増殖し、その細胞を破壊します。

すると、破壊された細胞の断片が刺激となり、全身の血管で血小板の凝集が発生、止血に必要なフィブリノーゲンが凝固します。

つまり局所的なフィブリノーゲンの乱用が発生するのです。

結果として血液中の未使用な血小板やフィブリノーゲン残量が極端に減ってしまい、血液は血ぺい形成能力を失います。ですから血管が塞がらず出血してしまうのです。

肝臓が既に冒されている場合は血液凝固因子を作る能力も落ちているので尚更です。

また、エボラウイルスに感染すると炎症性サイトカインの過剰産生が起こることが知られており、

サイトカインの効果によって血管透過性の亢進が生じることも出血に繋がると考えられています。

 

エボラウイルスが体内に入った後、最初の感染標的となるのがマクロファージや樹状細胞などの免疫細胞です。

これらは体内を広く移動することができるので、エボラウイルスが体内に広がるのに都合がよく、同時に免疫系も弱められるという利点があります。

普通ウイルスは免疫機構によって排除されるものなのですが、エボラウイルスは免疫機能の異常をもたらすことで免疫による排除を巧妙に逃れています。

例えば、エボラウイルスの構造タンパク質であるVP24 やVP35 は、インターフェロンという生体内で免疫機構を促すために必要な物質を抑えることでウイルス排除機構を抑制することが報告されています。

また、エボラウイルス感染に伴ってリンパ球(NK 細胞やキラーT 細胞)のアポトーシス誘導が見られることも免疫弱化の原因であると考えられています。

 

エボラ出血熱の急速な拡大

エボラは元々アフリカ中央部で度々流行る、局地的流行しかしないような病気でした。

しかし2014年、突然西アフリカにおいてエボラは大規模かつ急速に流行が拡大し、更に初めて流行地以外の国にも飛び火しました。なぜこんなことが起こったのでしょうか。

実はそこには「死者を埋葬する宗教儀式」が関わっていました。

西アフリカでは、肉親や友人が死者との別れに当たって、死者の身体を抱擁したり手足をさすったりします。

これが感染拡大の原因でした。

エボラウイルスは血液のみならず汗腺、唾液中にも存在し、身体に触れれば意図せず感染してしまいます。

触れれば感染するということは、エボラ患者を診た医療従事者にも感染するということです。

ここに航空機の発達も合わさって、このような感染拡大が生じてしまいました。

感染拡大を防ぐため、正しい知識を携えて国境なき医師団やWHO等の医療・支援関係者が流行地に入りました。

白い防護服を身につけた彼らに対し、住民は追い返したり、病院から患者を力ずくで奪還したり・・・葬儀方法についても中々改めませんでした。

白い悪魔が病気を広めている」と信じた住民の無知・誤解に基づく行為が、感染を必要以上に拡げました。

 

エボラウイルス自体はそんなに強くないウイルスなので、「触れない」という簡単な対策の徹底的で感染を防げます。

しかし、敵を知らなければ、知識がなければ、私たちは無力なのです。

今の時代、「知ることよりも考えることが大事」とよく言われ、知識は検索すれば得られるものだから頭に入れる必要はない、なんでも既に分かっているから研究する必要もない、という風潮すら少々ありますが、私はそれは違うだろうなと思います。

知識は武器なのです。

知るという行為を疎かにしてはいけませんし、どんな微かな知識でも得るための研究には価値があると思っています。

 

現在中国で新たなウイルスが突然現れていますが、これについても同様です。

知ることを疎かにせず、最新情報を追って自分の身を守って下さい。

M5Stackで心拍表示する 2nd try

私が作ったものはゴミ性能だった

前回↓
i-my-mine.hatenablog.com

作ってから色々試してみたんですけど、やっぱり駄目ね…という感じが強く、前回作ったものはポンコツだったな感が強まってきた。
いや分かってけど…

まず心拍上がった状態で測ると速攻で死ぬ。
BPMが0になる。
やめて…
平均を範囲動かさずにとってくせいでちょっとでも心拍の落ちるタイミングとかが平均とる範囲とずれていくと駄目になってしまう。
これは使えない。

しかもあれだよ、やっぱり上限と下限がずれていくと一回の失敗が大反響しちゃってもうがったがたになっちゃう。
波形的に死んでる。
これはいかんで、ということで二つのトライをしてみた。
①データをとって上限下限の設定のし直し
移動平均の実装


とりあえず様子を見るためのデータ取り

データをとれるようにシリアルモニタでlastMaxとlastMinがどう遷移していっていってるかを確認してみた。

/*
    Install MAX30100lib Library first.

    MAX30100_RawData.ino
*/
  
#include <M5Stack.h>
#include <Wire.h>
#include "MAX30100.h"

#define SAMPLING_RATE   MAX30100_SAMPRATE_100HZ
#define IR_LED_CURRENT  MAX30100_LED_CURR_50MA
#define RED_LED_CURRENT MAX30100_LED_CURR_27_1MA
// set HIGHRES_MODE to true only
// when setting PULSE_WIDTH to MAX30100_SPC_PW_1600US_16BITS
#define PULSE_WIDTH MAX30100_SPC_PW_1600US_16BITS
#define HIGHRES_MODE    true

// new a object
MAX30100 sensor;

void setup() {
    M5.begin();
    Serial.begin(115200);
    Serial.print("Initializing MAX30100..");
    if (!sensor.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }
    sensor.setMode(MAX30100_MODE_SPO2_HR);
    sensor.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT);
    sensor.setLedsPulseWidth(PULSE_WIDTH);
    sensor.setSamplingRate(SAMPLING_RATE);
    sensor.setHighresModeEnabled(HIGHRES_MODE);
}


const int LCD_WIDTH = 320;
const int LCD_HEIGHT = 240;
const int DOTS_DIV = 30;
#define GREY 0x7BEF

void DrawGrid() {
    for (int x = 0; x <= LCD_WIDTH; x += 2) { // Horizontal Line
        for (int y = 0; y <= LCD_HEIGHT; y += DOTS_DIV) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
        if (LCD_HEIGHT == 240) {
            M5.Lcd.drawPixel(x, LCD_HEIGHT - 1, GREY);
        }
    }
    for (int x = 0; x <= LCD_WIDTH; x += DOTS_DIV) { // Vertical Line
        for (int y = 0; y <= LCD_HEIGHT; y += 2) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
    }
}

#define REDRAW 20 // msec

int lastMin = 65000, lastMax = 62000;
int minS= 63000, maxS = 60000;
int lastY = 63000;
int x = 0;
int count1 = 0;
int count2 = 0;
int p = 0;
int lastp = 0;
int bpm;



void loop()
{
    M5.update();
    delay(REDRAW);
    uint16_t ir, red;
    sensor.update();
    while(sensor.getRawValues(&ir, &red)){
      //Serial.println(ir);
      //Serial.print('\t');
      //Serial.println(red);
    };
    uint16_t y = red;
    if (y < minS) minS = y;
    if (maxS < y) maxS = y;
    if (x > 0) {
        y = (int)(LCD_HEIGHT - (float)(y - lastMin) / (lastMax - lastMin) * LCD_HEIGHT);
        M5.Lcd.drawLine(x - 1, lastY, x, y, WHITE);
        lastY = y;
        p += y;
        count2++;
        if(count2 == 15){
          p/=15;
          if(lastp > p+(LCD_HEIGHT/8)) ++count1;
          lastp = p;
          p = 0;
          count2 = 0;  
        }
    }
    //Serial.print("minS: ");
    //Serial.print(minS);
    //Serial.print(" maxS: ");
    //Serial.print(maxS);
    //Serial.print(" y: ");
    //Serial.println(y);
    if (++x > LCD_WIDTH) {
        x = 0;
        M5.Lcd.fillScreen(BLACK);
        DrawGrid();
        lastMin = minS - 20;
        lastMax = maxS + 20;
        minS = 63000;
        maxS = 60000;
        bpm = (float)count1/6.5 * 60;
        M5.Lcd.setCursor(0, 0);
        M5.Lcd.setTextSize(4);
        M5.Lcd.printf("BPM: %d", bpm);
        count1 = 0;
        Serial.print("lastmin: ");
        Serial.print(lastMin);
        Serial.print("   lastmax: ");
        Serial.println(lastMax);
    }
}


シリアルモニタで出てきた値はこんな感じだった。
lastmin: -8 lastmax: 65555
lastmin: 62633 lastmax: 64932
lastmin: 61831 lastmax: 63631
lastmin: 60875 lastmax: 62621
lastmin: 60031 lastmax: 61655
lastmin: 59639 lastmax: 60937
lastmin: 59217 lastmax: 60638
lastmin: 58777 lastmax: 60020
lastmin: 58648 lastmax: 60020
lastmin: 56636 lastmax: 60020
lastmin: 55482 lastmax: 60020
lastmin: 55611 lastmax: 60020
lastmin: 55262 lastmax: 60020
lastmin: 58584 lastmax: 61697
lastmin: 55789 lastmax: 65555
lastmin: 55413 lastmax: 60020
lastmin: 54854 lastmax: 60020
lastmin: 54067 lastmax: 65180
lastmin: 58596 lastmax: 60020
lastmin: 56288 lastmax: 60020
lastmin: 54920 lastmax: 60020
lastmin: 51183 lastmax: 60020
lastmin: 50659 lastmax: 60020
lastmin: 56110 lastmax: 60020
lastmin: 58426 lastmax: 60036
lastmin: 58589 lastmax: 60061 ←ここらへんがよかった
lastmin: 58636 lastmax: 60365


最初の-8とか悲劇でしかない。
55000あたりの数字もだいぶやばかった。もうめっちゃ画面の上の方で波打つの。
よってなんとなくlastMax=60100くらい、lastMin=58000くらいが適正値なのではないか、という気がしてくる。
ただし自分だけでしかこれは見ていないのでわからない。
lastMaxとlastMinがあまりにも人依存で変化するなら、従来通り刻々と変化していくようにしなければいけないけれど、
もし人によってそう変化なくこれくらいの値になるのであればもはやlastMaxとlastMinは固定でもよい。と思う。

取り敢えずまぁまだ更新入れておきつつ、
int lastMin = 57000, lastMax = 59000;
int lastY = 57000;
で試していくことにした。
色んな人で試してここの値は調整しよう。
あとは、指を離した時の所謂0とかマイナスとかの値がminSとしてlastMinに反映されたり、強く押し込んだ時の高すぎる値がmaxSおよびlastMaxに反映されるのも嫌だったので、lastMinとlastMaxはあからさまにおかしい値が入りそうになったときにはブロックかけられるようにifで上限下限を設定した。



やるぜ移動平均

移動平均についてはいやだいやだと逃げていたけどもうもはや逃げられないなという気持ちなので実装することにした。
そこで考えた一番ストレートな方法がこれ。
①めちゃでかコンテナを用意して毎回出てくるyをがんがん入れていくスタイル
コンテナがkだとして、
データをとった順番iだとするとk[i]=yでどんどん更新して入れていく。
で、p + k[i] – k[i-n](nは移動平均をとるときの平均をとるデータ数)にして、
Lastpとpを比較後lastp=pにして更新

なんかもっさりしているなと思ったのでもう一個、もうちょっと見栄えが良くスタイリッシュなものを考えた。
②modを使っていくタイプ
コンテナがkだとして、
データをとった順番iだとするとk[i]=yでどんどん更新してまずはn個入れていく。
で、p + y – k[i%n](nは移動平均をとるときの平均をとるデータ数)にして、
その後k[i%n] = yに更新する。
Lastpとpを比較後lastp=pにして更新

modを使って出していく方法は個人的には凄くいいものが自分で思いつけた!と大満足だったんだけど
旦那さんにこのアイデアを話したら「それは〇〇という手法だね」と言われ(何かは忘れた)、すでに存在している&確立している方法としてあるらしい…残念。

なにはともあれとりあえず実装である。

/*
    Install MAX30100lib Library first.

    MAX30100_RawData.ino
*/
  
#include <M5Stack.h>
#include <Wire.h>
#include "MAX30100.h"

#define SAMPLING_RATE   MAX30100_SAMPRATE_100HZ
#define IR_LED_CURRENT  MAX30100_LED_CURR_50MA
#define RED_LED_CURRENT MAX30100_LED_CURR_27_1MA
// set HIGHRES_MODE to true only
// when setting PULSE_WIDTH to MAX30100_SPC_PW_1600US_16BITS
#define PULSE_WIDTH MAX30100_SPC_PW_1600US_16BITS
#define HIGHRES_MODE    true

// new a object
MAX30100 sensor;

void setup() {
    M5.begin();
    Serial.begin(115200);
    Serial.print("Initializing MAX30100..");
    if (!sensor.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }
    sensor.setMode(MAX30100_MODE_SPO2_HR);
    sensor.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT);
    sensor.setLedsPulseWidth(PULSE_WIDTH);
    sensor.setSamplingRate(SAMPLING_RATE);
    sensor.setHighresModeEnabled(HIGHRES_MODE);
}


const int LCD_WIDTH = 320;
const int LCD_HEIGHT = 240;
const int DOTS_DIV = 30;
#define GREY 0x7BEF

void DrawGrid() {
    for (int x = 0; x <= LCD_WIDTH; x += 2) { // Horizontal Line
        for (int y = 0; y <= LCD_HEIGHT; y += DOTS_DIV) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
        if (LCD_HEIGHT == 240) {
            M5.Lcd.drawPixel(x, LCD_HEIGHT - 1, GREY);
        }
    }
    for (int x = 0; x <= LCD_WIDTH; x += DOTS_DIV) { // Vertical Line
        for (int y = 0; y <= LCD_HEIGHT; y += 2) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
    }
}

#define REDRAW 20 // msec

int lastMin = 57000, lastMax = 59000;
int minS= 60000, maxS = 57000;
int lastY = 57000;
int x = 0;
int count1 = 0;
int p = 0;
int lastp = 0;
int k[10];
int i = 0;
int bpm;



void loop()
{
    M5.update();
    delay(REDRAW);
    uint16_t ir, red;
    sensor.update();
    while(sensor.getRawValues(&ir, &red)){
      //Serial.println(ir);
      //Serial.print('\t');
      //Serial.println(red);
    };
    uint16_t y = red;
    if (y < minS) minS = y;
    if (maxS < y) maxS = y;
    ++i;
    p += y;
if(i<=8){
k[i-1]=y; //0~7の8要素に値を入れる
if(i==8) lastp = p;
else {
  p -= k[(i-1)%8];
  k[(i-1)%8]=y;
  if(lastp>p) ++count1;
  lastp = p;
}
        

    if (x > 0) {
        y = (int)(LCD_HEIGHT - (float)(y - lastMin) / (lastMax - lastMin) * LCD_HEIGHT);
        M5.Lcd.drawLine(x - 1, lastY, x, y, WHITE);
        lastY = y;
    }

    if (++x > LCD_WIDTH) {
        x = 0;
        M5.Lcd.fillScreen(BLACK);
        DrawGrid();
        if(minS>50000) lastMin = minS - 20;
        if(maxS<65000) lastMax = maxS + 20;
        minS = 60000;
        maxS = 57000;
        bpm = (float)count1/6.4 * 60;
        M5.Lcd.setCursor(0, 0);
        M5.Lcd.setTextSize(4);
        M5.Lcd.printf("BPM: %d", bpm);
        count1 = 0;
        i = 0;
        Serial.print("lastmin: ");
        Serial.print(lastMin);
        Serial.print("   lastmax: ");
        Serial.println(lastMax);
    }
}

結果、BPM爆上がりですよ…これは試す時にドキドキしたからじゃないですよね…???(キレ
count1が足されるたびにプロッタに通知が表示されるような形を実装して追跡してみたら、一回の谷だけで5,6回分countしてた。そりゃ爆上がりしますよ。通常の人間の5倍くらいの心拍やで。
というわけで、所謂c++のboolにあたるものがあるのかよくわからなかったので、適当にflagをint型で作って、谷の入り始め…すなわち、一度でもpがlastpを下回ってcount1が足されたら、それ以降の連続するlastp>pの場合にはカウントしていかない仕組みを入れた。雑だけどまぁやってみよう。

/*
    Install MAX30100lib Library first.

    MAX30100_RawData.ino
*/
  
#include <M5Stack.h>
#include <Wire.h>
#include "MAX30100.h"

#define SAMPLING_RATE   MAX30100_SAMPRATE_100HZ
#define IR_LED_CURRENT  MAX30100_LED_CURR_50MA
#define RED_LED_CURRENT MAX30100_LED_CURR_27_1MA
// set HIGHRES_MODE to true only
// when setting PULSE_WIDTH to MAX30100_SPC_PW_1600US_16BITS
#define PULSE_WIDTH MAX30100_SPC_PW_1600US_16BITS
#define HIGHRES_MODE    true

// new a object
MAX30100 sensor;

void setup() {
    M5.begin();
    Serial.begin(115200);
    Serial.print("Initializing MAX30100..");
    if (!sensor.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }
    sensor.setMode(MAX30100_MODE_SPO2_HR);
    sensor.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT);
    sensor.setLedsPulseWidth(PULSE_WIDTH);
    sensor.setSamplingRate(SAMPLING_RATE);
    sensor.setHighresModeEnabled(HIGHRES_MODE);
}


const int LCD_WIDTH = 320;
const int LCD_HEIGHT = 240;
const int DOTS_DIV = 30;
#define GREY 0x7BEF

void DrawGrid() {
    for (int x = 0; x <= LCD_WIDTH; x += 2) { // Horizontal Line
        for (int y = 0; y <= LCD_HEIGHT; y += DOTS_DIV) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
        if (LCD_HEIGHT == 240) {
            M5.Lcd.drawPixel(x, LCD_HEIGHT - 1, GREY);
        }
    }
    for (int x = 0; x <= LCD_WIDTH; x += DOTS_DIV) { // Vertical Line
        for (int y = 0; y <= LCD_HEIGHT; y += 2) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
    }
}

#define REDRAW 20 // msec

int lastMin = 57000, lastMax = 59000;
int minS= 60000, maxS = 57000;
int lastY = 57000;
int x = 0;
int count1 = 0;
int p = 0;
int lastp = 0;
int k[10];
int i = 0;
int bpm;
int flag = 0;



void loop(){
    M5.update();
    delay(REDRAW);
    uint16_t ir, red;
    sensor.update();
    while(sensor.getRawValues(&ir, &red)){
      //Serial.println(ir);
      //Serial.print('\t');
      //Serial.println(red);
    };
    uint16_t y = red;
    if (y < minS) minS = y;
    if (maxS < y) maxS = y;
    ++i;
    p += y;
    if(i<=8){
    k[i-1]=y; //0~7の8要素に値を入れる
    if(i==8) lastp = p;
    } else {
      p -= k[(i-1)%8];
      k[(i-1)%8]=y;
      if(lastp>p+200){
        flag++;
        if(flag==1) ++count1;
      } else flag = 0;
      lastp = p;
      //Serial.print("   lastp: ");
      //Serial.println(lastp);
    }
        

    if (x > 0) {
        y = (int)(LCD_HEIGHT - (float)(y - lastMin) / (lastMax - lastMin) * LCD_HEIGHT);
        M5.Lcd.drawLine(x - 1, lastY, x, y, WHITE);
        lastY = y;
    }

    if (++x > LCD_WIDTH) {
        x = 0;
        M5.Lcd.fillScreen(BLACK);
        DrawGrid();
        if(minS>50000) lastMin = minS - 20;
        if(maxS<65000) lastMax = maxS + 20;
        minS = 60000;
        maxS = 57000;
        bpm = (float)count1/6.4 * 60;
        M5.Lcd.setCursor(0, 0);
        M5.Lcd.setTextSize(4);
        M5.Lcd.printf("BPM: %d", bpm);
        count1 = 0;
        i = 0;
        Serial.print("lastmin: ");
        Serial.print(lastMin);
        Serial.print("   lastmax: ");
        Serial.println(lastMax);
    }
}

なかなかいいんじゃね?と思ってやってみたが…

M5StackとMAX30100で心拍とBPMと表示する2-1
1つ目に、「連続してlastp>pになるとき」しか検出していかないので、一個でも飛ぶともうだめ。カウントしちゃう。
結果BPMがやばくなる。
これは安定させるこっち側の技術が必要。

2つ目に、ちょっと時間を置いてやってみたんだけども…

M5StackとMAX30100で心拍とBPMと表示する2-2
なんか低空飛行じゃない???????????????????????
モニタで追ってみると、なんか心拍弱まってるよ。時間によって弱まるのかも…
これだとやはり沿わせていく技術が必要だし、今の設定値もちょっと限定的すぎるみたいだなぁ。
もっと幅広く受け止められるものにしなければ…


ということで次の改善目標はあれですね、

  • カウント制度をなんとかしてBPMをより正確化できたらいいな
  • 色んな人、色んなタイミングで使えるものにしたいな
  • ほとんどできあがってきた感があるので(?)どうせならボタンを押すと何かができるとか、その時のBPMデータをふっとばしてどこかに保存するみたいな仕組みを作り(タイムスタンプと一緒に)、後から一日の変動を見られるような仕組みにできないかなor何人かの心拍を比較する仕組みをボタンを利用してできないかな

というところですかね。
とにかくまずは今夜RingFit Adventureやってから心拍測定に使ってみまーす!

ほかにやりたいこと

  • 動くロボをつくる
  • 加速器やx,y,z座標を検出しグラフで出す(画面をボタンで切り替えられる)やつをつくる
  • 温度検出記録器(wifi機能使いたい、Excelに記録してほしい)つくる
  • センサーで赤外線感知?だっけ 物の通過回数を記録するやつつくる
  • ビデオにとる(何かを、タイムラプスでも化)やつつくるor変化回数を記録するor変化検出するやつを作る

明日明後日は外出なので難しそうですが、できれば年始のお休みの間にもう一つくらい開発したいね…

まいばいお21 DNA折り紙

今回は、DNA折り紙とはなんぞや?どういう経緯でできたんだ?みたいな話をしようと思います!

 

DNA折り紙、と聞いて…

折り紙といえば、日本の文化的な遊びの一つで、紙を折って鶴などの形を作るものを想像しますね。

では「DNA折り紙」と言われたら、皆さんは何を想像するでしょう?

DNAを折る遊び…?いえいえ、違います! 

入念に計算されたDNAのパーツを混ぜて、あっためてから冷やすと、勝手にDNAが組みあがりある構造ができあがる!これがDNA折り紙です。

 

「DNAでナノサイズの構造が作れるんじゃない?」

そもそもDNA によって平面構造や立体構造を作成する原理は、1982年、ニューヨーク大学のSeemanによって提唱されました。

Seemanはまず、DNA組換えの時に出現する2本鎖DNAのホリデイジャンクションという構造に注目しました。

ホリデイジャンクションは,下図にあるように四方向に分岐した DNA 構造を持っています。

f:id:I_my_mine:20220222141348p:plain

パブリックドメイン

これを 1 つのユニットとして、これらの末端の 1本鎖 DNA 同士を相補的な塩基対を使って結合させ,2 次元のシート状構造を作れるのではないかと考え、実行しました。

DNAは相補性を使って勝手に特定の場所(相補的な塩基配列を持つ場所)に貼りつく性質があるので、これがいわゆる「のりしろ同士をくっつける行為」になるんですね。

 

この考え方をもとに、1998 年には、Seemanはまた異なる平面構造を作る挑戦をしました。

今回は、2 本鎖 DNA が 2 本並んだ構造体(ダブルクロスオーバー構造と呼ばれる)を1つのユニットとし、

両末端の4 本の1本鎖 DNA を使いその相補的な塩基対形成によって自己集合させることで,マイクロメーターサイズの2次元DNAナノ構造体を作成しました。

 

このような小さすぎる構造は、普通の顕微鏡では見えないので、原子間力顕微鏡(AFM)により観察されます。

観察の結果、自己集合したユニットが周期的に正しく並んでいる構造が確認され、

これにより、DNA鎖を1次元のひも状ではなく、横並びに配列することで、2次元の集合体を作成できることを実証しました。

 

DNA折り紙とは?

ではDNA折り紙はSeemanのものと何が違うかというと、より複雑な構造を作る技術に発展したところです。

2006年にカリフォルニア工科大学のRothemundによって開発されました。

「オリガミ」とはもちろん日本語の折り紙から連想したものであり,あるものから形が組みあがることから 命名されました。

 

この方法では,長鎖の1本鎖 DNA(環状1 本鎖DNA、7,249 塩基)と,構造にあわせて配列設計した相補鎖 DNA(多くは32塩基でステープル DNA と呼ばれる)を混合し、

85 ℃に熱してから徐々に室温まで冷却させます。

冷却している間に、長鎖DNAのあらかじめ決められていた相補的な場所に短鎖DNAがくっついていきます。

結果、自己集合によって平面構造体ができあがるのです。

折り畳まれた平面構造内では,2 本鎖 DNA は互いに横並びの状態でそれぞれのステープルDNA が架橋して、隣接する2本鎖 DNA を結び付けています。

その架橋の間隔は 32 塩基対でらせん 3 回転分となり、架橋の位置がちょうど二重らせん上で同じ方向を向き、隣接する二重らせんと結合して平面構造を保つようになっています。

 

また、その構造体の末端は直線的にそろえる以外に階段状にもできるため、三角形や星形、さらにはスマイルマークのような複雑な形状もデザインできてしまいます。

このように、DNA折り紙は100nmサイズの様々な形状の構造物を作ることを可能にした技術なのです。

画像が見たい人はリンク先をチェックしてみてください!

https://www.nature.com/articles/s43586-020-00009-8

https://www.sciencedirect.com/topics/neuroscience/dna-origami


加えて特筆すべきは、DNA折り紙には構造体のすべての位置に特定の塩基配列、すなわちアドレス代わりになるものが備わっているという点です。

この特徴によって、ステープルDNAに機能を持つ分子や粒子を結合させて構造体を形成させるなどの自由な構造上の物質配置も可能になっています。

 

その後、2本鎖DNA同士の幾何学的な結合を平面方向に対して垂直方向を加えることで3次元のDNAオリガミ構造体の構築方法も確立されました。

あわせて、DNAオリガミ設計用のソフトウエアcadnanoが開発されました。

 

DNA折り紙の応用技術

DNA折り紙を活用する先の一つに、医療行為があります。

なぜならDNA折り紙で作ったナノ構造物は、生体適応性が高く、金属などの他の物質に比べて免疫応答を引き起こしにくいということが知られているからです。

 

2018年には、DNA折り紙で作ったナノロボットによってがん細胞を破壊するという研究が発表されました。

このナノロボットは、90nm×60nmの長方形平面DNA折り紙を円筒形に丸めて作られています。

加えてこのシートには平均4個のトロンビン分子とDNAアプタマーが搭載されています。

アプタマーとは、特定分子と特異的に結合する核酸やペプチドのことで、今回の場合は細胞の核小体構成物質であるヌクレオリンに対して選択的に結合するDNAです。

クレオリンは、腫瘍内皮細胞の表面に大量に存在しますが正常細胞の表面には見られないという特徴があるため、がん腫瘍を標的とするために利用できるのです。

このナノロボットを血液中に送り込むと、がんに栄養を送っている血管のところに届き、トロンビンで血管内の血液を凝固させることによって腫瘍への血流を止め、死に追いやることができます。

メラノーマを起こさせたマウスに対してナノロボットによる治療を行ったところ、8体中3体で腫瘍の完全な縮小が見られ、生存期間中央値は20.5日から45日へと2倍以上に伸びました。

 

2019年にはドラッグデリバリーへDNA折り紙を応用した研究も発表されました。

この研究では抗菌性ペプチドであるリゾチームを、アプタマーを搭載したDNA折り紙によるナノ構造で直接標的細菌に輸送し、細菌を破壊することが試みられました。

というのも、現在医療の世界では、「抗生物質耐性」というのが問題になっています。

抗生物質耐性が細菌たちに発生することにより、以前使えていた抗生物質が効かなくなるという現象が生じているのです。

このような問題を解決して細菌を効率的に排除する方法の一つとして今回のDNA折り紙によるリゾチーム輸送は提案されました。本実験ではグラム陽性(枯草菌)およびグラム陰性(大腸菌)標的に対して効果を確認しており、この治療法が細菌増殖を遅らせるのに効果的だという結果が出ています。

普通にリゾチームを撒いてしまうと、色んな有用な菌まで殺されてしまうし、特定の菌だけを狙って減らすことは難しいわけで…

そういう意味でこのデリバリーシステムは非常に有用だと考えられます。

 

ただ…ぶっちゃけ、なんでこの方法だと「抗生物質耐性」の問題が解決されるのかが自分の中で理解できてないです。この方法でも抗生物質耐性菌、というかこのシステムへの耐性菌が出る可能性はあるんじゃないですかね?たまたまアプタマーが付きにくいタイプの壁を持ってるやつとかいたら生き残って増えるし…そういう問題ではないのだろうか?(知ってたら教えてほしい)

 

あとちょっと前にも関連で面白いニュースがあったので貼っておきます(もう気力が尽きた)

techable.jp

 

何はともあれDNA折り紙はおもしろい!まるでいわゆる「折り紙」のような楽しさがあると個人的には思っています。

そもそもナノサイズのロボとか、メカとか、面白くないですか?わくわくしません??

M5StackでMAX30100使って心拍を画面上に表示する

きっかけについて

はい
久しぶりのプログラミング系記事です!

ずーっと前にM5Stack買ったんですけど
i-my-mine.hatenablog.com

全然使えてなかったっていう…ハイ

そもそもM5Stack動かす時にはArduinoを使わなければいけないんですけど
Arduinoの記法がイマイチよくわかってないし
そもそもくっつけるものごとに記法があるんですけど
それを逐一調べるのも面倒だし

ということで
やりたいな~やりたいな~と思いながらちょっと触ってみても
ちょっと検索して心折れて「これは無理やで…」ってなって終わる
そんな日々を繰り返して半年近く放置してしまっていました。反省。

でもさすがと寝かしすぎたのでね?
熟成しちゃうからね?
そろそろ使いたいなと。

そんな時にRingFitAdventureを買ったんですけど
そしたらその心拍測定装置がどうも旦那さんにフィットしなくてうまく測れない。
「まいんさんが持ってたM5Stackで確か測れるようにできるよね?心不全とか怖いから測れるようにしてよ」と言われ
Oh... と思いながらもちょっと取り組んでみました。

ちなみにこの記事は発展途上であって完成品の話ではないので気をつけて欲しい。

目標:
MAX30100でとったデータを元にM5Stack画面上に心電図のような心拍推移グラフとBPMを表示させること

MAX30100は心拍を取得するためのセンサユニットですが、
これにはBPMを自動で計算するor取得するという機能がありません(他のタイプだとコードで一言かけばあっという間にとれるっぽい)。
ので、安価でいいじゃんってポチッたけど困る元になりました。BPMくらい計算してくれまじで。
更に心拍についても、サンプルコード(以下でリンクあり)使えばシリアルプロッタでパソコン画面上なら見れるようにはなるんですが、
このコードのままだとM5Stackの画面はまったくもって無意味です。ただの中継メカです。
かといってじゃあM5Stack画面上に出力しようというコードも公開されていないんだな。なんでだよ…
てなわけでここらへんで悪戦苦闘してます。

使用機材およびリファレンス

ではまず使ったものとか参考にしたページとか貼っときます。

使ったもの

M5Stackはこのタイプ
M5Stack Gray(9軸IMU搭載) - スイッチサイエンス
心拍取得はMAX30100
M5Stack用心拍センサユニット - スイッチサイエンス


Rawデータをとるサンプルコード
M5-ProductExampleCodes/MAX30100_RawData.ino at master · m5stack/M5-ProductExampleCodes · GitHub
商品ページのドキュメントのところに載っています。
先にも述べましたが、これを使うとシリアルプロッタでパソコン画面上で心拍の様子が見れるようにはなります。
あとはRawデータはとれるのでこれを元に画面に出そうとかBPM計算しようみたいなノリになるので大事。

参考にしたやつ

ambidata.io
違う心拍センサを用いて私が欲しい感じのやつを開発した人のブログ。めちゃ助かりました、ありがとうございました…
ただ心拍センサが違うので実装は地味に変えなければいけなかったけれど、ほとんど画面に表示するベースはここから移植している。
GPSのところは使ってないです。
ブログ内に以下のgithubに飛べるリンクがある↓
M5Stack_PulseSensor/M5Stack_PulseSensor.ino at master · AmbientDataInc/M5Stack_PulseSensor · GitHub


Arduino APIはここ
M5Stack - A series of modular stackable development devices

心拍センサーの記法はこれを見た
Arduino-MAX30100/MAX30100.h at master · oxullo/Arduino-MAX30100 · GitHub

実装していくぞ

取り敢えずRawデータとる

まずは単純に心拍センサユニットをM5Stackにつけ、上で紹介したMAX30100の商品ページにあるサンプルコード(Rawデータ取得用)を考え無で実装してみる。
すると普通にシリアルプロッタで線が出てくる。
第一段階はクリア?(何をしたんだ感)

M5Stack画面上にシリアルプロッタに出てくるやつみたいなのを描画する

ここからだぜ…
それをやるためにまず、なんにも手がでなかったので同じようなことしてる人いないの?って調べてみた。
そしたら上のリファレンスで紹介した人のブログが出てきたので、眺めながら必要そうなものだけ移植。
ありがたいことにほとんどセンサ特異的な書き方ではなかったので、Rawデータどりのサンプルコードと組み合わせるだけでそこそこの土台ができた。

ちなみに今回の描画実装のイメージとしては
・まずM5Stackの画面は縦240横320で左上が(0,0)
・maxSとminSは画面の上と下を指していて、横幅いっぱいにグラフ描画が終わったらその回の縦軸MAXと横軸MAXを参考に縦の幅が修正されるようになっている(綺麗に心拍の上下が見えるようになるように)
・心拍描画は前回の値との差でプロットされていく
こんな感じか?(どこまで書いておくべきかわかんない…)


一回ほとんど値いじらないまま実装したら死人になった。
f:id:I_my_mine:20191215165658j:plain
面白すぎ。


値いじくり倒して取り敢えず一回実装したのが下の感じ。
ただしこの段階ではBPMはずっと1になるようにしてある。
あとめっちゃ旦那さん頼ってる。ほぼ旦那さんの功績では?(私はリファレンス引っ張ってくっつけただけ感ある)

/*
    Install MAX30100lib Library first.

    MAX30100_RawData.ino
*/
  
#include <M5Stack.h>
#include <Wire.h>
#include "MAX30100.h"

#define SAMPLING_RATE   MAX30100_SAMPRATE_100HZ
#define IR_LED_CURRENT  MAX30100_LED_CURR_50MA
#define RED_LED_CURRENT MAX30100_LED_CURR_27_1MA
// set HIGHRES_MODE to true only
// when setting PULSE_WIDTH to MAX30100_SPC_PW_1600US_16BITS
#define PULSE_WIDTH MAX30100_SPC_PW_1600US_16BITS
#define HIGHRES_MODE    true

// new a object
MAX30100 sensor;

void setup() {
    M5.begin();
    Serial.begin(115200);
    Serial.print("Initializing MAX30100..");
    if (!sensor.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }
    sensor.setMode(MAX30100_MODE_SPO2_HR);
    sensor.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT);
    sensor.setLedsPulseWidth(PULSE_WIDTH);
    sensor.setSamplingRate(SAMPLING_RATE);
    sensor.setHighresModeEnabled(HIGHRES_MODE);
}


const int LCD_WIDTH = 320;
const int LCD_HEIGHT = 240;
const int DOTS_DIV = 30;
#define GREY 0x7BEF

void DrawGrid() {
    for (int x = 0; x <= LCD_WIDTH; x += 2) { // Horizontal Line
        for (int y = 0; y <= LCD_HEIGHT; y += DOTS_DIV) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
        if (LCD_HEIGHT == 240) {
            M5.Lcd.drawPixel(x, LCD_HEIGHT - 1, GREY);
        }
    }
    for (int x = 0; x <= LCD_WIDTH; x += DOTS_DIV) { // Vertical Line
        for (int y = 0; y <= LCD_HEIGHT; y += 2) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
    }
}

#define REDRAW 20 // msec

int lastMin = 65000, lastMax = 50000;
int minS= 65000, maxS = 50000;
int lastY = 65000;
int x = 0;




void loop()
{
    M5.update();
    delay(REDRAW);
    uint16_t ir, red;
    sensor.update();
    while(sensor.getRawValues(&ir, &red)){
      //Serial.println(ir);
      //Serial.print('\t');
      //Serial.println(red);
    };
    uint16_t y = red;
    if (y < minS) minS = y;
    if (maxS < y) maxS = y;
    if (x > 0) {
        y = (int)(LCD_HEIGHT - (float)(y - lastMin) / (lastMax - lastMin) * LCD_HEIGHT);
        M5.Lcd.drawLine(x - 1, lastY, x, y, WHITE);
        lastY = y;
    }
    Serial.print("minS: ");
    Serial.print(minS);
    Serial.print(" maxS: ");
    Serial.print(maxS);
    Serial.print(" y: ");
    Serial.println(y);
    if (++x > LCD_WIDTH) {
        x = 0;
        M5.Lcd.fillScreen(BLACK);
        DrawGrid();
        lastMin = minS - 20;
        lastMax = maxS + 20;
        minS = 65000;
        maxS = 50000;
        M5.Lcd.setCursor(0, 0);
        M5.Lcd.setTextSize(4);
        M5.Lcd.printf("BPM: %d", 1);
    }
}

www.youtube.com

これ、maxSとminSがうまくいくようになるまで2周期くらい待たないといけない。それまで心拍がなんかヤバいことになる。
でも取り敢えずたたき台はできたでしょといって一応満足する…。

BPMをとっていく

一番困ったのがBPMをとるアルゴリズムだった。
本当は移動平均とかとってスムーズにして、傾きや差を判定して「心拍の下がった瞬間」数をカウントしていくのがベストかなと個人的には思うのだけれど、さすがとちょっと気力がなかったので
一番アホみたいなアルゴリズムで「15回分のyの値を蓄積(p)して直前の15回分y総和(lastp)よりも低くなってたらカウントしていく」みたいななんともいえないやつで作ってみた。
でもこれだとなんかうまくでなかったのでよりちゃんと差があるときだけひっかかるようにpとlastpの差が3マス分になったときに変更してみた。
BPMは画面更新と同時に前回の画面のBPMが表示されるように、
「前回の画面中のpがlastpを下回った回数/6.5×60」にした。
ちなみに6.5というのは、データ取得が20msecごとで横幅320であることから、画面いっぱいにデータが集まるときに320*20/1000 = 6.5sec分のデータになるというところから出した値。

以下がコード。

/*
    Install MAX30100lib Library first.

    MAX30100_RawData.ino
*/
  
#include <M5Stack.h>
#include <Wire.h>
#include "MAX30100.h"

#define SAMPLING_RATE   MAX30100_SAMPRATE_100HZ
#define IR_LED_CURRENT  MAX30100_LED_CURR_50MA
#define RED_LED_CURRENT MAX30100_LED_CURR_27_1MA
// set HIGHRES_MODE to true only
// when setting PULSE_WIDTH to MAX30100_SPC_PW_1600US_16BITS
#define PULSE_WIDTH MAX30100_SPC_PW_1600US_16BITS
#define HIGHRES_MODE    true

// new a object
MAX30100 sensor;

void setup() {
    M5.begin();
    Serial.begin(115200);
    Serial.print("Initializing MAX30100..");
    if (!sensor.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }
    sensor.setMode(MAX30100_MODE_SPO2_HR);
    sensor.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT);
    sensor.setLedsPulseWidth(PULSE_WIDTH);
    sensor.setSamplingRate(SAMPLING_RATE);
    sensor.setHighresModeEnabled(HIGHRES_MODE);
}


const int LCD_WIDTH = 320;
const int LCD_HEIGHT = 240;
const int DOTS_DIV = 30;
#define GREY 0x7BEF

void DrawGrid() {
    for (int x = 0; x <= LCD_WIDTH; x += 2) { // Horizontal Line
        for (int y = 0; y <= LCD_HEIGHT; y += DOTS_DIV) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
        if (LCD_HEIGHT == 240) {
            M5.Lcd.drawPixel(x, LCD_HEIGHT - 1, GREY);
        }
    }
    for (int x = 0; x <= LCD_WIDTH; x += DOTS_DIV) { // Vertical Line
        for (int y = 0; y <= LCD_HEIGHT; y += 2) {
            M5.Lcd.drawPixel(x, y, GREY);
        }
    }
}

#define REDRAW 20 // msec

int lastMin = 65000, lastMax = 50000;
int minS= 65000, maxS = 50000;
int lastY = 65000;
int x = 0;
int count1 = 0;
int count2 = 0;
int p = 0;
int lastp = 0;
int bpm;



void loop()
{
    M5.update();
    delay(REDRAW);
    uint16_t ir, red;
    sensor.update();
    while(sensor.getRawValues(&ir, &red)){
      //Serial.println(ir);
      //Serial.print('\t');
      //Serial.println(red);
    };
    uint16_t y = red;
    if (y < minS) minS = y;
    if (maxS < y) maxS = y;
    if (x > 0) {
        y = (int)(LCD_HEIGHT - (float)(y - lastMin) / (lastMax - lastMin) * LCD_HEIGHT);
        M5.Lcd.drawLine(x - 1, lastY, x, y, WHITE);
        lastY = y;
        p += y;
        count2++;
        if(count2 == 15){
          p/=15;
          if(lastp > p+(LCD_HEIGHT/8)) ++count1;
          lastp = p;
          p = 0;
          count2 = 0;  
        }
    }
    Serial.print("minS: ");
    Serial.print(minS);
    Serial.print(" maxS: ");
    Serial.print(maxS);
    Serial.print(" y: ");
    Serial.println(y);
    if (++x > LCD_WIDTH) {
        x = 0;
        M5.Lcd.fillScreen(BLACK);
        DrawGrid();
        lastMin = minS - 20;
        lastMax = maxS + 20;
        minS = 65000;
        maxS = 50000;
        bpm = (float)count1/6.5 * 60;
        M5.Lcd.setCursor(0, 0);
        M5.Lcd.setTextSize(4);
        M5.Lcd.printf("BPM: %d", bpm);
        count1 = 0;
    }
}

もはやごりごり系の実装になってしまっているが知らない。
で、実際やってみる。
www.youtube.com


うーん、良いときとヤバい時があるね。
安定しないね…

今後の展望

・まず心拍プロットの方法を改善する(すぐ安定して~~~~~たのむ~~~~~~~)
初期値とかminSとmaxS更新規則とかをいじればいい気がする。
またはいっそ値を縦軸に入り切るように圧縮するとか。
シリアルモニタで値を追いながらここは調整かな…
BPM取得方法を考え直す
移動平均実装するのいやや~やれば一瞬なんだろうけど…
上でも言ったけど15×20msec = 300msecごと平均とって比べていくのだと心拍数に絶対上限あるし取りこぼすんだよね。ピークの場所ずれたら終わるし。
そもそも心拍一回分を何から判別するかも考えなおした方がいいかもしれない。
どのみち今のBPM計算、心拍クソ早い時に絶対機能しないからね?これが問題だと個人的には思っている。

色んな人に協力してもらって挙動を確認して改善していくしか…という感じだ。
うまくいけば授業でも使えるんじゃん?と目論んだりしているんだけど…


余談

あともう一個開発したいのが一問一答アプリで、日本語が出てくる→英語に直す系のやつを作りたい。
生物専門用語だけの。
自分が英語で生物説明しようとすると出てこないから…
論文読んでると英語→日本語変換はできるようになるんだけど逆が全然身に着かなくてだめ。
これもやっていくぞ…

まいばいお20 アサガオの話

植物系の話が少ないことを反省したので、今日はアサガオの話をします。

専門でもないのであんまり細かい話ができないけれど…

 

 

✿違う植物に見えるけど…

取り敢えずまずは下リンク先の花の画像を見てください。

変化朝顔研究会ホームページ_indexページです

 

ここに写っている花は、どれも違う種類に見えますが…

実はこれ、全て「アサガオ」です。

ここに写っているアサガオは、アサガオの中でも「変化朝顔」と呼ばれるものたちです。

 

アサガオは、奈良時代末期にその種が薬として遣唐使により持ち込まれたことで日本にやってきたらしい。

その後、江戸時代にアサガオの栽培ブームが起き、花を愛でられるようになりました。

栽培過程でこれらの変わった形のアサガオが突然変異によって生まれ、大事にされ、今も残っているようです。

 

しかし、実はこれら突然変異によってできた変化朝顔の多くは「不稔」といい、種子を作ることができません。

種子を作ることができないアサガオが、一体どうやって現代まで残ってきたのでしょうか・・・?

 

✿種子のできない変化朝顔を維持する方法

変化朝顔の一種に、おしべやめしべが全て花弁になり花弁の数が増える「八重咲き」というものがあります(先ほどの写真の左下がそれです)。

普通の一重咲きアサガオと、この八重咲きを例にとり、維持の理論を説明したいと思います。

 

生物の形質は遺伝子により決まっています。

一重咲きアサガオを作る遺伝子をA、八重咲きアサガオを作る遺伝子をaで表すとしましょう。

親個体の細胞は、ひとつの形質に対して2つの遺伝子をもち、その組み合わせで形質が決まります。

一重咲きの遺伝子を持っていると、八重咲きの遺伝子の効果を隠してしまうことが分かっていますから、親の形質と持つ遺伝子の組み合わせは以下のようになります。

一重咲き : AA または Aa

八重咲き : aaのみ

 

一重咲きであれば、種子を作ることができます。

種子を作る際、植物は卵細胞(動物でいう卵)と精細胞(動物でいう精子)という細胞を作り、それらを受精させます。

卵細胞と精細胞には、親がもつ遺伝子の半分が入るようになっているので、一重咲きであるAaの親が作るaをもつ卵細胞とaをもつ精細胞が受精すれば、aaの種子・・・つまり八重咲きの種子が得られます。

つまり、一重咲きのアサガオから八重咲きの種子がとれるのです!

 

つまり八重咲きのアサガオを維持したい場合は、八重咲きの遺伝子を持つ一重咲きのアサガオを維持することで実現できるということになります。
言われてみればできるなと思うけど、中々発想しないよ…と個人的には思う。

 

アサガオのつる、つぼみのねじれ、寿命 

他にも面白い話がいくつか。

まず、アサガオの蔓って、どっち向きに巻いているか皆さんご存知ですか?

 

アサガオの蔓は、実は必ず左向きに巻き込むように伸びていきます。

アサガオの茎は支柱に巻き付いていないとき、左回りに首を振り回す「回旋運動」を行い、結果支柱に触れると巻き付いていきます。

巻き付くときは蔓の中でも接触刺激がある側の伸長が抑制され、逆に接触刺激がない側の伸長が促進されるため、結果として巻くような形になります。

この分子的メカニズムはまだ未解明な部分が多く、実は謎深き領域です。

 

ではつぼみはどうでしょう?つぼみもねじれていますよね。

つぼみのねじれは右巻きです。

ねじれているのは、アサガオの花弁の部分が折りたたまれているからです。

アサガオの花には、白く分厚い星形の部分が存在しています。

この部分は「曜」といって、まるで傘の骨のような役割をしています。

曜は開花する時一番に、かつ急激に成長します。

曜の内側の成長速度が外側の成長速度よりも速いため、曜は外側に反り返りながら成長します。

そのとき花弁を構成する細胞には水が入り込み、細胞容積が急激に増加するため花弁が広がることができるようになります。

一説によると、花弁の細胞内ではデンプン粒が急激に分解され糖になり、細胞内の濃度が濃くなる結果水の移動が生じるようです。

閉じる時には逆に細胞から水分が抜けることで、一個一個の細胞が小さくなり萎れます。

 

あと、アサガオの寿命について。

アサガオって一日しか咲かない…って言われて、そうだなぁって思う人どれくらいいるんだろう…

とにかく一日しか咲かないんですよ。

 

でも

 

アサガオの寿命を延ばした実験がある」んです!

これは2014年に発表された研究で、花の寿命を調節する遺伝子EPHEMERAL1の話です。

ephemeralとは英語で「儚い」という意味で、この遺伝子の働きを抑えるとアサガオの寿命は2日間に延びるんです・・・!

 

 

 

私はあんまり生物個体そのものに興味がない(生物の普遍性の方に興味がある気がする)ので、こういう知識は中々持っておらず

知ると驚くことが多くあります。

皆にとっては当たり前なのかもしれませんが…

こういう感じの情報(ゆるめなやつ)も今後は発信していきたいですね~